Skip to content

Commit 177c701

Browse files
committed
Map attribute input tokens correctly
1 parent cee0267 commit 177c701

File tree

8 files changed

+159
-62
lines changed

8 files changed

+159
-62
lines changed

crates/hir/src/semantics.rs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ impl<'db> SemanticsImpl<'db> {
441441
.entry(file_id)
442442
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
443443
.as_ref()?
444-
.map_token_down(token.as_ref())?;
444+
.map_token_down(self.db.upcast(), None, token.as_ref())?;
445445

446446
if let Some(parent) = token.value.parent() {
447447
self.cache(find_root(&parent), token.file_id);
@@ -450,24 +450,21 @@ impl<'db> SemanticsImpl<'db> {
450450
return Some(token);
451451
},
452452
ast::Item(item) => {
453-
match self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item))) {
454-
Some(call_id) => {
455-
let file_id = call_id.as_file();
456-
let token = self
457-
.expansion_info_cache
458-
.borrow_mut()
459-
.entry(file_id)
460-
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
461-
.as_ref()?
462-
.map_token_down(token.as_ref())?;
463-
464-
if let Some(parent) = token.value.parent() {
465-
self.cache(find_root(&parent), token.file_id);
466-
}
467-
468-
return Some(token);
453+
if let Some(call_id) = self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item.clone()))) {
454+
let file_id = call_id.as_file();
455+
let token = self
456+
.expansion_info_cache
457+
.borrow_mut()
458+
.entry(file_id)
459+
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
460+
.as_ref()?
461+
.map_token_down(self.db.upcast(), Some(item), token.as_ref())?;
462+
463+
if let Some(parent) = token.value.parent() {
464+
self.cache(find_root(&parent), token.file_id);
469465
}
470-
None => {}
466+
467+
return Some(token);
471468
}
472469
},
473470
_ => {}
@@ -479,7 +476,6 @@ impl<'db> SemanticsImpl<'db> {
479476
})
480477
.last()
481478
.unwrap();
482-
483479
token.value
484480
}
485481

crates/hir_def/src/attr.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
33
use std::{
44
convert::{TryFrom, TryInto},
5-
fmt, ops,
5+
fmt,
6+
hash::Hash,
7+
ops,
68
sync::Arc,
79
};
810

@@ -12,7 +14,7 @@ use either::Either;
1214
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
1315
use itertools::Itertools;
1416
use la_arena::ArenaMap;
15-
use mbe::{syntax_node_to_token_tree, DelimiterKind};
17+
use mbe::{syntax_node_to_token_tree, DelimiterKind, MappedSubTree};
1618
use smallvec::{smallvec, SmallVec};
1719
use syntax::{
1820
ast::{self, AstNode, AttrsOwner},
@@ -165,11 +167,11 @@ impl RawAttrs {
165167
// Input subtree is: `(cfg, $(attr),+)`
166168
// Split it up into a `cfg` subtree and the `attr` subtrees.
167169
// FIXME: There should be a common API for this.
168-
let mut parts = subtree.token_trees.split(
170+
let mut parts = subtree.tree.token_trees.split(
169171
|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ','),
170172
);
171173
let cfg = parts.next().unwrap();
172-
let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
174+
let cfg = Subtree { delimiter: subtree.tree.delimiter, token_trees: cfg.to_vec() };
173175
let cfg = CfgExpr::parse(&cfg);
174176
let index = attr.id;
175177
let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
@@ -652,14 +654,14 @@ pub enum AttrInput {
652654
/// `#[attr = "string"]`
653655
Literal(SmolStr),
654656
/// `#[attr(subtree)]`
655-
TokenTree(Subtree),
657+
TokenTree(mbe::MappedSubTree),
656658
}
657659

658660
impl fmt::Display for AttrInput {
659661
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660662
match self {
661663
AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
662-
AttrInput::TokenTree(subtree) => subtree.fmt(f),
664+
AttrInput::TokenTree(subtree) => subtree.tree.fmt(f),
663665
}
664666
}
665667
}
@@ -679,7 +681,8 @@ impl Attr {
679681
};
680682
Some(Interned::new(AttrInput::Literal(value)))
681683
} else if let Some(tt) = ast.token_tree() {
682-
Some(Interned::new(AttrInput::TokenTree(syntax_node_to_token_tree(tt.syntax()).0)))
684+
let (tree, map) = syntax_node_to_token_tree(tt.syntax());
685+
Some(Interned::new(AttrInput::TokenTree(MappedSubTree { tree, map })))
683686
} else {
684687
None
685688
};
@@ -712,6 +715,7 @@ impl Attr {
712715
Some(AttrInput::TokenTree(args)) => {
713716
let mut counter = 0;
714717
let paths = args
718+
.tree
715719
.token_trees
716720
.iter()
717721
.group_by(move |tt| {
@@ -756,7 +760,7 @@ pub struct AttrQuery<'a> {
756760
impl<'a> AttrQuery<'a> {
757761
pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
758762
self.attrs().filter_map(|attr| match attr.input.as_deref()? {
759-
AttrInput::TokenTree(it) => Some(it),
763+
AttrInput::TokenTree(it) => Some(&it.tree),
760764
_ => None,
761765
})
762766
}

crates/hir_def/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,13 +786,13 @@ fn attr_macro_as_call_id(
786786
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
787787
let mut arg = match &macro_attr.input {
788788
Some(input) => match &**input {
789-
attr::AttrInput::Literal(_) => tt::Subtree::default(),
789+
attr::AttrInput::Literal(_) => Default::default(),
790790
attr::AttrInput::TokenTree(tt) => tt.clone(),
791791
},
792-
None => tt::Subtree::default(),
792+
None => Default::default(),
793793
};
794794
// The parentheses are always disposed here.
795-
arg.delimiter = None;
795+
arg.tree.delimiter = None;
796796

797797
let res = def.as_lazy_macro(
798798
db.upcast(),

crates/hir_def/src/nameres/collector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ impl DefCollector<'_> {
289289
|| *attr_name == hir_expand::name![register_tool]
290290
{
291291
match attr.input.as_deref() {
292-
Some(AttrInput::TokenTree(subtree)) => match &*subtree.token_trees {
292+
Some(AttrInput::TokenTree(subtree)) => match &*subtree.tree.token_trees {
293293
[tt::TokenTree::Leaf(tt::Leaf::Ident(name))] => name.as_name(),
294294
_ => continue,
295295
},

crates/hir_expand/src/db.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ fn parse_macro_expansion(
223223
Ok(it) => it,
224224
Err(err) => {
225225
log::debug!(
226-
"failed to parse expanstion to {:?} = {}",
226+
"failed to parse expansion to {:?} = {}",
227227
fragment_kind,
228228
tt.as_debug_string()
229229
);
@@ -386,11 +386,15 @@ fn expand_proc_macro(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<tt::
386386
};
387387

388388
let attr_arg = match &loc.kind {
389-
MacroCallKind::Attr { attr_args, .. } => Some(attr_args),
389+
MacroCallKind::Attr { attr_args, .. } => {
390+
let mut attr_args = attr_args.tree.clone();
391+
mbe::Shift::new(&macro_arg.0).shift_all(&mut attr_args);
392+
Some(attr_args)
393+
}
390394
_ => None,
391395
};
392396

393-
expander.expand(db, loc.krate, &macro_arg.0, attr_arg)
397+
expander.expand(db, loc.krate, &macro_arg.0, attr_arg.as_ref())
394398
}
395399

396400
fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool {

crates/hir_expand/src/lib.rs

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use std::{hash::Hash, iter, sync::Arc};
2727
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
2828
use syntax::{
2929
algo::skip_trivia_token,
30-
ast::{self, AstNode},
30+
ast::{self, AstNode, AttrsOwner},
3131
Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
3232
};
3333

@@ -36,6 +36,7 @@ use crate::{
3636
builtin_attr::BuiltinAttrExpander,
3737
builtin_derive::BuiltinDeriveExpander,
3838
builtin_macro::{BuiltinFnLikeExpander, EagerExpander},
39+
db::TokenExpander,
3940
proc_macro::ProcMacroExpander,
4041
};
4142

@@ -132,6 +133,17 @@ impl HirFileId {
132133
};
133134
Some(InFile::new(id.file_id, def_tt))
134135
});
136+
let def_or_attr_input = def.or_else(|| match loc.kind {
137+
MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
138+
let tt = ast_id
139+
.to_node(db)
140+
.attrs()
141+
.nth(invoc_attr_index as usize)
142+
.and_then(|attr| attr.token_tree())?;
143+
Some(InFile::new(ast_id.file_id, tt))
144+
}
145+
_ => None,
146+
});
135147

136148
let macro_def = db.macro_def(loc.def)?;
137149
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
@@ -140,7 +152,8 @@ impl HirFileId {
140152
Some(ExpansionInfo {
141153
expanded: InFile::new(self, parse.syntax_node()),
142154
arg: InFile::new(loc.kind.file_id(), arg_tt),
143-
def,
155+
attr_input_or_mac_def: def_or_attr_input,
156+
macro_arg_shift: mbe::Shift::new(&macro_arg.0),
144157
macro_arg,
145158
macro_def,
146159
exp_map,
@@ -270,7 +283,7 @@ pub enum MacroCallKind {
270283
Attr {
271284
ast_id: AstId<ast::Item>,
272285
attr_name: String,
273-
attr_args: tt::Subtree,
286+
attr_args: mbe::MappedSubTree,
274287
/// Syntactical index of the invoking `#[attribute]`.
275288
///
276289
/// Outer attributes are counted first, then inner attributes. This does not support
@@ -335,11 +348,12 @@ impl MacroCallId {
335348
pub struct ExpansionInfo {
336349
expanded: InFile<SyntaxNode>,
337350
arg: InFile<SyntaxNode>,
338-
/// The `macro_rules!` arguments.
339-
def: Option<InFile<ast::TokenTree>>,
351+
/// The `macro_rules!` arguments or attribute input.
352+
attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
340353

341-
macro_def: Arc<db::TokenExpander>,
354+
macro_def: Arc<TokenExpander>,
342355
macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
356+
macro_arg_shift: mbe::Shift,
343357
exp_map: Arc<mbe::TokenMap>,
344358
}
345359

@@ -350,11 +364,53 @@ impl ExpansionInfo {
350364
Some(self.arg.with_value(self.arg.value.parent()?))
351365
}
352366

353-
pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> {
367+
pub fn map_token_down(
368+
&self,
369+
db: &dyn db::AstDatabase,
370+
item: Option<ast::Item>,
371+
token: InFile<&SyntaxToken>,
372+
) -> Option<InFile<SyntaxToken>> {
354373
assert_eq!(token.file_id, self.arg.file_id);
355-
let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
356-
let token_id = self.macro_arg.1.token_by_range(range)?;
357-
let token_id = self.macro_def.map_id_down(token_id);
374+
let token_id = if let Some(item) = item {
375+
let call_id = match self.expanded.file_id.0 {
376+
HirFileIdRepr::FileId(_) => return None,
377+
HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
378+
};
379+
let loc = db.lookup_intern_macro(call_id);
380+
381+
let token_range = token.value.text_range();
382+
match &loc.kind {
383+
MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
384+
let attr = item.attrs().nth(*invoc_attr_index as usize)?;
385+
match attr.token_tree() {
386+
Some(token_tree)
387+
if token_tree.syntax().text_range().contains_range(token_range) =>
388+
{
389+
let attr_input_start =
390+
token_tree.left_delimiter_token()?.text_range().start();
391+
let range = token.value.text_range().checked_sub(attr_input_start)?;
392+
let token_id =
393+
self.macro_arg_shift.shift(attr_args.map.token_by_range(range)?);
394+
Some(token_id)
395+
}
396+
_ => None,
397+
}
398+
}
399+
_ => None,
400+
}
401+
} else {
402+
None
403+
};
404+
405+
let token_id = match token_id {
406+
Some(token_id) => token_id,
407+
None => {
408+
let range =
409+
token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
410+
let token_id = self.macro_arg.1.token_by_range(range)?;
411+
self.macro_def.map_id_down(token_id)
412+
}
413+
};
358414

359415
let range = self.exp_map.range_by_token(token_id, token.value.kind())?;
360416

@@ -365,20 +421,36 @@ impl ExpansionInfo {
365421

366422
pub fn map_token_up(
367423
&self,
424+
db: &dyn db::AstDatabase,
368425
token: InFile<&SyntaxToken>,
369426
) -> Option<(InFile<SyntaxToken>, Origin)> {
370427
let token_id = self.exp_map.token_by_range(token.value.text_range())?;
428+
let (mut token_id, origin) = self.macro_def.map_id_up(token_id);
371429

372-
let (token_id, origin) = self.macro_def.map_id_up(token_id);
373-
let (token_map, tt) = match origin {
374-
mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
375-
mbe::Origin::Def => match (&*self.macro_def, self.def.as_ref()) {
376-
(
377-
db::TokenExpander::MacroRules { def_site_token_map, .. }
378-
| db::TokenExpander::MacroDef { def_site_token_map, .. },
379-
Some(tt),
380-
) => (def_site_token_map, tt.syntax().cloned()),
381-
_ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
430+
let call_id = match self.expanded.file_id.0 {
431+
HirFileIdRepr::FileId(_) => return None,
432+
HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id,
433+
};
434+
let loc = db.lookup_intern_macro(call_id);
435+
436+
let (token_map, tt) = match &loc.kind {
437+
MacroCallKind::Attr { attr_args, .. } => match self.macro_arg_shift.unshift(token_id) {
438+
Some(unshifted) => {
439+
token_id = unshifted;
440+
(&attr_args.map, self.attr_input_or_mac_def.clone()?.syntax().cloned())
441+
}
442+
None => (&self.macro_arg.1, self.arg.clone()),
443+
},
444+
_ => match origin {
445+
mbe::Origin::Call => (&self.macro_arg.1, self.arg.clone()),
446+
mbe::Origin::Def => match (&*self.macro_def, self.attr_input_or_mac_def.as_ref()) {
447+
(
448+
TokenExpander::MacroRules { def_site_token_map, .. }
449+
| TokenExpander::MacroDef { def_site_token_map, .. },
450+
Some(tt),
451+
) => (def_site_token_map, tt.syntax().cloned()),
452+
_ => panic!("`Origin::Def` used with non-`macro_rules!` macro"),
453+
},
382454
},
383455
};
384456

@@ -532,7 +604,7 @@ fn ascend_call_token(
532604
expansion: &ExpansionInfo,
533605
token: InFile<SyntaxToken>,
534606
) -> Option<InFile<SyntaxToken>> {
535-
let (mapped, origin) = expansion.map_token_up(token.as_ref())?;
607+
let (mapped, origin) = expansion.map_token_up(db, token.as_ref())?;
536608
if origin != Origin::Call {
537609
return None;
538610
}

0 commit comments

Comments
 (0)