Skip to content

Commit 2f376f7

Browse files
Merge #9169
9169: internal: steps towards attribute macro token mapping r=jonas-schievink a=jonas-schievink This doesn't work yet, but we seem to be getting a bit further along (for example, we now stop highlighting `use` items inside item with attribute macros as if they were written verbatim). bors r+ Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
2 parents 1aff3b4 + 33be576 commit 2f376f7

File tree

8 files changed

+170
-30
lines changed

8 files changed

+170
-30
lines changed

crates/hir/src/semantics.rs

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
117117
pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
118118
self.imp.expand(macro_call)
119119
}
120+
121+
/// If `item` has an attribute macro attached to it, expands it.
122+
pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
123+
self.imp.expand_attr_macro(item)
124+
}
125+
126+
pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
127+
self.imp.is_attr_macro_call(item)
128+
}
129+
120130
pub fn speculative_expand(
121131
&self,
122132
actual_macro_call: &ast::MacroCall,
@@ -332,6 +342,22 @@ impl<'db> SemanticsImpl<'db> {
332342
Some(node)
333343
}
334344

345+
fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
346+
let sa = self.analyze(item.syntax());
347+
let src = InFile::new(sa.file_id, item.clone());
348+
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
349+
let file_id = macro_call_id.as_file();
350+
let node = self.db.parse_or_expand(file_id)?;
351+
self.cache(node.clone(), file_id);
352+
Some(node)
353+
}
354+
355+
fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
356+
let sa = self.analyze(item.syntax());
357+
let src = InFile::new(sa.file_id, item.clone());
358+
self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
359+
}
360+
335361
fn speculative_expand(
336362
&self,
337363
actual_macro_call: &ast::MacroCall,
@@ -362,25 +388,57 @@ impl<'db> SemanticsImpl<'db> {
362388

363389
let token = successors(Some(InFile::new(sa.file_id, token)), |token| {
364390
self.db.unwind_if_cancelled();
365-
let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
366-
let tt = macro_call.token_tree()?;
367-
if !tt.syntax().text_range().contains_range(token.value.text_range()) {
368-
return None;
369-
}
370-
let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
371-
let token = self
372-
.expansion_info_cache
373-
.borrow_mut()
374-
.entry(file_id)
375-
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
376-
.as_ref()?
377-
.map_token_down(token.as_ref())?;
378-
379-
if let Some(parent) = token.value.parent() {
380-
self.cache(find_root(&parent), token.file_id);
391+
392+
for node in token.value.ancestors() {
393+
match_ast! {
394+
match node {
395+
ast::MacroCall(macro_call) => {
396+
let tt = macro_call.token_tree()?;
397+
if !tt.syntax().text_range().contains_range(token.value.text_range()) {
398+
return None;
399+
}
400+
let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
401+
let token = self
402+
.expansion_info_cache
403+
.borrow_mut()
404+
.entry(file_id)
405+
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
406+
.as_ref()?
407+
.map_token_down(token.as_ref())?;
408+
409+
if let Some(parent) = token.value.parent() {
410+
self.cache(find_root(&parent), token.file_id);
411+
}
412+
413+
return Some(token);
414+
},
415+
ast::Item(item) => {
416+
match self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item))) {
417+
Some(call_id) => {
418+
let file_id = call_id.as_file();
419+
let token = self
420+
.expansion_info_cache
421+
.borrow_mut()
422+
.entry(file_id)
423+
.or_insert_with(|| file_id.expansion_info(self.db.upcast()))
424+
.as_ref()?
425+
.map_token_down(token.as_ref())?;
426+
427+
if let Some(parent) = token.value.parent() {
428+
self.cache(find_root(&parent), token.file_id);
429+
}
430+
431+
return Some(token);
432+
}
433+
None => {}
434+
}
435+
},
436+
_ => {}
437+
}
438+
}
381439
}
382440

383-
Some(token)
441+
None
384442
})
385443
.last()
386444
.unwrap();

crates/hir/src/semantics/source_to_def.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use hir_def::{
1010
ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
1111
UnionId, VariantId,
1212
};
13-
use hir_expand::{name::AsName, AstId, MacroDefKind};
13+
use hir_expand::{name::AsName, AstId, MacroCallId, MacroDefKind};
1414
use rustc_hash::FxHashMap;
1515
use smallvec::SmallVec;
1616
use stdx::impl_from;
@@ -145,16 +145,25 @@ impl SourceToDefCtx<'_, '_> {
145145
Some((container, label_id))
146146
}
147147

148+
pub(super) fn item_to_macro_call(&mut self, src: InFile<ast::Item>) -> Option<MacroCallId> {
149+
let map = self.dyn_map(src.as_ref())?;
150+
map[keys::ATTR_MACRO].get(&src).copied()
151+
}
152+
148153
fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
149154
&mut self,
150155
src: InFile<Ast>,
151156
key: Key<Ast, ID>,
152157
) -> Option<ID> {
153-
let container = self.find_container(src.as_ref().map(|it| it.syntax()))?;
158+
self.dyn_map(src.as_ref())?[key].get(&src).copied()
159+
}
160+
161+
fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
162+
let container = self.find_container(src.map(|it| it.syntax()))?;
154163
let db = self.db;
155164
let dyn_map =
156165
&*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
157-
dyn_map[key].get(&src).copied()
166+
Some(dyn_map)
158167
}
159168

160169
pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> {

crates/hir_def/src/child_by_source.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ impl ChildBySource for ItemScope {
8585
res[keys::CONST].insert(src, konst);
8686
});
8787
self.impls().for_each(|imp| add_impl(db, res, imp));
88+
self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
89+
let item = ast_id.with_value(ast_id.to_node(db.upcast()));
90+
res[keys::ATTR_MACRO].insert(item, call_id);
91+
});
8892

8993
fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
9094
match item {

crates/hir_def/src/item_scope.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
use std::collections::hash_map::Entry;
55

66
use base_db::CrateId;
7-
use hir_expand::name::Name;
8-
use hir_expand::MacroDefKind;
7+
use hir_expand::{name::Name, AstId, MacroCallId, MacroDefKind};
98
use once_cell::sync::Lazy;
109
use rustc_hash::{FxHashMap, FxHashSet};
1110
use stdx::format_to;
11+
use syntax::ast;
1212

1313
use crate::{
1414
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
@@ -53,6 +53,7 @@ pub struct ItemScope {
5353
// FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
5454
// be all resolved to the last one defined if shadowing happens.
5555
legacy_macros: FxHashMap<Name, MacroDefId>,
56+
attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
5657
}
5758

5859
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
@@ -169,6 +170,16 @@ impl ItemScope {
169170
self.legacy_macros.insert(name, mac);
170171
}
171172

173+
pub(crate) fn add_attr_macro_invoc(&mut self, item: AstId<ast::Item>, call: MacroCallId) {
174+
self.attr_macros.insert(item, call);
175+
}
176+
177+
pub(crate) fn attr_macro_invocs(
178+
&self,
179+
) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
180+
self.attr_macros.iter().map(|(k, v)| (*k, *v))
181+
}
182+
172183
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
173184
self.unnamed_trait_imports.get(&tr).copied()
174185
}
@@ -307,6 +318,7 @@ impl ItemScope {
307318
unnamed_consts,
308319
unnamed_trait_imports,
309320
legacy_macros,
321+
attr_macros,
310322
} = self;
311323
types.shrink_to_fit();
312324
values.shrink_to_fit();
@@ -317,6 +329,7 @@ impl ItemScope {
317329
unnamed_consts.shrink_to_fit();
318330
unnamed_trait_imports.shrink_to_fit();
319331
legacy_macros.shrink_to_fit();
332+
attr_macros.shrink_to_fit();
320333
}
321334
}
322335

crates/hir_def/src/keys.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::marker::PhantomData;
44

5-
use hir_expand::{InFile, MacroDefId};
5+
use hir_expand::{InFile, MacroCallId, MacroDefId};
66
use rustc_hash::FxHashMap;
77
use syntax::{ast, AstNode, AstPtr};
88

@@ -32,6 +32,7 @@ pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
3232
pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
3333

3434
pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
35+
pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
3536

3637
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
3738
/// equal if they point to exactly the same object.

crates/hir_def/src/nameres/collector.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,11 @@ impl DefCollector<'_> {
11121112
return false;
11131113
}
11141114
}
1115+
1116+
self.def_map.modules[directive.module_id]
1117+
.scope
1118+
.add_attr_macro_invoc(ast_id.ast_id, call_id);
1119+
11151120
resolved.push((directive.module_id, call_id, directive.depth));
11161121
res = ReachedFixedPoint::No;
11171122
return false;

crates/ide/src/expand_macro.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use std::iter;
33
use hir::Semantics;
44
use ide_db::RootDatabase;
55
use syntax::{
6-
algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*,
7-
SyntaxNode, WalkEvent, T,
6+
ast, match_ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T,
87
};
98

109
use crate::FilePosition;
@@ -28,16 +27,37 @@ pub struct ExpandedMacro {
2827
pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
2928
let sema = Semantics::new(db);
3029
let file = sema.parse(position.file_id);
31-
let mac = find_node_at_offset::<ast::MacroCall>(file.syntax(), position.offset)?;
32-
let name = mac.path()?.segment()?.name_ref()?;
3330

34-
let expanded = expand_macro_recur(&sema, &mac)?;
31+
let tok = file.syntax().token_at_offset(position.offset).left_biased()?;
32+
let mut expanded = None;
33+
let mut name = None;
34+
for node in tok.ancestors() {
35+
match_ast! {
36+
match node {
37+
ast::MacroCall(mac) => {
38+
name = Some(mac.path()?.segment()?.name_ref()?.to_string());
39+
expanded = expand_macro_recur(&sema, &mac);
40+
break;
41+
},
42+
ast::Item(item) => {
43+
// FIXME: add the macro name
44+
// FIXME: make this recursive too
45+
name = Some("?".to_string());
46+
expanded = sema.expand_attr_macro(&item);
47+
if expanded.is_some() {
48+
break;
49+
}
50+
},
51+
_ => {}
52+
}
53+
}
54+
}
3555

3656
// FIXME:
3757
// macro expansion may lose all white space information
3858
// But we hope someday we can use ra_fmt for that
39-
let expansion = insert_whitespaces(expanded);
40-
Some(ExpandedMacro { name: name.to_string(), expansion })
59+
let expansion = insert_whitespaces(expanded?);
60+
Some(ExpandedMacro { name: name?, expansion })
4161
}
4262

4363
fn expand_macro_recur(

crates/ide/src/syntax_highlighting.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ fn traverse(
192192
let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
193193

194194
let mut current_macro_call: Option<ast::MacroCall> = None;
195+
let mut current_attr_macro_call = None;
195196
let mut current_macro: Option<ast::Macro> = None;
196197
let mut macro_highlighter = MacroHighlighter::default();
197198
let mut inside_attribute = false;
@@ -227,6 +228,19 @@ fn traverse(
227228
}
228229
_ => (),
229230
}
231+
match event.clone().map(|it| it.into_node().and_then(ast::Item::cast)) {
232+
WalkEvent::Enter(Some(item)) => {
233+
if sema.is_attr_macro_call(&item) {
234+
current_attr_macro_call = Some(item);
235+
}
236+
}
237+
WalkEvent::Leave(Some(item)) => {
238+
if current_attr_macro_call == Some(item) {
239+
current_attr_macro_call = None;
240+
}
241+
}
242+
_ => (),
243+
}
230244

231245
match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) {
232246
WalkEvent::Enter(Some(mac)) => {
@@ -286,6 +300,22 @@ fn traverse(
286300
}
287301
None => token.into(),
288302
}
303+
} else if current_attr_macro_call.is_some() {
304+
let token = match element.clone().into_token() {
305+
Some(it) => it,
306+
_ => continue,
307+
};
308+
let token = sema.descend_into_macros(token.clone());
309+
match token.parent() {
310+
Some(parent) => {
311+
// We only care Name and Name_ref
312+
match (token.kind(), parent.kind()) {
313+
(IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
314+
_ => token.into(),
315+
}
316+
}
317+
None => token.into(),
318+
}
289319
} else {
290320
element.clone()
291321
};

0 commit comments

Comments
 (0)