Helper to pretty-print a targeted binder.
Equations
- GeneralizationLinter.Frontend.classBinderBracket Lean.BinderInfo.strictImplicit x✝ = toString "⦃" ++ toString x✝ ++ toString "⦄"
- GeneralizationLinter.Frontend.classBinderBracket Lean.BinderInfo.implicit x✝ = "{" ++ x✝ ++ "}"
- GeneralizationLinter.Frontend.classBinderBracket x✝¹ x✝ = toString "[" ++ toString x✝ ++ toString "]"
Instances For
Example
Suppose a file contains the following commands:
set_option A true in
open B in
set_option C true in
lemma something ‹binders› : ‹concl› := ‹value›
Every top-level command of the file gets parsed into its own syntax tree
stx : Syntax. In our example, we pretend that the file contains nothing else other than the four lines shown above, which would mean that there is only one top-level command.`Command.in` ├─ [0] `Command.set_option` │ └─ … (4 children) ├─ [1] `atom "in"` └─ [2] `Command.in` └─ … (3 children)Note that
stx[2]corresponds to the syntax tree of all but the first line of code. Also note thatstxitself is just theSyntax.nodecorresponding to the root node of the tree.Syntax.nodeis defined as one of the possible values of the inductive typeSyntax, and eachSyntax.nodecomes with argumentsinfo : SourceInfo,kind : SyntaxNodeKind, andargs : Array Syntax(the node's children).Lean calls
elabCommandTopLevel stx, which, for our purposes, we can understand as callingelabCommand stxfirst, and thenrunLintersAsync stx(many details are omitted here):elabCommand stx: Sincestx.getKind != nullKind,elabCommandcallsexpandMacroImpl?to check if there is a macro expander defined forstx's kind, and findsexpandInCmd. It then runsexpandInCmd stxto getstxNew:`null` ├─ [0] `Command.section` │ └─ … (3 children) ├─ [1] `Command.set_option` │ └─ … (4 children) ├─ [2] `Command.InternalSyntax.end_local_scope` │ └─ … (2 children) ├─ [3] `Command.in` │ └─ … (3 children) └─ [4] `Command.end` └─ … (2 children)stxNewwould correspond to the following source code (written in diff notation to highlight the changes):- set_option A true in + section + set_option A true + end_local_scope 1 open B in set_option C true in lemma something ‹binders› : ‹concl› := ‹value› + endNote that
stxNew[3] = stx[2]remains unexpanded; expansion is performed one layer at a time. After expandingstxtostxNew,elabCommandcallselabCommand stxNew.elabCommand stxNew: SincestxNew.getKind == nullKind,elabCommandcallsstxNew.getArgs.forM elabCommand. And so on. WhenelabCommandis called on a node for which a specialized elaborator exists, it will call that specialized elaborator. For the node kinds seen above, these specialized elaborators areelabSection,elabSetOption,elabEndLocalScope, andelabEnd(Command.inhas no elaborator, but rather just the macro expanderexpandInCmd). The full tree ofstxNewrequires many more specialized elaborators still.
runLintersAsync stx: By the time this call runs,stxhas been fully processed by the elaborator and macro expander, which may have well modified the command state; in our example, we find thatCommand.State.env.constants.map₂[`something]now exists, and contains theConstantInfocorresponding to the (fully elaborated) constantsomethingthat we declared. Nonetheless,stxstill refers to the original syntax tree that the parser returned at the very beginning.
So linter has access to the syntax tree of the user's source code pre-elaboration (and thus also
pre-expansion), while the command state within which linter runs also provides it with access to
the fully elaborated declaration.
Equations
- One or more equations did not get rendered due to their size.