feat: @[suggest_for] annotations for prompting easy-to-miss names (#11554)
This PR adds `@[suggest_for]` annotations to Lean, allowing lean to provide corrections for `.every` or `.some` methods in place of `.all` or `.any` methods for most default-imported types (arrays, lists, strings, substrings, and subarrays, and vectors). Due to the need for stage0 updates for new annotations, the `suggest_for` annotation itself was introduced in previous PRs: #11367, #11529, and #11590. ## Example ``` example := "abc".every (! ·.isWhitespace) ``` Error message: ``` Invalid field `every`: The environment does not contain `String.every`, so it is not possible to project the field `every` from an expression "abc" of type `String` Hint: Perhaps you meant `String.all` in place of `String.every`: .e̵v̵e̵r̵y̵a̲l̲l̲ ``` (the hint is added by this PR) ## Additional changes Adds suggestions that are not currently active but that can be used to generate autocompletion suggestions in the reference manual: - `Either` -> `Except` and `Sum` - `Exception` -> `Except` - `ℕ` -> `Nat` - `Nullable` -> `Option` - `Maybe` -> `Option` - `Optional` -> `Option` - `Result` -> `Except`
This commit is contained in:
parent
2e4b079e73
commit
f88e503f3d
11 changed files with 141 additions and 11 deletions
|
|
@ -201,6 +201,7 @@ An element of `α ⊕ β` is either an `a : α` wrapped in `Sum.inl` or a `b :
|
|||
indication of which of the two types was chosen. The union of a singleton set with itself contains
|
||||
one element, while `Unit ⊕ Unit` contains distinct values `inl ()` and `inr ()`.
|
||||
-/
|
||||
@[suggest_for Either]
|
||||
inductive Sum (α : Type u) (β : Type v) where
|
||||
/-- Left injection into the sum type `α ⊕ β`. -/
|
||||
| inl (val : α) : Sum α β
|
||||
|
|
|
|||
|
|
@ -1348,7 +1348,7 @@ Examples:
|
|||
* `#[2, 4, 5, 6].any (· % 2 = 0) = true`
|
||||
* `#[2, 4, 5, 6].any (· % 2 = 1) = true`
|
||||
-/
|
||||
@[inline, expose]
|
||||
@[inline, expose, suggest_for Array.some]
|
||||
def any (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool :=
|
||||
Id.run <| as.anyM (pure <| p ·) start stop
|
||||
|
||||
|
|
@ -1366,7 +1366,7 @@ Examples:
|
|||
* `#[2, 4, 6].all (· % 2 = 0) = true`
|
||||
* `#[2, 4, 5, 6].all (· % 2 = 0) = false`
|
||||
-/
|
||||
@[inline]
|
||||
@[inline, suggest_for Array.every]
|
||||
def all (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool :=
|
||||
Id.run <| as.allM (pure <| p ·) start stop
|
||||
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ Checks whether any of the elements in a subarray satisfy a Boolean predicate.
|
|||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that satisfies the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
@[inline, suggest_for Subarray.some]
|
||||
def any {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.anyM (pure <| p ·)
|
||||
|
||||
|
|
@ -290,7 +290,7 @@ Checks whether all of the elements in a subarray satisfy a Boolean predicate.
|
|||
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
|
||||
an element that does not satisfy the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
@[inline, suggest_for Subarray.every]
|
||||
def all {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.allM (pure <| p ·)
|
||||
|
||||
|
|
|
|||
|
|
@ -1847,6 +1847,7 @@ Examples:
|
|||
* `[2, 4, 5, 6].any (· % 2 = 0) = true`
|
||||
* `[2, 4, 5, 6].any (· % 2 = 1) = true`
|
||||
-/
|
||||
@[suggest_for List.some]
|
||||
def any : (l : List α) → (p : α → Bool) → Bool
|
||||
| [], _ => false
|
||||
| h :: t, p => p h || any t p
|
||||
|
|
@ -1866,6 +1867,7 @@ Examples:
|
|||
* `[2, 4, 6].all (· % 2 = 0) = true`
|
||||
* `[2, 4, 5, 6].all (· % 2 = 0) = false`
|
||||
-/
|
||||
@[suggest_for List.every]
|
||||
def all : List α → (α → Bool) → Bool
|
||||
| [], _ => true
|
||||
| h :: t, p => p h && all t p
|
||||
|
|
|
|||
|
|
@ -324,7 +324,8 @@ Examples:
|
|||
* {lean}`"tea".contains (fun (c : Char) => c == 'X') = false`
|
||||
* {lean}`"coffee tea water".contains "tea" = true`
|
||||
-/
|
||||
@[inline] def contains (s : String) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
|
||||
@[inline, suggest_for String.some]
|
||||
def contains (s : String) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
|
||||
s.toSlice.contains pat
|
||||
|
||||
@[export lean_string_contains]
|
||||
|
|
@ -352,7 +353,7 @@ Examples:
|
|||
* {lean}`"aaaaaa".all "aa" = true`
|
||||
* {lean}`"aaaaaaa".all "aa" = false`
|
||||
-/
|
||||
@[inline] def all (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
|
||||
@[inline, suggest_for String.every] def all (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
|
||||
s.toSlice.all pat
|
||||
|
||||
/--
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ Examples:
|
|||
* {lean}`"tea".toSlice.contains (fun (c : Char) => c == 'X') = false`
|
||||
* {lean}`"coffee tea water".toSlice.contains "tea" = true`
|
||||
-/
|
||||
@[specialize pat]
|
||||
@[specialize pat, suggest_for String.Slice.some]
|
||||
def contains (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] : Bool :=
|
||||
let searcher := ToForwardSearcher.toSearcher pat s
|
||||
searcher.any (· matches .matched ..)
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ Checks whether the Boolean predicate `p` returns `true` for any character in a s
|
|||
|
||||
Short-circuits at the first character for which `p` returns `true`.
|
||||
-/
|
||||
@[inline] def any (s : Substring.Raw) (p : Char → Bool) : Bool :=
|
||||
@[inline, suggest_for Substring.Raw.some] def any (s : Substring.Raw) (p : Char → Bool) : Bool :=
|
||||
s.toSlice?.get!.any p
|
||||
|
||||
/--
|
||||
|
|
@ -282,7 +282,7 @@ Checks whether the Boolean predicate `p` returns `true` for every character in a
|
|||
|
||||
Short-circuits at the first character for which `p` returns `false`.
|
||||
-/
|
||||
@[inline] def all (s : Substring.Raw) (p : Char → Bool) : Bool :=
|
||||
@[inline, suggest_for Substring.Raw.every] def all (s : Substring.Raw) (p : Char → Bool) : Bool :=
|
||||
s.toSlice?.get!.all p
|
||||
|
||||
@[export lean_substring_all]
|
||||
|
|
|
|||
|
|
@ -474,11 +474,11 @@ to avoid having to have the predicate live in `p : α → m (ULift Bool)`.
|
|||
xs.toArray.allM p
|
||||
|
||||
/-- Returns `true` if `p` returns `true` for any element of the vector. -/
|
||||
@[inline, expose] def any (xs : Vector α n) (p : α → Bool) : Bool :=
|
||||
@[inline, expose, suggest_for Vector.some] def any (xs : Vector α n) (p : α → Bool) : Bool :=
|
||||
xs.toArray.any p
|
||||
|
||||
/-- Returns `true` if `p` returns `true` for all elements of the vector. -/
|
||||
@[inline, expose] def all (xs : Vector α n) (p : α → Bool) : Bool :=
|
||||
@[inline, expose, suggest_for Vector.every] def all (xs : Vector α n) (p : α → Bool) : Bool :=
|
||||
xs.toArray.all p
|
||||
|
||||
/-- Count the number of elements of a vector that satisfy the predicate `p`. -/
|
||||
|
|
|
|||
|
|
@ -1197,6 +1197,7 @@ This type is special-cased by both the kernel and the compiler, and overridden w
|
|||
implementation. Both use a fast arbitrary-precision arithmetic library (usually
|
||||
[GMP](https://gmplib.org/)); at runtime, `Nat` values that are sufficiently small are unboxed.
|
||||
-/
|
||||
@[suggest_for ℕ]
|
||||
inductive Nat where
|
||||
/--
|
||||
Zero, the smallest natural number.
|
||||
|
|
@ -2856,6 +2857,7 @@ Optional values, which are either `some` around a value from the underlying type
|
|||
`Option` can represent nullable types or computations that might fail. In the codomain of a function
|
||||
type, it can also represent partiality.
|
||||
-/
|
||||
@[suggest_for Maybe, suggest_for Optional, suggest_for Nullable]
|
||||
inductive Option (α : Type u) where
|
||||
/-- No value. -/
|
||||
| none : Option α
|
||||
|
|
@ -3939,6 +3941,7 @@ value of type `α`.
|
|||
the `pure` operation is `Except.ok` and the `bind` operation returns the first encountered
|
||||
`Except.error`.
|
||||
-/
|
||||
@[suggest_for Result, suggest_for Exception, suggest_for Either]
|
||||
inductive Except (ε : Type u) (α : Type v) where
|
||||
/-- A failure value of type `ε` -/
|
||||
| error : ε → Except ε α
|
||||
|
|
|
|||
110
tests/lean/run/idSuggestEvery.lean
Normal file
110
tests/lean/run/idSuggestEvery.lean
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
-- test every/all replacements in default imports
|
||||
|
||||
/-
|
||||
Expected replacements for `Subarray.any` and `Subarray.all` do not work as suggestion annotations
|
||||
when using generalized field notation: Subarray is an abbreviation and so the underlying `Std.Slice`
|
||||
type, which does not have a corresponding `.any`/`.all` function.
|
||||
-/
|
||||
|
||||
/--
|
||||
error: Invalid field `every`: The environment does not contain `Std.Slice.every`, so it is not possible to project the field `every` from an expression
|
||||
x
|
||||
of type
|
||||
Std.Slice (Std.Slice.Internal.SubarrayData Nat)
|
||||
-/
|
||||
#guard_msgs in example (x : Subarray Nat) := x.every fun _ => true
|
||||
#guard_msgs in example (x : Subarray Nat) := x.all fun _ => true
|
||||
|
||||
/--
|
||||
error: Unknown constant `Subarray.every`
|
||||
|
||||
Hint: Perhaps you meant `Subarray.all` in place of `Subarray.every`:
|
||||
[apply] `Subarray.all`
|
||||
-/
|
||||
#guard_msgs in example := (@Subarray.every Nat (fun _ => true) ·)
|
||||
#guard_msgs in example := (@Subarray.all Nat (fun _ => true) ·)
|
||||
|
||||
/--
|
||||
error: Unknown constant `Subarray.some`
|
||||
|
||||
Hint: Perhaps you meant `Subarray.any` in place of `Subarray.some`:
|
||||
[apply] `Subarray.any`
|
||||
-/
|
||||
#guard_msgs in example := (@Subarray.some Nat (fun _ => true) ·)
|
||||
#guard_msgs in example := (@Subarray.any Nat (fun _ => true) ·)
|
||||
|
||||
/--
|
||||
error: Invalid field `every`: The environment does not contain `String.every`, so it is not possible to project the field `every` from an expression
|
||||
x
|
||||
of type `String`
|
||||
|
||||
Hint: Perhaps you meant `String.all` in place of `String.every`:
|
||||
.e̵v̵e̵r̵y̵a̲l̲l̲
|
||||
-/
|
||||
#guard_msgs in example (x : String) := x.every fun _ => true
|
||||
#guard_msgs in example (x : String) := x.all fun _ => true
|
||||
/--
|
||||
error: Invalid field `some`: The environment does not contain `String.some`, so it is not possible to project the field `some` from an expression
|
||||
x
|
||||
of type `String`
|
||||
|
||||
Hint: Perhaps you meant `String.contains` in place of `String.some`:
|
||||
.s̵o̵m̵e̵c̲o̲n̲t̲a̲i̲n̲s̲
|
||||
-/
|
||||
#guard_msgs in example (x : String) := x.some fun _ => true
|
||||
#guard_msgs in example (x : String) := x.contains fun _ => true
|
||||
|
||||
|
||||
/--
|
||||
error: Invalid field `every`: The environment does not contain `Array.every`, so it is not possible to project the field `every` from an expression
|
||||
x
|
||||
of type `Array Nat`
|
||||
|
||||
Hint: Perhaps you meant `Array.all` in place of `Array.every`:
|
||||
.e̵v̵e̵r̵y̵a̲l̲l̲
|
||||
-/
|
||||
#guard_msgs in example (x : Array Nat) := x.every fun _ => true
|
||||
#guard_msgs in example (x : Array Nat) := x.all fun _ => true
|
||||
/--
|
||||
error: Invalid field `some`: The environment does not contain `Array.some`, so it is not possible to project the field `some` from an expression
|
||||
x
|
||||
of type `Array Nat`
|
||||
|
||||
Hint: Perhaps you meant `Array.any` in place of `Array.some`:
|
||||
.s̵o̵m̵e̵a̲n̲y̲
|
||||
-/
|
||||
#guard_msgs in example (x : Array Nat) := x.some fun _ => true
|
||||
#guard_msgs in example (x : Array Nat) := x.all fun _ => true
|
||||
|
||||
/--
|
||||
error: Invalid field `every`: The environment does not contain `List.every`, so it is not possible to project the field `every` from an expression
|
||||
x
|
||||
of type `List Nat`
|
||||
|
||||
Hint: Perhaps you meant `List.all` in place of `List.every`:
|
||||
.e̵v̵e̵r̵y̵a̲l̲l̲
|
||||
-/
|
||||
#guard_msgs in example (x : List Nat) := x.every fun _ => true
|
||||
#guard_msgs in example (x : List Nat) := x.all fun _ => true
|
||||
/--
|
||||
error: Invalid field `some`: The environment does not contain `List.some`, so it is not possible to project the field `some` from an expression
|
||||
x
|
||||
of type `List Nat`
|
||||
|
||||
Hint: Perhaps you meant `List.any` in place of `List.some`:
|
||||
.s̵o̵m̵e̵a̲n̲y̲
|
||||
-/
|
||||
#guard_msgs in example (x : List Nat) := x.some fun _ => true
|
||||
#guard_msgs in example (x : List Nat) := x.all fun _ => true
|
||||
|
||||
/--
|
||||
@ +1:17...22
|
||||
error: Invalid field `every`: The environment does not contain `List.every`, so it is not possible to project the field `every` from an expression
|
||||
[1, 2, 3]
|
||||
of type `List ?m.10`
|
||||
|
||||
Hint: Perhaps you meant `List.all` in place of `List.every`:
|
||||
.e̵v̵e̵r̵y̵a̲l̲l̲
|
||||
-/
|
||||
#guard_msgs (positions := true) in
|
||||
#check [1, 2, 3].every
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
-- test suggest_for independently of any library annotations
|
||||
|
||||
@[suggest_for String.test0 String.test1 String.test2]
|
||||
public def String.foo (x: String) := x.length + 1
|
||||
|
||||
|
|
@ -37,6 +39,17 @@ Hint: Perhaps you meant `String.foo` in place of `String.test0`:
|
|||
#guard_msgs in
|
||||
#check String.test0
|
||||
|
||||
/--
|
||||
error: Unknown constant `String.test0`
|
||||
|
||||
Hint: Perhaps you meant `String.foo` in place of `String.test0`:
|
||||
[apply] `String.foo`
|
||||
---
|
||||
info: fun x1 x2 x3 => sorry : (x1 : ?m.1) → (x2 : ?m.5 x1) → (x3 : ?m.6 x1 x2) → ?m.7 x1 x2 x3
|
||||
-/
|
||||
#guard_msgs in
|
||||
#check (String.test0 · · ·)
|
||||
|
||||
-- Two suggested replacements: the bar replacement is for `test1`, which does not apply
|
||||
/--
|
||||
error: Invalid field `test1`: The environment does not contain `String.test1`, so it is not possible to project the field `test1` from an expression
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue