Skip to content

Commit 7fdbdc4

Browse files
committed
Enable auto-import and qualify-path in derive attributes
1 parent 3018ffd commit 7fdbdc4

File tree

8 files changed

+123
-37
lines changed

8 files changed

+123
-37
lines changed

crates/hir/src/semantics/source_to_def.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,14 @@ impl SourceToDefCtx<'_, '_> {
299299
dyn_map[keys::CONST_PARAM].get(&src).copied()
300300
}
301301

302-
// FIXME: use DynMap as well?
303302
pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
303+
let makro = self.dyn_map(src.as_ref()).and_then(|it| it[keys::MACRO].get(&src).copied());
304+
if let res @ Some(_) = makro {
305+
return res;
306+
}
307+
308+
// Not all macros are recorded in the dyn map, only the ones behaving like items, so fall back
309+
// for the non-item like definitions.
304310
let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
305311
let ast_id = AstId::new(src.file_id, file_ast_id.upcast());
306312
let kind = MacroDefKind::Declarative(ast_id);

crates/hir_def/src/child_by_source.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ impl ChildBySource for ModuleId {
101101
impl ChildBySource for ItemScope {
102102
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
103103
self.declarations().for_each(|item| add_module_def(db, file_id, res, item));
104+
self.macros().for_each(|(_, makro)| {
105+
let ast_id = makro.ast_id();
106+
if ast_id.either(|it| it.file_id, |it| it.file_id) == file_id {
107+
let src = match ast_id {
108+
Either::Left(ast_id) => ast_id.with_value(ast_id.to_node(db.upcast())),
109+
// FIXME: Do we need to add proc-macros into a PROCMACRO dynmap here?
110+
Either::Right(_fn) => return,
111+
};
112+
res[keys::MACRO].insert(src, makro);
113+
}
114+
});
104115
self.unnamed_consts().for_each(|konst| {
105116
let src = konst.lookup(db).source(db);
106117
res[keys::CONST].insert(src, konst);

crates/hir_def/src/keys.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
3131
pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
3232
pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
3333

34-
pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
34+
pub const MACRO: Key<ast::Macro, MacroDefId> = Key::new();
3535
pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
3636
pub const DERIVE_MACRO: Key<ast::Attr, Box<[MacroCallId]>> = Key::new();
3737

crates/hir_def/src/nameres/collector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1907,7 +1907,7 @@ impl ModCollector<'_, '_> {
19071907
let mac = &self.item_tree[id];
19081908
let ast_id = InFile::new(self.file_id(), mac.ast_id.upcast());
19091909

1910-
// Case 1: bulitin macros
1910+
// Case 1: builtin macros
19111911
let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
19121912
if attrs.by_key("rustc_builtin_macro").exists() {
19131913
let macro_id = find_builtin_macro(&mac.name, krate, ast_id)

crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
<span class="brace">}</span>
129129

130130
<span class="comment documentation">/// ```</span>
131+
<span class="comment documentation">/// </span><span class="keyword injected">macro_rules</span><span class="punctuation injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="angle injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
131132
<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
132133
<span class="comment documentation">/// ```</span>
133134
<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>

crates/ide/src/syntax_highlighting/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ pub mod module {
707707
}
708708
709709
/// ```
710+
/// macro_rules! noop { ($expr:expr) => { $expr }}
710711
/// noop!(1);
711712
/// ```
712713
macro_rules! noop {

crates/ide_assists/src/handlers/auto_import.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ide_db::helpers::{
33
insert_use::{insert_use, ImportScope},
44
mod_path_to_ast,
55
};
6-
use syntax::{ast, AstNode, AstToken, SyntaxNode};
6+
use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxElement};
77

88
use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
99

@@ -90,9 +90,18 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
9090
return None;
9191
}
9292

93-
let range = ctx.sema.original_range(&syntax_under_caret).range;
93+
let range = match &syntax_under_caret {
94+
NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
95+
NodeOrToken::Token(token) => token.text_range(),
96+
};
9497
let group_label = group_label(import_assets.import_candidate());
95-
let scope = ImportScope::find_insert_use_container_with_macros(&syntax_under_caret, &ctx.sema)?;
98+
let scope = ImportScope::find_insert_use_container_with_macros(
99+
&match syntax_under_caret {
100+
NodeOrToken::Node(it) => it,
101+
NodeOrToken::Token(it) => it.parent()?,
102+
},
103+
&ctx.sema,
104+
)?;
96105

97106
// we aren't interested in different namespaces
98107
proposed_imports.dedup_by(|a, b| a.import_path == b.import_path);
@@ -115,23 +124,24 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
115124
Some(())
116125
}
117126

118-
pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> {
127+
pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxElement)> {
119128
if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
120129
ImportAssets::for_exact_path(&path_under_caret, &ctx.sema)
121-
.zip(Some(path_under_caret.syntax().clone()))
130+
.zip(Some(path_under_caret.syntax().clone().into()))
122131
} else if let Some(method_under_caret) =
123132
ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>()
124133
{
125134
ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
126-
.zip(Some(method_under_caret.syntax().clone()))
135+
.zip(Some(method_under_caret.syntax().clone().into()))
127136
} else if let Some(pat) = ctx
128137
.find_node_at_offset_with_descend::<ast::IdentPat>()
129138
.filter(ast::IdentPat::is_simple_ident)
130139
{
131-
ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone()))
140+
ImportAssets::for_ident_pat(&ctx.sema, &pat).zip(Some(pat.syntax().clone().into()))
132141
} else {
142+
// FIXME: Descend?
133143
let ident = ctx.find_token_at_offset()?;
134-
ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(ident.syntax().parent())
144+
ImportAssets::for_derive_ident(&ctx.sema, &ident).zip(Some(ident.syntax().clone().into()))
135145
}
136146
}
137147

@@ -1028,6 +1038,32 @@ mod foo {
10281038
fn foo() {
10291039
let Foo;
10301040
}
1041+
"#,
1042+
);
1043+
}
1044+
1045+
#[test]
1046+
fn works_in_derives() {
1047+
check_assist(
1048+
auto_import,
1049+
r#"
1050+
//- minicore:derive
1051+
mod foo {
1052+
#[rustc_builtin_macro]
1053+
pub macro Copy {}
1054+
}
1055+
#[derive(Copy$0)]
1056+
struct Foo;
1057+
"#,
1058+
r#"
1059+
use foo::Copy;
1060+
1061+
mod foo {
1062+
#[rustc_builtin_macro]
1063+
pub macro Copy {}
1064+
}
1065+
#[derive(Copy)]
1066+
struct Foo;
10311067
"#,
10321068
);
10331069
}

crates/ide_assists/src/handlers/qualify_path.rs

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use ide_db::RootDatabase;
99
use syntax::{
1010
ast,
1111
ast::{make, HasArgList},
12-
AstNode,
12+
AstNode, NodeOrToken,
1313
};
1414

1515
use crate::{
@@ -42,32 +42,39 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
4242
return None;
4343
}
4444

45-
let range = ctx.sema.original_range(&syntax_under_caret).range;
45+
let range = match &syntax_under_caret {
46+
NodeOrToken::Node(node) => ctx.sema.original_range(node).range,
47+
NodeOrToken::Token(token) => token.text_range(),
48+
};
4649
let candidate = import_assets.import_candidate();
47-
let qualify_candidate = match candidate {
48-
ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
49-
cov_mark::hit!(qualify_path_qualifier_start);
50-
let path = ast::Path::cast(syntax_under_caret)?;
51-
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
52-
QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
53-
}
54-
ImportCandidate::Path(_) => {
55-
cov_mark::hit!(qualify_path_unqualified_name);
56-
let path = ast::Path::cast(syntax_under_caret)?;
57-
let generics = path.segment()?.generic_arg_list();
58-
QualifyCandidate::UnqualifiedName(generics)
59-
}
60-
ImportCandidate::TraitAssocItem(_) => {
61-
cov_mark::hit!(qualify_path_trait_assoc_item);
62-
let path = ast::Path::cast(syntax_under_caret)?;
63-
let (qualifier, segment) = (path.qualifier()?, path.segment()?);
64-
QualifyCandidate::TraitAssocItem(qualifier, segment)
65-
}
66-
ImportCandidate::TraitMethod(_) => {
67-
cov_mark::hit!(qualify_path_trait_method);
68-
let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
69-
QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
70-
}
50+
let qualify_candidate = match syntax_under_caret {
51+
NodeOrToken::Node(syntax_under_caret) => match candidate {
52+
ImportCandidate::Path(candidate) if candidate.qualifier.is_some() => {
53+
cov_mark::hit!(qualify_path_qualifier_start);
54+
let path = ast::Path::cast(syntax_under_caret)?;
55+
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
56+
QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
57+
}
58+
ImportCandidate::Path(_) => {
59+
cov_mark::hit!(qualify_path_unqualified_name);
60+
let path = ast::Path::cast(syntax_under_caret)?;
61+
let generics = path.segment()?.generic_arg_list();
62+
QualifyCandidate::UnqualifiedName(generics)
63+
}
64+
ImportCandidate::TraitAssocItem(_) => {
65+
cov_mark::hit!(qualify_path_trait_assoc_item);
66+
let path = ast::Path::cast(syntax_under_caret)?;
67+
let (qualifier, segment) = (path.qualifier()?, path.segment()?);
68+
QualifyCandidate::TraitAssocItem(qualifier, segment)
69+
}
70+
ImportCandidate::TraitMethod(_) => {
71+
cov_mark::hit!(qualify_path_trait_method);
72+
let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
73+
QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
74+
}
75+
},
76+
// derive attribute path
77+
NodeOrToken::Token(_) => QualifyCandidate::UnqualifiedName(None),
7178
};
7279

7380
// we aren't interested in different namespaces
@@ -1236,6 +1243,30 @@ fn main() {
12361243
let test_struct = test_mod::TestStruct {};
12371244
test_mod::TestTrait::test_method::<()>(&test_struct)
12381245
}
1246+
"#,
1247+
);
1248+
}
1249+
1250+
#[test]
1251+
fn works_in_derives() {
1252+
check_assist(
1253+
qualify_path,
1254+
r#"
1255+
//- minicore:derive
1256+
mod foo {
1257+
#[rustc_builtin_macro]
1258+
pub macro Copy {}
1259+
}
1260+
#[derive(Copy$0)]
1261+
struct Foo;
1262+
"#,
1263+
r#"
1264+
mod foo {
1265+
#[rustc_builtin_macro]
1266+
pub macro Copy {}
1267+
}
1268+
#[derive(foo::Copy)]
1269+
struct Foo;
12391270
"#,
12401271
);
12411272
}

0 commit comments

Comments
 (0)