Rollup merge of #69870 - petrochenkov:cfgacc, r=matthewjasper
expand: Implement something similar to `#[cfg(accessible(path))]` cc https://github.com/rust-lang/rust/issues/64797 The feature is implemented as a `#[cfg_accessible(path)]` attribute macro rather than as `#[cfg(accessible(path))]` because it needs to wait until `path` becomes resolvable, and `cfg` cannot wait, but macros can wait. Later we can think about desugaring or not desugaring `#[cfg(accessible(path))]` into `#[cfg_accessible(path)]`. This implementation is also incomplete in the sense that it never returns "false" from `cfg_accessible(path)`, it requires some tweaks to resolve, which is not quite ready to answer queries like this during early resolution. However, the most important part of this PR is not `cfg_accessible` itself, but expansion infrastructure for retrying expansions. Before this PR we could say "we cannot resolve this macro path, let's try it later", with this PR we can say "we cannot expand this macro, let's try it later" as well. This is a pre-requisite for - turning `#[derive(...)]` into a regular attribute macro, - properly supporting eager expansion for macros that cannot yet be resolved like ``` fn main() { println!(not_available_yet!()); } macro_rules! make_available { () => { #[macro_export] macro_rules! not_available_yet { () => { "Hello world!" } }} } make_available!(); ```
This commit is contained in:
commit
9fc5c2d00d
22 changed files with 439 additions and 71 deletions
|
@ -1404,6 +1404,18 @@ pub(crate) mod builtin {
|
|||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Keeps the item it's applied to if the passed path is accessible, and removes it otherwise.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(
|
||||
feature = "cfg_accessible",
|
||||
issue = "64797",
|
||||
reason = "`cfg_accessible` is not fully implemented"
|
||||
)]
|
||||
#[rustc_builtin_macro]
|
||||
pub macro cfg_accessible($item:item) {
|
||||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Unstable implementation detail of the `rustc` compiler, do not use.
|
||||
#[rustc_builtin_macro]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
|
|
@ -67,3 +67,12 @@ pub use crate::{
|
|||
pub use crate::macros::builtin::{
|
||||
bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable,
|
||||
};
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(
|
||||
feature = "cfg_accessible",
|
||||
issue = "64797",
|
||||
reason = "`cfg_accessible` is not fully implemented"
|
||||
)]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::macros::builtin::cfg_accessible;
|
||||
|
|
54
src/librustc_builtin_macros/cfg_accessible.rs
Normal file
54
src/librustc_builtin_macros/cfg_accessible.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
//! Implementation of the `#[cfg_accessible(path)]` attribute macro.
|
||||
|
||||
use rustc_ast::ast;
|
||||
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
|
||||
use rustc_feature::AttributeTemplate;
|
||||
use rustc_parse::validate_attr;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
||||
crate struct Expander;
|
||||
|
||||
fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
|
||||
match mi.meta_item_list() {
|
||||
None => {}
|
||||
Some([]) => ecx.span_err(mi.span, "`cfg_accessible` path is not specified"),
|
||||
Some([_, .., l]) => ecx.span_err(l.span(), "multiple `cfg_accessible` paths are specified"),
|
||||
Some([nmi]) => match nmi.meta_item() {
|
||||
None => ecx.span_err(nmi.span(), "`cfg_accessible` path cannot be a literal"),
|
||||
Some(mi) => {
|
||||
if !mi.is_word() {
|
||||
ecx.span_err(mi.span, "`cfg_accessible` path cannot accept arguments");
|
||||
}
|
||||
return Some(&mi.path);
|
||||
}
|
||||
},
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl MultiItemModifier for Expander {
|
||||
fn expand(
|
||||
&self,
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
_span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
||||
let template = AttributeTemplate { list: Some("path"), ..Default::default() };
|
||||
let attr = &ecx.attribute(meta_item.clone());
|
||||
validate_attr::check_builtin_attribute(ecx.parse_sess, attr, sym::cfg_accessible, template);
|
||||
|
||||
let path = match validate_input(ecx, meta_item) {
|
||||
Some(path) => path,
|
||||
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) {
|
||||
Ok(true) => ExpandResult::Ready(vec![item]),
|
||||
Ok(false) => ExpandResult::Ready(Vec::new()),
|
||||
Err(_) => ExpandResult::Retry(item, failure_msg.into()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use rustc_ast::ast::{self, ItemKind, MetaItem};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_expand::base::{Annotatable, ExtCtxt, MultiItemModifier};
|
||||
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
|
@ -48,13 +48,13 @@ impl MultiItemModifier for BuiltinDerive {
|
|||
span: Span,
|
||||
meta_item: &MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
||||
// FIXME: Built-in derives often forget to give spans contexts,
|
||||
// so we are doing it here in a centralized way.
|
||||
let span = ecx.with_def_site_ctxt(span);
|
||||
let mut items = Vec::new();
|
||||
(self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a));
|
||||
items
|
||||
ExpandResult::Ready(items)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ use rustc_span::symbol::sym;
|
|||
mod asm;
|
||||
mod assert;
|
||||
mod cfg;
|
||||
mod cfg_accessible;
|
||||
mod compile_error;
|
||||
mod concat;
|
||||
mod concat_idents;
|
||||
|
@ -85,6 +86,7 @@ pub fn register_builtin_macros(resolver: &mut dyn Resolver, edition: Edition) {
|
|||
|
||||
register_attr! {
|
||||
bench: test::expand_bench,
|
||||
cfg_accessible: cfg_accessible::Expander,
|
||||
global_allocator: global_allocator::expand,
|
||||
test: test::expand_test,
|
||||
test_case: test::expand_test_case,
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_span::Symbol;
|
|||
|
||||
pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) {
|
||||
// All the built-in macro attributes are "words" at the moment.
|
||||
let template = AttributeTemplate::only_word();
|
||||
let template = AttributeTemplate { word: true, ..Default::default() };
|
||||
let attr = ecx.attribute(meta_item.clone());
|
||||
validate_attr::check_builtin_attribute(ecx.parse_sess, &attr, name, template);
|
||||
}
|
||||
|
|
|
@ -258,8 +258,17 @@ impl Annotatable {
|
|||
}
|
||||
}
|
||||
|
||||
// `meta_item` is the annotation, and `item` is the item being modified.
|
||||
// FIXME Decorators should follow the same pattern too.
|
||||
/// Result of an expansion that may need to be retried.
|
||||
/// Consider using this for non-`MultiItemModifier` expanders as well.
|
||||
pub enum ExpandResult<T, U> {
|
||||
/// Expansion produced a result (possibly dummy).
|
||||
Ready(T),
|
||||
/// 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, String),
|
||||
}
|
||||
|
||||
// `meta_item` is the attribute, and `item` is the item being modified.
|
||||
pub trait MultiItemModifier {
|
||||
fn expand(
|
||||
&self,
|
||||
|
@ -267,13 +276,12 @@ pub trait MultiItemModifier {
|
|||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable>;
|
||||
) -> ExpandResult<Vec<Annotatable>, Annotatable>;
|
||||
}
|
||||
|
||||
impl<F, T> MultiItemModifier for F
|
||||
impl<F> MultiItemModifier for F
|
||||
where
|
||||
F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> T,
|
||||
T: Into<Vec<Annotatable>>,
|
||||
F: Fn(&mut ExtCtxt<'_>, Span, &ast::MetaItem, Annotatable) -> Vec<Annotatable>,
|
||||
{
|
||||
fn expand(
|
||||
&self,
|
||||
|
@ -281,14 +289,8 @@ where
|
|||
span: Span,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
(*self)(ecx, span, meta_item, item).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<Annotatable>> for Annotatable {
|
||||
fn into(self) -> Vec<Annotatable> {
|
||||
vec![self]
|
||||
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
||||
ExpandResult::Ready(self(ecx, span, meta_item, item))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -895,6 +897,7 @@ pub trait Resolver {
|
|||
|
||||
fn has_derive_copy(&self, expn_id: ExpnId) -> bool;
|
||||
fn add_derive_copy(&mut self, expn_id: ExpnId);
|
||||
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -408,7 +408,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
let mut undetermined_invocations = Vec::new();
|
||||
let (mut progress, mut force) = (false, !self.monotonic);
|
||||
loop {
|
||||
let invoc = if let Some(invoc) = invocations.pop() {
|
||||
let (invoc, res) = if let Some(invoc) = invocations.pop() {
|
||||
invoc
|
||||
} else {
|
||||
self.resolve_imports();
|
||||
|
@ -420,30 +420,51 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
continue;
|
||||
};
|
||||
|
||||
let eager_expansion_root =
|
||||
if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id };
|
||||
let res = match self.cx.resolver.resolve_macro_invocation(
|
||||
&invoc,
|
||||
eager_expansion_root,
|
||||
force,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(Indeterminate) => {
|
||||
undetermined_invocations.push(invoc);
|
||||
continue;
|
||||
let res = match res {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
let eager_expansion_root = if self.monotonic {
|
||||
invoc.expansion_data.id
|
||||
} else {
|
||||
orig_expansion_data.id
|
||||
};
|
||||
match self.cx.resolver.resolve_macro_invocation(
|
||||
&invoc,
|
||||
eager_expansion_root,
|
||||
force,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(Indeterminate) => {
|
||||
// Cannot resolve, will retry this invocation later.
|
||||
undetermined_invocations.push((invoc, None));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
progress = true;
|
||||
let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
|
||||
self.cx.current_expansion = invoc.expansion_data.clone();
|
||||
|
||||
// FIXME(jseyfried): Refactor out the following logic
|
||||
let (expanded_fragment, new_invocations) = match res {
|
||||
InvocationRes::Single(ext) => {
|
||||
let fragment = self.expand_invoc(invoc, &ext.kind);
|
||||
self.collect_invocations(fragment, &[])
|
||||
}
|
||||
InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) {
|
||||
ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]),
|
||||
ExpandResult::Retry(invoc, explanation) => {
|
||||
if force {
|
||||
// We are stuck, stop retrying and produce a dummy fragment.
|
||||
let span = invoc.span();
|
||||
self.cx.span_err(span, &explanation);
|
||||
let fragment = invoc.fragment_kind.dummy(span);
|
||||
self.collect_invocations(fragment, &[])
|
||||
} else {
|
||||
// Cannot expand, will retry this invocation later.
|
||||
undetermined_invocations
|
||||
.push((invoc, Some(InvocationRes::Single(ext))));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
InvocationRes::DeriveContainer(_exts) => {
|
||||
// FIXME: Consider using the derive resolutions (`_exts`) immediately,
|
||||
// instead of enqueuing the derives to be resolved again later.
|
||||
|
@ -463,14 +484,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
for path in derives {
|
||||
let expn_id = ExpnId::fresh(None);
|
||||
derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id));
|
||||
invocations.push(Invocation {
|
||||
kind: InvocationKind::Derive { path, item: item.clone() },
|
||||
fragment_kind: invoc.fragment_kind,
|
||||
expansion_data: ExpansionData {
|
||||
id: expn_id,
|
||||
..invoc.expansion_data.clone()
|
||||
invocations.push((
|
||||
Invocation {
|
||||
kind: InvocationKind::Derive { path, item: item.clone() },
|
||||
fragment_kind: invoc.fragment_kind,
|
||||
expansion_data: ExpansionData {
|
||||
id: expn_id,
|
||||
..invoc.expansion_data.clone()
|
||||
},
|
||||
},
|
||||
});
|
||||
None,
|
||||
));
|
||||
}
|
||||
let fragment =
|
||||
invoc.fragment_kind.expect_from_annotatables(::std::iter::once(item));
|
||||
|
@ -478,6 +502,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
}
|
||||
};
|
||||
|
||||
progress = true;
|
||||
if expanded_fragments.len() < depth {
|
||||
expanded_fragments.push(Vec::new());
|
||||
}
|
||||
|
@ -535,7 +560,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
&mut self,
|
||||
mut fragment: AstFragment,
|
||||
extra_placeholders: &[NodeId],
|
||||
) -> (AstFragment, Vec<Invocation>) {
|
||||
) -> (AstFragment, Vec<(Invocation, Option<InvocationRes>)>) {
|
||||
// Resolve `$crate`s in the fragment for pretty-printing.
|
||||
self.cx.resolver.resolve_dollar_crates();
|
||||
|
||||
|
@ -635,13 +660,17 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
self.cx.trace_macros_diag();
|
||||
}
|
||||
|
||||
fn expand_invoc(&mut self, invoc: Invocation, ext: &SyntaxExtensionKind) -> AstFragment {
|
||||
fn expand_invoc(
|
||||
&mut self,
|
||||
invoc: Invocation,
|
||||
ext: &SyntaxExtensionKind,
|
||||
) -> ExpandResult<AstFragment, Invocation> {
|
||||
if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit {
|
||||
self.error_recursion_limit_reached();
|
||||
}
|
||||
|
||||
let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
|
||||
match invoc.kind {
|
||||
ExpandResult::Ready(match invoc.kind {
|
||||
InvocationKind::Bang { mac, .. } => match ext {
|
||||
SyntaxExtensionKind::Bang(expander) => {
|
||||
self.gate_proc_macro_expansion_kind(span, fragment_kind);
|
||||
|
@ -663,7 +692,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
InvocationKind::Attr { attr, mut item, .. } => match ext {
|
||||
InvocationKind::Attr { attr, mut item, derives, after_derive } => match ext {
|
||||
SyntaxExtensionKind::Attr(expander) => {
|
||||
self.gate_proc_macro_input(&item);
|
||||
self.gate_proc_macro_attr_item(span, &item);
|
||||
|
@ -679,8 +708,25 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
SyntaxExtensionKind::LegacyAttr(expander) => {
|
||||
match validate_attr::parse_meta(self.cx.parse_sess, &attr) {
|
||||
Ok(meta) => {
|
||||
let item = expander.expand(self.cx, span, &meta, item);
|
||||
fragment_kind.expect_from_annotatables(item)
|
||||
let items = match expander.expand(self.cx, span, &meta, item) {
|
||||
ExpandResult::Ready(items) => items,
|
||||
ExpandResult::Retry(item, explanation) => {
|
||||
// Reassemble the original invocation for retrying.
|
||||
return ExpandResult::Retry(
|
||||
Invocation {
|
||||
kind: InvocationKind::Attr {
|
||||
attr,
|
||||
item,
|
||||
derives,
|
||||
after_derive,
|
||||
},
|
||||
..invoc
|
||||
},
|
||||
explanation,
|
||||
);
|
||||
}
|
||||
};
|
||||
fragment_kind.expect_from_annotatables(items)
|
||||
}
|
||||
Err(mut err) => {
|
||||
err.emit();
|
||||
|
@ -702,19 +748,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
SyntaxExtensionKind::Derive(expander)
|
||||
| SyntaxExtensionKind::LegacyDerive(expander) => {
|
||||
if !item.derive_allowed() {
|
||||
return fragment_kind.dummy(span);
|
||||
return ExpandResult::Ready(fragment_kind.dummy(span));
|
||||
}
|
||||
if let SyntaxExtensionKind::Derive(..) = ext {
|
||||
self.gate_proc_macro_input(&item);
|
||||
}
|
||||
let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path };
|
||||
let items = expander.expand(self.cx, span, &meta, item);
|
||||
let items = match expander.expand(self.cx, span, &meta, item) {
|
||||
ExpandResult::Ready(items) => items,
|
||||
ExpandResult::Retry(item, explanation) => {
|
||||
// Reassemble the original invocation for retrying.
|
||||
return ExpandResult::Retry(
|
||||
Invocation {
|
||||
kind: InvocationKind::Derive { path: meta.path, item },
|
||||
..invoc
|
||||
},
|
||||
explanation,
|
||||
);
|
||||
}
|
||||
};
|
||||
fragment_kind.expect_from_annotatables(items)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
InvocationKind::DeriveContainer { .. } => unreachable!(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
|
||||
|
@ -933,7 +991,7 @@ pub fn ensure_complete_parse<'a>(
|
|||
struct InvocationCollector<'a, 'b> {
|
||||
cx: &'a mut ExtCtxt<'b>,
|
||||
cfg: StripUnconfigured<'a>,
|
||||
invocations: Vec<Invocation>,
|
||||
invocations: Vec<(Invocation, Option<InvocationRes>)>,
|
||||
monotonic: bool,
|
||||
}
|
||||
|
||||
|
@ -955,15 +1013,18 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
|||
};
|
||||
let expn_id = ExpnId::fresh(expn_data);
|
||||
let vis = kind.placeholder_visibility();
|
||||
self.invocations.push(Invocation {
|
||||
kind,
|
||||
fragment_kind,
|
||||
expansion_data: ExpansionData {
|
||||
id: expn_id,
|
||||
depth: self.cx.current_expansion.depth + 1,
|
||||
..self.cx.current_expansion.clone()
|
||||
self.invocations.push((
|
||||
Invocation {
|
||||
kind,
|
||||
fragment_kind,
|
||||
expansion_data: ExpansionData {
|
||||
id: expn_id,
|
||||
depth: self.cx.current_expansion.depth + 1,
|
||||
..self.cx.current_expansion.clone()
|
||||
},
|
||||
},
|
||||
});
|
||||
None,
|
||||
));
|
||||
placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id), vis)
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ impl MultiItemModifier for ProcMacroDerive {
|
|||
span: Span,
|
||||
_meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
|
||||
let item = match item {
|
||||
Annotatable::Arm(..)
|
||||
| Annotatable::Field(..)
|
||||
|
@ -99,7 +99,7 @@ impl MultiItemModifier for ProcMacroDerive {
|
|||
"proc-macro derives may only be \
|
||||
applied to a struct, enum, or union",
|
||||
);
|
||||
return Vec::new();
|
||||
return ExpandResult::Ready(Vec::new());
|
||||
}
|
||||
};
|
||||
match item.kind {
|
||||
|
@ -110,7 +110,7 @@ impl MultiItemModifier for ProcMacroDerive {
|
|||
"proc-macro derives may only be \
|
||||
applied to a struct, enum, or union",
|
||||
);
|
||||
return Vec::new();
|
||||
return ExpandResult::Ready(Vec::new());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ impl MultiItemModifier for ProcMacroDerive {
|
|||
FatalError.raise();
|
||||
}
|
||||
|
||||
items
|
||||
ExpandResult::Ready(items)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,19 +85,13 @@ impl AttributeGate {
|
|||
|
||||
/// A template that the attribute input must match.
|
||||
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct AttributeTemplate {
|
||||
pub word: bool,
|
||||
pub list: Option<&'static str>,
|
||||
pub name_value_str: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl AttributeTemplate {
|
||||
pub fn only_word() -> Self {
|
||||
Self { word: true, list: None, name_value_str: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience macro for constructing attribute templates.
|
||||
/// E.g., `template!(Word, List: "description")` means that the attribute
|
||||
/// supports forms `#[attr]` and `#[attr(description)]`.
|
||||
|
|
|
@ -345,6 +345,42 @@ impl<'a> base::Resolver for Resolver<'a> {
|
|||
fn add_derive_copy(&mut self, expn_id: ExpnId) {
|
||||
self.containers_deriving_copy.insert(expn_id);
|
||||
}
|
||||
|
||||
// The function that implements the resolution logic of `#[cfg_accessible(path)]`.
|
||||
// Returns true if the path can certainly be resolved in one of three namespaces,
|
||||
// returns false if the path certainly cannot be resolved in any of the three namespaces.
|
||||
// Returns `Indeterminate` if we cannot give a certain answer yet.
|
||||
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate> {
|
||||
let span = path.span;
|
||||
let path = &Segment::from_path(path);
|
||||
let parent_scope = self.invocation_parent_scopes[&expn_id];
|
||||
|
||||
let mut indeterminate = false;
|
||||
for ns in [TypeNS, ValueNS, MacroNS].iter().copied() {
|
||||
match self.resolve_path(path, Some(ns), &parent_scope, false, span, CrateLint::No) {
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true),
|
||||
PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => {
|
||||
return Ok(true);
|
||||
}
|
||||
PathResult::Indeterminate => indeterminate = true,
|
||||
// FIXME: `resolve_path` is not ready to report partially resolved paths
|
||||
// correctly, so we just report an error if the path was reported as unresolved.
|
||||
// This needs to be fixed for `cfg_accessible` to be useful.
|
||||
PathResult::NonModule(..) | PathResult::Failed { .. } => {}
|
||||
PathResult::Module(_) => panic!("unexpected path resolution"),
|
||||
}
|
||||
}
|
||||
|
||||
if indeterminate {
|
||||
return Err(Indeterminate);
|
||||
}
|
||||
|
||||
self.session
|
||||
.struct_span_err(span, "not sure whether the path is accessible or not")
|
||||
.span_note(span, "`cfg_accessible` is not fully implemented")
|
||||
.emit();
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
|
|
|
@ -182,6 +182,7 @@ symbols! {
|
|||
caller_location,
|
||||
cdylib,
|
||||
cfg,
|
||||
cfg_accessible,
|
||||
cfg_attr,
|
||||
cfg_attr_multi,
|
||||
cfg_doctest,
|
||||
|
|
|
@ -240,6 +240,7 @@
|
|||
#![feature(atomic_mut_ptr)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(c_variadic)]
|
||||
#![cfg_attr(not(bootstrap), feature(cfg_accessible))]
|
||||
#![feature(cfg_target_has_atomic)]
|
||||
#![feature(cfg_target_thread_local)]
|
||||
#![feature(char_error_internals)]
|
||||
|
|
|
@ -53,6 +53,15 @@ pub use core::prelude::v1::{
|
|||
PartialEq, PartialOrd, RustcDecodable, RustcEncodable,
|
||||
};
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(
|
||||
feature = "cfg_accessible",
|
||||
issue = "64797",
|
||||
reason = "`cfg_accessible` is not fully implemented"
|
||||
)]
|
||||
#[doc(hidden)]
|
||||
pub use core::prelude::v1::cfg_accessible;
|
||||
|
||||
// The file so far is equivalent to src/libcore/prelude/v1.rs,
|
||||
// and below to src/liballoc/prelude.rs.
|
||||
// Those files are duplicated rather than using glob imports
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#![feature(cfg_accessible)]
|
||||
|
||||
#[cfg_accessible] //~ ERROR malformed `cfg_accessible` attribute input
|
||||
struct S1;
|
||||
|
||||
#[cfg_accessible = "value"] //~ ERROR malformed `cfg_accessible` attribute input
|
||||
struct S2;
|
||||
|
||||
#[cfg_accessible()] //~ ERROR `cfg_accessible` path is not specified
|
||||
struct S3;
|
||||
|
||||
#[cfg_accessible(std, core)] //~ ERROR multiple `cfg_accessible` paths are specified
|
||||
struct S4;
|
||||
|
||||
#[cfg_accessible("std")] //~ ERROR `cfg_accessible` path cannot be a literal
|
||||
struct S5;
|
||||
|
||||
#[cfg_accessible(std = "value")] //~ ERROR `cfg_accessible` path cannot accept arguments
|
||||
struct S6;
|
||||
|
||||
#[cfg_accessible(std(value))] //~ ERROR `cfg_accessible` path cannot accept arguments
|
||||
struct S7;
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,44 @@
|
|||
error: malformed `cfg_accessible` attribute input
|
||||
--> $DIR/cfg_accessible-input-validation.rs:3:1
|
||||
|
|
||||
LL | #[cfg_accessible]
|
||||
| ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]`
|
||||
|
||||
error: malformed `cfg_accessible` attribute input
|
||||
--> $DIR/cfg_accessible-input-validation.rs:6:1
|
||||
|
|
||||
LL | #[cfg_accessible = "value"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]`
|
||||
|
||||
error: `cfg_accessible` path is not specified
|
||||
--> $DIR/cfg_accessible-input-validation.rs:9:1
|
||||
|
|
||||
LL | #[cfg_accessible()]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: multiple `cfg_accessible` paths are specified
|
||||
--> $DIR/cfg_accessible-input-validation.rs:12:23
|
||||
|
|
||||
LL | #[cfg_accessible(std, core)]
|
||||
| ^^^^
|
||||
|
||||
error: `cfg_accessible` path cannot be a literal
|
||||
--> $DIR/cfg_accessible-input-validation.rs:15:18
|
||||
|
|
||||
LL | #[cfg_accessible("std")]
|
||||
| ^^^^^
|
||||
|
||||
error: `cfg_accessible` path cannot accept arguments
|
||||
--> $DIR/cfg_accessible-input-validation.rs:18:18
|
||||
|
|
||||
LL | #[cfg_accessible(std = "value")]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `cfg_accessible` path cannot accept arguments
|
||||
--> $DIR/cfg_accessible-input-validation.rs:21:18
|
||||
|
|
||||
LL | #[cfg_accessible(std(value))]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#![feature(cfg_accessible)]
|
||||
|
||||
#[cfg_accessible(Z)] //~ ERROR cannot determine whether the path is accessible or not
|
||||
struct S;
|
||||
|
||||
#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not
|
||||
struct Z;
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,14 @@
|
|||
error: cannot determine whether the path is accessible or not
|
||||
--> $DIR/cfg_accessible-stuck.rs:6:1
|
||||
|
|
||||
LL | #[cfg_accessible(S)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot determine whether the path is accessible or not
|
||||
--> $DIR/cfg_accessible-stuck.rs:3:1
|
||||
|
|
||||
LL | #[cfg_accessible(Z)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#[cfg_accessible(std)] //~ ERROR use of unstable library feature 'cfg_accessible'
|
||||
fn main() {}
|
|
@ -0,0 +1,12 @@
|
|||
error[E0658]: use of unstable library feature 'cfg_accessible': `cfg_accessible` is not fully implemented
|
||||
--> $DIR/cfg_accessible-unstable.rs:1:3
|
||||
|
|
||||
LL | #[cfg_accessible(std)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #64797 <https://github.com/rust-lang/rust/issues/64797> for more information
|
||||
= help: add `#![feature(cfg_accessible)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
43
src/test/ui/conditional-compilation/cfg_accessible.rs
Normal file
43
src/test/ui/conditional-compilation/cfg_accessible.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
#![feature(cfg_accessible)]
|
||||
|
||||
mod m {
|
||||
pub struct ExistingPublic;
|
||||
struct ExistingPrivate;
|
||||
}
|
||||
|
||||
#[cfg_accessible(m::ExistingPublic)]
|
||||
struct ExistingPublic;
|
||||
|
||||
// FIXME: Not implemented yet.
|
||||
#[cfg_accessible(m::ExistingPrivate)] //~ ERROR not sure whether the path is accessible or not
|
||||
struct ExistingPrivate;
|
||||
|
||||
// FIXME: Not implemented yet.
|
||||
#[cfg_accessible(m::NonExistent)] //~ ERROR not sure whether the path is accessible or not
|
||||
struct ExistingPrivate;
|
||||
|
||||
#[cfg_accessible(n::AccessibleExpanded)] // OK, `cfg_accessible` can wait and retry.
|
||||
struct AccessibleExpanded;
|
||||
|
||||
macro_rules! generate_accessible_expanded {
|
||||
() => {
|
||||
mod n {
|
||||
pub struct AccessibleExpanded;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_accessible_expanded!();
|
||||
|
||||
struct S {
|
||||
field: u8,
|
||||
}
|
||||
|
||||
// FIXME: Not implemented yet.
|
||||
#[cfg_accessible(S::field)] //~ ERROR not sure whether the path is accessible or not
|
||||
struct Field;
|
||||
|
||||
fn main() {
|
||||
ExistingPublic;
|
||||
AccessibleExpanded;
|
||||
}
|
38
src/test/ui/conditional-compilation/cfg_accessible.stderr
Normal file
38
src/test/ui/conditional-compilation/cfg_accessible.stderr
Normal file
|
@ -0,0 +1,38 @@
|
|||
error: not sure whether the path is accessible or not
|
||||
--> $DIR/cfg_accessible.rs:12:18
|
||||
|
|
||||
LL | #[cfg_accessible(m::ExistingPrivate)]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: `cfg_accessible` is not fully implemented
|
||||
--> $DIR/cfg_accessible.rs:12:18
|
||||
|
|
||||
LL | #[cfg_accessible(m::ExistingPrivate)]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: not sure whether the path is accessible or not
|
||||
--> $DIR/cfg_accessible.rs:16:18
|
||||
|
|
||||
LL | #[cfg_accessible(m::NonExistent)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
note: `cfg_accessible` is not fully implemented
|
||||
--> $DIR/cfg_accessible.rs:16:18
|
||||
|
|
||||
LL | #[cfg_accessible(m::NonExistent)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: not sure whether the path is accessible or not
|
||||
--> $DIR/cfg_accessible.rs:37:18
|
||||
|
|
||||
LL | #[cfg_accessible(S::field)]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
note: `cfg_accessible` is not fully implemented
|
||||
--> $DIR/cfg_accessible.rs:37:18
|
||||
|
|
||||
LL | #[cfg_accessible(S::field)]
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue