Let's talk about expressions, the same approach generalizes to other nodes too.
Suppose we have an expression that is passed to a macro as an argument.
macro_rules! mac {
($expr:expr) => {
$expr + 1
}
}
mac!(a);
mac!(a + b);block | expr | ident | item | lifetime | literal
meta | pat | pat_param | path | stmt | tt | ty | visWe want to make a suggestion to add some code to the left or to the right of the expression. I'll assume that a piece of code has the same context for left and right insertions, and not separate contexts. Spans of tokens to the left/right are available during parsing,
tt- cannot add&to input,&adoesn't matchttident- cannot add&to input,&adoesn't matchidentpath- cannot add&to input,&adoesn't matchpathexpr- can add&to input,&astill matchesexpr(tt)*- can add&to input,&astill matches attsequence
Always can add & inside the macro body: &$expr, &$path, &$ident, &$tt will all work.
Still not reliable though, the suggestion can make sense for one expansion of a macro, and not make
sense for another expansion of the same macro.
Any code removal may generally have the same issues as code insertion, but to a less extent because
you cannot remove anything from tt or ident, and removing something from expr or path still
keep it an expr or path.
Need access to HIR ID to get surrounding span as a span of the HIR parent node. If HIR ID is unavailable the parent span needs to be passed from a place where it is available. Doesn't work before HIR is built, need to pass parent spans around manually.
Interesting example: suggestion to add unsafe(...) around unsafe attributes.
Can only be inserteed into input if the fragment is passed as meta,
can only be inserted into macro body if the fragment is passed as ident/tt.
Hard to determine the parent context because there is currently no node to save the span of $meta
in AST. Idea: can try to add it to the metavar span table, or just skip the suggestion.
Tokens from (tt)* sequences don't currently keep metavar spans.
So span_in_ctxt will be the same as span.
Typically used for passing arbitrarily large amounts of code that needs to be considered by itself,
and not in context of a macro.
- Exact span - better for showing any diagnostics
- Span in context - better for suggesting changes to code
Do not emit code insertion suggestions at all for ... what exactly?
Need to know both exact and in-context spans.
- If
span == span_in_ctxt.- If
!from_expansion(span)- Can make a suggestion
- If
from_expansion(span)- Cannot make a suggestion
- If the span is from something that we cannot modify (external macro, desugaring, maybe procedural macro), then we certainly cannot make a suggestion.
- If the span is from a "good" macro (e.g. local
macro_rules) then the suggestion will be unreliable due to the hazard of being specific to a single macro expansion but not others.
- If
- If
span != span_in_ctxt- Cannot make a reliable suggestion
Emit the suggestions with Applicability::MaybeIncorrect or something like that for suspicious
cases, e.g.
span == span_in_ctxt && in_good_macro(span)- TODO
Used randomly and inconsistently. find_ancestor_inside find_ancestor_in_same_ctxt find_ancestor_inside_same_ctxt find_oldest_ancestor_in_same_ctxt can_be_used_for_suggestions from_expansion in_derive_expansion is_desugaring in_external_macro is_from_async_await