Skip to content

Commit 2e65289

Browse files
committed
builtin_macros: Add attribute macro #[cfg_accessible(path)]
1 parent 552a887 commit 2e65289

17 files changed

+311
-0
lines changed

src/libcore/macros/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,18 @@ pub(crate) mod builtin {
13831383
/* compiler built-in */
13841384
}
13851385

1386+
/// Keeps the item it's applied to if the passed path is accessible, and removes it otherwise.
1387+
#[cfg(not(bootstrap))]
1388+
#[unstable(
1389+
feature = "cfg_accessible",
1390+
issue = "64797",
1391+
reason = "`cfg_accessible` is not fully implemented"
1392+
)]
1393+
#[rustc_builtin_macro]
1394+
pub macro cfg_accessible($item:item) {
1395+
/* compiler built-in */
1396+
}
1397+
13861398
/// Unstable implementation detail of the `rustc` compiler, do not use.
13871399
#[rustc_builtin_macro]
13881400
#[stable(feature = "rust1", since = "1.0.0")]

src/libcore/prelude/v1.rs

+9
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,12 @@ pub use crate::{
6767
pub use crate::macros::builtin::{
6868
bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable,
6969
};
70+
71+
#[cfg(not(bootstrap))]
72+
#[unstable(
73+
feature = "cfg_accessible",
74+
issue = "64797",
75+
reason = "`cfg_accessible` is not fully implemented"
76+
)]
77+
#[doc(no_inline)]
78+
pub use crate::macros::builtin::cfg_accessible;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//! Implementation of the `#[cfg_accessible(path)]` attribute macro.
2+
3+
use rustc_ast::ast;
4+
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
5+
use rustc_feature::AttributeTemplate;
6+
use rustc_parse::validate_attr;
7+
use rustc_span::symbol::sym;
8+
use rustc_span::Span;
9+
10+
crate struct Expander;
11+
12+
fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
13+
match mi.meta_item_list() {
14+
None => {}
15+
Some([]) => ecx.span_err(mi.span, "`cfg_accessible` path is not specified"),
16+
Some([_, .., l]) => ecx.span_err(l.span(), "multiple `cfg_accessible` paths are specified"),
17+
Some([nmi]) => match nmi.meta_item() {
18+
None => ecx.span_err(nmi.span(), "`cfg_accessible` path cannot be a literal"),
19+
Some(mi) => {
20+
if !mi.is_word() {
21+
ecx.span_err(mi.span, "`cfg_accessible` path cannot accept arguments");
22+
}
23+
return Some(&mi.path);
24+
}
25+
},
26+
}
27+
None
28+
}
29+
30+
impl MultiItemModifier for Expander {
31+
fn expand(
32+
&self,
33+
ecx: &mut ExtCtxt<'_>,
34+
_span: Span,
35+
meta_item: &ast::MetaItem,
36+
item: Annotatable,
37+
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
38+
let template = AttributeTemplate { list: Some("path"), ..Default::default() };
39+
let attr = &ecx.attribute(meta_item.clone());
40+
validate_attr::check_builtin_attribute(ecx.parse_sess, attr, sym::cfg_accessible, template);
41+
42+
let path = match validate_input(ecx, meta_item) {
43+
Some(path) => path,
44+
None => return ExpandResult::Ready(Vec::new()),
45+
};
46+
47+
let failure_msg = "cannot determine whether the path is accessible or not";
48+
match ecx.resolver.cfg_accessible(ecx.current_expansion.id, path) {
49+
Ok(true) => ExpandResult::Ready(vec![item]),
50+
Ok(false) => ExpandResult::Ready(Vec::new()),
51+
Err(_) => ExpandResult::Retry(item, failure_msg.into()),
52+
}
53+
}
54+
}

src/librustc_builtin_macros/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc_span::symbol::sym;
2222
mod asm;
2323
mod assert;
2424
mod cfg;
25+
mod cfg_accessible;
2526
mod compile_error;
2627
mod concat;
2728
mod concat_idents;
@@ -85,6 +86,7 @@ pub fn register_builtin_macros(resolver: &mut dyn Resolver, edition: Edition) {
8586

8687
register_attr! {
8788
bench: test::expand_bench,
89+
cfg_accessible: cfg_accessible::Expander,
8890
global_allocator: global_allocator::expand,
8991
test: test::expand_test,
9092
test_case: test::expand_test_case,

src/librustc_expand/base.rs

+1
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,7 @@ pub trait Resolver {
897897

898898
fn has_derive_copy(&self, expn_id: ExpnId) -> bool;
899899
fn add_derive_copy(&mut self, expn_id: ExpnId);
900+
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;
900901
}
901902

902903
#[derive(Clone)]

src/librustc_resolve/macros.rs

+36
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,42 @@ impl<'a> base::Resolver for Resolver<'a> {
339339
fn add_derive_copy(&mut self, expn_id: ExpnId) {
340340
self.containers_deriving_copy.insert(expn_id);
341341
}
342+
343+
// The function that implements the resolution logic of `#[cfg_accessible(path)]`.
344+
// Returns true if the path can certainly be resolved in one of three namespaces,
345+
// returns false if the path certainly cannot be resolved in any of the three namespaces.
346+
// Returns `Indeterminate` if we cannot give a certain answer yet.
347+
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate> {
348+
let span = path.span;
349+
let path = &Segment::from_path(path);
350+
let parent_scope = self.invocation_parent_scopes[&expn_id];
351+
352+
let mut indeterminate = false;
353+
for ns in [TypeNS, ValueNS, MacroNS].iter().copied() {
354+
match self.resolve_path(path, Some(ns), &parent_scope, false, span, CrateLint::No) {
355+
PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true),
356+
PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => {
357+
return Ok(true);
358+
}
359+
PathResult::Indeterminate => indeterminate = true,
360+
// FIXME: `resolve_path` is not ready to report partially resolved paths
361+
// correctly, so we just report an error if the path was reported as unresolved.
362+
// This needs to be fixed for `cfg_accessible` to be useful.
363+
PathResult::NonModule(..) | PathResult::Failed { .. } => {}
364+
PathResult::Module(_) => panic!("unexpected path resolution"),
365+
}
366+
}
367+
368+
if indeterminate {
369+
return Err(Indeterminate);
370+
}
371+
372+
self.session
373+
.struct_span_err(span, "not sure whether the path is accessible or not")
374+
.span_note(span, "`cfg_accessible` is not fully implemented")
375+
.emit();
376+
Ok(false)
377+
}
342378
}
343379

344380
impl<'a> Resolver<'a> {

src/librustc_span/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ symbols! {
181181
caller_location,
182182
cdylib,
183183
cfg,
184+
cfg_accessible,
184185
cfg_attr,
185186
cfg_attr_multi,
186187
cfg_doctest,

src/libstd/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@
240240
#![feature(atomic_mut_ptr)]
241241
#![feature(box_syntax)]
242242
#![feature(c_variadic)]
243+
#![cfg_attr(not(bootstrap), feature(cfg_accessible))]
243244
#![feature(cfg_target_has_atomic)]
244245
#![feature(cfg_target_thread_local)]
245246
#![feature(char_error_internals)]

src/libstd/prelude/v1.rs

+9
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ pub use core::prelude::v1::{
5353
PartialEq, PartialOrd, RustcDecodable, RustcEncodable,
5454
};
5555

56+
#[cfg(not(bootstrap))]
57+
#[unstable(
58+
feature = "cfg_accessible",
59+
issue = "64797",
60+
reason = "`cfg_accessible` is not fully implemented"
61+
)]
62+
#[doc(hidden)]
63+
pub use core::prelude::v1::cfg_accessible;
64+
5665
// The file so far is equivalent to src/libcore/prelude/v1.rs,
5766
// and below to src/liballoc/prelude.rs.
5867
// Those files are duplicated rather than using glob imports
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#![feature(cfg_accessible)]
2+
3+
#[cfg_accessible] //~ ERROR malformed `cfg_accessible` attribute input
4+
struct S1;
5+
6+
#[cfg_accessible = "value"] //~ ERROR malformed `cfg_accessible` attribute input
7+
struct S2;
8+
9+
#[cfg_accessible()] //~ ERROR `cfg_accessible` path is not specified
10+
struct S3;
11+
12+
#[cfg_accessible(std, core)] //~ ERROR multiple `cfg_accessible` paths are specified
13+
struct S4;
14+
15+
#[cfg_accessible("std")] //~ ERROR `cfg_accessible` path cannot be a literal
16+
struct S5;
17+
18+
#[cfg_accessible(std = "value")] //~ ERROR `cfg_accessible` path cannot accept arguments
19+
struct S6;
20+
21+
#[cfg_accessible(std(value))] //~ ERROR `cfg_accessible` path cannot accept arguments
22+
struct S7;
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error: malformed `cfg_accessible` attribute input
2+
--> $DIR/cfg_accessible-input-validation.rs:3:1
3+
|
4+
LL | #[cfg_accessible]
5+
| ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]`
6+
7+
error: malformed `cfg_accessible` attribute input
8+
--> $DIR/cfg_accessible-input-validation.rs:6:1
9+
|
10+
LL | #[cfg_accessible = "value"]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[cfg_accessible(path)]`
12+
13+
error: `cfg_accessible` path is not specified
14+
--> $DIR/cfg_accessible-input-validation.rs:9:1
15+
|
16+
LL | #[cfg_accessible()]
17+
| ^^^^^^^^^^^^^^^^^^^
18+
19+
error: multiple `cfg_accessible` paths are specified
20+
--> $DIR/cfg_accessible-input-validation.rs:12:23
21+
|
22+
LL | #[cfg_accessible(std, core)]
23+
| ^^^^
24+
25+
error: `cfg_accessible` path cannot be a literal
26+
--> $DIR/cfg_accessible-input-validation.rs:15:18
27+
|
28+
LL | #[cfg_accessible("std")]
29+
| ^^^^^
30+
31+
error: `cfg_accessible` path cannot accept arguments
32+
--> $DIR/cfg_accessible-input-validation.rs:18:18
33+
|
34+
LL | #[cfg_accessible(std = "value")]
35+
| ^^^^^^^^^^^^^
36+
37+
error: `cfg_accessible` path cannot accept arguments
38+
--> $DIR/cfg_accessible-input-validation.rs:21:18
39+
|
40+
LL | #[cfg_accessible(std(value))]
41+
| ^^^^^^^^^^
42+
43+
error: aborting due to 7 previous errors
44+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#![feature(cfg_accessible)]
2+
3+
#[cfg_accessible(Z)] //~ ERROR cannot determine whether the path is accessible or not
4+
struct S;
5+
6+
#[cfg_accessible(S)] //~ ERROR cannot determine whether the path is accessible or not
7+
struct Z;
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: cannot determine whether the path is accessible or not
2+
--> $DIR/cfg_accessible-stuck.rs:6:1
3+
|
4+
LL | #[cfg_accessible(S)]
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
7+
error: cannot determine whether the path is accessible or not
8+
--> $DIR/cfg_accessible-stuck.rs:3:1
9+
|
10+
LL | #[cfg_accessible(Z)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 2 previous errors
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[cfg_accessible(std)] //~ ERROR use of unstable library feature 'cfg_accessible'
2+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: use of unstable library feature 'cfg_accessible': `cfg_accessible` is not fully implemented
2+
--> $DIR/cfg_accessible-unstable.rs:1:3
3+
|
4+
LL | #[cfg_accessible(std)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #64797 <https://github.com/rust-lang/rust/issues/64797> for more information
8+
= help: add `#![feature(cfg_accessible)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#![feature(cfg_accessible)]
2+
3+
mod m {
4+
pub struct ExistingPublic;
5+
struct ExistingPrivate;
6+
}
7+
8+
#[cfg_accessible(m::ExistingPublic)]
9+
struct ExistingPublic;
10+
11+
// FIXME: Not implemented yet.
12+
#[cfg_accessible(m::ExistingPrivate)] //~ ERROR not sure whether the path is accessible or not
13+
struct ExistingPrivate;
14+
15+
// FIXME: Not implemented yet.
16+
#[cfg_accessible(m::NonExistent)] //~ ERROR not sure whether the path is accessible or not
17+
struct ExistingPrivate;
18+
19+
#[cfg_accessible(n::AccessibleExpanded)] // OK, `cfg_accessible` can wait and retry.
20+
struct AccessibleExpanded;
21+
22+
macro_rules! generate_accessible_expanded {
23+
() => {
24+
mod n {
25+
pub struct AccessibleExpanded;
26+
}
27+
};
28+
}
29+
30+
generate_accessible_expanded!();
31+
32+
struct S {
33+
field: u8,
34+
}
35+
36+
// FIXME: Not implemented yet.
37+
#[cfg_accessible(S::field)] //~ ERROR not sure whether the path is accessible or not
38+
struct Field;
39+
40+
fn main() {
41+
ExistingPublic;
42+
AccessibleExpanded;
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: not sure whether the path is accessible or not
2+
--> $DIR/cfg_accessible.rs:12:18
3+
|
4+
LL | #[cfg_accessible(m::ExistingPrivate)]
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
note: `cfg_accessible` is not fully implemented
8+
--> $DIR/cfg_accessible.rs:12:18
9+
|
10+
LL | #[cfg_accessible(m::ExistingPrivate)]
11+
| ^^^^^^^^^^^^^^^^^^
12+
13+
error: not sure whether the path is accessible or not
14+
--> $DIR/cfg_accessible.rs:16:18
15+
|
16+
LL | #[cfg_accessible(m::NonExistent)]
17+
| ^^^^^^^^^^^^^^
18+
|
19+
note: `cfg_accessible` is not fully implemented
20+
--> $DIR/cfg_accessible.rs:16:18
21+
|
22+
LL | #[cfg_accessible(m::NonExistent)]
23+
| ^^^^^^^^^^^^^^
24+
25+
error: not sure whether the path is accessible or not
26+
--> $DIR/cfg_accessible.rs:37:18
27+
|
28+
LL | #[cfg_accessible(S::field)]
29+
| ^^^^^^^^
30+
|
31+
note: `cfg_accessible` is not fully implemented
32+
--> $DIR/cfg_accessible.rs:37:18
33+
|
34+
LL | #[cfg_accessible(S::field)]
35+
| ^^^^^^^^
36+
37+
error: aborting due to 3 previous errors
38+

0 commit comments

Comments
 (0)