This PR verifies the `String.dropWhile` and `String.takeWhile` functions. It also includes a refactor of the `PatternModel` class so that the `not_matches_empty` condition is moved into a separate typeclass `StrictPatternModel`. This allows string patterns to implement `LawfulForwardPatternModel` unconditionally, which means that more of the general theory about patterns directly applies to string patterns without having to do a case distinction for empty strings. This PR also includes a study of the `PatternModel` machinery given to slices `s` and `t` such that `s.copy = t.copy`. From these results, we deduce statements like `s.copy.startsWith pat = s.startsWith pat` (which is far from obvious!).
198 lines
6.4 KiB
Text
198 lines
6.4 KiB
Text
import Lean.Elab.Command
|
||
|
||
/-!
|
||
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
|
||
|
||
-- The test harness passes `-Dlinter.all=false`, which would disable this linter.
|
||
-- We unset `linter.all` so that the linter uses its default value (`true`).
|
||
-- This is a minimal inline version of Mathlib's `unset_option`.
|
||
open Lean Elab Command in
|
||
elab "unset_option " opt:ident : command => do
|
||
modifyScope fun scope => { scope with opts := scope.opts.erase opt.getId.eraseMacroScopes }
|
||
|
||
unset_option linter.all
|
||
|
||
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 => ()
|
||
|
||
-- Check that turning it off via `linter.all` works
|
||
#guard_msgs in
|
||
set_option linter.all 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.cons`
|
||
[apply] `Lean.AssocList.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`
|
||
[apply] `Std.DHashMap.Internal.AssocList.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestMatchAtChain.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestRevMatchAtChain.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestMatchAtChain.below.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestRevMatchAtChain.below.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.cons`
|
||
[apply] `StringList.cons`
|
||
[apply] `Lean.AssocList.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`
|
||
[apply] `Std.DHashMap.Internal.AssocList.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestMatchAtChain.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestRevMatchAtChain.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestMatchAtChain.below.cons`
|
||
[apply] `String.Slice.Pattern.Model.IsLongestRevMatchAtChain.below.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
|