提交 8ebc6d6d 编写于 作者: M Manish Goregaokar

Rollup merge of #54862 - Havvy:cfg_attr_multi, r=petrochenkov

Fixes #47311.
r? @nrc
# `cfg_attr_multi`
The tracking issue for this feature is: [#54881]
The RFC for this feature is: [#2539]
[#54881]: https://github.com/rust-lang/rust/issues/54881
[#2539]: https://github.com/rust-lang/rfcs/pull/2539
------------------------
This feature flag lets you put multiple attributes into a `cfg_attr` attribute.
Example:
```rust,ignore
#[cfg_attr(all(), must_use, optimize)]
```
Because `cfg_attr` resolves before procedural macros, this does not affect
macro resolution at all.
\ No newline at end of file
......@@ -9,7 +9,14 @@
// except according to those terms.
use attr::HasAttrs;
use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue};
use feature_gate::{
feature_err,
EXPLAIN_STMT_ATTR_SYNTAX,
Features,
get_features,
GateIssue,
emit_feature_err,
};
use {fold, attr};
use ast;
use source_map::Spanned;
......@@ -73,49 +80,103 @@ pub fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
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 gate_cfg_attr_multi = if let Some(ref features) = self.features {
!features.cfg_attr_multi
} else {
false
};
let cfg_attr_span = attr.span;
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 {
// Check feature gate and lint on zero attributes in source. Even if the feature is gated,
// we still compute as if it wasn't, since the emitted error will stop compilation futher
// along the compilation.
match (expanded_attrs.len(), gate_cfg_attr_multi) {
(0, false) => {
// FIXME: Emit unused attribute lint here.
},
(1, _) => {},
(_, true) => {
emit_feature_err(
self.sess,
"cfg_attr_multi",
cfg_attr_span,
GateIssue::Language,
"cfg_attr with zero or more than one attributes is experimental",
);
},
(_, false) => {}
}
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 +226,7 @@ pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
})
}
// 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() {
......
......@@ -499,6 +499,9 @@ pub fn walk_feature_fields<F>(&self, mut f: F)
// Allows `impl Trait` in bindings (`let`, `const`, `static`)
(active, impl_trait_in_bindings, "1.30.0", Some(34511), None),
// #[cfg_attr(predicate, multiple, attributes, here)]
(active, cfg_attr_multi, "1.31.0", Some(54881), None),
);
declare_features! (
......
......@@ -678,7 +678,7 @@ pub fn expect(&mut self, t: &token::Token) -> PResult<'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 {
......
// 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() {}
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
......
// 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)]
#![feature(cfg_attr_multi)]
#[cfg_attr(any(), deprecated, must_use)]
struct Struct {}
impl Struct {
fn new() -> Struct {
Struct {}
}
}
fn main() {
Struct::new();
}
// 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
#![feature(cfg_attr_multi)]
#![cfg_attr(broken, no_core, no_std)] //~ ERROR no_core is experimental
fn main() { }
error[E0658]: no_core is experimental (see issue #29639)
--> $DIR/cfg-attr-multi-invalid-1.rs:14: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`.
// 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
#![feature(cfg_attr_multi)]
#![cfg_attr(broken, no_std, no_core)] //~ ERROR no_core is experimental
fn main() { }
error[E0658]: no_core is experimental (see issue #29639)
--> $DIR/cfg-attr-multi-invalid-2.rs:14: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`.
// 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)]
#![feature(cfg_attr_multi)]
#[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
}
warning: use of deprecated item 'MustUseDeprecated'
--> $DIR/cfg-attr-multi-true.rs:13: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:20:5
|
LL | MustUseDeprecated::new(); //~ warning: use of deprecated item
| ^^^^^^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'MustUseDeprecated'
--> $DIR/cfg-attr-multi-true.rs:14:17
|
LL | fn new() -> MustUseDeprecated { //~ warning: use of deprecated item
| ^^^^^^^^^^^^^^^^^
warning: use of deprecated item 'MustUseDeprecated'
--> $DIR/cfg-attr-multi-true.rs:15:9
|
LL | MustUseDeprecated {} //~ warning: use of deprecated item
| ^^^^^^^^^^^^^^^^^
warning: unused `MustUseDeprecated` which must be used
--> $DIR/cfg-attr-multi-true.rs:20: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)]
| ^^^^^^^^^^^^^^^
// 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() {}
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
// gate-test-cfg_attr_multi
#![cfg_attr(all(), warn(nonstandard_style), allow(unused_attributes))]
//~^ ERROR cfg_attr with zero or more than one attributes is experimental
fn main() {}
error[E0658]: cfg_attr with zero or more than one attributes is experimental (see issue #54881)
--> $DIR/feature-gate-cfg-attr-multi-1.rs:3:1
|
LL | #![cfg_attr(all(), warn(nonstandard_style), allow(unused_attributes))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(cfg_attr_multi)] to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.
#![cfg_attr(all(),)]
//~^ ERROR cfg_attr with zero or more than one attributes is experimental
fn main() {}
error[E0658]: cfg_attr with zero or more than one attributes is experimental (see issue #54881)
--> $DIR/feature-gate-cfg-attr-multi-2.rs:1:1
|
LL | #![cfg_attr(all(),)]
| ^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(cfg_attr_multi)] to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.
// Test that settingt the featute gate while using its functionality doesn't error.
// compile-pass
#![cfg_attr(all(), feature(cfg_attr_multi), crate_type="bin")]
fn main() {}
// Test that settingt the featute gate while using its functionality doesn't error.
// Specifically, if there's a cfg-attr *before* the feature gate.
// compile-pass
#![cfg_attr(all(),)]
#![cfg_attr(all(), feature(cfg_attr_multi), crate_type="bin")]
fn main() {}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册