cfg_attr_multi: Basic implementation
Does not implement the warning or a feature flag.
This commit is contained in:
parent
9568ec6bef
commit
1a867dc346
14 changed files with 252 additions and 45 deletions
|
@ -73,49 +73,76 @@ impl<'a> StripUnconfigured<'a> {
|
|||
if self.in_cfg(node.attrs()) { Some(node) } else { None }
|
||||
}
|
||||
|
||||
/// Parse and expand all `cfg_attr` attributes into a list of attributes
|
||||
/// that are within each `cfg_attr` that has a true configuration predicate.
|
||||
///
|
||||
/// Gives compiler warnigns if any `cfg_attr` does not contain any
|
||||
/// attributes and is in the original source code. Gives compiler errors if
|
||||
/// the syntax of any `cfg_attr` is incorrect.
|
||||
pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
|
||||
node.map_attrs(|attrs| {
|
||||
attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect()
|
||||
attrs.into_iter().flat_map(|attr| self.process_cfg_attr(attr)).collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
|
||||
/// Parse and expand a single `cfg_attr` attribute into a list of attributes
|
||||
/// when the configuration predicate is true, or otherwise expand into an
|
||||
/// empty list of attributes.
|
||||
///
|
||||
/// Gives a compiler warning when the `cfg_attr` contains no attribtes and
|
||||
/// is in the original source file. Gives a compiler error if the syntax of
|
||||
/// the attribute is incorrect
|
||||
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
|
||||
if !attr.check_name("cfg_attr") {
|
||||
return Some(attr);
|
||||
return vec![attr];
|
||||
}
|
||||
|
||||
let (cfg, path, tokens, span) = match attr.parse(self.sess, |parser| {
|
||||
let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
|
||||
parser.expect(&token::OpenDelim(token::Paren))?;
|
||||
let cfg = parser.parse_meta_item()?;
|
||||
|
||||
let cfg_predicate = parser.parse_meta_item()?;
|
||||
parser.expect(&token::Comma)?;
|
||||
let lo = parser.span.lo();
|
||||
let (path, tokens) = parser.parse_meta_item_unrestricted()?;
|
||||
parser.eat(&token::Comma); // Optional trailing comma
|
||||
|
||||
// Presumably, the majority of the time there will only be one attr.
|
||||
let mut expanded_attrs = Vec::with_capacity(1);
|
||||
|
||||
while !parser.check(&token::CloseDelim(token::Paren)) {
|
||||
let lo = parser.span.lo();
|
||||
let (path, tokens) = parser.parse_meta_item_unrestricted()?;
|
||||
expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo)));
|
||||
parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
|
||||
}
|
||||
|
||||
parser.expect(&token::CloseDelim(token::Paren))?;
|
||||
Ok((cfg, path, tokens, parser.prev_span.with_lo(lo)))
|
||||
Ok((cfg_predicate, expanded_attrs))
|
||||
}) {
|
||||
Ok(result) => result,
|
||||
Err(mut e) => {
|
||||
e.emit();
|
||||
return None;
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
if attr::cfg_matches(&cfg, self.sess, self.features) {
|
||||
self.process_cfg_attr(ast::Attribute {
|
||||
if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
|
||||
// We call `process_cfg_attr` recursively in case there's a
|
||||
// `cfg_attr` inside of another `cfg_attr`. E.g.
|
||||
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
|
||||
expanded_attrs.into_iter()
|
||||
.flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute {
|
||||
id: attr::mk_attr_id(),
|
||||
style: attr.style,
|
||||
path,
|
||||
tokens,
|
||||
is_sugared_doc: false,
|
||||
span,
|
||||
})
|
||||
}))
|
||||
.collect()
|
||||
} else {
|
||||
None
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if a node with the given attributes should be included in this configuration.
|
||||
/// Determine if a node with the given attributes should be included in this configuration.
|
||||
pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().all(|attr| {
|
||||
if !is_cfg(attr) {
|
||||
|
@ -165,7 +192,7 @@ impl<'a> StripUnconfigured<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
// Visit attributes on expression and statements (but not attributes on items in blocks).
|
||||
/// Visit attributes on expression and statements (but not attributes on items in blocks).
|
||||
fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
|
||||
// flag the offending attributes
|
||||
for attr in attrs.iter() {
|
||||
|
|
|
@ -678,7 +678,7 @@ impl<'a> Parser<'a> {
|
|||
/// Expect next token to be edible or inedible token. If edible,
|
||||
/// then consume it; if inedible, then return without consuming
|
||||
/// anything. Signal a fatal error if next token is unexpected.
|
||||
fn expect_one_of(&mut self,
|
||||
pub fn expect_one_of(&mut self,
|
||||
edible: &[token::Token],
|
||||
inedible: &[token::Token]) -> PResult<'a, ()>{
|
||||
fn tokens_to_string(tokens: &[TokenType]) -> String {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
// compile-flags: --cfg TRUE
|
||||
|
||||
#[cfg_attr(TRUE, inline,)] // OK
|
||||
fn f() {}
|
||||
|
||||
#[cfg_attr(FALSE, inline,)] // OK
|
||||
fn g() {}
|
||||
|
||||
#[cfg_attr(TRUE, inline,,)] //~ ERROR expected `)`, found `,`
|
||||
fn h() {}
|
||||
|
||||
#[cfg_attr(FALSE, inline,,)] //~ ERROR expected `)`, found `,`
|
||||
fn i() {}
|
|
@ -1,14 +0,0 @@
|
|||
error: expected `)`, found `,`
|
||||
--> $DIR/cfg-attr-trailing-comma.rs:9:25
|
||||
|
|
||||
LL | #[cfg_attr(TRUE, inline,,)] //~ ERROR expected `)`, found `,`
|
||||
| ^ expected `)`
|
||||
|
||||
error: expected `)`, found `,`
|
||||
--> $DIR/cfg-attr-trailing-comma.rs:12:26
|
||||
|
|
||||
LL | #[cfg_attr(FALSE, inline,,)] //~ ERROR expected `)`, found `,`
|
||||
| ^ expected `)`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -2,7 +2,7 @@ error[E0658]: no_core is experimental (see issue #29639)
|
|||
--> $DIR/cfg-attr-crate-2.rs:15:21
|
||||
|
|
||||
LL | #![cfg_attr(broken, no_core)] //~ ERROR no_core is experimental
|
||||
| ^^^^^^^^
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: add #![feature(no_core)] to the crate attributes to enable
|
||||
|
||||
|
|
19
src/test/ui/conditional-compilation/cfg-attr-multi-false.rs
Normal file
19
src/test/ui/conditional-compilation/cfg-attr-multi-false.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Test that cfg_attr doesn't emit any attributes when the
|
||||
// configuation variable is false. This mirrors `cfg-attr-multi-true.rs`
|
||||
|
||||
// compile-pass
|
||||
|
||||
#![warn(unused_must_use)]
|
||||
|
||||
#[cfg_attr(any(), deprecated, must_use)]
|
||||
struct Struct {}
|
||||
|
||||
impl Struct {
|
||||
fn new() -> Struct {
|
||||
Struct {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Struct::new();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// compile-flags: --cfg broken
|
||||
|
||||
#![cfg_attr(broken, no_core, no_std)] //~ ERROR no_core is experimental
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,11 @@
|
|||
error[E0658]: no_core is experimental (see issue #29639)
|
||||
--> $DIR/cfg-attr-multi-invalid-1.rs:13:21
|
||||
|
|
||||
LL | #![cfg_attr(broken, no_core, no_std)] //~ ERROR no_core is experimental
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: add #![feature(no_core)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// compile-flags: --cfg broken
|
||||
|
||||
#![cfg_attr(broken, no_std, no_core)] //~ ERROR no_core is experimental
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,11 @@
|
|||
error[E0658]: no_core is experimental (see issue #29639)
|
||||
--> $DIR/cfg-attr-multi-invalid-2.rs:13:29
|
||||
|
|
||||
LL | #![cfg_attr(broken, no_std, no_core)] //~ ERROR no_core is experimental
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: add #![feature(no_core)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
21
src/test/ui/conditional-compilation/cfg-attr-multi-true.rs
Normal file
21
src/test/ui/conditional-compilation/cfg-attr-multi-true.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Test that cfg_attr with multiple attributes actually emits both attributes.
|
||||
// This is done by emitting two attributes that cause new warnings, and then
|
||||
// triggering those warnings.
|
||||
|
||||
// compile-pass
|
||||
|
||||
#![warn(unused_must_use)]
|
||||
|
||||
#[cfg_attr(all(), deprecated, must_use)]
|
||||
struct MustUseDeprecated {}
|
||||
|
||||
impl MustUseDeprecated { //~ warning: use of deprecated item
|
||||
fn new() -> MustUseDeprecated { //~ warning: use of deprecated item
|
||||
MustUseDeprecated {} //~ warning: use of deprecated item
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
MustUseDeprecated::new(); //~ warning: use of deprecated item
|
||||
//| warning: unused `MustUseDeprecated` which must be used
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
warning: use of deprecated item 'MustUseDeprecated'
|
||||
--> $DIR/cfg-attr-multi-true.rs:12:6
|
||||
|
|
||||
LL | impl MustUseDeprecated { //~ warning: use of deprecated item
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: #[warn(deprecated)] on by default
|
||||
|
||||
warning: use of deprecated item 'MustUseDeprecated'
|
||||
--> $DIR/cfg-attr-multi-true.rs:19:5
|
||||
|
|
||||
LL | MustUseDeprecated::new(); //~ warning: use of deprecated item
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: use of deprecated item 'MustUseDeprecated'
|
||||
--> $DIR/cfg-attr-multi-true.rs:13:17
|
||||
|
|
||||
LL | fn new() -> MustUseDeprecated { //~ warning: use of deprecated item
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: use of deprecated item 'MustUseDeprecated'
|
||||
--> $DIR/cfg-attr-multi-true.rs:14:9
|
||||
|
|
||||
LL | MustUseDeprecated {} //~ warning: use of deprecated item
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: unused `MustUseDeprecated` which must be used
|
||||
--> $DIR/cfg-attr-multi-true.rs:19:5
|
||||
|
|
||||
LL | MustUseDeprecated::new(); //~ warning: use of deprecated item
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/cfg-attr-multi-true.rs:7:9
|
||||
|
|
||||
LL | #![warn(unused_must_use)]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
45
src/test/ui/conditional-compilation/cfg-attr-parse.rs
Normal file
45
src/test/ui/conditional-compilation/cfg-attr-parse.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Parse `cfg_attr` with varying numbers of attributes and trailing commas
|
||||
|
||||
#![feature(cfg_attr_multi)]
|
||||
|
||||
// Completely empty `cfg_attr` input
|
||||
#[cfg_attr()] //~ error: expected identifier, found `)`
|
||||
struct NoConfigurationPredicate;
|
||||
|
||||
// Zero attributes, zero trailing comma (comma manatory here)
|
||||
#[cfg_attr(all())] //~ error: expected `,`, found `)`
|
||||
struct A0C0;
|
||||
|
||||
// Zero attributes, one trailing comma
|
||||
#[cfg_attr(all(),)] // Ok
|
||||
struct A0C1;
|
||||
|
||||
// Zero attributes, two trailing commas
|
||||
#[cfg_attr(all(),,)] //~ ERROR expected identifier
|
||||
struct A0C2;
|
||||
|
||||
// One attribute, no trailing comma
|
||||
#[cfg_attr(all(), must_use)] // Ok
|
||||
struct A1C0;
|
||||
|
||||
// One attribute, one trailing comma
|
||||
#[cfg_attr(all(), must_use,)] // Ok
|
||||
struct A1C1;
|
||||
|
||||
// One attribute, two trailing commas
|
||||
#[cfg_attr(all(), must_use,,)] //~ ERROR expected identifier
|
||||
struct A1C2;
|
||||
|
||||
// Two attributes, no trailing comma
|
||||
#[cfg_attr(all(), must_use, deprecated)] // Ok
|
||||
struct A2C0;
|
||||
|
||||
// Two attributes, one trailing comma
|
||||
#[cfg_attr(all(), must_use, deprecated,)] // Ok
|
||||
struct A2C1;
|
||||
|
||||
// Two attributes, two trailing commas
|
||||
#[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
|
||||
struct A2C2;
|
||||
|
||||
fn main() {}
|
32
src/test/ui/conditional-compilation/cfg-attr-parse.stderr
Normal file
32
src/test/ui/conditional-compilation/cfg-attr-parse.stderr
Normal file
|
@ -0,0 +1,32 @@
|
|||
error: expected identifier, found `)`
|
||||
--> $DIR/cfg-attr-parse.rs:6:12
|
||||
|
|
||||
LL | #[cfg_attr()] //~ error: expected identifier, found `)`
|
||||
| ^ expected identifier
|
||||
|
||||
error: expected `,`, found `)`
|
||||
--> $DIR/cfg-attr-parse.rs:10:17
|
||||
|
|
||||
LL | #[cfg_attr(all())] //~ error: expected `,`, found `)`
|
||||
| ^ expected `,`
|
||||
|
||||
error: expected identifier, found `,`
|
||||
--> $DIR/cfg-attr-parse.rs:18:18
|
||||
|
|
||||
LL | #[cfg_attr(all(),,)] //~ ERROR expected identifier
|
||||
| ^ expected identifier
|
||||
|
||||
error: expected identifier, found `,`
|
||||
--> $DIR/cfg-attr-parse.rs:30:28
|
||||
|
|
||||
LL | #[cfg_attr(all(), must_use,,)] //~ ERROR expected identifier
|
||||
| ^ expected identifier
|
||||
|
||||
error: expected identifier, found `,`
|
||||
--> $DIR/cfg-attr-parse.rs:42:40
|
||||
|
|
||||
LL | #[cfg_attr(all(), must_use, deprecated,,)] //~ ERROR expected identifier
|
||||
| ^ expected identifier
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
Loading…
Add table
Reference in a new issue