builtin_macros: Add attribute macro #[cfg_accessible(path)]
This commit is contained in:
parent
552a8875bd
commit
2e6528961c
@ -1383,6 +1383,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()),
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -897,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)]
|
||||
|
@ -339,6 +339,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> {
|
||||
|
@ -181,6 +181,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…
Reference in New Issue
Block a user