Skip to content

Commit 9e87560

Browse files
authored
chore: switch the association of stored suggestions (#11590)
This PR switches the way olean files store identifier suggestions. The ordering introduced in #11367 and #11529 made sense if we were only storing incorrect -> correct mappings, but for the reference manual we want to store the correct -> incorrect mappings as well, and so it is more sensible to store just the correct -> incorrect mapping that mimics the author-generated data better. Also tweaks error messages further in preparation for public-facing @[suggest_for] annotations and forbids suggestions on non-public names. Does not make generally-visible changes as there are no introduced uses of @[suggest_for] annotations yet.
1 parent 466a248 commit 9e87560

File tree

4 files changed

+109
-38
lines changed

4 files changed

+109
-38
lines changed

src/Lean/Elab/App.lean

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,13 +1375,13 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
13751375
-- inheritance, nor `import all`.
13761376
(declHint := (mkPrivateName env structName).mkStr fieldName)
13771377

1378-
| .forallE .., LVal.fieldName ref fieldName suffix? _fullRef =>
1378+
| .forallE .., LVal.fieldName ref fieldName suffix? fullRef =>
13791379
let fullName := Name.str `Function fieldName
13801380
if (← getEnv).contains fullName then
13811381
return LValResolution.const `Function `Function fullName
13821382
match e.getAppFn, suffix? with
13831383
| Expr.const c _, some suffix =>
1384-
throwUnknownConstantWithSuggestions (c ++ suffix)
1384+
throwUnknownConstantWithSuggestions (ref? := fullRef) (c ++ suffix)
13851385
| _, _ =>
13861386
throwInvalidFieldAt ref fieldName fullName
13871387
| .forallE .., .fieldIdx .. =>
@@ -1402,8 +1402,8 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
14021402

14031403
| _, _ =>
14041404
match e.getAppFn, lval with
1405-
| Expr.const c _, .fieldName _ref _fieldName (some suffix) _fullRef =>
1406-
throwUnknownConstantWithSuggestions (c ++ suffix)
1405+
| Expr.const c _, .fieldName _ref _fieldName (some suffix) fullRef =>
1406+
throwUnknownConstantWithSuggestions (ref? := fullRef) (c ++ suffix)
14071407
| _, .fieldName .. =>
14081408
throwNamedError lean.invalidField m!"Invalid field notation: Field projection operates on \
14091409
types of the form `C ...` where C is a constant. The expression{indentExpr e}\nhas \
@@ -1424,26 +1424,26 @@ where
14241424
type{inlineExprTrailing eType}"
14251425

14261426
-- Possible alternatives provided with `@[suggest_for]` annotations
1427-
let suggestions := (← Lean.getSuggestions fullName).filter (·.getPrefix = fullName.getPrefix)
1427+
let suggestions := (← Lean.getSuggestions fullName).filter (·.getPrefix = fullName.getPrefix) |>.toArray
14281428
let suggestForHint ←
14291429
if h : suggestions.size = 0 then
14301430
pure .nil
14311431
else if suggestions.size = 1 then
14321432
MessageData.hint (ref? := ref)
14331433
m!"Perhaps you meant `{.ofConstName suggestions[0]}` in place of `{fullName}`:"
14341434
(suggestions.map fun suggestion => {
1435-
preInfo? := .some s!"{e}.",
1435+
preInfo? := .some s!".",
14361436
suggestion := suggestion.getString!,
1437-
toCodeActionTitle? := .some (s!"Change to {e}.{·}"),
1437+
toCodeActionTitle? := .some (s!"Change to .{·}"),
14381438
diffGranularity := .all,
14391439
})
14401440
else
14411441
MessageData.hint (ref? := ref)
14421442
m!"Perhaps you meant one of these in place of `{fullName}`:"
14431443
(suggestions.map fun suggestion => {
14441444
suggestion := suggestion.getString!,
1445-
toCodeActionTitle? := .some (s!"Change to {e}.{·}"),
1446-
messageData? := .some m!"`{.ofConstName suggestion}`: {e}.{suggestion.getString!}",
1445+
toCodeActionTitle? := .some (s!"Change to .{·}"),
1446+
messageData? := .some m!"`{.ofConstName suggestion}`",
14471447
})
14481448

14491449
-- By using `mkUnknownIdentifierMessage`, the tag `Lean.unknownIdentifierMessageTag` is

src/Lean/IdentifierSuggestion.lean

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,35 @@ namespace Lean
1717

1818
set_option doc.verso true
1919

20+
public structure IdentSuggestion where
21+
existingToIncorrect : NameMap NameSet := {}
22+
incorrectToExisting : NameMap NameSet := {}
23+
deriving Inhabited
24+
25+
def IdentSuggestion.add (table : IdentSuggestion) (trueName : Name) (altNames : Array Name) : IdentSuggestion :=
26+
{ existingToIncorrect :=
27+
table.existingToIncorrect.alter trueName fun old =>
28+
altNames.foldl (β := NameSet) (init := old.getD {}) fun accum altName =>
29+
accum.insert altName
30+
incorrectToExisting :=
31+
altNames.foldl (init := table.incorrectToExisting) fun accum altName =>
32+
accum.alter altName fun old =>
33+
old.getD {} |>.insert trueName
34+
}
2035

2136
builtin_initialize identifierSuggestionForAttr : PersistentEnvExtension
22-
(Name × Array Name) /- *Store* mappings from incorrect names to corrections (identifiers that exist) -/
23-
(Name × Array Name) /- *Add* mappings from existing functions to possible incorrect namings -/
24-
(NameMap NameSet) /- *Use* mapping from incorrect names to corrections (identifiers that exist) -/
37+
(Name × Array Name) /- Mapping from existing constant to potential incorrect alternative names -/
38+
(Name × Array Name) /- Mapping from existing constant to potential incorrect alternative names -/
39+
IdentSuggestion /- Two directional mapping between existing identifier and alternative incorrect mappings -/
2540
let ext ← registerPersistentEnvExtension {
2641
mkInitial := pure {},
2742
addImportedFn := fun modules => pure <|
28-
modules.foldl (init := {}) fun accum entries =>
29-
entries.foldl (init := accum) fun accum (altName, trueNames) =>
30-
accum.alter altName (fun old => trueNames.foldl (β := NameSet) (init := old.getD ∅) fun accum trueName =>
31-
accum.insert trueName),
32-
addEntryFn := fun table (trueName, altNames) =>
33-
altNames.foldl (init := table) fun accum alt =>
34-
accum.alter alt (·.getD {} |>.insert trueName)
35-
exportEntriesFn table := table.toArray.map fun (altName, trueNames) =>
36-
(altName, trueNames.toArray)
43+
(modules.foldl (init := {}) fun accum entries =>
44+
entries.foldl (init := accum) fun accum (trueName, altNames) =>
45+
accum.add trueName altNames),
46+
addEntryFn := fun table (trueName, altNames) => table.add trueName altNames
47+
exportEntriesFn table := table.existingToIncorrect.toArray.map fun (trueName, altNames) =>
48+
(trueName, altNames.toArray)
3749
}
3850

3951
registerBuiltinAttribute {
@@ -47,32 +59,48 @@ builtin_initialize identifierSuggestionForAttr : PersistentEnvExtension
4759
| -- Attributes parsed _before the suggest_for notation is added
4860
.node _ ``Parser.Attr.simple #[.ident _ _ `suggest_for [], .node _ `null #[id]] => pure #[id]
4961
| _ => throwError "Invalid `[suggest_for]` attribute syntax {repr stx}"
62+
if isPrivateName decl then
63+
throwError "Cannot make suggestions for private names"
5064
modifyEnv (ext.addEntry · (decl, altSyntaxIds.map (·.getId)))
5165
}
5266

5367
return ext
5468

55-
public def getSuggestions [Monad m] [MonadEnv m] (fullName : Name) : m (Array Name) := do
69+
/--
70+
Given a name, find all the stored correct, existing identifiers that mention that name in a
71+
{lit}`suggest_for` annotation.
72+
-/
73+
public def getSuggestions [Monad m] [MonadEnv m] (incorrectName : Name) : m NameSet := do
74+
return identifierSuggestionForAttr.getState (← getEnv)
75+
|>.incorrectToExisting
76+
|>.find? incorrectName
77+
|>.getD {}
78+
79+
/--
80+
Given a (presumably existing) identifier, find all the {lit}`suggest_for` annotations that were given
81+
for that identifier.
82+
-/
83+
public def getStoredSuggestions [Monad m] [MonadEnv m] (trueName : Name) : m NameSet := do
5684
return identifierSuggestionForAttr.getState (← getEnv)
57-
|>.find? fullName
85+
|>.existingToIncorrect
86+
|>.find? trueName
5887
|>.getD {}
59-
|>.toArray
6088

6189
/--
6290
Throw an unknown constant error message, potentially suggesting alternatives using
6391
{name}`suggest_for` attributes. (Like {name}`throwUnknownConstantAt` but with suggestions.)
6492
6593
The "Unknown constant `<id>`" message will fully qualify the name, whereas the
6694
-/
67-
public def throwUnknownConstantWithSuggestions (constName : Name) : CoreM α := do
68-
let suggestions ← getSuggestions constName
69-
let ref ← getRef
95+
public def throwUnknownConstantWithSuggestions (constName : Name) (ref? : Option Syntax := .none) : CoreM α := do
96+
let suggestions := (← getSuggestions constName).toArray
97+
let ref := ref?.getD (← getRef)
7098
let hint ← if suggestions.size = 0 then
7199
pure MessageData.nil
72100
else
73101
-- Modify suggestions to have the same structure as the user-provided identifier, but only
74102
-- if that doesn't cause ambiguity.
75-
let rawId := (← getRef).getId
103+
let rawId := ref.getId
76104
let env ← getEnv
77105
let ns ← getCurrNamespace
78106
let openDecls ← getOpenDecls
@@ -86,7 +114,8 @@ public def throwUnknownConstantWithSuggestions (constName : Name) : CoreM α :=
86114
candidate
87115

88116
let alternative := if h : suggestions.size = 1 then m!"`{.ofConstName suggestions[0]}`" else m!"one of these"
89-
m!"Perhaps you meant {alternative} in place of `{.ofName rawId}`:".hint (suggestions.map fun suggestion =>
117+
let inPlaceOfThis := if rawId.isAnonymous then .nil else m!" in place of `{.ofName rawId}`"
118+
m!"Perhaps you meant {alternative}{inPlaceOfThis}:".hint (suggestions.map fun suggestion =>
90119
let modified := modifySuggestion suggestion
91120
{
92121
suggestion := s!"{modified}",
Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ error: Invalid field `test0`: The environment does not contain `String.test0`, s
2323
of type `String`
2424
2525
Hint: Perhaps you meant `String.foo` in place of `String.test0`:
26-
"abc".t̵e̵s̵t̵0̵f̲o̲o̲
26+
.t̵e̵s̵t̵0̵f̲o̲o̲
2727
-/
2828
#guard_msgs in
2929
#check "abc".test0
@@ -44,8 +44,8 @@ error: Invalid field `test1`: The environment does not contain `String.test1`, s
4444
of type `String`
4545
4646
Hint: Perhaps you meant one of these in place of `String.test1`:
47-
[apply] `String.foo`: "abc".foo
48-
[apply] `String.baz`: "abc".baz
47+
[apply] `String.foo`
48+
[apply] `String.baz`
4949
-/
5050
#guard_msgs in
5151
#check "abc".test1
@@ -67,9 +67,9 @@ error: Invalid field `test2`: The environment does not contain `String.test2`, s
6767
of type `String`
6868
6969
Hint: Perhaps you meant one of these in place of `String.test2`:
70-
[apply] `String.foo`: "abc".foo
71-
[apply] `String.baz`: "abc".baz
72-
[apply] `String.bar`: "abc".bar
70+
[apply] `String.foo`
71+
[apply] `String.baz`
72+
[apply] `String.bar`
7373
-/
7474
#guard_msgs in
7575
#check "abc".test2
@@ -119,7 +119,7 @@ error: Invalid field `toNum`: The environment does not contain `Foo.Bar.toNum`,
119119
of type `Foo.Bar`
120120
121121
Hint: Perhaps you meant `Foo.Bar.toNat` in place of `Foo.Bar.toNum`:
122-
Foo.Bar.three.t̵o̵N̵u̵m̵t̲o̲N̲a̲t̲
122+
.t̵o̵N̵u̵m̵t̲o̲N̲a̲t̲
123123
-/
124124
#guard_msgs in
125125
#eval Foo.Bar.three.toNum
@@ -130,7 +130,7 @@ error: Invalid field `toStr`: The environment does not contain `Foo.Bar.toStr`,
130130
of type `Foo.Bar`
131131
132132
Hint: Perhaps you meant `Foo.Bar.toString` in place of `Foo.Bar.toStr`:
133-
Foo.Bar.two.t̵o̵S̵t̵r̵t̲o̲S̲t̲r̲i̲n̲g̲
133+
.t̵o̵S̵t̵r̵t̲o̲S̲t̲r̲i̲n̲g̲
134134
-/
135135
#guard_msgs in
136136
#eval Foo.Bar.two.toStr
@@ -274,7 +274,35 @@ error: Invalid field `not`: The environment does not contain `MyBool.not`, so it
274274
of type `MyBool`
275275
276276
Hint: Perhaps you meant `MyBool.swap` in place of `MyBool.not`:
277-
MyBool.tt.n̵o̵t̵s̲w̲a̲p̲
277+
.n̵o̵t̵s̲w̲a̲p̲
278278
-/
279279
#guard_msgs in
280280
example := MyBool.tt.not
281+
282+
/--
283+
error: Invalid field `not`: The environment does not contain `MyBool.not`, so it is not possible to project the field `not` from an expression
284+
(fun x => if x < 3 then MyBool.tt else MyBool.ff) 4
285+
of type `MyBool`
286+
287+
Hint: Perhaps you meant `MyBool.swap` in place of `MyBool.not`:
288+
.n̵o̵t̵s̲w̲a̲p̲
289+
-/
290+
#guard_msgs in
291+
example := ((fun x => if x < 3 then MyBool.tt else .ff) 4).not
292+
293+
294+
@[suggest_for MyBool.not]
295+
def MyBool.justFalse : MyBool → MyBool
296+
| _ => ff
297+
298+
/--
299+
error: Invalid field `not`: The environment does not contain `MyBool.not`, so it is not possible to project the field `not` from an expression
300+
(fun x => if x < 3 then MyBool.tt else MyBool.ff) 4
301+
of type `MyBool`
302+
303+
Hint: Perhaps you meant one of these in place of `MyBool.not`:
304+
[apply] `MyBool.justFalse`
305+
[apply] `MyBool.swap`
306+
-/
307+
#guard_msgs in
308+
example := ((fun x => if x < 3 then MyBool.tt else .ff) 4).not

tests/pkg/module/Module/Basic.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,17 @@ public meta def delab : Lean.PrettyPrinter.Delaborator.Delab :=
510510
511511
public def noMetaDelab : Lean.PrettyPrinter.Delaborator.Delab :=
512512
default
513+
514+
/-- error: Cannot make suggestions for private names -/
515+
#guard_msgs in
516+
@[suggest_for Bar1]
517+
def FooBar1 := 4
518+
519+
/-- error: Cannot make suggestions for private names -/
520+
#guard_msgs in
521+
@[suggest_for Bar2]
522+
meta def FooBar2 := 4
523+
524+
#guard_msgs in
525+
@[suggest_for Bar3 FooBar1 FooBar2]
526+
public def FooBar3 := 4

0 commit comments

Comments
 (0)