mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
Speculatively expand attributes in completions
This commit is contained in:
parent
c3eb646487
commit
2b907652ee
7 changed files with 238 additions and 66 deletions
|
@ -141,27 +141,40 @@ pub trait AstDatabase: SourceDatabase {
|
|||
pub fn expand_speculative(
|
||||
db: &dyn AstDatabase,
|
||||
actual_macro_call: MacroCallId,
|
||||
speculative_args: &ast::TokenTree,
|
||||
speculative_args: &SyntaxNode,
|
||||
token_to_map: SyntaxToken,
|
||||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||
let (tt, tmap_1) = mbe::syntax_node_to_token_tree(speculative_args.syntax());
|
||||
let range =
|
||||
token_to_map.text_range().checked_sub(speculative_args.syntax().text_range().start())?;
|
||||
let token_id = tmap_1.token_by_range(range)?;
|
||||
let loc = db.lookup_intern_macro(actual_macro_call);
|
||||
let macro_def = db.macro_def(loc.def)?;
|
||||
|
||||
let macro_def = {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro(actual_macro_call);
|
||||
db.macro_def(loc.def)?
|
||||
// Fetch token id in the speculative args
|
||||
let censor = censor_for_macro_input(&loc, &speculative_args);
|
||||
let (tt, args_tmap) = mbe::syntax_node_to_token_tree_censored(&speculative_args, censor);
|
||||
let range = token_to_map.text_range().checked_sub(speculative_args.text_range().start())?;
|
||||
let token_id = args_tmap.token_by_range(range)?;
|
||||
|
||||
let speculative_expansion = if let MacroDefKind::ProcMacro(expander, ..) = loc.def.kind {
|
||||
let attr_arg = match &loc.kind {
|
||||
// FIXME make attr arg speculative as well
|
||||
MacroCallKind::Attr { attr_args, .. } => {
|
||||
let mut attr_args = attr_args.0.clone();
|
||||
mbe::Shift::new(&tt).shift_all(&mut attr_args);
|
||||
Some(attr_args)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
expander.expand(db, loc.krate, &tt, attr_arg.as_ref())
|
||||
} else {
|
||||
macro_def.expand(db, actual_macro_call, &tt)
|
||||
};
|
||||
|
||||
let speculative_expansion = macro_def.expand(db, actual_macro_call, &tt);
|
||||
|
||||
let expand_to = macro_expand_to(db, actual_macro_call);
|
||||
|
||||
let (node, tmap_2) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to).ok()?;
|
||||
let (node, rev_tmap) =
|
||||
token_tree_to_syntax_node(&speculative_expansion.value, expand_to).ok()?;
|
||||
|
||||
let token_id = macro_def.map_id_down(token_id);
|
||||
let range = tmap_2.first_range_by_token(token_id, token_to_map.kind())?;
|
||||
let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?;
|
||||
let token = node.syntax_node().covering_element(range).into_token()?;
|
||||
Some((node.syntax_node(), token))
|
||||
}
|
||||
|
@ -259,7 +272,19 @@ fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree,
|
|||
let loc = db.lookup_intern_macro(id);
|
||||
|
||||
let node = SyntaxNode::new_root(arg);
|
||||
let censor = match loc.kind {
|
||||
let censor = censor_for_macro_input(&loc, &node);
|
||||
let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(&node, censor);
|
||||
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter = None;
|
||||
}
|
||||
|
||||
Some(Arc::new((tt, tmap)))
|
||||
}
|
||||
|
||||
fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> Option<TextRange> {
|
||||
match loc.kind {
|
||||
MacroCallKind::FnLike { .. } => None,
|
||||
MacroCallKind::Derive { derive_attr_index, .. } => match ast::Item::cast(node.clone()) {
|
||||
Some(item) => item
|
||||
|
@ -275,15 +300,7 @@ fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree,
|
|||
}
|
||||
None => None,
|
||||
},
|
||||
};
|
||||
let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(&node, censor);
|
||||
|
||||
if loc.def.is_proc_macro() {
|
||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||
tt.delimiter = None;
|
||||
}
|
||||
|
||||
Some(Arc::new((tt, tmap)))
|
||||
}
|
||||
|
||||
fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
|
||||
|
@ -367,11 +384,11 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
|
|||
None => return ExpandResult::str_err("Failed to lower macro args to token tree".into()),
|
||||
};
|
||||
|
||||
let macro_rules = match db.macro_def(loc.def) {
|
||||
let expander = match db.macro_def(loc.def) {
|
||||
Some(it) => it,
|
||||
None => return ExpandResult::str_err("Failed to find macro definition".into()),
|
||||
};
|
||||
let ExpandResult { value: tt, err } = macro_rules.expand(db, id, ¯o_arg.0);
|
||||
let ExpandResult { value: tt, err } = expander.expand(db, id, ¯o_arg.0);
|
||||
// Set a hard limit for the expanded tt
|
||||
let count = tt.count();
|
||||
// XXX: Make ExpandResult a real error and use .map_err instead?
|
||||
|
|
|
@ -370,6 +370,7 @@ impl ExpansionInfo {
|
|||
) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
|
||||
assert_eq!(token.file_id, self.arg.file_id);
|
||||
let token_id = if let Some(item) = item {
|
||||
// check if we are mapping down in an attribute input
|
||||
let call_id = match self.expanded.file_id.0 {
|
||||
HirFileIdRepr::FileId(_) => return None,
|
||||
HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue