fn expand_maybe_stop(
sema: &Semantics<'_, RootDatabase>,
original_file: InFile<SyntaxNode>,
speculative_file: SyntaxNode,
original_offset: TextSize,
fake_ident_token: SyntaxToken,
relative_offset: TextSize,
) -> Option<ExpansionResult>Expand description
Expand attributes and macro calls at the current cursor position for both the original file and fake file repeatedly. As soon as one of the two expansions fail we stop so the original and speculative states stay in sync.
We do this by recursively expanding all macros and picking the best possible match. We cannot just choose the first expansion each time because macros can expand to something that does not include our completion marker, e.g.:
macro_rules! helper { ($v:ident) => {} }
macro_rules! my_macro {
($v:ident) => {
helper!($v);
$v
};
}
my_macro!(complete_me_here);If we would expand the first thing we encounter only (which in fact this method used to do), we would be unable to complete here, because we would be walking directly into the void. So we instead try every possible path.
This can also creates discrepancies between the speculative and real expansions: because we insert
tokens, we insert characters, which means if we try the second occurrence it may not be at the same
position in the original and speculative file. We take an educated guess here, and for each token
that we check, we subtract COMPLETION_MARKER.len(). This may not be accurate because proc macros
can insert the text of the completion marker in other places while removing the span, but this is
the best we can do.