Skip to content

Commit

Permalink
builtin_macros: Add attribute macro #[cfg_accessible(path)]
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Mar 9, 2020
1 parent 552a887 commit 2e65289
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/libcore/macros/mod.rs
Expand Up @@ -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")]
Expand Down
9 changes: 9 additions & 0 deletions src/libcore/prelude/v1.rs
Expand Up @@ -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 changes: 54 additions & 0 deletions src/librustc_builtin_macros/cfg_accessible.rs
@@ -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 changes: 2 additions & 0 deletions src/librustc_builtin_macros/lib.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_expand/base.rs
Expand Up @@ -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)]
Expand Down
36 changes: 36 additions & 0 deletions src/librustc_resolve/macros.rs
Expand Up @@ -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> {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_span/symbol.rs
Expand Up @@ -181,6 +181,7 @@ symbols! {
caller_location,
cdylib,
cfg,
cfg_accessible,
cfg_attr,
cfg_attr_multi,
cfg_doctest,
Expand Down
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Expand Up @@ -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)]
Expand Down
9 changes: 9 additions & 0 deletions src/libstd/prelude/v1.rs
Expand Up @@ -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
Expand Down
@@ -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

9 changes: 9 additions & 0 deletions src/test/ui/conditional-compilation/cfg_accessible-stuck.rs
@@ -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() {}
14 changes: 14 additions & 0 deletions src/test/ui/conditional-compilation/cfg_accessible-stuck.stderr
@@ -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() {}
12 changes: 12 additions & 0 deletions src/test/ui/conditional-compilation/cfg_accessible-unstable.stderr
@@ -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 changes: 43 additions & 0 deletions src/test/ui/conditional-compilation/cfg_accessible.rs
@@ -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 changes: 38 additions & 0 deletions src/test/ui/conditional-compilation/cfg_accessible.stderr
@@ -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

0 comments on commit 2e65289

Please sign in to comment.