Skip to content

Commit 2083ef2

Browse files
tamasfeVeykril
authored andcommitted
feat: ignored and disabled macro expansion
1 parent e5a1118 commit 2083ef2

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
@@ -239,6 +239,7 @@ impl CrateDisplayName {
239239
CrateDisplayName { crate_name, canonical_name }
240240
}
241241
}
242+
242243
pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
243244

244245
#[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
@@ -635,7 +635,6 @@ impl<'a> AssocItemCollector<'a> {
635635
attr,
636636
) {
637637
Ok(ResolvedAttr::Macro(call_id)) => {
638-
self.attr_calls.push((ast_id, call_id));
639638
// If proc attribute macro expansion is disabled, skip expanding it here
640639
if !self.db.expand_proc_attr_macros() {
641640
continue 'attrs;
@@ -648,10 +647,20 @@ impl<'a> AssocItemCollector<'a> {
648647
// disabled. This is analogous to the handling in
649648
// `DefCollector::collect_macros`.
650649
if exp.is_dummy() {
650+
self.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
651+
self.module_id.local_id,
652+
loc.kind,
653+
loc.def.krate,
654+
));
655+
656+
continue 'attrs;
657+
} else if exp.is_disabled() {
651658
continue 'attrs;
652659
}
653660
}
654661

662+
self.attr_calls.push((ast_id, call_id));
663+
655664
let res =
656665
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
657666
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
@@ -99,9 +99,13 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
9999
};
100100
(
101101
name.as_name(),
102-
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
103-
idx as u32,
104-
)),
102+
if it.expander.should_expand() {
103+
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
104+
idx as u32,
105+
))
106+
} else {
107+
CustomProcMacroExpander::disabled()
108+
},
105109
)
106110
})
107111
.collect())
@@ -1155,6 +1159,28 @@ impl DefCollector<'_> {
11551159
self.def_map.modules[directive.module_id]
11561160
.scope
11571161
.add_macro_invoc(ast_id.ast_id, call_id);
1162+
1163+
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
1164+
1165+
if let MacroDefKind::ProcMacro(expander, _, _) = loc.def.kind {
1166+
if expander.is_dummy() || expander.is_disabled() {
1167+
// If there's no expander for the proc macro (e.g.
1168+
// because proc macros are disabled, or building the
1169+
// proc macro crate failed), report this and skip
1170+
// expansion like we would if it was disabled
1171+
self.def_map.diagnostics.push(
1172+
DefDiagnostic::unresolved_proc_macro(
1173+
directive.module_id,
1174+
loc.kind,
1175+
loc.def.krate,
1176+
),
1177+
);
1178+
1179+
res = ReachedFixedPoint::No;
1180+
return false;
1181+
}
1182+
}
1183+
11581184
push_resolved(directive, call_id);
11591185

11601186
res = ReachedFixedPoint::No;
@@ -1344,6 +1370,8 @@ impl DefCollector<'_> {
13441370
def.krate,
13451371
));
13461372

1373+
return recollect_without(self);
1374+
} else if exp.is_disabled() {
13471375
return recollect_without(self);
13481376
}
13491377
}

crates/hir-expand/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ pub type ExpandResult<T> = ValueResult<T, ExpandError>;
126126
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
127127
pub enum ExpandError {
128128
UnresolvedProcMacro(CrateId),
129+
/// The macro expansion is disabled.
130+
MacroDisabled,
129131
Mbe(mbe::ExpandError),
130132
RecursionOverflowPoisoned,
131133
Other(Box<Box<str>>),
@@ -157,6 +159,7 @@ impl fmt::Display for ExpandError {
157159
f.write_str(it)
158160
}
159161
ExpandError::Other(it) => f.write_str(it),
162+
ExpandError::MacroDisabled => f.write_str("macro disabled"),
160163
}
161164
}
162165
}

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
@@ -1180,7 +1180,7 @@ impl Config {
11801180
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(&path)))
11811181
}
11821182

1183-
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
1183+
pub fn ignored_proc_macros(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
11841184
&self.data.procMacro_ignored
11851185
}
11861186

crates/rust-analyzer/src/reload.rs

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

293293
pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
294294
tracing::info!(%cause, "will load proc macros");
295-
let dummy_replacements = self.config.dummy_replacements().clone();
295+
let ignored_proc_macros = self.config.ignored_proc_macros().clone();
296296
let proc_macro_clients = self.proc_macro_clients.clone();
297297

298298
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
299299
sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap();
300300

301-
let dummy_replacements = &dummy_replacements;
301+
let ignored_proc_macros = &ignored_proc_macros;
302302
let progress = {
303303
let sender = sender.clone();
304304
&move |msg| {
@@ -326,7 +326,13 @@ impl GlobalState {
326326
crate_name
327327
.as_deref()
328328
.and_then(|crate_name| {
329-
dummy_replacements.get(crate_name).map(|v| &**v)
329+
ignored_proc_macros.iter().find_map(|c| {
330+
if eq_ignore_underscore(&*c.0, crate_name) {
331+
Some(&**c.1)
332+
} else {
333+
None
334+
}
335+
})
330336
})
331337
.unwrap_or_default(),
332338
)
@@ -670,3 +676,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
670676
}
671677
false
672678
}
679+
680+
/// Similar to [`str::eq_ignore_ascii_case`] but instead of ignoring
681+
/// case, we say that `-` and `_` are equal.
682+
fn eq_ignore_underscore(s1: &str, s2: &str) -> bool {
683+
if s1.len() != s2.len() {
684+
return false;
685+
}
686+
687+
s1.as_bytes().iter().zip(s2.as_bytes()).all(|(c1, c2)| {
688+
let c1_underscore = c1 == &b'_' || c1 == &b'-';
689+
let c2_underscore = c2 == &b'_' || c2 == &b'-';
690+
691+
c1 == c2 || (c1_underscore && c2_underscore)
692+
})
693+
}

0 commit comments

Comments
 (0)