diff --git a/doc/examples/Certora2022/ex8.lean b/doc/examples/Certora2022/ex8.lean index 0e30270ac4..b29cff7e6b 100644 --- a/doc/examples/Certora2022/ex8.lean +++ b/doc/examples/Certora2022/ex8.lean @@ -4,16 +4,16 @@ def ack : Nat → Nat → Nat | 0, y => y+1 | x+1, 0 => ack x 1 | x+1, y+1 => ack x (ack (x+1) y) -termination_by ack x y => (x, y) +termination_by x y => (x, y) def sum (a : Array Int) : Int := let rec go (i : Nat) := - if i < a.size then + if _ : i < a.size then a[i] + go (i+1) else 0 + termination_by a.size - i go 0 -termination_by go i => a.size - i set_option pp.proofs true #print sum.go diff --git a/doc/examples/ICERM2022/ctor.lean b/doc/examples/ICERM2022/ctor.lean index fa7fdd8b8d..fc59b5b4de 100644 --- a/doc/examples/ICERM2022/ctor.lean +++ b/doc/examples/ICERM2022/ctor.lean @@ -4,43 +4,42 @@ open Lean Meta def ctor (mvarId : MVarId) (idx : Nat) : MetaM (List MVarId) := do /- Set `MetaM` context using `mvarId` -/ - withMVarContext mvarId do + mvarId.withContext do /- Fail if the metavariable is already assigned. -/ - checkNotAssigned mvarId `ctor + mvarId.checkNotAssigned `ctor /- Retrieve the target type, instantiateMVars, and use `whnf`. -/ - let target ← getMVarType' mvarId + let target ← mvarId.getType' let .const declName us := target.getAppFn | throwTacticEx `ctor mvarId "target is not an inductive datatype" let .inductInfo { ctors, .. } ← getConstInfo declName | throwTacticEx `ctor mvarId "target is not an inductive datatype" if idx = 0 then - throwTacticEx `ctor mvarId "invalid index, it must be > 0" + throwTacticEx `ctor mvarId "invalid index, it must be > 0" else if h : idx - 1 < ctors.length then - apply mvarId (.const ctors[idx - 1] us) + mvarId.apply (.const ctors[idx - 1] us) else - throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors" + throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors" open Elab Tactic -elab "ctor" idx:num : tactic => +elab "ctor" idx:num : tactic => liftMetaTactic (ctor · idx.getNat) -example (p : Prop) : p := by +example (p : Prop) : p := by ctor 1 -- Error -example (h : q) : p ∨ q := by +example (h : q) : p ∨ q := by ctor 0 -- Error exact h -example (h : q) : p ∨ q := by +example (h : q) : p ∨ q := by ctor 3 -- Error exact h -example (h : q) : p ∨ q := by +example (h : q) : p ∨ q := by ctor 2 exact h -example (h : q) : p ∨ q := by +example (h : q) : p ∨ q := by ctor 1 - exact h -- Error - + exact h -- Error diff --git a/doc/examples/ICERM2022/meta.lean b/doc/examples/ICERM2022/meta.lean index b7082614fa..2155267892 100644 --- a/doc/examples/ICERM2022/meta.lean +++ b/doc/examples/ICERM2022/meta.lean @@ -5,15 +5,15 @@ open Lean Meta def ex1 (declName : Name) : MetaM Unit := do let info ← getConstInfo declName IO.println s!"{declName} : {← ppExpr info.type}" - if let some val := info.value? then + if let some val := info.value? then IO.println s!"{declName} : {← ppExpr val}" - + #eval ex1 ``Nat def ex2 (declName : Name) : MetaM Unit := do let info ← getConstInfo declName trace[Meta.debug] "{declName} : {info.type}" - if let some val := info.value? then + if let some val := info.value? then trace[Meta.debug] "{declName} : {val}" #eval ex2 ``Add.add @@ -30,9 +30,9 @@ def ex3 (declName : Name) : MetaM Unit := do trace[Meta.debug] "{x} : {← inferType x}" def myMin [LT α] [DecidableRel (α := α) (·<·)] (a b : α) : α := - if a < b then + if a < b then a - else + else b set_option trace.Meta.debug true in @@ -40,7 +40,7 @@ set_option trace.Meta.debug true in def ex4 : MetaM Unit := do let nat := mkConst ``Nat - withLocalDeclD `a nat fun a => + withLocalDeclD `a nat fun a => withLocalDeclD `b nat fun b => do let e ← mkAppM ``HAdd.hAdd #[a, b] trace[Meta.debug] "{e} : {← inferType e}" @@ -66,15 +66,17 @@ open Elab Term def ex5 : TermElabM Unit := do let nat := Lean.mkConst ``Nat - withLocalDeclD `a nat fun a => do + withLocalDeclD `a nat fun a => do withLocalDeclD `b nat fun b => do let ab ← mkAppM ``HAdd.hAdd #[a, b] - let stx ← `(fun x => if x < 10 then $(← exprToSyntax ab) + x else x + $(← exprToSyntax a)) + let abStx ← exprToSyntax ab + let aStx ← exprToSyntax a + let stx ← `(fun x => if x < 10 then $abStx + x else x + $aStx) let e ← elabTerm stx none trace[Meta.debug] "{e} : {← inferType e}" let e := mkApp e (mkNatLit 5) let e ← whnf e trace[Meta.debug] "{e}" - + set_option trace.Meta.debug true in #eval ex5 diff --git a/doc/examples/NFM2022/nfm8.lean b/doc/examples/NFM2022/nfm8.lean index 0e30270ac4..b29cff7e6b 100644 --- a/doc/examples/NFM2022/nfm8.lean +++ b/doc/examples/NFM2022/nfm8.lean @@ -4,16 +4,16 @@ def ack : Nat → Nat → Nat | 0, y => y+1 | x+1, 0 => ack x 1 | x+1, y+1 => ack x (ack (x+1) y) -termination_by ack x y => (x, y) +termination_by x y => (x, y) def sum (a : Array Int) : Int := let rec go (i : Nat) := - if i < a.size then + if _ : i < a.size then a[i] + go (i+1) else 0 + termination_by a.size - i go 0 -termination_by go i => a.size - i set_option pp.proofs true #print sum.go diff --git a/src/Init/Notation.lean b/src/Init/Notation.lean index 353cdc8459..e4f9f8cd76 100644 --- a/src/Init/Notation.lean +++ b/src/Init/Notation.lean @@ -492,9 +492,12 @@ The attribute `@[deprecated]` on a declaration indicates that the declaration is discouraged for use in new code, and/or should be migrated away from in existing code. It may be removed in a future version of the library. -`@[deprecated myBetterDef]` means that `myBetterDef` is the suggested replacement. +* `@[deprecated myBetterDef]` means that `myBetterDef` is the suggested replacement. +* `@[deprecated myBetterDef "use myBetterDef instead"]` allows customizing the deprecation message. +* `@[deprecated (since := "2024-04-21")]` records when the deprecation was first applied. -/ -syntax (name := deprecated) "deprecated" (ppSpace ident)? : attr +syntax (name := deprecated) "deprecated" (ppSpace ident)? (ppSpace str)? + (" (" &"since" " := " str ")")? : attr /-- The `@[coe]` attribute on a function (which should also appear in a diff --git a/src/Lean/Attributes.lean b/src/Lean/Attributes.lean index 85a7cc5d12..ffe226a381 100644 --- a/src/Lean/Attributes.lean +++ b/src/Lean/Attributes.lean @@ -183,7 +183,6 @@ structure ParametricAttribute (α : Type) where deriving Inhabited structure ParametricAttributeImpl (α : Type) extends AttributeImplCore where - /-- This is used as the target for go-to-definition queries for simple attributes -/ getParam : Name → Syntax → AttrM α afterSet : Name → α → AttrM Unit := fun _ _ _ => pure () afterImport : Array (Array (Name × α)) → ImportM Unit := fun _ => pure () diff --git a/src/Lean/Linter/Deprecated.lean b/src/Lean/Linter/Deprecated.lean index 206c3853da..0e64288885 100644 --- a/src/Lean/Linter/Deprecated.lean +++ b/src/Lean/Linter/Deprecated.lean @@ -15,17 +15,23 @@ register_builtin_option linter.deprecated : Bool := { descr := "if true, generate deprecation warnings" } -builtin_initialize deprecatedAttr : ParametricAttribute (Option Name) ← +structure DeprecationEntry where + newName? : Option Name := none + text? : Option String := none + since? : Option String := none + deriving Inhabited + +builtin_initialize deprecatedAttr : ParametricAttribute DeprecationEntry ← registerParametricAttribute { name := `deprecated descr := "mark declaration as deprecated", getParam := fun _ stx => do - match stx with - | `(attr| deprecated $[$id?]?) => - let some id := id? | return none - let declNameNew ← Elab.realizeGlobalConstNoOverloadWithInfo id - return some declNameNew - | _ => throwError "invalid `[deprecated]` attribute" + let `(attr| deprecated $[$id?]? $[$text?]? $[(since := $since?)]?) := stx + | throwError "invalid `[deprecated]` attribute" + let newName? ← id?.mapM Elab.realizeGlobalConstNoOverloadWithInfo + let text? := text?.map TSyntax.getString + let since? := since?.map TSyntax.getString + return { newName?, text?, since? } } def isDeprecated (env : Environment) (declName : Name) : Bool := @@ -34,12 +40,13 @@ def isDeprecated (env : Environment) (declName : Name) : Bool := def _root_.Lean.MessageData.isDeprecationWarning (msg : MessageData) : Bool := msg.hasTag (· == ``deprecatedAttr) -def getDeprecatedNewName (env : Environment) (declName : Name) : Option Name := - (deprecatedAttr.getParam? env declName).getD none +def getDeprecatedNewName (env : Environment) (declName : Name) : Option Name := do + (← deprecatedAttr.getParam? env declName).newName? def checkDeprecated [Monad m] [MonadEnv m] [MonadLog m] [AddMessageContext m] [MonadOptions m] (declName : Name) : m Unit := do if getLinterValue linter.deprecated (← getOptions) then - match deprecatedAttr.getParam? (← getEnv) declName with - | none => pure () - | some none => logWarning <| .tagged ``deprecatedAttr m!"`{declName}` has been deprecated" - | some (some newName) => logWarning <| .tagged ``deprecatedAttr m!"`{declName}` has been deprecated, use `{newName}` instead" + let some attr := deprecatedAttr.getParam? (← getEnv) declName | pure () + logWarning <| .tagged ``deprecatedAttr <| attr.text?.getD <| + match attr.newName? with + | none => s!"`{declName}` has been deprecated" + | some newName => s!"`{declName}` has been deprecated, use `{newName}` instead" diff --git a/tests/lean/deprecated.lean b/tests/lean/deprecated.lean index ba76e764d0..d92bde5d05 100644 --- a/tests/lean/deprecated.lean +++ b/tests/lean/deprecated.lean @@ -17,14 +17,14 @@ def f1 (x : Nat) := x + 1 def Foo.g1 := 10 -@[deprecated Foo.g1] +@[deprecated Foo.g1 (since := "2022-07-24")] def f2 (x : Nat) := x + 1 @[deprecated g1] def f3 (x : Nat) := x + 1 open Foo -@[deprecated g1] +@[deprecated g1 "use g1 instead, f4 is not a good name"] def f4 (x : Nat) := x + 1 #eval f2 0 + 1 diff --git a/tests/lean/deprecated.lean.expected.out b/tests/lean/deprecated.lean.expected.out index 40a428f3d4..f778143a2d 100644 --- a/tests/lean/deprecated.lean.expected.out +++ b/tests/lean/deprecated.lean.expected.out @@ -7,5 +7,5 @@ deprecated.lean:23:13-23:15: error: unknown constant 'g1' deprecated.lean:30:6-30:8: warning: `f2` has been deprecated, use `Foo.g1` instead 2 2 -deprecated.lean:33:6-33:8: warning: `f4` has been deprecated, use `Foo.g1` instead +deprecated.lean:33:6-33:8: warning: use g1 instead, f4 is not a good name 2