mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
feat: ignored and disabled macro expansion
This commit is contained in:
parent
5e1b09bb76
commit
6d45afd8d8
8 changed files with 105 additions and 12 deletions
|
@ -243,6 +243,7 @@ impl CrateDisplayName {
|
||||||
CrateDisplayName { crate_name, canonical_name }
|
CrateDisplayName { crate_name, canonical_name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
|
pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
|
@ -634,7 +634,6 @@ impl<'a> AssocItemCollector<'a> {
|
||||||
attr,
|
attr,
|
||||||
) {
|
) {
|
||||||
Ok(ResolvedAttr::Macro(call_id)) => {
|
Ok(ResolvedAttr::Macro(call_id)) => {
|
||||||
self.attr_calls.push((ast_id, call_id));
|
|
||||||
// If proc attribute macro expansion is disabled, skip expanding it here
|
// If proc attribute macro expansion is disabled, skip expanding it here
|
||||||
if !self.db.expand_proc_attr_macros() {
|
if !self.db.expand_proc_attr_macros() {
|
||||||
continue 'attrs;
|
continue 'attrs;
|
||||||
|
@ -647,10 +646,20 @@ impl<'a> AssocItemCollector<'a> {
|
||||||
// disabled. This is analogous to the handling in
|
// disabled. This is analogous to the handling in
|
||||||
// `DefCollector::collect_macros`.
|
// `DefCollector::collect_macros`.
|
||||||
if exp.is_dummy() {
|
if exp.is_dummy() {
|
||||||
|
self.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
|
||||||
|
self.module_id.local_id,
|
||||||
|
loc.kind,
|
||||||
|
loc.def.krate,
|
||||||
|
));
|
||||||
|
|
||||||
|
continue 'attrs;
|
||||||
|
} else if exp.is_disabled() {
|
||||||
continue 'attrs;
|
continue 'attrs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.attr_calls.push((ast_id, call_id));
|
||||||
|
|
||||||
let res =
|
let res =
|
||||||
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
||||||
self.collect_macro_items(res, &|| loc.kind.clone());
|
self.collect_macro_items(res, &|| loc.kind.clone());
|
||||||
|
|
|
@ -98,9 +98,13 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
|
||||||
};
|
};
|
||||||
(
|
(
|
||||||
name.as_name(),
|
name.as_name(),
|
||||||
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
|
if it.expander.should_expand() {
|
||||||
idx as u32,
|
CustomProcMacroExpander::new(hir_expand::proc_macro::ProcMacroId(
|
||||||
)),
|
idx as u32,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
CustomProcMacroExpander::disabled()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
|
@ -1156,6 +1160,28 @@ impl DefCollector<'_> {
|
||||||
self.def_map.modules[directive.module_id]
|
self.def_map.modules[directive.module_id]
|
||||||
.scope
|
.scope
|
||||||
.add_macro_invoc(ast_id.ast_id, call_id);
|
.add_macro_invoc(ast_id.ast_id, call_id);
|
||||||
|
|
||||||
|
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
|
||||||
|
|
||||||
|
if let MacroDefKind::ProcMacro(expander, _, _) = loc.def.kind {
|
||||||
|
if expander.is_dummy() || expander.is_disabled() {
|
||||||
|
// If there's no expander for the proc macro (e.g.
|
||||||
|
// because proc macros are disabled, or building the
|
||||||
|
// proc macro crate failed), report this and skip
|
||||||
|
// expansion like we would if it was disabled
|
||||||
|
self.def_map.diagnostics.push(
|
||||||
|
DefDiagnostic::unresolved_proc_macro(
|
||||||
|
directive.module_id,
|
||||||
|
loc.kind,
|
||||||
|
loc.def.krate,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
res = ReachedFixedPoint::No;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
push_resolved(directive, call_id);
|
push_resolved(directive, call_id);
|
||||||
|
|
||||||
res = ReachedFixedPoint::No;
|
res = ReachedFixedPoint::No;
|
||||||
|
@ -1348,6 +1374,8 @@ impl DefCollector<'_> {
|
||||||
def.krate,
|
def.krate,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
return recollect_without(self);
|
||||||
|
} else if exp.is_disabled() {
|
||||||
return recollect_without(self);
|
return recollect_without(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,8 @@ pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
pub enum ExpandError {
|
pub enum ExpandError {
|
||||||
UnresolvedProcMacro(CrateId),
|
UnresolvedProcMacro(CrateId),
|
||||||
|
/// The macro expansion is disabled.
|
||||||
|
MacroDisabled,
|
||||||
Mbe(mbe::ExpandError),
|
Mbe(mbe::ExpandError),
|
||||||
RecursionOverflowPoisoned,
|
RecursionOverflowPoisoned,
|
||||||
Other(Box<Box<str>>),
|
Other(Box<Box<str>>),
|
||||||
|
@ -160,6 +162,7 @@ impl fmt::Display for ExpandError {
|
||||||
f.write_str(it)
|
f.write_str(it)
|
||||||
}
|
}
|
||||||
ExpandError::Other(it) => f.write_str(it),
|
ExpandError::Other(it) => f.write_str(it),
|
||||||
|
ExpandError::MacroDisabled => f.write_str("macro disabled"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,16 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
|
||||||
call_site: Span,
|
call_site: Span,
|
||||||
mixed_site: Span,
|
mixed_site: Span,
|
||||||
) -> Result<tt::Subtree, ProcMacroExpansionError>;
|
) -> Result<tt::Subtree, ProcMacroExpansionError>;
|
||||||
|
|
||||||
|
/// If this returns `false`, expansions via [`expand`](ProcMacroExpander::expand) will always
|
||||||
|
/// return the input subtree or will always return an error.
|
||||||
|
///
|
||||||
|
/// This is used to skip any additional expansion-related work,
|
||||||
|
/// e.g. to make sure we do not touch the syntax tree in any way
|
||||||
|
/// if a proc macro will never be expanded.
|
||||||
|
fn should_expand(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -57,6 +67,7 @@ pub struct CustomProcMacroExpander {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DUMMY_ID: u32 = !0;
|
const DUMMY_ID: u32 = !0;
|
||||||
|
const DISABLED_ID: u32 = !1;
|
||||||
|
|
||||||
impl CustomProcMacroExpander {
|
impl CustomProcMacroExpander {
|
||||||
pub fn new(proc_macro_id: ProcMacroId) -> Self {
|
pub fn new(proc_macro_id: ProcMacroId) -> Self {
|
||||||
|
@ -68,10 +79,20 @@ impl CustomProcMacroExpander {
|
||||||
Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
|
Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The macro was not yet resolved.
|
||||||
pub fn is_dummy(&self) -> bool {
|
pub fn is_dummy(&self) -> bool {
|
||||||
self.proc_macro_id.0 == DUMMY_ID
|
self.proc_macro_id.0 == DUMMY_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn disabled() -> Self {
|
||||||
|
Self { proc_macro_id: ProcMacroId(DISABLED_ID) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The macro is explicitly disabled and cannot be expanded.
|
||||||
|
pub fn is_disabled(&self) -> bool {
|
||||||
|
self.proc_macro_id.0 == DISABLED_ID
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expand(
|
pub fn expand(
|
||||||
self,
|
self,
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
|
@ -88,6 +109,10 @@ impl CustomProcMacroExpander {
|
||||||
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||||
ExpandError::UnresolvedProcMacro(def_crate),
|
ExpandError::UnresolvedProcMacro(def_crate),
|
||||||
),
|
),
|
||||||
|
ProcMacroId(DISABLED_ID) => ExpandResult::new(
|
||||||
|
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
|
||||||
|
ExpandError::MacroDisabled,
|
||||||
|
),
|
||||||
ProcMacroId(id) => {
|
ProcMacroId(id) => {
|
||||||
let proc_macros = db.proc_macros();
|
let proc_macros = db.proc_macros();
|
||||||
let proc_macros = match proc_macros.get(&def_crate) {
|
let proc_macros = match proc_macros.get(&def_crate) {
|
||||||
|
|
|
@ -273,7 +273,7 @@ impl SourceRootConfig {
|
||||||
pub fn load_proc_macro(
|
pub fn load_proc_macro(
|
||||||
server: &ProcMacroServer,
|
server: &ProcMacroServer,
|
||||||
path: &AbsPath,
|
path: &AbsPath,
|
||||||
dummy_replace: &[Box<str>],
|
ignored_macros: &[Box<str>],
|
||||||
) -> ProcMacroLoadResult {
|
) -> ProcMacroLoadResult {
|
||||||
let res: Result<Vec<_>, String> = (|| {
|
let res: Result<Vec<_>, String> = (|| {
|
||||||
let dylib = MacroDylib::new(path.to_path_buf());
|
let dylib = MacroDylib::new(path.to_path_buf());
|
||||||
|
@ -283,7 +283,7 @@ pub fn load_proc_macro(
|
||||||
}
|
}
|
||||||
Ok(vec
|
Ok(vec
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|expander| expander_to_proc_macro(expander, dummy_replace))
|
.map(|expander| expander_to_proc_macro(expander, ignored_macros))
|
||||||
.collect())
|
.collect())
|
||||||
})();
|
})();
|
||||||
match res {
|
match res {
|
||||||
|
@ -349,7 +349,7 @@ fn load_crate_graph(
|
||||||
|
|
||||||
fn expander_to_proc_macro(
|
fn expander_to_proc_macro(
|
||||||
expander: proc_macro_api::ProcMacro,
|
expander: proc_macro_api::ProcMacro,
|
||||||
dummy_replace: &[Box<str>],
|
ignored_macros: &[Box<str>],
|
||||||
) -> ProcMacro {
|
) -> ProcMacro {
|
||||||
let name = From::from(expander.name());
|
let name = From::from(expander.name());
|
||||||
let kind = match expander.kind() {
|
let kind = match expander.kind() {
|
||||||
|
@ -358,7 +358,7 @@ fn expander_to_proc_macro(
|
||||||
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
|
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
|
||||||
};
|
};
|
||||||
let expander: sync::Arc<dyn ProcMacroExpander> =
|
let expander: sync::Arc<dyn ProcMacroExpander> =
|
||||||
if dummy_replace.iter().any(|replace| **replace == name) {
|
if ignored_macros.iter().any(|replace| &**replace == name) {
|
||||||
match kind {
|
match kind {
|
||||||
ProcMacroKind::Attr => sync::Arc::new(IdentityExpander),
|
ProcMacroKind::Attr => sync::Arc::new(IdentityExpander),
|
||||||
_ => sync::Arc::new(EmptyExpander),
|
_ => sync::Arc::new(EmptyExpander),
|
||||||
|
@ -407,6 +407,9 @@ impl ProcMacroExpander for IdentityExpander {
|
||||||
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
|
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
|
||||||
Ok(subtree.clone())
|
Ok(subtree.clone())
|
||||||
}
|
}
|
||||||
|
fn should_expand(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Empty expander, used for proc-macros that are deliberately ignored by the user.
|
/// Empty expander, used for proc-macros that are deliberately ignored by the user.
|
||||||
|
@ -425,6 +428,9 @@ impl ProcMacroExpander for EmptyExpander {
|
||||||
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
|
) -> Result<tt::Subtree<Span>, ProcMacroExpansionError> {
|
||||||
Ok(tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }))
|
Ok(tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }))
|
||||||
}
|
}
|
||||||
|
fn should_expand(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1202,7 +1202,7 @@ impl Config {
|
||||||
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
|
Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
|
pub fn ignored_proc_macros(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
|
||||||
&self.data.procMacro_ignored
|
&self.data.procMacro_ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,13 +299,13 @@ impl GlobalState {
|
||||||
|
|
||||||
pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
|
pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
|
||||||
tracing::info!(%cause, "will load proc macros");
|
tracing::info!(%cause, "will load proc macros");
|
||||||
let dummy_replacements = self.config.dummy_replacements().clone();
|
let ignored_proc_macros = self.config.ignored_proc_macros().clone();
|
||||||
let proc_macro_clients = self.proc_macro_clients.clone();
|
let proc_macro_clients = self.proc_macro_clients.clone();
|
||||||
|
|
||||||
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
|
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
|
||||||
sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap();
|
sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap();
|
||||||
|
|
||||||
let dummy_replacements = &dummy_replacements;
|
let ignored_proc_macros = &ignored_proc_macros;
|
||||||
let progress = {
|
let progress = {
|
||||||
let sender = sender.clone();
|
let sender = sender.clone();
|
||||||
&move |msg| {
|
&move |msg| {
|
||||||
|
@ -333,7 +333,13 @@ impl GlobalState {
|
||||||
crate_name
|
crate_name
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.and_then(|crate_name| {
|
.and_then(|crate_name| {
|
||||||
dummy_replacements.get(crate_name).map(|v| &**v)
|
ignored_proc_macros.iter().find_map(|c| {
|
||||||
|
if eq_ignore_underscore(&*c.0, crate_name) {
|
||||||
|
Some(&**c.1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
|
@ -695,3 +701,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind)
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Similar to [`str::eq_ignore_ascii_case`] but instead of ignoring
|
||||||
|
/// case, we say that `-` and `_` are equal.
|
||||||
|
fn eq_ignore_underscore(s1: &str, s2: &str) -> bool {
|
||||||
|
if s1.len() != s2.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
s1.as_bytes().iter().zip(s2.as_bytes()).all(|(c1, c2)| {
|
||||||
|
let c1_underscore = c1 == &b'_' || c1 == &b'-';
|
||||||
|
let c2_underscore = c2 == &b'_' || c2 == &b'-';
|
||||||
|
|
||||||
|
c1 == c2 || (c1_underscore && c2_underscore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue