expand: Tell built-in macros whether we are currently in forced expansion mode
This commit is contained in:
parent
7e2032390c
commit
b49fbc9432
5 changed files with 42 additions and 40 deletions
|
@ -1,7 +1,7 @@
|
||||||
//! Implementation of the `#[cfg_accessible(path)]` attribute macro.
|
//! Implementation of the `#[cfg_accessible(path)]` attribute macro.
|
||||||
|
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
|
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
|
||||||
use rustc_feature::AttributeTemplate;
|
use rustc_feature::AttributeTemplate;
|
||||||
use rustc_parse::validate_attr;
|
use rustc_parse::validate_attr;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
@ -31,7 +31,7 @@ impl MultiItemModifier for Expander {
|
||||||
fn expand(
|
fn expand(
|
||||||
&self,
|
&self,
|
||||||
ecx: &mut ExtCtxt<'_>,
|
ecx: &mut ExtCtxt<'_>,
|
||||||
_span: Span,
|
span: Span,
|
||||||
meta_item: &ast::MetaItem,
|
meta_item: &ast::MetaItem,
|
||||||
item: Annotatable,
|
item: Annotatable,
|
||||||
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
||||||
|
@ -49,11 +49,14 @@ impl MultiItemModifier for Expander {
|
||||||
None => return ExpandResult::Ready(Vec::new()),
|
None => return ExpandResult::Ready(Vec::new()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let failure_msg = "cannot determine whether the path is accessible or not";
|
|
||||||
match ecx.resolver.cfg_accessible(ecx.current_expansion.id, path) {
|
match ecx.resolver.cfg_accessible(ecx.current_expansion.id, path) {
|
||||||
Ok(true) => ExpandResult::Ready(vec![item]),
|
Ok(true) => ExpandResult::Ready(vec![item]),
|
||||||
Ok(false) => ExpandResult::Ready(Vec::new()),
|
Ok(false) => ExpandResult::Ready(Vec::new()),
|
||||||
Err(_) => ExpandResult::Retry(item, failure_msg.into()),
|
Err(Indeterminate) if ecx.force_mode => {
|
||||||
|
ecx.span_err(span, "cannot determine whether the path is accessible or not");
|
||||||
|
ExpandResult::Ready(vec![item])
|
||||||
|
}
|
||||||
|
Err(Indeterminate) => ExpandResult::Retry(item),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,8 +251,7 @@ pub enum ExpandResult<T, U> {
|
||||||
/// Expansion produced a result (possibly dummy).
|
/// Expansion produced a result (possibly dummy).
|
||||||
Ready(T),
|
Ready(T),
|
||||||
/// Expansion could not produce a result and needs to be retried.
|
/// Expansion could not produce a result and needs to be retried.
|
||||||
/// The string is an explanation that will be printed if we are stuck in an infinite retry loop.
|
Retry(U),
|
||||||
Retry(U, String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `meta_item` is the attribute, and `item` is the item being modified.
|
// `meta_item` is the attribute, and `item` is the item being modified.
|
||||||
|
@ -919,6 +918,9 @@ pub struct ExtCtxt<'a> {
|
||||||
pub root_path: PathBuf,
|
pub root_path: PathBuf,
|
||||||
pub resolver: &'a mut dyn ResolverExpand,
|
pub resolver: &'a mut dyn ResolverExpand,
|
||||||
pub current_expansion: ExpansionData,
|
pub current_expansion: ExpansionData,
|
||||||
|
/// Error recovery mode entered when expansion is stuck
|
||||||
|
/// (or during eager expansion, but that's a hack).
|
||||||
|
pub force_mode: bool,
|
||||||
pub expansions: FxHashMap<Span, Vec<String>>,
|
pub expansions: FxHashMap<Span, Vec<String>>,
|
||||||
/// Called directly after having parsed an external `mod foo;` in expansion.
|
/// Called directly after having parsed an external `mod foo;` in expansion.
|
||||||
pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
|
pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
|
||||||
|
@ -945,6 +947,7 @@ impl<'a> ExtCtxt<'a> {
|
||||||
directory_ownership: DirectoryOwnership::Owned { relative: None },
|
directory_ownership: DirectoryOwnership::Owned { relative: None },
|
||||||
prior_type_ascription: None,
|
prior_type_ascription: None,
|
||||||
},
|
},
|
||||||
|
force_mode: false,
|
||||||
expansions: FxHashMap::default(),
|
expansions: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,6 +404,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
// Recursively expand all macro invocations in this AST fragment.
|
// Recursively expand all macro invocations in this AST fragment.
|
||||||
pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment {
|
pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment {
|
||||||
let orig_expansion_data = self.cx.current_expansion.clone();
|
let orig_expansion_data = self.cx.current_expansion.clone();
|
||||||
|
let orig_force_mode = self.cx.force_mode;
|
||||||
self.cx.current_expansion.depth = 0;
|
self.cx.current_expansion.depth = 0;
|
||||||
|
|
||||||
// Collect all macro invocations and replace them with placeholders.
|
// Collect all macro invocations and replace them with placeholders.
|
||||||
|
@ -432,6 +433,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
}
|
}
|
||||||
invocations = mem::take(&mut undetermined_invocations);
|
invocations = mem::take(&mut undetermined_invocations);
|
||||||
force = !mem::replace(&mut progress, false);
|
force = !mem::replace(&mut progress, false);
|
||||||
|
if force && self.monotonic {
|
||||||
|
self.cx.sess.delay_span_bug(
|
||||||
|
invocations.last().unwrap().0.span(),
|
||||||
|
"expansion entered force mode without producing any errors",
|
||||||
|
);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -460,18 +467,18 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
|
|
||||||
let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
|
let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
|
||||||
self.cx.current_expansion = invoc.expansion_data.clone();
|
self.cx.current_expansion = invoc.expansion_data.clone();
|
||||||
|
self.cx.force_mode = force;
|
||||||
|
|
||||||
// FIXME(jseyfried): Refactor out the following logic
|
// FIXME(jseyfried): Refactor out the following logic
|
||||||
let (expanded_fragment, new_invocations) = match res {
|
let (expanded_fragment, new_invocations) = match res {
|
||||||
InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) {
|
InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) {
|
||||||
ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]),
|
ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]),
|
||||||
ExpandResult::Retry(invoc, explanation) => {
|
ExpandResult::Retry(invoc) => {
|
||||||
if force {
|
if force {
|
||||||
// We are stuck, stop retrying and produce a dummy fragment.
|
self.cx.span_bug(
|
||||||
let span = invoc.span();
|
invoc.span(),
|
||||||
self.cx.span_err(span, &explanation);
|
"expansion entered force mode but is still stuck",
|
||||||
let fragment = invoc.fragment_kind.dummy(span);
|
);
|
||||||
self.collect_invocations(fragment, &[])
|
|
||||||
} else {
|
} else {
|
||||||
// Cannot expand, will retry this invocation later.
|
// Cannot expand, will retry this invocation later.
|
||||||
undetermined_invocations
|
undetermined_invocations
|
||||||
|
@ -526,6 +533,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cx.current_expansion = orig_expansion_data;
|
self.cx.current_expansion = orig_expansion_data;
|
||||||
|
self.cx.force_mode = orig_force_mode;
|
||||||
|
|
||||||
// Finally incorporate all the expanded macros into the input AST fragment.
|
// Finally incorporate all the expanded macros into the input AST fragment.
|
||||||
let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic);
|
let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic);
|
||||||
|
@ -735,20 +743,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
Ok(meta) => {
|
Ok(meta) => {
|
||||||
let items = match expander.expand(self.cx, span, &meta, item) {
|
let items = match expander.expand(self.cx, span, &meta, item) {
|
||||||
ExpandResult::Ready(items) => items,
|
ExpandResult::Ready(items) => items,
|
||||||
ExpandResult::Retry(item, explanation) => {
|
ExpandResult::Retry(item) => {
|
||||||
// Reassemble the original invocation for retrying.
|
// Reassemble the original invocation for retrying.
|
||||||
return ExpandResult::Retry(
|
return ExpandResult::Retry(Invocation {
|
||||||
Invocation {
|
kind: InvocationKind::Attr {
|
||||||
kind: InvocationKind::Attr {
|
attr,
|
||||||
attr,
|
item,
|
||||||
item,
|
derives,
|
||||||
derives,
|
after_derive,
|
||||||
after_derive,
|
|
||||||
},
|
|
||||||
..invoc
|
|
||||||
},
|
},
|
||||||
explanation,
|
..invoc
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fragment_kind.expect_from_annotatables(items)
|
fragment_kind.expect_from_annotatables(items)
|
||||||
|
@ -781,15 +786,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path };
|
let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path };
|
||||||
let items = match expander.expand(self.cx, span, &meta, item) {
|
let items = match expander.expand(self.cx, span, &meta, item) {
|
||||||
ExpandResult::Ready(items) => items,
|
ExpandResult::Ready(items) => items,
|
||||||
ExpandResult::Retry(item, explanation) => {
|
ExpandResult::Retry(item) => {
|
||||||
// Reassemble the original invocation for retrying.
|
// Reassemble the original invocation for retrying.
|
||||||
return ExpandResult::Retry(
|
return ExpandResult::Retry(Invocation {
|
||||||
Invocation {
|
kind: InvocationKind::Derive { path: meta.path, item },
|
||||||
kind: InvocationKind::Derive { path: meta.path, item },
|
..invoc
|
||||||
..invoc
|
});
|
||||||
},
|
|
||||||
explanation,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fragment_kind.expect_from_annotatables(items)
|
fragment_kind.expect_from_annotatables(items)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![feature(cfg_accessible)]
|
#![feature(cfg_accessible)]
|
||||||
|
|
||||||
#[cfg_accessible(Z)] //~ ERROR cannot determine whether the path is accessible or not
|
#[cfg_accessible(Z)] // OK, recovered after the other `cfg_accessible` produces an error.
|
||||||
struct S;
|
struct S;
|
||||||
|
|
||||||
#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not
|
#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not
|
||||||
|
|
|
@ -4,11 +4,5 @@ error: cannot determine whether the path is accessible or not
|
||||||
LL | #[cfg_accessible(S)]
|
LL | #[cfg_accessible(S)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: cannot determine whether the path is accessible or not
|
error: aborting due to previous error
|
||||||
--> $DIR/cfg_accessible-stuck.rs:3:1
|
|
||||||
|
|
|
||||||
LL | #[cfg_accessible(Z)]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue