Skip to content

Commit aa1491e

Browse files
committed
Record derive helper attributes, resolve them in IDE layer
1 parent 4e60db2 commit aa1491e

22 files changed

+144
-58
lines changed

crates/hir-def/src/lib.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -934,11 +934,11 @@ fn derive_macro_as_call_id(
934934
derive_attr: AttrId,
935935
derive_pos: u32,
936936
krate: CrateId,
937-
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
938-
) -> Result<MacroCallId, UnresolvedMacro> {
939-
let def: MacroDefId = resolver(item_attr.path.clone())
937+
resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
938+
) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
939+
let (macro_id, def_id) = resolver(item_attr.path.clone())
940940
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
941-
let res = def.as_lazy_macro(
941+
let call_id = def_id.as_lazy_macro(
942942
db.upcast(),
943943
krate,
944944
MacroCallKind::Derive {
@@ -947,7 +947,7 @@ fn derive_macro_as_call_id(
947947
derive_attr_index: derive_attr.ast_index,
948948
},
949949
);
950-
Ok(res)
950+
Ok((macro_id, def_id, call_id))
951951
}
952952

953953
fn attr_macro_as_call_id(

crates/hir-def/src/nameres.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ use crate::{
7676
path::ModPath,
7777
per_ns::PerNs,
7878
visibility::Visibility,
79-
AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, ModuleId, ProcMacroId,
79+
AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
8080
};
8181

8282
/// Contains the results of (early) name resolution.
@@ -108,7 +108,7 @@ pub struct DefMap {
108108
proc_macro_loading_error: Option<Box<str>>,
109109
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
110110
/// attributes.
111-
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroCallId)>>,
111+
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
112112

113113
/// Custom attributes registered with `#![register_attr]`.
114114
registered_attrs: Vec<SmolStr>,
@@ -299,7 +299,10 @@ impl DefMap {
299299
self.modules.iter()
300300
}
301301

302-
pub fn derive_helpers_in_scope(&self, id: AstId<ast::Adt>) -> Option<&[(Name, MacroCallId)]> {
302+
pub fn derive_helpers_in_scope(
303+
&self,
304+
id: AstId<ast::Adt>,
305+
) -> Option<&[(Name, MacroId, MacroCallId)]> {
303306
self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref)
304307
}
305308

crates/hir-def/src/nameres/collector.rs

+28-22
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use hir_expand::{
1818
ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
1919
MacroDefKind,
2020
};
21-
use itertools::Itertools;
21+
use itertools::{izip, Itertools};
2222
use la_arena::Idx;
2323
use limit::Limit;
2424
use rustc_hash::{FxHashMap, FxHashSet};
@@ -1055,16 +1055,20 @@ impl DefCollector<'_> {
10551055
};
10561056
let mut res = ReachedFixedPoint::Yes;
10571057
macros.retain(|directive| {
1058-
let resolver = |path| {
1058+
let resolver2 = |path| {
10591059
let resolved_res = self.def_map.resolve_path_fp_with_macro(
10601060
self.db,
10611061
ResolveMode::Other,
10621062
directive.module_id,
10631063
&path,
10641064
BuiltinShadowMode::Module,
10651065
);
1066-
resolved_res.resolved_def.take_macros().map(|it| macro_id_to_def_id(self.db, it))
1066+
resolved_res
1067+
.resolved_def
1068+
.take_macros()
1069+
.map(|it| (it, macro_id_to_def_id(self.db, it)))
10671070
};
1071+
let resolver = |path| resolver2(path).map(|(_, it)| it);
10681072

10691073
match &directive.kind {
10701074
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
@@ -1083,21 +1087,37 @@ impl DefCollector<'_> {
10831087
}
10841088
}
10851089
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos } => {
1086-
let call_id = derive_macro_as_call_id(
1090+
let id = derive_macro_as_call_id(
10871091
self.db,
10881092
ast_id,
10891093
*derive_attr,
10901094
*derive_pos as u32,
10911095
self.def_map.krate,
1092-
&resolver,
1096+
&resolver2,
10931097
);
1094-
if let Ok(call_id) = call_id {
1098+
1099+
if let Ok((macro_id, def_id, call_id)) = id {
10951100
self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc(
10961101
ast_id.ast_id,
10971102
call_id,
10981103
*derive_attr,
10991104
*derive_pos,
11001105
);
1106+
// Record its helper attributes.
1107+
if def_id.krate != self.def_map.krate {
1108+
let def_map = self.db.crate_def_map(def_id.krate);
1109+
if let Some(helpers) = def_map.exported_derives.get(&def_id) {
1110+
self.def_map
1111+
.derive_helpers_in_scope
1112+
.entry(ast_id.ast_id.map(|it| it.upcast()))
1113+
.or_default()
1114+
.extend(izip!(
1115+
helpers.iter().cloned(),
1116+
iter::repeat(macro_id),
1117+
iter::repeat(call_id),
1118+
));
1119+
}
1120+
}
11011121

11021122
push_resolved(directive, call_id);
11031123
res = ReachedFixedPoint::No;
@@ -1129,7 +1149,7 @@ impl DefCollector<'_> {
11291149

11301150
if let Some(ident) = path.as_ident() {
11311151
if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) {
1132-
if helpers.iter().any(|(it, _)| it == ident) {
1152+
if helpers.iter().any(|(it, ..)| it == ident) {
11331153
cov_mark::hit!(resolved_derive_helper);
11341154
// Resolved to derive helper. Collect the item's attributes again,
11351155
// starting after the derive helper.
@@ -1144,7 +1164,7 @@ impl DefCollector<'_> {
11441164
};
11451165
if matches!(
11461166
def,
1147-
MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. }
1167+
MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. }
11481168
if expander.is_derive()
11491169
) {
11501170
// Resolved to `#[derive]`
@@ -1313,20 +1333,6 @@ impl DefCollector<'_> {
13131333
self.def_map.diagnostics.push(diag);
13141334
}
13151335

1316-
// If we've just resolved a derive, record its helper attributes.
1317-
if let MacroCallKind::Derive { ast_id, .. } = &loc.kind {
1318-
if loc.def.krate != self.def_map.krate {
1319-
let def_map = self.db.crate_def_map(loc.def.krate);
1320-
if let Some(helpers) = def_map.exported_derives.get(&loc.def) {
1321-
self.def_map
1322-
.derive_helpers_in_scope
1323-
.entry(ast_id.map(|it| it.upcast()))
1324-
.or_default()
1325-
.extend(helpers.iter().cloned().zip(std::iter::repeat(macro_call_id)));
1326-
}
1327-
}
1328-
}
1329-
13301336
// Then, fetch and process the item tree. This will reuse the expansion result from above.
13311337
let item_tree = self.db.file_item_tree(file_id);
13321338
let mod_dir = self.mod_dirs[&module_id].clone();

crates/hir-def/src/resolver.rs

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ impl Resolver {
149149
self.resolve_module_path(db, path, BuiltinShadowMode::Module)
150150
}
151151

152+
// FIXME: This shouldn't exist
152153
pub fn resolve_module_path_in_trait_assoc_items(
153154
&self,
154155
db: &dyn DefDatabase,

crates/hir/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -2252,6 +2252,17 @@ impl Local {
22522252
}
22532253
}
22542254

2255+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2256+
pub struct DeriveHelper {
2257+
pub(crate) derive: MacroId,
2258+
}
2259+
2260+
impl DeriveHelper {
2261+
pub fn derive(&self) -> Macro {
2262+
Macro { id: self.derive.into() }
2263+
}
2264+
}
2265+
22552266
// FIXME: Wrong name? This is could also be a registered attribute
22562267
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
22572268
pub struct BuiltinAttr {

crates/hir/src/semantics.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ use crate::{
2929
db::HirDatabase,
3030
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
3131
source_analyzer::{resolve_hir_path, SourceAnalyzer},
32-
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
33-
HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path,
34-
ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
32+
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function,
33+
HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef,
34+
Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
3535
};
3636

3737
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -47,6 +47,7 @@ pub enum PathResolution {
4747
SelfType(Impl),
4848
BuiltinAttr(BuiltinAttr),
4949
ToolModule(ToolModule),
50+
DeriveHelper(DeriveHelper),
5051
}
5152

5253
impl PathResolution {
@@ -71,6 +72,7 @@ impl PathResolution {
7172
PathResolution::BuiltinAttr(_)
7273
| PathResolution::ToolModule(_)
7374
| PathResolution::Local(_)
75+
| PathResolution::DeriveHelper(_)
7476
| PathResolution::ConstParam(_) => None,
7577
PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
7678
PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
@@ -856,7 +858,9 @@ impl<'db> SemanticsImpl<'db> {
856858
None
857859
}
858860
}?;
859-
861+
if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(token.file_id, &adt))) {
862+
return None;
863+
}
860864
// Not an attribute, nor a derive, so it's either a builtin or a derive helper
861865
// Try to resolve to a derive helper and downmap
862866
let attr_name = attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
@@ -865,7 +869,7 @@ impl<'db> SemanticsImpl<'db> {
865869
def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?;
866870
let item = Some(adt.into());
867871
let mut res = None;
868-
for (_, derive) in helpers.iter().filter(|(helper, _)| *helper == attr_name) {
872+
for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) {
869873
res = res.or(process_expansion_for_token(
870874
&mut stack,
871875
derive.as_file(),

crates/hir/src/source_analyzer.rs

+50-14
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ use syntax::{
4343

4444
use crate::{
4545
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
46-
BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct,
47-
ToolModule, Trait, Type, TypeAlias, Variant,
46+
BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
47+
Struct, ToolModule, Trait, Type, TypeAlias, Variant,
4848
};
4949

5050
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -429,36 +429,72 @@ impl SourceAnalyzer {
429429
}
430430
}
431431

432-
let is_path_of_attr = path
432+
let meta_path = path
433433
.syntax()
434434
.ancestors()
435-
.map(|it| it.kind())
436-
.take_while(|&kind| ast::Path::can_cast(kind) || ast::Meta::can_cast(kind))
435+
.take_while(|it| {
436+
let kind = it.kind();
437+
ast::Path::can_cast(kind) || ast::Meta::can_cast(kind)
438+
})
437439
.last()
438-
.map_or(false, ast::Meta::can_cast);
440+
.and_then(ast::Meta::cast);
439441

440442
// Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are
441443
// trying to resolve foo::bar.
442444
if path.parent_path().is_some() {
443445
return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path) {
444-
None if is_path_of_attr => {
446+
None if meta_path.is_some() => {
445447
path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
446448
ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
447449
.map(PathResolution::ToolModule)
448450
})
449451
}
450452
res => res,
451453
};
452-
} else if is_path_of_attr {
454+
} else if let Some(meta_path) = meta_path {
453455
// Case where we are resolving the final path segment of a path in an attribute
454456
// in this case we have to check for inert/builtin attributes and tools and prioritize
455457
// resolution of attributes over other namespaces
456-
let name_ref = path.as_single_name_ref();
457-
let builtin = name_ref.as_ref().and_then(|name_ref| {
458-
BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text())
459-
});
460-
if let Some(_) = builtin {
461-
return builtin.map(PathResolution::BuiltinAttr);
458+
if let Some(name_ref) = path.as_single_name_ref() {
459+
let builtin =
460+
BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text());
461+
if let Some(_) = builtin {
462+
return builtin.map(PathResolution::BuiltinAttr);
463+
}
464+
465+
if let Some(attr) = meta_path.parent_attr() {
466+
let adt = if let Some(field) =
467+
attr.syntax().parent().and_then(ast::RecordField::cast)
468+
{
469+
field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
470+
} else if let Some(field) =
471+
attr.syntax().parent().and_then(ast::TupleField::cast)
472+
{
473+
field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
474+
} else if let Some(variant) =
475+
attr.syntax().parent().and_then(ast::Variant::cast)
476+
{
477+
variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast)
478+
} else {
479+
None
480+
};
481+
if let Some(adt) = adt {
482+
let ast_id = db.ast_id_map(self.file_id).ast_id(&adt);
483+
if let Some(helpers) = self
484+
.resolver
485+
.def_map()
486+
.derive_helpers_in_scope(InFile::new(self.file_id, ast_id))
487+
{
488+
// FIXME: Multiple derives can have the same helper
489+
let name_ref = name_ref.as_name();
490+
if let Some(&(_, derive, _)) =
491+
helpers.iter().find(|(name, ..)| *name == name_ref)
492+
{
493+
return Some(PathResolution::DeriveHelper(DeriveHelper { derive }));
494+
}
495+
}
496+
}
497+
}
462498
}
463499
return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
464500
Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))),

crates/ide-completion/src/item.rs

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ impl CompletionItemKind {
292292
SymbolKind::Const => "ct",
293293
SymbolKind::ConstParam => "cp",
294294
SymbolKind::Derive => "de",
295+
SymbolKind::DeriveHelper => "dh",
295296
SymbolKind::Enum => "en",
296297
SymbolKind::Field => "fd",
297298
SymbolKind::Function => "fn",

0 commit comments

Comments
 (0)