lean4-htt/tests/elab_fail/linterUnusedVariables.lean
Wojciech Różowski 3fc99eef10
feat: add instance validation checks in addInstance (#13389)
This PR adds two validation checks to `addInstance` that provide early
feedback for common mistakes in instance declarations:

1. **Non-class instance check**: errors when an instance target type is
not a type class. This catches the common mistake of writing `instance`
for a plain structure. Previously handled by the `nonClassInstance`
linter in Batteries (`Batteries.Tactic.Lint.TypeClass`), this is now
checked directly at declaration time.

2. **Impossible argument check**: errors when an instance has arguments
that cannot be inferred by instance synthesis. Specifically, it flags
arguments that are not instance-implicit and do not appear in any
subsequent instance-implicit argument or in the return type. Previously
such instances would be silently accepted but could never be
synthesised.

Supersedes #13237 and #13333.
2026-04-16 17:48:16 +00:00

293 lines
6.4 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Lean
set_option linter.missingDocs false
set_option linter.all true
set_option linter.unusedSimpArgs false
set_option linter.redundantVisibility false
def explicitlyUsedVariable (x : Nat) : Nat :=
x
theorem implicitlyUsedVariable : P ∧ Q → Q := by
intro HPQ
have HQ : Q := by exact And.right HPQ
assumption
axiom axiomVariable (x : Prop) : True
def unusedVariables (x : Nat) : Nat :=
let y := 5
3
def usedAndUnusedVariables : Nat :=
let x : Nat :=
let x := 5
3
x
def letRecVariable : Nat :=
let rec x := 5
3
def whereVariable : Nat :=
3
where
x := 5 -- x is globally available via `whereVariable.x`
def unusedWhereArgument : Nat :=
f 2
where
f (x : Nat) := 3
def whereFunction : Nat :=
2
where
f (x : Nat) := 3
def unusedFunctionArgument : Nat :=
(fun x => 3) (x := 2)
def unusedTypedFunctionArgument : Nat :=
(fun (x : Nat) => 3) 2
def pattern (x y : Option Nat) : Nat :=
match x with
| some z =>
match y with
| some z => 1
| none => 0
| none => 0
def patternLet (x : Option Nat) : Nat :=
if let some y := x then
0
else
1
def patternMatches (x : Option Nat) : Nat :=
if x matches some y then
0
else
1
def implicitVariables {α : Type} [inst : ToString α] : Nat := 4
def autoImplicitVariable [Inhabited α] := 5
def unusedArrow : (x : Nat) → Nat := fun x => x
def mutVariable (x : Nat) : Nat := Id.run <| do
let mut y := 5
if x == 5 then
y := 3
y
def mutVariableDo (list : List Nat) : Nat := Id.run <| do
let mut sum := 0
for elem in list do
sum := sum + elem
return sum
def mutVariableDo2 (list : List Nat) : Nat := Id.run <| do
let mut sum := 0
for _ in list do
sum := sum.add 1
return sum
def unusedVariablesPattern (_x : Nat) : Nat :=
let _y := 5
3
set_option linter.unusedVariables false in
def nolintUnusedVariables (x : Nat) : Nat :=
let y := 5
3
set_option linter.all false in
def nolintAll (x : Nat) : Nat :=
let y := 5
3
set_option linter.all false in
set_option linter.unusedVariables true in
def lintUnusedVariables (x : Nat) : Nat :=
let y := 5
3
set_option linter.unusedVariables.funArgs false in
def nolintFunArgs (w : Nat) : Nat :=
let a := 5
let f (x : Nat) := 3
let g := fun (y : Nat) => 3
f <| g <| h <| 2
where
h (z : Nat) := 3
set_option linter.unusedVariables.patternVars false in
def nolintPatternVars (x : Option (Option Nat)) : Nat :=
match x with
| some (some y) => (fun z => 1) 2
| _ => 0
set_option linter.unusedVariables.analyzeTactics true in
set_option linter.unusedVariables.patternVars false in
theorem nolintPatternVarsInduction (n : Nat) : True := by
induction n with
| zero => exact True.intro
| succ m =>
have h : True := by simp
exact True.intro
inductive Foo (α : Type)
| foo (x : Nat) (y : Nat)
structure Bar (α : Type) where
bar (x : Nat) : Nat
bar' (x : Nat) : Nat := 3
class Baz (α : Type) where
baz (x : Nat) : Nat
baz' (x : Nat) : Nat :=
let y := 5
3
@[reducible] def instBaz (α β : Type) : Baz α where
baz (x : Nat) := 5
structure State where
fieldA : Nat
fieldB : Nat
abbrev M := StateT State Id
def modifyState : M Unit := do
let s ← get
modify fun s => { s with fieldA := s.fieldA + 1 }
def modifyState' : M Unit := do
modify fun s => { s with fieldA := 1}
def modifyStateUnnecessaryWith : M Unit := do
modify fun s => { s with fieldA := 1, fieldB := 2 }
def universeParam.{u} (T : Type u) (t : T) : T := t
open Lean in
initialize tc : Unit ← registerTraceClass `Baz
register_option opt : Nat := {
defValue := 3
descr := "test option"
}
opaque foo (x : Nat) : Nat
opaque foo' (x : Nat) : Nat :=
let y := 5
3
section
variable (bar)
variable (bar' : (x : Nat) → Nat)
variable {α β} [inst : ToString α]
end
@[specialize]
def specializeDef (x : Nat) : Nat := 3
@[implemented_by specializeDef]
def implementedByDef (x : Nat) : Nat :=
let y := 3
5
@[extern "test"]
def externDef (x : Nat) : Nat :=
let y := 3
5
@[extern "test"]
opaque externConst (x : Nat) : Nat :=
let y := 3
5
section
variable {α : Type}
macro "useArg " name:declId arg:ident : command => `(def $name ($arg : α) : α := $arg)
useArg usedMacroVariable a
macro (name := doNotUse) "doNotUseArg " name:declId arg:ident : command =>
`(def $name ($arg : α) : Nat := 3)
doNotUseArg unusedMacroVariable b
@[unused_variables_ignore_fn]
def ignoreDoNotUse : Lean.Linter.IgnoreFunction := fun _ stack _ => stack.matches [``doNotUse]
doNotUseArg unusedMacroVariable2 b
end
macro "ignoreArg " id:declId sig:declSig : command => `(opaque $id $sig)
ignoreArg ignoredMacroVariable (x : UInt32) : UInt32
theorem not_eq_zero_of_lt (h : b < a) : a ≠ 0 := by -- *not* unused
cases a
exact absurd h (Nat.not_lt_zero _)
apply Nat.noConfusion
-- should not be reported either
example (a : Nat) : Nat := _
example (a : Nat) : Nat := sorry
example (a : sorry) : Nat := 0
example (a : Nat) : Nat := by
theorem async (a : Nat) : Nat := by skip
theorem Fin.eqq_of_val_eq {n : Nat} : ∀ {x y : Fin n}, x.val = y.val → x = y
| ⟨_, _⟩, _, rfl => rfl
def Nat.discriminate (n : Nat) (H1 : n = 0 → α) (H2 : ∀ m, n = succ m → α) : α :=
match n with
| 0 => H1 rfl
| succ m => H2 m rfl
/-! These are *not* linted against anymore as they are parameters used in the eventual body term. -/
example [ord : Ord β] (f : α → β) (x y : α) : Ordering := compare (f x) (f y)
example {α β} [ord : Ord β] (f : α → β) (x y : α) : Ordering := compare (f x) (f y)
example {h : Decidable True} (t e : α) : ite True t e = t := if_pos trivial
inductive A where
| intro : Nat → A
def A.out : A → Nat
| .intro n => n
/-! `h` is used indirectly via an alias introduced by `match` that is used only via the mvar ctx -/
theorem problematicAlias (n : A) (i : Nat) (h : i ≤ n.out) : i ≤ n.out :=
match n with
| .intro _ => by assumption
/-!
The wildcard pattern introduces a copy of `x` that should not be linted as it is in an
inaccessible annotation.
-/
example : (x = y) → y = x
| .refl _ => .refl _
/-! We do lint parameters by default (`analyzeTactics false`) even when they have lexical uses -/
theorem lexicalTacticUse (p : α → Prop) (ha : p a) (hb : p b) : p b := by
simp [ha, hb]
/-!
... however, `analyzeTactics true` consistently takes lexical uses for all variables into account
-/
set_option linter.unusedVariables.analyzeTactics true in
theorem lexicalTacticUse' (p : α → Prop) (ha : p a) (hb : p b) : p b := by
simp [ha, hb]