Skip to content

Commit 6eecd84

Browse files
bors[bot]Veykril
andauthored
Merge #10126
10126: feat: Speculatively expand attributes in completions r=Veykril a=Veykril ![j1OjBt5Nca](https://user-images.githubusercontent.com/3757771/133163858-91930072-1441-4ce4-9979-b0ad2727b47f.gif) Fixes #9866 Co-authored-by: Lukas Wirth <[email protected]>
2 parents bcf0072 + dfb94d0 commit 6eecd84

File tree

8 files changed

+377
-71
lines changed

8 files changed

+377
-71
lines changed

crates/base_db/src/fixture.rs

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,15 @@ impl ChangeFixture {
210210
let proc_lib_file = file_id;
211211
file_id.0 += 1;
212212

213+
let (proc_macro, source) = test_proc_macros(&proc_macros);
213214
let mut fs = FileSet::default();
214215
fs.insert(
215216
proc_lib_file,
216217
VfsPath::new_virtual_path("/sysroot/proc_macros/lib.rs".to_string()),
217218
);
218219
roots.push(SourceRoot::new_library(fs));
219220

220-
change.change_file(proc_lib_file, Some(Arc::new(String::new())));
221+
change.change_file(proc_lib_file, Some(Arc::new(String::from(source))));
221222

222223
let all_crates = crate_graph.crates_in_topological_order();
223224

@@ -228,7 +229,7 @@ impl ChangeFixture {
228229
CfgOptions::default(),
229230
CfgOptions::default(),
230231
Env::default(),
231-
test_proc_macros(&proc_macros),
232+
proc_macro,
232233
);
233234

234235
for krate in all_crates {
@@ -250,14 +251,33 @@ impl ChangeFixture {
250251
}
251252
}
252253

253-
fn test_proc_macros(proc_macros: &[String]) -> Vec<ProcMacro> {
254-
std::array::IntoIter::new([ProcMacro {
255-
name: "identity".into(),
256-
kind: crate::ProcMacroKind::Attr,
257-
expander: Arc::new(IdentityProcMacroExpander),
258-
}])
254+
fn test_proc_macros(proc_macros: &[String]) -> (Vec<ProcMacro>, String) {
255+
// The source here is only required so that paths to the macros exist and are resolvable.
256+
let source = r#"
257+
#[proc_macro_attribute]
258+
pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
259+
item
260+
}
261+
#[proc_macro_attribute]
262+
pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
263+
attr
264+
}
265+
"#;
266+
let proc_macros = std::array::IntoIter::new([
267+
ProcMacro {
268+
name: "identity".into(),
269+
kind: crate::ProcMacroKind::Attr,
270+
expander: Arc::new(IdentityProcMacroExpander),
271+
},
272+
ProcMacro {
273+
name: "input_replace".into(),
274+
kind: crate::ProcMacroKind::Attr,
275+
expander: Arc::new(AttributeInputReplaceProcMacroExpander),
276+
},
277+
])
259278
.filter(|pm| proc_macros.iter().any(|name| name == &pm.name))
260-
.collect()
279+
.collect();
280+
(proc_macros, source.into())
261281
}
262282

263283
#[derive(Debug, Clone, Copy)]
@@ -299,8 +319,9 @@ impl From<Fixture> for FileMeta {
299319
}
300320
}
301321

322+
// Identity mapping
302323
#[derive(Debug)]
303-
pub struct IdentityProcMacroExpander;
324+
struct IdentityProcMacroExpander;
304325
impl ProcMacroExpander for IdentityProcMacroExpander {
305326
fn expand(
306327
&self,
@@ -311,3 +332,19 @@ impl ProcMacroExpander for IdentityProcMacroExpander {
311332
Ok(subtree.clone())
312333
}
313334
}
335+
336+
// Pastes the attribute input as its output
337+
#[derive(Debug)]
338+
struct AttributeInputReplaceProcMacroExpander;
339+
impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
340+
fn expand(
341+
&self,
342+
_: &Subtree,
343+
attrs: Option<&Subtree>,
344+
_: &Env,
345+
) -> Result<Subtree, ProcMacroExpansionError> {
346+
attrs
347+
.cloned()
348+
.ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
349+
}
350+
}

crates/hir/src/semantics.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
166166
self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map)
167167
}
168168

169+
pub fn speculative_expand_attr_macro(
170+
&self,
171+
actual_macro_call: &ast::Item,
172+
speculative_args: &ast::Item,
173+
token_to_map: SyntaxToken,
174+
) -> Option<(SyntaxNode, SyntaxToken)> {
175+
self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map)
176+
}
177+
169178
// FIXME: Rename to descend_into_macros_single
170179
pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
171180
self.imp.descend_into_macros(token).pop().unwrap()
@@ -452,7 +461,24 @@ impl<'db> SemanticsImpl<'db> {
452461
hir_expand::db::expand_speculative(
453462
self.db.upcast(),
454463
macro_call_id,
455-
speculative_args,
464+
speculative_args.syntax(),
465+
token_to_map,
466+
)
467+
}
468+
469+
fn speculative_expand_attr(
470+
&self,
471+
actual_macro_call: &ast::Item,
472+
speculative_args: &ast::Item,
473+
token_to_map: SyntaxToken,
474+
) -> Option<(SyntaxNode, SyntaxToken)> {
475+
let sa = self.analyze(actual_macro_call.syntax());
476+
let macro_call = InFile::new(sa.file_id, actual_macro_call.clone());
477+
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(macro_call))?;
478+
hir_expand::db::expand_speculative(
479+
self.db.upcast(),
480+
macro_call_id,
481+
speculative_args.syntax(),
456482
token_to_map,
457483
)
458484
}

crates/hir_expand/src/db.rs

Lines changed: 75 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::sync::Arc;
55
use base_db::{salsa, SourceDatabase};
66
use itertools::Itertools;
77
use limit::Limit;
8-
use mbe::{ExpandError, ExpandResult};
8+
use mbe::{syntax_node_to_token_tree, ExpandError, ExpandResult};
99
use syntax::{
1010
algo::diff,
1111
ast::{self, AttrsOwner, NameOwner},
@@ -141,27 +141,72 @@ pub trait AstDatabase: SourceDatabase {
141141
pub fn expand_speculative(
142142
db: &dyn AstDatabase,
143143
actual_macro_call: MacroCallId,
144-
speculative_args: &ast::TokenTree,
144+
speculative_args: &SyntaxNode,
145145
token_to_map: SyntaxToken,
146146
) -> Option<(SyntaxNode, SyntaxToken)> {
147-
let (tt, tmap_1) = mbe::syntax_node_to_token_tree(speculative_args.syntax());
148-
let range =
149-
token_to_map.text_range().checked_sub(speculative_args.syntax().text_range().start())?;
150-
let token_id = tmap_1.token_by_range(range)?;
151-
152-
let macro_def = {
153-
let loc: MacroCallLoc = db.lookup_intern_macro(actual_macro_call);
154-
db.macro_def(loc.def)?
147+
let loc = db.lookup_intern_macro(actual_macro_call);
148+
let macro_def = db.macro_def(loc.def)?;
149+
let token_range = token_to_map.text_range();
150+
151+
// Build the subtree and token mapping for the speculative args
152+
let censor = censor_for_macro_input(&loc, &speculative_args);
153+
let (mut tt, spec_args_tmap) =
154+
mbe::syntax_node_to_token_tree_censored(&speculative_args, censor);
155+
156+
let (attr_arg, token_id) = match loc.kind {
157+
MacroCallKind::Attr { invoc_attr_index, .. } => {
158+
// Attributes may have an input token tree, build the subtree and map for this as well
159+
// then try finding a token id for our token if it is inside this input subtree.
160+
let item = ast::Item::cast(speculative_args.clone())?;
161+
let attr = item.attrs().nth(invoc_attr_index as usize)?;
162+
match attr.token_tree() {
163+
Some(token_tree) => {
164+
let (mut tree, map) = syntax_node_to_token_tree(attr.token_tree()?.syntax());
165+
tree.delimiter = None;
166+
167+
let shift = mbe::Shift::new(&tt);
168+
shift.shift_all(&mut tree);
169+
170+
let token_id = if token_tree.syntax().text_range().contains_range(token_range) {
171+
let attr_input_start =
172+
token_tree.left_delimiter_token()?.text_range().start();
173+
let range = token_range.checked_sub(attr_input_start)?;
174+
let token_id = shift.shift(map.token_by_range(range)?);
175+
Some(token_id)
176+
} else {
177+
None
178+
};
179+
(Some(tree), token_id)
180+
}
181+
_ => (None, None),
182+
}
183+
}
184+
_ => (None, None),
185+
};
186+
let token_id = match token_id {
187+
Some(token_id) => token_id,
188+
// token wasn't inside an attribute input so it has to be in the general macro input
189+
None => {
190+
let range = token_range.checked_sub(speculative_args.text_range().start())?;
191+
let token_id = spec_args_tmap.token_by_range(range)?;
192+
macro_def.map_id_down(token_id)
193+
}
155194
};
156195

157-
let speculative_expansion = macro_def.expand(db, actual_macro_call, &tt);
196+
// Do the actual expansion, we need to directly expand the proc macro due to the attribute args
197+
// Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
198+
let speculative_expansion = if let MacroDefKind::ProcMacro(expander, ..) = loc.def.kind {
199+
tt.delimiter = None;
200+
expander.expand(db, loc.krate, &tt, attr_arg.as_ref())
201+
} else {
202+
macro_def.expand(db, actual_macro_call, &tt)
203+
};
158204

159205
let expand_to = macro_expand_to(db, actual_macro_call);
206+
let (node, rev_tmap) =
207+
token_tree_to_syntax_node(&speculative_expansion.value, expand_to).ok()?;
160208

161-
let (node, tmap_2) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to).ok()?;
162-
163-
let token_id = macro_def.map_id_down(token_id);
164-
let range = tmap_2.first_range_by_token(token_id, token_to_map.kind())?;
209+
let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?;
165210
let token = node.syntax_node().covering_element(range).into_token()?;
166211
Some((node.syntax_node(), token))
167212
}
@@ -259,7 +304,19 @@ fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree,
259304
let loc = db.lookup_intern_macro(id);
260305

261306
let node = SyntaxNode::new_root(arg);
262-
let censor = match loc.kind {
307+
let censor = censor_for_macro_input(&loc, &node);
308+
let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(&node, censor);
309+
310+
if loc.def.is_proc_macro() {
311+
// proc macros expect their inputs without parentheses, MBEs expect it with them included
312+
tt.delimiter = None;
313+
}
314+
315+
Some(Arc::new((tt, tmap)))
316+
}
317+
318+
fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> Option<TextRange> {
319+
match loc.kind {
263320
MacroCallKind::FnLike { .. } => None,
264321
MacroCallKind::Derive { derive_attr_index, .. } => match ast::Item::cast(node.clone()) {
265322
Some(item) => item
@@ -275,15 +332,7 @@ fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree,
275332
}
276333
None => None,
277334
},
278-
};
279-
let (mut tt, tmap) = mbe::syntax_node_to_token_tree_censored(&node, censor);
280-
281-
if loc.def.is_proc_macro() {
282-
// proc macros expect their inputs without parentheses, MBEs expect it with them included
283-
tt.delimiter = None;
284335
}
285-
286-
Some(Arc::new((tt, tmap)))
287336
}
288337

289338
fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
@@ -367,11 +416,11 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
367416
None => return ExpandResult::str_err("Failed to lower macro args to token tree".into()),
368417
};
369418

370-
let macro_rules = match db.macro_def(loc.def) {
419+
let expander = match db.macro_def(loc.def) {
371420
Some(it) => it,
372421
None => return ExpandResult::str_err("Failed to find macro definition".into()),
373422
};
374-
let ExpandResult { value: tt, err } = macro_rules.expand(db, id, &macro_arg.0);
423+
let ExpandResult { value: tt, err } = expander.expand(db, id, &macro_arg.0);
375424
// Set a hard limit for the expanded tt
376425
let count = tt.count();
377426
// XXX: Make ExpandResult a real error and use .map_err instead?

crates/hir_expand/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ impl ExpansionInfo {
370370
) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
371371
assert_eq!(token.file_id, self.arg.file_id);
372372
let token_id = if let Some(item) = item {
373+
// check if we are mapping down in an attribute input
373374
let call_id = match self.expanded.file_id.0 {
374375
HirFileIdRepr::FileId(_) => return None,
375376
HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,

0 commit comments

Comments
 (0)