Skip to content

Commit e36a20c

Browse files
committed
Auto merge of #12864 - Veykril:derive-helpers, r=Veykril
feat: Resolve derive helper attributes in IDE layer ![image](https://user-images.githubusercontent.com/3757771/180647125-98cae13b-a7b4-446b-8a64-fae014f982e2.png) Also downmaps tokens inside deriver helpers to the derive expansion ![image](https://user-images.githubusercontent.com/3757771/180642110-699bdc73-e1f3-4692-94f2-21544f824ab9.png) This does not handle the case where multiple derives make use of the same helper name though, unsure how to tackle that yet. Partially addresses rust-lang/rust-analyzer#10935
2 parents 7ba94a8 + ddad284 commit e36a20c

27 files changed

+332
-135
lines changed

crates/hir-def/src/data.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
db::DefDatabase,
1313
intern::Interned,
1414
item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
15-
nameres::{attr_resolution::ResolvedAttr, DefMap},
15+
nameres::{attr_resolution::ResolvedAttr, proc_macro::ProcMacroKind, DefMap},
1616
type_ref::{TraitRef, TypeBound, TypeRef},
1717
visibility::RawVisibility,
1818
AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@@ -348,7 +348,8 @@ impl MacroRulesData {
348348
#[derive(Debug, Clone, PartialEq, Eq)]
349349
pub struct ProcMacroData {
350350
pub name: Name,
351-
// FIXME: Record deriver helper here?
351+
/// Derive helpers, if this is a derive
352+
pub helpers: Option<Box<[Name]>>,
352353
}
353354

354355
impl ProcMacroData {
@@ -360,17 +361,23 @@ impl ProcMacroData {
360361
let item_tree = loc.id.item_tree(db);
361362
let makro = &item_tree[loc.id.value];
362363

363-
let name = if let Some(def) = item_tree
364+
let (name, helpers) = if let Some(def) = item_tree
364365
.attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into())
365366
.parse_proc_macro_decl(&makro.name)
366367
{
367-
def.name
368+
(
369+
def.name,
370+
match def.kind {
371+
ProcMacroKind::CustomDerive { helpers } => Some(helpers),
372+
ProcMacroKind::FnLike | ProcMacroKind::Attr => None,
373+
},
374+
)
368375
} else {
369376
// eeeh...
370377
stdx::never!("proc macro declaration is not a proc macro");
371-
makro.name.clone()
378+
(makro.name.clone(), None)
372379
};
373-
Arc::new(ProcMacroData { name })
380+
Arc::new(ProcMacroData { name, helpers })
374381
}
375382
}
376383

crates/hir-def/src/item_scope.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,14 @@ pub struct ItemScope {
6666
attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
6767
/// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes
6868
/// paired with the derive macro invocations for the specific attribute.
69-
derive_macros: FxHashMap<
70-
AstId<ast::Adt>,
71-
SmallVec<[(AttrId, MacroCallId, SmallVec<[Option<MacroCallId>; 1]>); 1]>,
72-
>,
69+
derive_macros: FxHashMap<AstId<ast::Adt>, SmallVec<[DeriveMacroInvocation; 1]>>,
70+
}
71+
72+
#[derive(Debug, PartialEq, Eq)]
73+
struct DeriveMacroInvocation {
74+
attr_id: AttrId,
75+
attr_call_id: MacroCallId,
76+
derive_call_ids: SmallVec<[Option<MacroCallId>; 1]>,
7377
}
7478

7579
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
@@ -210,12 +214,14 @@ impl ItemScope {
210214
&mut self,
211215
adt: AstId<ast::Adt>,
212216
call: MacroCallId,
213-
attr_id: AttrId,
217+
id: AttrId,
214218
idx: usize,
215219
) {
216220
if let Some(derives) = self.derive_macros.get_mut(&adt) {
217-
if let Some((.., invocs)) = derives.iter_mut().find(|&&mut (id, ..)| id == attr_id) {
218-
invocs[idx] = Some(call);
221+
if let Some(DeriveMacroInvocation { derive_call_ids, .. }) =
222+
derives.iter_mut().find(|&&mut DeriveMacroInvocation { attr_id, .. }| id == attr_id)
223+
{
224+
derive_call_ids[idx] = Some(call);
219225
}
220226
}
221227
}
@@ -227,10 +233,14 @@ impl ItemScope {
227233
&mut self,
228234
adt: AstId<ast::Adt>,
229235
attr_id: AttrId,
230-
call_id: MacroCallId,
236+
attr_call_id: MacroCallId,
231237
len: usize,
232238
) {
233-
self.derive_macros.entry(adt).or_default().push((attr_id, call_id, smallvec![None; len]));
239+
self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation {
240+
attr_id,
241+
attr_call_id,
242+
derive_call_ids: smallvec![None; len],
243+
});
234244
}
235245

236246
pub(crate) fn derive_macro_invocs(
@@ -242,7 +252,12 @@ impl ItemScope {
242252
),
243253
> + '_ {
244254
self.derive_macros.iter().map(|(k, v)| {
245-
(*k, v.iter().map(|&(attr_id, call_id, ref invocs)| (attr_id, call_id, &**invocs)))
255+
(
256+
*k,
257+
v.iter().map(|DeriveMacroInvocation { attr_id, attr_call_id, derive_call_ids }| {
258+
(*attr_id, *attr_call_id, &**derive_call_ids)
259+
}),
260+
)
246261
})
247262
}
248263

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

+22-5
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,19 @@
4848
//! the result
4949
5050
pub mod attr_resolution;
51-
mod collector;
51+
pub mod proc_macro;
5252
pub mod diagnostics;
53+
mod collector;
5354
mod mod_resolution;
5455
mod path_resolution;
55-
mod proc_macro;
5656

5757
#[cfg(test)]
5858
mod tests;
5959

60-
use std::{cmp::Ord, sync::Arc};
60+
use std::{ops::Deref, sync::Arc};
6161

6262
use base_db::{CrateId, Edition, FileId};
63-
use hir_expand::{name::Name, InFile, MacroDefId};
63+
use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
6464
use itertools::Itertools;
6565
use la_arena::Arena;
6666
use profile::Count;
@@ -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.
@@ -106,6 +106,9 @@ pub struct DefMap {
106106
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
107107
/// The error that occurred when failing to load the proc-macro dll.
108108
proc_macro_loading_error: Option<Box<str>>,
109+
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
110+
/// attributes.
111+
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
109112

110113
/// Custom attributes registered with `#![register_attr]`.
111114
registered_attrs: Vec<SmolStr>,
@@ -275,6 +278,7 @@ impl DefMap {
275278
exported_derives: FxHashMap::default(),
276279
fn_proc_macro_mapping: FxHashMap::default(),
277280
proc_macro_loading_error: None,
281+
derive_helpers_in_scope: FxHashMap::default(),
278282
prelude: None,
279283
root,
280284
modules,
@@ -294,19 +298,30 @@ impl DefMap {
294298
pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
295299
self.modules.iter()
296300
}
301+
302+
pub fn derive_helpers_in_scope(
303+
&self,
304+
id: AstId<ast::Adt>,
305+
) -> Option<&[(Name, MacroId, MacroCallId)]> {
306+
self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref)
307+
}
308+
297309
pub fn registered_tools(&self) -> &[SmolStr] {
298310
&self.registered_tools
299311
}
312+
300313
pub fn registered_attrs(&self) -> &[SmolStr] {
301314
&self.registered_attrs
302315
}
316+
303317
pub fn root(&self) -> LocalModuleId {
304318
self.root
305319
}
306320

307321
pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
308322
self.fn_proc_macro_mapping.get(&id).copied()
309323
}
324+
310325
pub fn proc_macro_loading_error(&self) -> Option<&str> {
311326
self.proc_macro_loading_error.as_deref()
312327
}
@@ -467,6 +482,7 @@ impl DefMap {
467482
registered_attrs,
468483
registered_tools,
469484
fn_proc_macro_mapping,
485+
derive_helpers_in_scope,
470486
proc_macro_loading_error: _,
471487
block: _,
472488
edition: _,
@@ -483,6 +499,7 @@ impl DefMap {
483499
registered_attrs.shrink_to_fit();
484500
registered_tools.shrink_to_fit();
485501
fn_proc_macro_mapping.shrink_to_fit();
502+
derive_helpers_in_scope.shrink_to_fit();
486503
for (_, module) in modules.iter_mut() {
487504
module.children.shrink_to_fit();
488505
module.scope.shrink_to_fit();

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

+29-27
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};
@@ -110,7 +110,6 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
110110
proc_macros,
111111
from_glob_import: Default::default(),
112112
skip_attrs: Default::default(),
113-
derive_helpers_in_scope: Default::default(),
114113
is_proc_macro,
115114
};
116115
if tree_id.is_block() {
@@ -258,9 +257,6 @@ struct DefCollector<'a> {
258257
/// This also stores the attributes to skip when we resolve derive helpers and non-macro
259258
/// non-builtin attributes in general.
260259
skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
261-
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
262-
/// attributes.
263-
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
264260
}
265261

266262
impl DefCollector<'_> {
@@ -1059,16 +1055,20 @@ impl DefCollector<'_> {
10591055
};
10601056
let mut res = ReachedFixedPoint::Yes;
10611057
macros.retain(|directive| {
1062-
let resolver = |path| {
1058+
let resolver2 = |path| {
10631059
let resolved_res = self.def_map.resolve_path_fp_with_macro(
10641060
self.db,
10651061
ResolveMode::Other,
10661062
directive.module_id,
10671063
&path,
10681064
BuiltinShadowMode::Module,
10691065
);
1070-
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)))
10711070
};
1071+
let resolver = |path| resolver2(path).map(|(_, it)| it);
10721072

10731073
match &directive.kind {
10741074
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
@@ -1087,21 +1087,37 @@ impl DefCollector<'_> {
10871087
}
10881088
}
10891089
MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos } => {
1090-
let call_id = derive_macro_as_call_id(
1090+
let id = derive_macro_as_call_id(
10911091
self.db,
10921092
ast_id,
10931093
*derive_attr,
10941094
*derive_pos as u32,
10951095
self.def_map.krate,
1096-
&resolver,
1096+
&resolver2,
10971097
);
1098-
if let Ok(call_id) = call_id {
1098+
1099+
if let Ok((macro_id, def_id, call_id)) = id {
10991100
self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc(
11001101
ast_id.ast_id,
11011102
call_id,
11021103
*derive_attr,
11031104
*derive_pos,
11041105
);
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+
}
11051121

11061122
push_resolved(directive, call_id);
11071123
res = ReachedFixedPoint::No;
@@ -1132,8 +1148,8 @@ impl DefCollector<'_> {
11321148
};
11331149

11341150
if let Some(ident) = path.as_ident() {
1135-
if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) {
1136-
if helpers.contains(ident) {
1151+
if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) {
1152+
if helpers.iter().any(|(it, ..)| it == ident) {
11371153
cov_mark::hit!(resolved_derive_helper);
11381154
// Resolved to derive helper. Collect the item's attributes again,
11391155
// starting after the derive helper.
@@ -1148,7 +1164,7 @@ impl DefCollector<'_> {
11481164
};
11491165
if matches!(
11501166
def,
1151-
MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. }
1167+
MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. }
11521168
if expander.is_derive()
11531169
) {
11541170
// Resolved to `#[derive]`
@@ -1317,19 +1333,6 @@ impl DefCollector<'_> {
13171333
self.def_map.diagnostics.push(diag);
13181334
}
13191335

1320-
// If we've just resolved a derive, record its helper attributes.
1321-
if let MacroCallKind::Derive { ast_id, .. } = &loc.kind {
1322-
if loc.def.krate != self.def_map.krate {
1323-
let def_map = self.db.crate_def_map(loc.def.krate);
1324-
if let Some(helpers) = def_map.exported_derives.get(&loc.def) {
1325-
self.derive_helpers_in_scope
1326-
.entry(ast_id.map(|it| it.upcast()))
1327-
.or_default()
1328-
.extend(helpers.iter().cloned());
1329-
}
1330-
}
1331-
}
1332-
13331336
// Then, fetch and process the item tree. This will reuse the expansion result from above.
13341337
let item_tree = self.db.file_item_tree(file_id);
13351338
let mod_dir = self.mod_dirs[&module_id].clone();
@@ -2140,7 +2143,6 @@ mod tests {
21402143
proc_macros: Default::default(),
21412144
from_glob_import: Default::default(),
21422145
skip_attrs: Default::default(),
2143-
derive_helpers_in_scope: Default::default(),
21442146
is_proc_macro: false,
21452147
};
21462148
collector.seed_with_top_level();

0 commit comments

Comments
 (0)