Skip to content

Commit e5e95eb

Browse files
committed
MCP636: Add simpler and more explicit syntax to check-cfg
This add a new form and deprecated the other ones: - cfg(name1, ..., nameN, values("value1", "value2", ... "valueN")) - cfg(name1, ..., nameN) or cfg(name1, ..., nameN, values()) - cfg(any()) It also changes the default exhaustiveness to be enable-by-default in the presence of any --check-cfg arguments.
1 parent a4a10bd commit e5e95eb

File tree

4 files changed

+268
-92
lines changed

4 files changed

+268
-92
lines changed

Diff for: compiler/rustc_interface/src/interface.rs

+132-7
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,13 @@ pub fn parse_cfgspecs(
124124
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
125125
pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg {
126126
rustc_span::create_default_session_if_not_set_then(move |_| {
127-
let mut check_cfg = CheckCfg::default();
127+
// If any --check-cfg is passed then exhaustive_values and exhaustive_names
128+
// are enabled by default.
129+
let exhaustive_names = !specs.is_empty();
130+
let exhaustive_values = !specs.is_empty();
131+
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
128132

133+
let mut old_syntax = None;
129134
for s in specs {
130135
let sess = ParseSess::with_silent_emitter(Some(format!(
131136
"this error occurred on the command line: `--check-cfg={s}`"
@@ -141,18 +146,21 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
141146
};
142147
}
143148

144-
let expected_error = || {
145-
error!(
146-
"expected `names(name1, name2, ... nameN)` or \
147-
`values(name, \"value1\", \"value2\", ... \"valueN\")`"
148-
)
149-
};
149+
let expected_error =
150+
|| error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`");
150151

151152
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
152153
Ok(mut parser) => match parser.parse_meta_item() {
153154
Ok(meta_item) if parser.token == token::Eof => {
154155
if let Some(args) = meta_item.meta_item_list() {
155156
if meta_item.has_name(sym::names) {
157+
// defaults are flipped for the old syntax
158+
if old_syntax == None {
159+
check_cfg.exhaustive_names = false;
160+
check_cfg.exhaustive_values = false;
161+
}
162+
old_syntax = Some(true);
163+
156164
check_cfg.exhaustive_names = true;
157165
for arg in args {
158166
if arg.is_word() && arg.ident().is_some() {
@@ -166,6 +174,13 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
166174
}
167175
}
168176
} else if meta_item.has_name(sym::values) {
177+
// defaults are flipped for the old syntax
178+
if old_syntax == None {
179+
check_cfg.exhaustive_names = false;
180+
check_cfg.exhaustive_values = false;
181+
}
182+
old_syntax = Some(true);
183+
169184
if let Some((name, values)) = args.split_first() {
170185
if name.is_word() && name.ident().is_some() {
171186
let ident = name.ident().expect("multi-segment cfg key");
@@ -215,6 +230,116 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
215230
} else {
216231
expected_error();
217232
}
233+
} else if meta_item.has_name(sym::cfg) {
234+
old_syntax = Some(false);
235+
236+
let mut names = Vec::new();
237+
let mut values: FxHashSet<_> = Default::default();
238+
239+
let mut any_specified = false;
240+
let mut values_specified = false;
241+
let mut values_any_specified = false;
242+
243+
for arg in args {
244+
if arg.is_word() && let Some(ident) = arg.ident() {
245+
if values_specified {
246+
error!("`cfg()` names cannot be after values");
247+
}
248+
names.push(ident);
249+
} else if arg.has_name(sym::any)
250+
&& let Some(args) = arg.meta_item_list()
251+
{
252+
if any_specified {
253+
error!("`any()` cannot be specified multiple times");
254+
}
255+
any_specified = true;
256+
if !args.is_empty() {
257+
error!("`any()` must be empty");
258+
}
259+
} else if arg.has_name(sym::values)
260+
&& let Some(args) = arg.meta_item_list()
261+
{
262+
if names.is_empty() {
263+
error!(
264+
"`values()` cannot be specified before the names"
265+
);
266+
} else if values_specified {
267+
error!(
268+
"`values()` cannot be specified multiple times"
269+
);
270+
}
271+
values_specified = true;
272+
273+
for arg in args {
274+
if let Some(LitKind::Str(s, _)) =
275+
arg.lit().map(|lit| &lit.kind)
276+
{
277+
values.insert(Some(s.to_string()));
278+
} else if arg.has_name(sym::any)
279+
&& let Some(args) = arg.meta_item_list()
280+
{
281+
if values_any_specified {
282+
error!(
283+
"`any()` in `values()` cannot be specified multiple times"
284+
);
285+
}
286+
values_any_specified = true;
287+
if !args.is_empty() {
288+
error!("`any()` must be empty");
289+
}
290+
} else {
291+
error!(
292+
"`values()` arguments must be string literals or `any()`"
293+
);
294+
}
295+
}
296+
} else {
297+
error!(
298+
"`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
299+
);
300+
}
301+
}
302+
303+
if values.is_empty() && !values_any_specified && !any_specified {
304+
values.insert(None);
305+
} else if !values.is_empty() && values_any_specified {
306+
error!(
307+
"`values()` arguments cannot specify string literals and `any()` at the same time"
308+
);
309+
}
310+
311+
if any_specified {
312+
if !names.is_empty()
313+
|| !values.is_empty()
314+
|| values_any_specified
315+
{
316+
error!("`cfg(any())` can only be provided in isolation");
317+
}
318+
319+
check_cfg.exhaustive_names = false;
320+
} else {
321+
for name in names {
322+
check_cfg
323+
.expecteds
324+
.entry(name.to_string())
325+
.and_modify(|v| match v {
326+
ExpectedValues::Some(v)
327+
if !values_any_specified =>
328+
{
329+
v.extend(values.clone())
330+
}
331+
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
332+
ExpectedValues::Any => {}
333+
})
334+
.or_insert_with(|| {
335+
if values_any_specified {
336+
ExpectedValues::Any
337+
} else {
338+
ExpectedValues::Some(values.clone())
339+
}
340+
});
341+
}
342+
}
218343
} else {
219344
expected_error();
220345
}

Diff for: compiler/rustc_interface/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![feature(internal_output_capture)]
44
#![feature(thread_spawn_unchecked)]
55
#![feature(lazy_cell)]
6+
#![feature(let_chains)]
67
#![feature(try_blocks)]
78
#![recursion_limit = "256"]
89
#![allow(rustc::potential_query_instability)]

Diff for: src/doc/rustdoc/src/unstable-features.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -628,10 +628,10 @@ Using this flag looks like this:
628628

629629
```bash
630630
$ rustdoc src/lib.rs -Z unstable-options \
631-
--check-cfg='names()' --check-cfg='values(feature, "foo", "bar")'
631+
--check-cfg='cfg(feature, values("foo", "bar"))'
632632
```
633633

634-
The example above check every well known names (`target_os`, `doc`, `test`, ... via `names()`)
634+
The example above check every well known names and values (`target_os`, `doc`, `test`, ...)
635635
and check the values of `feature`: `foo` and `bar`.
636636

637637
### `--generate-link-to-definition`: Generate links on types in source code

0 commit comments

Comments
 (0)