Skip to content

Commit ce4670f

Browse files
bors[bot]Veykril
andauthored
Merge #10014
10014: feat: Expand derive macros under cursor in `Expand Macro Recursively` r=Veykril a=Veykril Expands the derive macros under the cursor if it is one a derive attribute, with this the feature should be basically feature complete I believe(except for the whitespace problem ofc)? Actually this might interact a bit funky with items that have attributes ***and*** derives since we don't descend the cursor token into macro invocations first, for obvious reasons. So I expected trying to expand a derive in that case will either just expand the attributes on the item or fail in general. Closes #4005 Co-authored-by: Lukas Wirth <[email protected]>
2 parents 6287d38 + d99b81f commit ce4670f

File tree

7 files changed

+90
-2
lines changed

7 files changed

+90
-2
lines changed

crates/hir/src/semantics.rs

+16
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
148148
self.imp.expand_attr_macro(item)
149149
}
150150

151+
pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option<SyntaxNode> {
152+
self.imp.expand_derive_macro(derive)
153+
}
154+
151155
pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
152156
self.imp.is_attr_macro_call(item)
153157
}
@@ -385,6 +389,18 @@ impl<'db> SemanticsImpl<'db> {
385389
Some(node)
386390
}
387391

392+
fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
393+
let item = attr.syntax().parent().and_then(ast::Item::cast)?;
394+
let sa = self.analyze(item.syntax());
395+
let item = InFile::new(sa.file_id, &item);
396+
let src = InFile::new(sa.file_id, attr.clone());
397+
let macro_call_id = self.with_ctx(|ctx| ctx.attr_to_derive_macro_call(item, src))?;
398+
let file_id = macro_call_id.as_file();
399+
let node = self.db.parse_or_expand(file_id)?;
400+
self.cache(node.clone(), file_id);
401+
Some(node)
402+
}
403+
388404
fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
389405
let sa = self.analyze(item.syntax());
390406
let src = InFile::new(sa.file_id, item.clone());

crates/hir/src/semantics/source_to_def.rs

+9
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,15 @@ impl SourceToDefCtx<'_, '_> {
242242
map[keys::ATTR_MACRO].get(&src).copied()
243243
}
244244

245+
pub(super) fn attr_to_derive_macro_call(
246+
&mut self,
247+
item: InFile<&ast::Item>,
248+
src: InFile<ast::Attr>,
249+
) -> Option<MacroCallId> {
250+
let map = self.dyn_map(item)?;
251+
map[keys::DERIVE_MACRO].get(&src).copied()
252+
}
253+
245254
fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
246255
&mut self,
247256
src: InFile<Ast>,

crates/hir_def/src/child_by_source.rs

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
use either::Either;
88
use hir_expand::HirFileId;
9+
use syntax::ast::AttrsOwner;
910

1011
use crate::{
1112
db::DefDatabase,
@@ -108,6 +109,12 @@ impl ChildBySource for ItemScope {
108109
let item = ast_id.with_value(ast_id.to_node(db.upcast()));
109110
res[keys::ATTR_MACRO].insert(item, call_id);
110111
});
112+
self.derive_macro_invocs().for_each(|(ast_id, (attr_id, call_id))| {
113+
let item = ast_id.to_node(db.upcast());
114+
if let Some(attr) = item.attrs().nth(attr_id.ast_index as usize) {
115+
res[keys::DERIVE_MACRO].insert(ast_id.with_value(attr), call_id);
116+
}
117+
});
111118

112119
fn add_module_def(
113120
db: &dyn DefDatabase,

crates/hir_def/src/item_scope.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use stdx::format_to;
1212
use syntax::ast;
1313

1414
use crate::{
15-
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
16-
LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
15+
attr::AttrId, db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType,
16+
ConstId, ImplId, LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
1717
};
1818

1919
#[derive(Copy, Clone)]
@@ -61,6 +61,7 @@ pub struct ItemScope {
6161
// be all resolved to the last one defined if shadowing happens.
6262
legacy_macros: FxHashMap<Name, MacroDefId>,
6363
attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
64+
derive_macros: FxHashMap<AstId<ast::Item>, (AttrId, MacroCallId)>,
6465
}
6566

6667
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
@@ -182,6 +183,21 @@ impl ItemScope {
182183
self.attr_macros.iter().map(|(k, v)| (*k, *v))
183184
}
184185

186+
pub(crate) fn add_derive_macro_invoc(
187+
&mut self,
188+
item: AstId<ast::Item>,
189+
call: MacroCallId,
190+
attr_id: AttrId,
191+
) {
192+
self.derive_macros.insert(item, (attr_id, call));
193+
}
194+
195+
pub(crate) fn derive_macro_invocs(
196+
&self,
197+
) -> impl Iterator<Item = (AstId<ast::Item>, (AttrId, MacroCallId))> + '_ {
198+
self.derive_macros.iter().map(|(k, v)| (*k, *v))
199+
}
200+
185201
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
186202
self.unnamed_trait_imports.get(&tr).copied()
187203
}
@@ -320,6 +336,7 @@ impl ItemScope {
320336
unnamed_trait_imports,
321337
legacy_macros,
322338
attr_macros,
339+
derive_macros,
323340
} = self;
324341
types.shrink_to_fit();
325342
values.shrink_to_fit();
@@ -331,6 +348,7 @@ impl ItemScope {
331348
unnamed_trait_imports.shrink_to_fit();
332349
legacy_macros.shrink_to_fit();
333350
attr_macros.shrink_to_fit();
351+
derive_macros.shrink_to_fit();
334352
}
335353
}
336354

crates/hir_def/src/keys.rs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
3333

3434
pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
3535
pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
36+
pub const DERIVE_MACRO: Key<ast::Attr, MacroCallId> = Key::new();
3637

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

crates/hir_def/src/nameres/collector.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,12 @@ impl DefCollector<'_> {
10471047
&resolver,
10481048
) {
10491049
Ok(call_id) => {
1050+
self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
1051+
ast_id.ast_id,
1052+
call_id,
1053+
*derive_attr,
1054+
);
1055+
10501056
resolved.push((directive.module_id, call_id, directive.depth));
10511057
res = ReachedFixedPoint::No;
10521058
return false;

crates/ide/src/expand_macro.rs

+31
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::iter;
22

33
use hir::Semantics;
44
use ide_db::{helpers::pick_best_token, RootDatabase};
5+
use itertools::Itertools;
56
use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T};
67

78
use crate::FilePosition;
@@ -33,6 +34,18 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
3334
let mut expanded = None;
3435
let mut name = None;
3536
for node in tok.ancestors() {
37+
if let Some(attr) = ast::Attr::cast(node.clone()) {
38+
if let Some((path, tt)) = attr.as_simple_call() {
39+
if path == "derive" {
40+
let mut tt = tt.syntax().children_with_tokens().skip(1).join("");
41+
tt.pop();
42+
name = Some(tt);
43+
expanded = sema.expand_derive_macro(&attr);
44+
break;
45+
}
46+
}
47+
}
48+
3649
if let Some(item) = ast::Item::cast(node.clone()) {
3750
if let Some(def) = sema.resolve_attr_macro_call(&item) {
3851
name = def.name(db).map(|name| name.to_string());
@@ -325,4 +338,22 @@ fn main() {
325338
0 "#]],
326339
);
327340
}
341+
342+
#[test]
343+
fn macro_expand_derive() {
344+
check(
345+
r#"
346+
347+
#[rustc_builtin_macro]
348+
pub macro Clone {}
349+
350+
#[derive(C$0lone)]
351+
struct Foo {}
352+
"#,
353+
expect![[r#"
354+
Clone
355+
impl< >crate::clone::Clone for Foo< >{}
356+
"#]],
357+
);
358+
}
328359
}

0 commit comments

Comments
 (0)