Skip to content

Commit 6d45afd

Browse files
tamasfeVeykril
authored andcommitted
feat: ignored and disabled macro expansion
1 parent 5e1b09b commit 6d45afd

File tree

8 files changed

+105
-12
lines changed

8 files changed

+105
-12
lines changed

crates/base-db/src/input.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ impl CrateDisplayName {
243243
CrateDisplayName { crate_name, canonical_name }
244244
}
245245
}
246+
246247
pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
247248

248249
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]

crates/hir-def/src/data.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,6 @@ impl<'a> AssocItemCollector<'a> {
634634
attr,
635635
) {
636636
Ok(ResolvedAttr::Macro(call_id)) => {
637-
self.attr_calls.push((ast_id, call_id));
638637
// If proc attribute macro expansion is disabled, skip expanding it here
639638
if !self.db.expand_proc_attr_macros() {
640639
continue 'attrs;
@@ -647,10 +646,20 @@ impl<'a> AssocItemCollector<'a> {
647646
// disabled. This is analogous to the handling in
648647
// `DefCollector::collect_macros`.
649648
if exp.is_dummy() {
649+
self.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
650+
self.module_id.local_id,
651+
loc.kind,
652+
loc.def.krate,
653+
));
654+
655+
continue 'attrs;
656+
} else if exp.is_disabled() {
650657
continue 'attrs;
651658
}
652659
}
653660

661+
self.attr_calls.push((ast_id, call_id));
662+
654663
let res =
655664
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
656665
self.collect_macro_items(res, &|| loc.kind.clone());

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

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,13 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
9898
};
9999
(
100100
name.as_name(),
101-
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
102-
idx as u32,
103-
)),
101+
if it.expander.should_expand() {
102+
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
103+
idx as u32,
104+
))
105+
} else {
106+
CustomProcMacroExpander::disabled()
107+
},
104108
)
105109
})
106110
.collect())
@@ -1156,6 +1160,28 @@ impl DefCollector<'_> {
11561160
self.def_map.modules[directive.module_id]
11571161
.scope
11581162
.add_macro_invoc(ast_id.ast_id, call_id);
1163+
1164+
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
1165+
1166+
if let MacroDefKind::ProcMacro(expander, _, _) = loc.def.kind {
1167+
if expander.is_dummy() || expander.is_disabled() {
1168+
// If there's no expander for the proc macro (e.g.
1169+
// because proc macros are disabled, or building the
1170+
// proc macro crate failed), report this and skip
1171+
// expansion like we would if it was disabled
1172+
self.def_map.diagnostics.push(
1173+
DefDiagnostic::unresolved_proc_macro(
1174+
directive.module_id,
1175+
loc.kind,
1176+
loc.def.krate,
1177+
),
1178+
);
1179+
1180+
res = ReachedFixedPoint::No;
1181+
return false;
1182+
}
1183+
}
1184+
11591185
push_resolved(directive, call_id);
11601186

11611187
res = ReachedFixedPoint::No;
@@ -1348,6 +1374,8 @@ impl DefCollector<'_> {
13481374
def.krate,
13491375
));
13501376

1377+
return recollect_without(self);
1378+
} else if exp.is_disabled() {
13511379
return recollect_without(self);
13521380
}
13531381
}

crates/hir-expand/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ pub type ExpandResult<T> = ValueResult<T, ExpandError>;
129129
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
130130
pub enum ExpandError {
131131
UnresolvedProcMacro(CrateId),
132+
/// The macro expansion is disabled.
133+
MacroDisabled,
132134
Mbe(mbe::ExpandError),
133135
RecursionOverflowPoisoned,
134136
Other(Box<Box<str>>),
@@ -160,6 +162,7 @@ impl fmt::Display for ExpandError {
160162
f.write_str(it)
161163
}
162164
ExpandError::Other(it) => f.write_str(it),
165+
ExpandError::MacroDisabled => f.write_str("macro disabled"),
163166
}
164167
}
165168
}

crates/hir-expand/src/proc_macro.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
3131
call_site: Span,
3232
mixed_site: Span,
3333
) -> Result<tt::Subtree, ProcMacroExpansionError>;
34+
35+
/// If this returns `false`, expansions via [`expand`](ProcMacroExpander::expand) will always
36+
/// return the input subtree or will always return an error.
37+
///
38+
/// This is used to skip any additional expansion-related work,
39+
/// e.g. to make sure we do not touch the syntax tree in any way
40+
/// if a proc macro will never be expanded.
41+
fn should_expand(&self) -> bool {
42+
true
43+
}
3444
}
3545

3646
#[derive(Debug)]
@@ -57,6 +67,7 @@ pub struct CustomProcMacroExpander {
5767
}
5868

5969
const DUMMY_ID: u32 = !0;
70+
const DISABLED_ID: u32 = !1;
6071

6172
impl CustomProcMacroExpander {
6273
pub fn new(proc_macro_id: ProcMacroId) -> Self {
@@ -68,10 +79,20 @@ impl CustomProcMacroExpander {
6879
Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
6980
}
7081

82+
/// The macro was not yet resolved.
7183
pub fn is_dummy(&self) -> bool {
7284
self.proc_macro_id.0 == DUMMY_ID
7385
}
7486

87+
pub fn disabled() -> Self {
88+
Self { proc_macro_id: ProcMacroId(DISABLED_ID) }
89+
}
90+
91+
/// The macro is explicitly disabled and cannot be expanded.
92+
pub fn is_disabled(&self) -> bool {
93+
self.proc_macro_id.0 == DISABLED_ID
94+
}
95+
7596
pub fn expand(
7697
self,
7798
db: &dyn ExpandDatabase,
@@ -88,6 +109,10 @@ impl CustomProcMacroExpander {
88109
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
89110
ExpandError::UnresolvedProcMacro(def_crate),
90111
),
112+
ProcMacroId(DISABLED_ID) => ExpandResult::new(
113+
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
114+
ExpandError::MacroDisabled,
115+
),
91116
ProcMacroId(id) => {
92117
let proc_macros = db.proc_macros();
93118
let proc_macros = match proc_macros.get(&def_crate) {

crates/load-cargo/src/lib.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ impl SourceRootConfig {
273273
pub fn load_proc_macro(
274274
server: &ProcMacroServer,
275275
path: &AbsPath,
276-
dummy_replace: &[Box<str>],
276+
ignored_macros: &[Box<str>],
277277
) -> ProcMacroLoadResult {
278278
let res: Result<Vec<_>, String> = (|| {
279279
let dylib = MacroDylib::new(path.to_path_buf());
@@ -283,7 +283,7 @@ pub fn load_proc_macro(
283283
}
284284
Ok(vec
285285
.into_iter()
286-
.map(|expander| expander_to_proc_macro(expander, dummy_replace))
286+
.map(|expander| expander_to_proc_macro(expander, ignored_macros))
287287
.collect())
288288
})();
289289
match res {
@@ -349,7 +349,7 @@ fn load_crate_graph(
349349

350350
fn expander_to_proc_macro(
351351
expander: proc_macro_api::ProcMacro,
352-
dummy_replace: &[Box<str>],
352+
ignored_macros: &[Box<str>],
353353
) -> ProcMacro {
354354
let name = From::from(expander.name());
355355
let kind = match expander.kind() {
@@ -358,7 +358,7 @@ fn expander_to_proc_macro(
358358
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
359359
};
360360
let expander: sync::Arc<dyn ProcMacroExpander> =
361-
if dummy_replace.iter().any(|replace| **replace == name) {
361+
if ignored_macros.iter().any(|replace| &**replace == name) {
362362
match kind {
363363
ProcMacroKind::Attr => sync::Arc::new(IdentityExpander),
364364
_ => sync::Arc::new(EmptyExpander),
@@ -407,6 +407,9 @@ impl ProcMacroExpander for IdentityExpander {
407407
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
408408
Ok(subtree.clone())
409409
}
410+
fn should_expand(&self) -> bool {
411+
false
412+
}
410413
}
411414

412415
/// Empty expander, used for proc-macros that are deliberately ignored by the user.
@@ -425,6 +428,9 @@ impl ProcMacroExpander for EmptyExpander {
425428
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
426429
Ok(tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }))
427430
}
431+
fn should_expand(&self) -> bool {
432+
false
433+
}
428434
}
429435

430436
#[cfg(test)]

crates/rust-analyzer/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ impl Config {
12021202
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
12031203
}
12041204

1205-
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
1205+
pub fn ignored_proc_macros(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
12061206
&self.data.procMacro_ignored
12071207
}
12081208

crates/rust-analyzer/src/reload.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,13 @@ impl GlobalState {
299299

300300
pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
301301
tracing::info!(%cause, "will load proc macros");
302-
let dummy_replacements = self.config.dummy_replacements().clone();
302+
let ignored_proc_macros = self.config.ignored_proc_macros().clone();
303303
let proc_macro_clients = self.proc_macro_clients.clone();
304304

305305
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
306306
sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap();
307307

308-
let dummy_replacements = &dummy_replacements;
308+
let ignored_proc_macros = &ignored_proc_macros;
309309
let progress = {
310310
let sender = sender.clone();
311311
&move |msg| {
@@ -333,7 +333,13 @@ impl GlobalState {
333333
crate_name
334334
.as_deref()
335335
.and_then(|crate_name| {
336-
dummy_replacements.get(crate_name).map(|v| &**v)
336+
ignored_proc_macros.iter().find_map(|c| {
337+
if eq_ignore_underscore(&*c.0, crate_name) {
338+
Some(&**c.1)
339+
} else {
340+
None
341+
}
342+
})
337343
})
338344
.unwrap_or_default(),
339345
)
@@ -695,3 +701,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
695701
}
696702
false
697703
}
704+
705+
/// Similar to [`str::eq_ignore_ascii_case`] but instead of ignoring
706+
/// case, we say that `-` and `_` are equal.
707+
fn eq_ignore_underscore(s1: &str, s2: &str) -> bool {
708+
if s1.len() != s2.len() {
709+
return false;
710+
}
711+
712+
s1.as_bytes().iter().zip(s2.as_bytes()).all(|(c1, c2)| {
713+
let c1_underscore = c1 == &b'_' || c1 == &b'-';
714+
let c2_underscore = c2 == &b'_' || c2 == &b'-';
715+
716+
c1 == c2 || (c1_underscore && c2_underscore)
717+
})
718+
}

0 commit comments

Comments
 (0)