Skip to content

Commit 8ebc6d6

Browse files
committed
Rollup merge of #54862 - Havvy:cfg_attr_multi, r=petrochenkov
Fixes #47311. r? @nrc
2 parents a267b3a + bbe832d commit 8ebc6d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+360
-46
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# `cfg_attr_multi`
2+
3+
The tracking issue for this feature is: [#54881]
4+
The RFC for this feature is: [#2539]
5+
6+
[#54881]: https://github.com/rust-lang/rust/issues/54881
7+
[#2539]: https://github.com/rust-lang/rfcs/pull/2539
8+
9+
------------------------
10+
11+
This feature flag lets you put multiple attributes into a `cfg_attr` attribute.
12+
13+
Example:
14+
15+
```rust,ignore
16+
#[cfg_attr(all(), must_use, optimize)]
17+
```
18+
19+
Because `cfg_attr` resolves before procedural macros, this does not affect
20+
macro resolution at all.

src/libsyntax/config.rs

+78-17
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@
99
// except according to those terms.
1010

1111
use attr::HasAttrs;
12-
use feature_gate::{feature_err, EXPLAIN_STMT_ATTR_SYNTAX, Features, get_features, GateIssue};
12+
use feature_gate::{
13+
feature_err,
14+
EXPLAIN_STMT_ATTR_SYNTAX,
15+
Features,
16+
get_features,
17+
GateIssue,
18+
emit_feature_err,
19+
};
1320
use {fold, attr};
1421
use ast;
1522
use source_map::Spanned;
@@ -73,49 +80,103 @@ impl<'a> StripUnconfigured<'a> {
7380
if self.in_cfg(node.attrs()) { Some(node) } else { None }
7481
}
7582

83+
/// Parse and expand all `cfg_attr` attributes into a list of attributes
84+
/// that are within each `cfg_attr` that has a true configuration predicate.
85+
///
86+
/// Gives compiler warnigns if any `cfg_attr` does not contain any
87+
/// attributes and is in the original source code. Gives compiler errors if
88+
/// the syntax of any `cfg_attr` is incorrect.
7689
pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: T) -> T {
7790
node.map_attrs(|attrs| {
78-
attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect()
91+
attrs.into_iter().flat_map(|attr| self.process_cfg_attr(attr)).collect()
7992
})
8093
}
8194

82-
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
95+
/// Parse and expand a single `cfg_attr` attribute into a list of attributes
96+
/// when the configuration predicate is true, or otherwise expand into an
97+
/// empty list of attributes.
98+
///
99+
/// Gives a compiler warning when the `cfg_attr` contains no attribtes and
100+
/// is in the original source file. Gives a compiler error if the syntax of
101+
/// the attribute is incorrect
102+
fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
83103
if !attr.check_name("cfg_attr") {
84-
return Some(attr);
104+
return vec![attr];
85105
}
86106

87-
let (cfg, path, tokens, span) = match attr.parse(self.sess, |parser| {
107+
let gate_cfg_attr_multi = if let Some(ref features) = self.features {
108+
!features.cfg_attr_multi
109+
} else {
110+
false
111+
};
112+
let cfg_attr_span = attr.span;
113+
114+
let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
88115
parser.expect(&token::OpenDelim(token::Paren))?;
89-
let cfg = parser.parse_meta_item()?;
116+
117+
let cfg_predicate = parser.parse_meta_item()?;
90118
parser.expect(&token::Comma)?;
91-
let lo = parser.span.lo();
92-
let (path, tokens) = parser.parse_meta_item_unrestricted()?;
93-
parser.eat(&token::Comma); // Optional trailing comma
119+
120+
// Presumably, the majority of the time there will only be one attr.
121+
let mut expanded_attrs = Vec::with_capacity(1);
122+
123+
while !parser.check(&token::CloseDelim(token::Paren)) {
124+
let lo = parser.span.lo();
125+
let (path, tokens) = parser.parse_meta_item_unrestricted()?;
126+
expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo)));
127+
parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
128+
}
129+
94130
parser.expect(&token::CloseDelim(token::Paren))?;
95-
Ok((cfg, path, tokens, parser.prev_span.with_lo(lo)))
131+
Ok((cfg_predicate, expanded_attrs))
96132
}) {
97133
Ok(result) => result,
98134
Err(mut e) => {
99135
e.emit();
100-
return None;
136+
return Vec::new();
101137
}
102138
};
103139

104-
if attr::cfg_matches(&cfg, self.sess, self.features) {
105-
self.process_cfg_attr(ast::Attribute {
140+
// Check feature gate and lint on zero attributes in source. Even if the feature is gated,
141+
// we still compute as if it wasn't, since the emitted error will stop compilation futher
142+
// along the compilation.
143+
match (expanded_attrs.len(), gate_cfg_attr_multi) {
144+
(0, false) => {
145+
// FIXME: Emit unused attribute lint here.
146+
},
147+
(1, _) => {},
148+
(_, true) => {
149+
emit_feature_err(
150+
self.sess,
151+
"cfg_attr_multi",
152+
cfg_attr_span,
153+
GateIssue::Language,
154+
"cfg_attr with zero or more than one attributes is experimental",
155+
);
156+
},
157+
(_, false) => {}
158+
}
159+
160+
if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
161+
// We call `process_cfg_attr` recursively in case there's a
162+
// `cfg_attr` inside of another `cfg_attr`. E.g.
163+
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
164+
expanded_attrs.into_iter()
165+
.flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute {
106166
id: attr::mk_attr_id(),
107167
style: attr.style,
108168
path,
109169
tokens,
110170
is_sugared_doc: false,
111171
span,
112-
})
172+
}))
173+
.collect()
113174
} else {
114-
None
175+
Vec::new()
115176
}
116177
}
117178

118-
// Determine if a node with the given attributes should be included in this configuration.
179+
/// Determine if a node with the given attributes should be included in this configuration.
119180
pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
120181
attrs.iter().all(|attr| {
121182
if !is_cfg(attr) {
@@ -165,7 +226,7 @@ impl<'a> StripUnconfigured<'a> {
165226
})
166227
}
167228

168-
// Visit attributes on expression and statements (but not attributes on items in blocks).
229+
/// Visit attributes on expression and statements (but not attributes on items in blocks).
169230
fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
170231
// flag the offending attributes
171232
for attr in attrs.iter() {

src/libsyntax/feature_gate.rs

+3
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ declare_features! (
499499

500500
// Allows `impl Trait` in bindings (`let`, `const`, `static`)
501501
(active, impl_trait_in_bindings, "1.30.0", Some(34511), None),
502+
503+
// #[cfg_attr(predicate, multiple, attributes, here)]
504+
(active, cfg_attr_multi, "1.31.0", Some(54881), None),
502505
);
503506

504507
declare_features! (

src/libsyntax/parse/parser.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ impl<'a> Parser<'a> {
678678
/// Expect next token to be edible or inedible token. If edible,
679679
/// then consume it; if inedible, then return without consuming
680680
/// anything. Signal a fatal error if next token is unexpected.
681-
fn expect_one_of(&mut self,
681+
pub fn expect_one_of(&mut self,
682682
edible: &[token::Token],
683683
inedible: &[token::Token]) -> PResult<'a, ()>{
684684
fn tokens_to_string(tokens: &[TokenType]) -> String {

src/test/ui/cfg-attr-trailing-comma.rs

-13
This file was deleted.

src/test/ui/cfg-attr-trailing-comma.stderr

-14
This file was deleted.

src/test/ui/cfg-attr-crate-2.stderr renamed to src/test/ui/conditional-compilation/cfg-attr-crate-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0658]: no_core is experimental (see issue #29639)
22
--> $DIR/cfg-attr-crate-2.rs:15:21
33
|
44
LL | #![cfg_attr(broken, no_core)] //~ ERROR no_core is experimental
5-
| ^^^^^^^^
5+
| ^^^^^^^
66
|
77
= help: add #![feature(no_core)] to the crate attributes to enable
88

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Test that cfg_attr doesn't emit any attributes when the
2+
// configuation variable is false. This mirrors `cfg-attr-multi-true.rs`
3+
4+
// compile-pass
5+
6+
#![warn(unused_must_use)]
7+
#![feature(cfg_attr_multi)]
8+
9+
#[cfg_attr(any(), deprecated, must_use)]
10+
struct Struct {}
11+
12+
impl Struct {
13+
fn new() -> Struct {
14+
Struct {}
15+
}
16+
}
17+
18+
fn main() {
19+
Struct::new();
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// compile-flags: --cfg broken
12+
13+
#![feature(cfg_attr_multi)]
14+
#![cfg_attr(broken, no_core, no_std)] //~ ERROR no_core is experimental
15+
16+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0658]: no_core is experimental (see issue #29639)
2+
--> $DIR/cfg-attr-multi-invalid-1.rs:14:21
3+
|
4+
LL | #![cfg_attr(broken, no_core, no_std)] //~ ERROR no_core is experimental
5+
| ^^^^^^^
6+
|
7+
= help: add #![feature(no_core)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// compile-flags: --cfg broken
12+
13+
#![feature(cfg_attr_multi)]
14+
#![cfg_attr(broken, no_std, no_core)] //~ ERROR no_core is experimental
15+
16+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0658]: no_core is experimental (see issue #29639)
2+
--> $DIR/cfg-attr-multi-invalid-2.rs:14:29
3+
|
4+
LL | #![cfg_attr(broken, no_std, no_core)] //~ ERROR no_core is experimental
5+
| ^^^^^^^
6+
|
7+
= help: add #![feature(no_core)] to the crate attributes to enable
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Test that cfg_attr with multiple attributes actually emits both attributes.
2+
// This is done by emitting two attributes that cause new warnings, and then
3+
// triggering those warnings.
4+
5+
// compile-pass
6+
7+
#![warn(unused_must_use)]
8+
#![feature(cfg_attr_multi)]
9+
10+
#[cfg_attr(all(), deprecated, must_use)]
11+
struct MustUseDeprecated {}
12+
13+
impl MustUseDeprecated { //~ warning: use of deprecated item
14+
fn new() -> MustUseDeprecated { //~ warning: use of deprecated item
15+
MustUseDeprecated {} //~ warning: use of deprecated item
16+
}
17+
}
18+
19+
fn main() {
20+
MustUseDeprecated::new(); //~ warning: use of deprecated item
21+
//| warning: unused `MustUseDeprecated` which must be used
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
warning: use of deprecated item 'MustUseDeprecated'
2+
--> $DIR/cfg-attr-multi-true.rs:13:6
3+
|
4+
LL | impl MustUseDeprecated { //~ warning: use of deprecated item
5+
| ^^^^^^^^^^^^^^^^^
6+
|
7+
= note: #[warn(deprecated)] on by default
8+
9+
warning: use of deprecated item 'MustUseDeprecated'
10+
--> $DIR/cfg-attr-multi-true.rs:20:5
11+
|
12+
LL | MustUseDeprecated::new(); //~ warning: use of deprecated item
13+
| ^^^^^^^^^^^^^^^^^^^^^^
14+
15+
warning: use of deprecated item 'MustUseDeprecated'
16+
--> $DIR/cfg-attr-multi-true.rs:14:17
17+
|
18+
LL | fn new() -> MustUseDeprecated { //~ warning: use of deprecated item
19+
| ^^^^^^^^^^^^^^^^^
20+
21+
warning: use of deprecated item 'MustUseDeprecated'
22+
--> $DIR/cfg-attr-multi-true.rs:15:9
23+
|
24+
LL | MustUseDeprecated {} //~ warning: use of deprecated item
25+
| ^^^^^^^^^^^^^^^^^
26+
27+
warning: unused `MustUseDeprecated` which must be used
28+
--> $DIR/cfg-attr-multi-true.rs:20:5
29+
|
30+
LL | MustUseDeprecated::new(); //~ warning: use of deprecated item
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
note: lint level defined here
34+
--> $DIR/cfg-attr-multi-true.rs:7:9
35+
|
36+
LL | #![warn(unused_must_use)]
37+
| ^^^^^^^^^^^^^^^
38+

0 commit comments

Comments
 (0)