This PR scans the environment for viable replacements for a dotted identifier (like `.zero`) and suggests concrete alternatives as replacements. ## Example ``` #example .zero ``` Error message: ``` Invalid dotted identifier notation: The expected type of `.cons` could not be determined ``` Additional hint added by this PR: ``` Hint: Using one of these would be unambiguous: [apply] `BitVec.cons` [apply] `List.cons` [apply] `List.Lex.cons` [apply] `List.Pairwise.cons` [apply] `List.Perm.cons` [apply] `List.Sublist.cons` [apply] `List.Lex.below.cons` [apply] `List.Pairwise.below.cons` [apply] `List.Perm.below.cons` [apply] `List.Sublist.below.cons` [apply] `Lean.Grind.AC.Seq.cons` ``` ## Additional changes This PR also brings several related error message descriptions and code actions more in line with each other, changing several "Suggested replacement: " code actions to the more common "Change to " wording, and sorts suggestions obtained from searching the context by the default sort for Names (which prefers names with fewer segments).
169 lines
5.1 KiB
Text
169 lines
5.1 KiB
Text
/-!
|
||
Testing for linter.constructorNameAsVariable
|
||
|
||
This linter checks warns when a bound variable's name is the name of a constructor of the variable's
|
||
type, which probably indicates a namespace mistake, but can be otherwise hard to find.
|
||
|
||
The linter is designed to interact well with the suggestions provided when a non-constructor is used
|
||
where a constructor would be expected in a pattern, so that users who don't know Lean namespaces
|
||
will be guided to the right qualified names. Thus, both are tested together here.
|
||
-/
|
||
set_option linter.unusedVariables false
|
||
|
||
inductive A where
|
||
| x | y
|
||
|
||
-- Test that the linter works even in the presence of errors (making it useful for confused new
|
||
-- users)
|
||
|
||
/--
|
||
warning: Local variable 'x' resembles constructor 'A.x' - write '.x' (with a dot) or 'A.x' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
-/
|
||
#guard_msgs(drop error, warning) in
|
||
def f : A → Unit
|
||
| x => _
|
||
|
||
-- Show that the linter also works when there are no errors
|
||
/--
|
||
warning: Local variable 'x' resembles constructor 'A.x' - write '.x' (with a dot) or 'A.x' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
-/
|
||
#guard_msgs(warning) in
|
||
def g : A → Unit
|
||
| x => ()
|
||
|
||
-- Check that turning it off works
|
||
#guard_msgs in
|
||
set_option linter.constructorNameAsVariable false in
|
||
def g' : A → Unit
|
||
| x => ()
|
||
|
||
-- Avoid false positives
|
||
#guard_msgs in
|
||
def h : A → Unit
|
||
| z => ()
|
||
|
||
-- Check that it works for let-bindings
|
||
/--
|
||
warning: Local variable 'x' resembles constructor 'A.x' - write '.x' (with a dot) or 'A.x' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
---
|
||
warning: Local variable 'y' resembles constructor 'A.y' - write '.y' (with a dot) or 'A.y' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
-/
|
||
#guard_msgs in
|
||
def i (a : A × A) : Unit :=
|
||
let (x, y) := a
|
||
()
|
||
|
||
/--
|
||
warning: Local variable 'x' resembles constructor 'A.x' - write '.x' (with a dot) or 'A.x' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
-/
|
||
#guard_msgs in
|
||
def i' : Unit :=
|
||
let x : A := .x
|
||
()
|
||
|
||
-- Check that it works in tactic proofs
|
||
/--
|
||
warning: Local variable 'x' resembles constructor 'A.x' - write '.x' (with a dot) or 'A.x' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
-/
|
||
#guard_msgs in
|
||
theorem j (a : A ⊕ A) : True := by
|
||
cases a with
|
||
| inl x => trivial
|
||
| inr z => trivial
|
||
|
||
-- Top-level names do not trigger the lint
|
||
#guard_msgs in
|
||
def x : A := A.x
|
||
|
||
/-! Test the interaction with the invalid match pattern error messages -/
|
||
|
||
inductive MyProd where
|
||
| construct : Nat → Nat → MyProd
|
||
|
||
/--
|
||
error: Invalid pattern: Expected a constructor or constant marked with `[match_pattern]`
|
||
|
||
Hint: Using `MyProd.construct` would be valid:
|
||
[apply] `MyProd.construct`
|
||
-/
|
||
#guard_msgs in
|
||
def ctorSuggestion1 (pair : MyProd) : Nat :=
|
||
match pair with
|
||
| construct x y => y
|
||
|
||
-- This test is a realistic situation if a user doesn't know how Lean namespaces work
|
||
/--
|
||
error: Invalid pattern: Expected a constructor or constant marked with `[match_pattern]`
|
||
|
||
Hint: Using one of these would be valid:
|
||
[apply] `List.Sublist.cons`
|
||
[apply] `Lean.Grind.AC.Seq.cons`
|
||
[apply] `List.Lex.cons`
|
||
[apply] `List.Perm.below.cons`
|
||
[apply] `List.Lex.below.cons`
|
||
[apply] `List.Pairwise.below.cons`
|
||
[apply] `List.cons`
|
||
[apply] `List.Sublist.below.cons`
|
||
[apply] `List.Perm.cons`
|
||
[apply] `List.Pairwise.cons`
|
||
---
|
||
warning: Local variable 'nil' resembles constructor 'List.nil' - write '.nil' (with a dot) or 'List.nil' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
-/
|
||
#guard_msgs in
|
||
def ctorSuggestion2 (list : List α) : Nat :=
|
||
match list with
|
||
| nil => 0
|
||
| cons x xs => 1 + ctorSuggestion2 xs
|
||
|
||
-- Adding another `cons` also adds a suggestion
|
||
inductive StringList : Type where
|
||
| nil
|
||
| cons (s : String) (ss : StringList)
|
||
|
||
/--
|
||
error: Invalid pattern: Expected a constructor or constant marked with `[match_pattern]`
|
||
|
||
Hint: Using one of these would be valid:
|
||
[apply] `List.Sublist.cons`
|
||
[apply] `Lean.Grind.AC.Seq.cons`
|
||
[apply] `List.Lex.cons`
|
||
[apply] `List.Perm.below.cons`
|
||
[apply] `List.Lex.below.cons`
|
||
[apply] `List.Pairwise.below.cons`
|
||
[apply] `List.cons`
|
||
[apply] `List.Sublist.below.cons`
|
||
[apply] `List.Perm.cons`
|
||
[apply] `List.Pairwise.cons`
|
||
[apply] `StringList.cons`
|
||
---
|
||
warning: Local variable 'nil' resembles constructor 'List.nil' - write '.nil' (with a dot) or 'List.nil' to use the constructor.
|
||
|
||
Note: This linter can be disabled with `set_option linter.constructorNameAsVariable false`
|
||
-/
|
||
#guard_msgs in
|
||
def ctorSuggestion3 (list : List α) : Nat :=
|
||
match list with
|
||
| nil => 0
|
||
| cons x xs => 1 + ctorSuggestion2 xs
|
||
|
||
-- There isn't always a suggestion to provide
|
||
|
||
/-- error: Invalid pattern: Expected a constructor or constant marked with `[match_pattern]` -/
|
||
#guard_msgs in
|
||
def ctorNoSuggestion (x : α) :=
|
||
match x with
|
||
| notAConstructor a b c => 42
|