@@ -17,23 +17,35 @@ namespace Lean
1717
1818set_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
2136builtin_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/--
6290Throw an unknown constant error message, potentially suggesting alternatives using
6391{name}`suggest_for` attributes. (Like {name}`throwUnknownConstantAt` but with suggestions.)
6492
6593The "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}" ,
0 commit comments