Skip to content

Commit 0125668

Browse files
authored
Merge pull request #2539 from Havvy/cfg_attr_multi
#[cfg_attr] expanding to multiple attributes
2 parents 43dcccf + 5534887 commit 0125668

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

text/2539-cfg_attr-multiple-attrs.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
- Feature Name: `cfg_attr_multi`
2+
- Start Date: 2018-09-10
3+
- RFC PR: [rust-lang/rfcs#2539](https://github.com/rust-lang/rfcs/pull/2539)
4+
- Rust Issue: [rust-lang/rust#54881](https://github.com/rust-lang/rust/issues/54881)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Change `cfg_attr` to allow multiple attributes after the configuration
10+
predicate, instead of just one. When the configuration predicate is true,
11+
replace the attribute with all following attributes.
12+
13+
# Motivation
14+
[motivation]: #motivation
15+
16+
Simply put, ergonomics and intent. When you have multiple attributes you
17+
configure away behind the same predicate today, you need to duplicate the entire
18+
predicate. And then when you read code that does this, you have to check the
19+
entire predicates with each other to make sure they're the same. By allowing
20+
multiple attributes it removes that duplication and shows explicitly that the
21+
author wanted those attributes configured behind the same predicate.
22+
23+
# Guide-level explanation
24+
[guide-level-explanation]: #guide-level-explanation
25+
26+
The `cfg_attr` attribute takes a configuration predicate and then a list of
27+
attributes that will be in effect when the predicate is true.
28+
29+
For an example of multiple attributes, say we want to have two attribute macros
30+
(`sparkles` and `crackles`), but only when `feature = "magic"` is enabled. We
31+
can write this as:
32+
33+
```rust,igore
34+
#[cfg_attr(feature = "magic", sparkles, crackles)]
35+
fn bewitched() {}
36+
```
37+
38+
When the feature flag is enabled, it expands to:
39+
40+
```rust,ignore
41+
#[sparkles]
42+
#[crackles]
43+
fn bewitche() {}
44+
```
45+
46+
The list of attributes may be empty, but will warn if the actual source code
47+
contains an empty list.
48+
49+
# Reference-level explanation
50+
[reference-level-explanation]: #reference-level-explanation
51+
52+
The next section replaces what's in the Conditional Compilation Chapter for the
53+
`cfg_attr` attribute. It explains both current and new behavior, mainly because
54+
the current reference material needs improvement.
55+
56+
## `cfg_attr` Attribute
57+
58+
The `cfg_attr` attribute conditionally includes attributes based on a
59+
configuration predicate.
60+
61+
It is written as `cfg_attr` followed by `(`, a comma separated metaitem
62+
sequence, and then `)` The metaitem sequence contains one or more metaitems.
63+
The first is a conditional predicate. The rest are metaitems that are also
64+
attributes. Trailing commas after attributes are permitted. The following list
65+
are all allowed:
66+
67+
* `cfg_attr(predicate, attr)`
68+
* `cfg_attr(predicate, attr_1, attr_2)`
69+
* `cfg_attr(predicate, attr,)`
70+
* `cfg_attr(predicate, attr_1, attr_2,)`
71+
* `cfg_attr(predicate,)`
72+
73+
> Note: `cfg_attr(predicate)` is not allowed. That comma is semantically
74+
> distinct from the commas following attributes, so we require it.
75+
76+
When the configuration predicate is true, this attribute expands out to be an
77+
attribute for each attribute metaitem. For example, the following module will
78+
either be found at `linux.rs` or `windows.rs` based on the target.
79+
80+
```rust,ignore
81+
#[cfg_attr(linux, path = "linux.rs")]
82+
#[cfg_attr(windows, path = "windows.rs")]
83+
mod os;
84+
```
85+
86+
For an example of multiple attributes, say we want to have two attribute macros,
87+
but only when `feature = "magic"` is enabled. We can write this as:
88+
89+
```rust,igore
90+
#[cfg_attr(feature = "magic", sparkles, crackles)]
91+
fn bewitched() {}
92+
```
93+
94+
When the feature flag is enabled, the attribute expands to:
95+
96+
```rust,ignore
97+
#[sparkles]
98+
#[crackles]
99+
fn bewitche() {}
100+
```
101+
102+
Note: The `cfg_attr` can expand to another `cfg_attr`. For example,
103+
`#[cfg_attr(linux, cfg_attr(feature = "multithreaded", some_other_attribute))`
104+
is valid. This example would be equivalent to
105+
`#[cfg_attr(and(linux, feaure ="multithreaded"), some_other_attribute)]`.
106+
107+
## Warning When Zero Attributes
108+
109+
This RFC allows `#[cfg_attr(predicate)]`. This is so that macros can generate
110+
it. Having it in the source text emits an `unused_attributes` warning.
111+
112+
## Attribute Syntax Opportunity Cost
113+
114+
This would be the first place attributes would be allowed in a comma-separated
115+
list. As such, it adds a restriction that attributes cannot have a non-delimited
116+
comma.
117+
118+
Today, an attribute can look like:
119+
120+
* `name`,
121+
* ``name(`TokenStream`)``
122+
* ``name = `TokenTree` ``
123+
124+
where `TokenStream` is a sequence of tokens that only has the restriction that
125+
delimiters match and `TokenTree` is a single identifer, literal, punctuation
126+
mark, or a delimited `TokenStream`.
127+
128+
With this RFC accepted, the following cannot ever be parsed as attributes:
129+
130+
* `name, option`
131+
* `name = some, options`
132+
133+
Arguably, we could allow `(name, option)`, but we shouldn't.
134+
135+
This restriction is also useful if we want to put multiple attributes in a
136+
single `#[]` container, which has been suggested, but this RFC will not tackle.
137+
138+
# Drawbacks
139+
[drawbacks]: #drawbacks
140+
141+
It's another thing that has to be learned. Though even there, it's just learning
142+
that the attribute takes 1+, and not just 1 attribute.
143+
144+
It restricts the future allowable syntaxes for attributes.
145+
146+
# Rationale and alternatives
147+
[rationale-and-alternatives]: #rationale-and-alternatives
148+
149+
We could require that multiple attributes must be within in a delimiter to make
150+
it so that it's always two arguments at the top level. E.g.,
151+
`#[cfg_attr(predicate, [attr, attr])]`. While this could increase clarity, it
152+
mostly seems like it would just add noise. In the multiline case, it already
153+
reads pretty clear with the predicate on the first line and each attribute
154+
indented.
155+
156+
The default alternative of not doing this is a possibility. It would just mean
157+
that conditionally including attributes is slightly less ergonomic than it
158+
could be.
159+
160+
We could change attribute container syntax to allow multiple attributes and then
161+
state that `cfg_attr` takes the attribute container syntax without the `#[]`
162+
part. While this could be a good final state, it's a more ambitious change that
163+
has more drawbacks. There are legitimate reasons we'd want `cfg_attr` to take
164+
multiple attributes but not the attribute container. As such, I would like to
165+
go with the conservative change first.
166+
167+
The original draft of this RFC only allowed one or more attributes and did not
168+
allow the trailing comma. Because it helps macros and fits the rest of the
169+
language, it now allows those.
170+
171+
# Prior art
172+
[prior-art]: #prior-art
173+
174+
I cannot think of any prior art specifically, but changing something from taking
175+
one of something to one or more of something is pretty common.
176+
177+
# Unresolved questions
178+
[unresolved-questions]: #unresolved-questions
179+
180+
None.

0 commit comments

Comments
 (0)