Skip to content

Commit 4bfc7f2

Browse files
authored
Unrolled build for rust-lang#111072
Rollup merge of rust-lang#111072 - Urgau:check-cfg-new-syntax, r=petrochenkov Add new simpler and more explicit syntax for check-cfg <details> <summary> Old proposition (before the MCP) </summary> This PR adds a new simpler and more explicit syntax for check-cfg. It consist of two new form: - `exhaustive(names, values)` - `configure(name, "value1", "value2", ... "valueN")` The preview forms `names(...)` and `values(...)` have implicit meaning that are not strait-forward. In particular `values(foo)`&`values(bar)` and `names(foo, bar)` are not equivalent which has created [some confusions](rust-lang#98080). Also the `names()` and `values()` form are not clear either and again created some confusions where peoples believed that `values()`&`values(foo)` could be reduced to just `values(foo)`. To fix that the two new forms are made to be explicit and simpler. See the table of correspondence: - `names()` -> `exhaustive(names)` - `values()` -> `exhaustive(values)` - `names(foo)` -> `exhaustive(names)`&`configure(foo)` - `values(foo)` -> `configure(foo)` - `values(feat, "foo", "bar")` -> `configure(feat, "foo", "bar")` - `values(foo)`&`values(bar)` -> `configure(foo, bar)` - `names()`&`values()`&`values(my_cfg)` -> `exhaustive(names, values)`&`configure(my_cfg)` Another benefits of the new syntax is that it allow for further options (like conditional checking for --cfg, currently always on) without syntax change. The two previous forms are deprecated and will be removed once cargo and beta rustc have the necessary support. </details> This PR is the first part of the implementation of [MCP636 - Simplify and improve explicitness of the check-cfg syntax](rust-lang/compiler-team#636). ## New `cfg` form It introduces the new [`cfg` form](rust-lang/compiler-team#636) and deprecate the other two: ``` rustc --check-cfg 'cfg(name1, ..., nameN, values("value1", "value2", ... "valueN"))' ``` ## Default built-in names and values It also changes the default for the built-in names and values checking. - Built-in values checking would always be activated as long as a `--check-cfg` argument is present - Built-in names checking would always be activated as long as a `--check-cfg` argument is present **unless** if any `cfg(any())` arg is passed ~~**Note: depends on rust-lang#111068 but is reviewable (last two commits)!**~~ Resolve rust-lang/compiler-team#636 r? `@petrochenkov`
2 parents bb74d1f + fc78d78 commit 4bfc7f2

File tree

72 files changed

+1017
-216
lines changed

Some content is hidden

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

72 files changed

+1017
-216
lines changed

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

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

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

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

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

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)