Rollup merge of #117025 - Urgau:cleanup-improve-check-cfg-impl, r=petrochenkov
Cleanup and improve `--check-cfg` implementation This PR removes some indentation in the code, as well as preventing some bugs/misusages and fix a nit in the doc. r? ```@petrochenkov``` (maybe)
This commit is contained in:
commit
87a564d271
@ -140,7 +140,7 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
|
|||||||
let filename = FileName::cfg_spec_source_code(&s);
|
let filename = FileName::cfg_spec_source_code(&s);
|
||||||
|
|
||||||
macro_rules! error {
|
macro_rules! error {
|
||||||
($reason: expr) => {
|
($reason:expr) => {
|
||||||
handler.early_error(format!(
|
handler.early_error(format!(
|
||||||
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
|
||||||
s
|
s
|
||||||
@ -148,217 +148,202 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_error =
|
let expected_error = || -> ! {
|
||||||
|| error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`");
|
error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`")
|
||||||
|
};
|
||||||
|
|
||||||
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
|
let Ok(mut parser) = maybe_new_parser_from_source_str(&sess, filename, s.to_string())
|
||||||
Ok(mut parser) => match parser.parse_meta_item() {
|
else {
|
||||||
Ok(meta_item) if parser.token == token::Eof => {
|
expected_error();
|
||||||
if let Some(args) = meta_item.meta_item_list() {
|
};
|
||||||
if meta_item.has_name(sym::names) {
|
|
||||||
// defaults are flipped for the old syntax
|
|
||||||
if old_syntax == None {
|
|
||||||
check_cfg.exhaustive_names = false;
|
|
||||||
check_cfg.exhaustive_values = false;
|
|
||||||
}
|
|
||||||
old_syntax = Some(true);
|
|
||||||
|
|
||||||
check_cfg.exhaustive_names = true;
|
let meta_item = match parser.parse_meta_item() {
|
||||||
for arg in args {
|
Ok(meta_item) if parser.token == token::Eof => meta_item,
|
||||||
if arg.is_word() && arg.ident().is_some() {
|
Ok(..) => expected_error(),
|
||||||
let ident = arg.ident().expect("multi-segment cfg key");
|
Err(err) => {
|
||||||
check_cfg
|
err.cancel();
|
||||||
.expecteds
|
|
||||||
.entry(ident.name.to_string())
|
|
||||||
.or_insert(ExpectedValues::Any);
|
|
||||||
} else {
|
|
||||||
error!("`names()` arguments must be simple identifiers");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if meta_item.has_name(sym::values) {
|
|
||||||
// defaults are flipped for the old syntax
|
|
||||||
if old_syntax == None {
|
|
||||||
check_cfg.exhaustive_names = false;
|
|
||||||
check_cfg.exhaustive_values = false;
|
|
||||||
}
|
|
||||||
old_syntax = Some(true);
|
|
||||||
|
|
||||||
if let Some((name, values)) = args.split_first() {
|
|
||||||
if name.is_word() && name.ident().is_some() {
|
|
||||||
let ident = name.ident().expect("multi-segment cfg key");
|
|
||||||
let expected_values = check_cfg
|
|
||||||
.expecteds
|
|
||||||
.entry(ident.name.to_string())
|
|
||||||
.and_modify(|expected_values| match expected_values {
|
|
||||||
ExpectedValues::Some(_) => {}
|
|
||||||
ExpectedValues::Any => {
|
|
||||||
// handle the case where names(...) was done
|
|
||||||
// before values by changing to a list
|
|
||||||
*expected_values =
|
|
||||||
ExpectedValues::Some(FxHashSet::default());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
ExpectedValues::Some(FxHashSet::default())
|
|
||||||
});
|
|
||||||
|
|
||||||
let ExpectedValues::Some(expected_values) = expected_values
|
|
||||||
else {
|
|
||||||
bug!("`expected_values` should be a list a values")
|
|
||||||
};
|
|
||||||
|
|
||||||
for val in values {
|
|
||||||
if let Some(LitKind::Str(s, _)) =
|
|
||||||
val.lit().map(|lit| &lit.kind)
|
|
||||||
{
|
|
||||||
expected_values.insert(Some(s.to_string()));
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"`values()` arguments must be string literals"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if values.is_empty() {
|
|
||||||
expected_values.insert(None);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"`values()` first argument must be a simple identifier"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if args.is_empty() {
|
|
||||||
check_cfg.exhaustive_values = true;
|
|
||||||
} else {
|
|
||||||
expected_error();
|
|
||||||
}
|
|
||||||
} else if meta_item.has_name(sym::cfg) {
|
|
||||||
old_syntax = Some(false);
|
|
||||||
|
|
||||||
let mut names = Vec::new();
|
|
||||||
let mut values: FxHashSet<_> = Default::default();
|
|
||||||
|
|
||||||
let mut any_specified = false;
|
|
||||||
let mut values_specified = false;
|
|
||||||
let mut values_any_specified = false;
|
|
||||||
|
|
||||||
for arg in args {
|
|
||||||
if arg.is_word() && let Some(ident) = arg.ident() {
|
|
||||||
if values_specified {
|
|
||||||
error!("`cfg()` names cannot be after values");
|
|
||||||
}
|
|
||||||
names.push(ident);
|
|
||||||
} else if arg.has_name(sym::any)
|
|
||||||
&& let Some(args) = arg.meta_item_list()
|
|
||||||
{
|
|
||||||
if any_specified {
|
|
||||||
error!("`any()` cannot be specified multiple times");
|
|
||||||
}
|
|
||||||
any_specified = true;
|
|
||||||
if !args.is_empty() {
|
|
||||||
error!("`any()` must be empty");
|
|
||||||
}
|
|
||||||
} else if arg.has_name(sym::values)
|
|
||||||
&& let Some(args) = arg.meta_item_list()
|
|
||||||
{
|
|
||||||
if names.is_empty() {
|
|
||||||
error!(
|
|
||||||
"`values()` cannot be specified before the names"
|
|
||||||
);
|
|
||||||
} else if values_specified {
|
|
||||||
error!(
|
|
||||||
"`values()` cannot be specified multiple times"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
values_specified = true;
|
|
||||||
|
|
||||||
for arg in args {
|
|
||||||
if let Some(LitKind::Str(s, _)) =
|
|
||||||
arg.lit().map(|lit| &lit.kind)
|
|
||||||
{
|
|
||||||
values.insert(Some(s.to_string()));
|
|
||||||
} else if arg.has_name(sym::any)
|
|
||||||
&& let Some(args) = arg.meta_item_list()
|
|
||||||
{
|
|
||||||
if values_any_specified {
|
|
||||||
error!(
|
|
||||||
"`any()` in `values()` cannot be specified multiple times"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
values_any_specified = true;
|
|
||||||
if !args.is_empty() {
|
|
||||||
error!("`any()` must be empty");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"`values()` arguments must be string literals or `any()`"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if values.is_empty() && !values_any_specified && !any_specified {
|
|
||||||
values.insert(None);
|
|
||||||
} else if !values.is_empty() && values_any_specified {
|
|
||||||
error!(
|
|
||||||
"`values()` arguments cannot specify string literals and `any()` at the same time"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if any_specified {
|
|
||||||
if !names.is_empty()
|
|
||||||
|| !values.is_empty()
|
|
||||||
|| values_any_specified
|
|
||||||
{
|
|
||||||
error!("`cfg(any())` can only be provided in isolation");
|
|
||||||
}
|
|
||||||
|
|
||||||
check_cfg.exhaustive_names = false;
|
|
||||||
} else {
|
|
||||||
for name in names {
|
|
||||||
check_cfg
|
|
||||||
.expecteds
|
|
||||||
.entry(name.to_string())
|
|
||||||
.and_modify(|v| match v {
|
|
||||||
ExpectedValues::Some(v)
|
|
||||||
if !values_any_specified =>
|
|
||||||
{
|
|
||||||
v.extend(values.clone())
|
|
||||||
}
|
|
||||||
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
|
||||||
ExpectedValues::Any => {}
|
|
||||||
})
|
|
||||||
.or_insert_with(|| {
|
|
||||||
if values_any_specified {
|
|
||||||
ExpectedValues::Any
|
|
||||||
} else {
|
|
||||||
ExpectedValues::Some(values.clone())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expected_error();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expected_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(..) => expected_error(),
|
|
||||||
Err(err) => {
|
|
||||||
err.cancel();
|
|
||||||
expected_error();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(errs) => {
|
|
||||||
drop(errs);
|
|
||||||
expected_error();
|
expected_error();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(args) = meta_item.meta_item_list() else {
|
||||||
|
expected_error();
|
||||||
|
};
|
||||||
|
|
||||||
|
if meta_item.has_name(sym::names) {
|
||||||
|
// defaults are flipped for the old syntax
|
||||||
|
if old_syntax == None {
|
||||||
|
check_cfg.exhaustive_names = false;
|
||||||
|
check_cfg.exhaustive_values = false;
|
||||||
|
}
|
||||||
|
old_syntax = Some(true);
|
||||||
|
|
||||||
|
check_cfg.exhaustive_names = true;
|
||||||
|
for arg in args {
|
||||||
|
if arg.is_word() && arg.ident().is_some() {
|
||||||
|
let ident = arg.ident().expect("multi-segment cfg key");
|
||||||
|
check_cfg
|
||||||
|
.expecteds
|
||||||
|
.entry(ident.name.to_string())
|
||||||
|
.or_insert(ExpectedValues::Any);
|
||||||
|
} else {
|
||||||
|
error!("`names()` arguments must be simple identifiers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if meta_item.has_name(sym::values) {
|
||||||
|
// defaults are flipped for the old syntax
|
||||||
|
if old_syntax == None {
|
||||||
|
check_cfg.exhaustive_names = false;
|
||||||
|
check_cfg.exhaustive_values = false;
|
||||||
|
}
|
||||||
|
old_syntax = Some(true);
|
||||||
|
|
||||||
|
if let Some((name, values)) = args.split_first() {
|
||||||
|
if name.is_word() && name.ident().is_some() {
|
||||||
|
let ident = name.ident().expect("multi-segment cfg key");
|
||||||
|
let expected_values = check_cfg
|
||||||
|
.expecteds
|
||||||
|
.entry(ident.name.to_string())
|
||||||
|
.and_modify(|expected_values| match expected_values {
|
||||||
|
ExpectedValues::Some(_) => {}
|
||||||
|
ExpectedValues::Any => {
|
||||||
|
// handle the case where names(...) was done
|
||||||
|
// before values by changing to a list
|
||||||
|
*expected_values = ExpectedValues::Some(FxHashSet::default());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| ExpectedValues::Some(FxHashSet::default()));
|
||||||
|
|
||||||
|
let ExpectedValues::Some(expected_values) = expected_values else {
|
||||||
|
bug!("`expected_values` should be a list a values")
|
||||||
|
};
|
||||||
|
|
||||||
|
for val in values {
|
||||||
|
if let Some(LitKind::Str(s, _)) = val.lit().map(|lit| &lit.kind) {
|
||||||
|
expected_values.insert(Some(s.to_string()));
|
||||||
|
} else {
|
||||||
|
error!("`values()` arguments must be string literals");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if values.is_empty() {
|
||||||
|
expected_values.insert(None);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("`values()` first argument must be a simple identifier");
|
||||||
|
}
|
||||||
|
} else if args.is_empty() {
|
||||||
|
check_cfg.exhaustive_values = true;
|
||||||
|
} else {
|
||||||
|
expected_error();
|
||||||
|
}
|
||||||
|
} else if meta_item.has_name(sym::cfg) {
|
||||||
|
old_syntax = Some(false);
|
||||||
|
|
||||||
|
let mut names = Vec::new();
|
||||||
|
let mut values: FxHashSet<_> = Default::default();
|
||||||
|
|
||||||
|
let mut any_specified = false;
|
||||||
|
let mut values_specified = false;
|
||||||
|
let mut values_any_specified = false;
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
if arg.is_word() && let Some(ident) = arg.ident() {
|
||||||
|
if values_specified {
|
||||||
|
error!("`cfg()` names cannot be after values");
|
||||||
|
}
|
||||||
|
names.push(ident);
|
||||||
|
} else if arg.has_name(sym::any)
|
||||||
|
&& let Some(args) = arg.meta_item_list()
|
||||||
|
{
|
||||||
|
if any_specified {
|
||||||
|
error!("`any()` cannot be specified multiple times");
|
||||||
|
}
|
||||||
|
any_specified = true;
|
||||||
|
if !args.is_empty() {
|
||||||
|
error!("`any()` must be empty");
|
||||||
|
}
|
||||||
|
} else if arg.has_name(sym::values)
|
||||||
|
&& let Some(args) = arg.meta_item_list()
|
||||||
|
{
|
||||||
|
if names.is_empty() {
|
||||||
|
error!("`values()` cannot be specified before the names");
|
||||||
|
} else if values_specified {
|
||||||
|
error!("`values()` cannot be specified multiple times");
|
||||||
|
}
|
||||||
|
values_specified = true;
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
if let Some(LitKind::Str(s, _)) =
|
||||||
|
arg.lit().map(|lit| &lit.kind)
|
||||||
|
{
|
||||||
|
values.insert(Some(s.to_string()));
|
||||||
|
} else if arg.has_name(sym::any)
|
||||||
|
&& let Some(args) = arg.meta_item_list()
|
||||||
|
{
|
||||||
|
if values_any_specified {
|
||||||
|
error!(
|
||||||
|
"`any()` in `values()` cannot be specified multiple times"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
values_any_specified = true;
|
||||||
|
if !args.is_empty() {
|
||||||
|
error!("`any()` must be empty");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"`values()` arguments must be string literals or `any()`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if values.is_empty() && !values_any_specified && !any_specified {
|
||||||
|
values.insert(None);
|
||||||
|
} else if !values.is_empty() && values_any_specified {
|
||||||
|
error!(
|
||||||
|
"`values()` arguments cannot specify string literals and `any()` at the same time"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if any_specified {
|
||||||
|
if names.is_empty()
|
||||||
|
&& values.is_empty()
|
||||||
|
&& !values_specified
|
||||||
|
&& !values_any_specified
|
||||||
|
{
|
||||||
|
check_cfg.exhaustive_names = false;
|
||||||
|
} else {
|
||||||
|
error!("`cfg(any())` can only be provided in isolation");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for name in names {
|
||||||
|
check_cfg
|
||||||
|
.expecteds
|
||||||
|
.entry(name.to_string())
|
||||||
|
.and_modify(|v| match v {
|
||||||
|
ExpectedValues::Some(v) if !values_any_specified => {
|
||||||
|
v.extend(values.clone())
|
||||||
|
}
|
||||||
|
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
||||||
|
ExpectedValues::Any => {}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
if values_any_specified {
|
||||||
|
ExpectedValues::Any
|
||||||
|
} else {
|
||||||
|
ExpectedValues::Some(values.clone())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expected_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ fn do_mumble_frotz() {}
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# This turns on checking for feature values, but not for condition names.
|
# This turns on checking for feature values, but not for condition names.
|
||||||
rustc --check-cfg 'configure(feature, values("zapping", "lasers"))' \
|
rustc --check-cfg 'cfg(feature, values("zapping", "lasers"))' \
|
||||||
--check-cfg 'cfg(any())' \
|
--check-cfg 'cfg(any())' \
|
||||||
--cfg 'feature="zapping"' -Z unstable-options
|
--cfg 'feature="zapping"' -Z unstable-options
|
||||||
```
|
```
|
||||||
|
2
tests/ui/check-cfg/invalid-arguments.any_values.stderr
Normal file
2
tests/ui/check-cfg/invalid-arguments.any_values.stderr
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
error: invalid `--check-cfg` argument: `cfg(any(),values())` (`values()` cannot be specified before the names)
|
||||||
|
|
@ -6,7 +6,7 @@
|
|||||||
// revisions: multiple_values_any not_empty_any not_empty_values_any
|
// revisions: multiple_values_any not_empty_any not_empty_values_any
|
||||||
// revisions: values_any_missing_values values_any_before_ident ident_in_values_1
|
// revisions: values_any_missing_values values_any_before_ident ident_in_values_1
|
||||||
// revisions: ident_in_values_2 unknown_meta_item_1 unknown_meta_item_2 unknown_meta_item_3
|
// revisions: ident_in_values_2 unknown_meta_item_1 unknown_meta_item_2 unknown_meta_item_3
|
||||||
// revisions: mixed_values_any mixed_any giberich
|
// revisions: mixed_values_any mixed_any any_values giberich unterminated
|
||||||
//
|
//
|
||||||
// compile-flags: -Z unstable-options
|
// compile-flags: -Z unstable-options
|
||||||
// [anything_else]compile-flags: --check-cfg=anything_else(...)
|
// [anything_else]compile-flags: --check-cfg=anything_else(...)
|
||||||
@ -29,6 +29,8 @@
|
|||||||
// [unknown_meta_item_3]compile-flags: --check-cfg=cfg(foo,values(test()))
|
// [unknown_meta_item_3]compile-flags: --check-cfg=cfg(foo,values(test()))
|
||||||
// [mixed_values_any]compile-flags: --check-cfg=cfg(foo,values("bar",any()))
|
// [mixed_values_any]compile-flags: --check-cfg=cfg(foo,values("bar",any()))
|
||||||
// [mixed_any]compile-flags: --check-cfg=cfg(any(),values(any()))
|
// [mixed_any]compile-flags: --check-cfg=cfg(any(),values(any()))
|
||||||
|
// [any_values]compile-flags: --check-cfg=cfg(any(),values())
|
||||||
// [giberich]compile-flags: --check-cfg=cfg(...)
|
// [giberich]compile-flags: --check-cfg=cfg(...)
|
||||||
|
// [unterminated]compile-flags: --check-cfg=cfg(
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
2
tests/ui/check-cfg/invalid-arguments.unterminated.stderr
Normal file
2
tests/ui/check-cfg/invalid-arguments.unterminated.stderr
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
error: invalid `--check-cfg` argument: `cfg(` (expected `cfg(name, values("value1", "value2", ... "valueN"))`)
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user