1
Fork 0

Rollup merge of #102971 - est31:tidy_duplicate_lang_features, r=jyn514

tidy: error if a lang feature is already present

If a lang feature gets declared twice, like for example as a result of a mistake during stabilization, emit an error in tidy. Library features already have this logic.

Inspired by a mistake done during `half_open_range_patterns` stabilization: https://github.com/rust-lang/rust/pull/102275/files#r991292215

The PR requires #102883 to be merged before CI turns green because the check is doing its job.

For reviewers, I suggest [turning off whitespace changes](https://github.com/rust-lang/rust/pull/102971/files?w=1) in the diff by adding `?w=1` to the url, as a large part of the diff is just about removing one level of indentation.
This commit is contained in:
Yuki Okushi 2022-10-13 09:41:27 +09:00 committed by GitHub
commit 18b1342f23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -10,7 +10,7 @@
//! * Language features in a group are sorted by feature name. //! * Language features in a group are sorted by feature name.
use crate::walk::{filter_dirs, walk, walk_many}; use crate::walk::{filter_dirs, walk, walk_many};
use std::collections::HashMap; use std::collections::hash_map::{Entry, HashMap};
use std::fmt; use std::fmt;
use std::fs; use std::fs;
use std::num::NonZeroU32; use std::num::NonZeroU32;
@ -280,13 +280,14 @@ fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
} }
pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features { pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features {
let mut all = collect_lang_features_in(base_compiler_path, "active.rs", bad); let mut features = Features::new();
all.extend(collect_lang_features_in(base_compiler_path, "accepted.rs", bad)); collect_lang_features_in(&mut features, base_compiler_path, "active.rs", bad);
all.extend(collect_lang_features_in(base_compiler_path, "removed.rs", bad)); collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad);
all collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad);
features
} }
fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features { fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, bad: &mut bool) {
let path = base.join("rustc_feature").join("src").join(file); let path = base.join("rustc_feature").join("src").join(file);
let contents = t!(fs::read_to_string(&path)); let contents = t!(fs::read_to_string(&path));
@ -298,21 +299,19 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features
let mut in_feature_group = false; let mut in_feature_group = false;
let mut prev_names = vec![]; let mut prev_names = vec![];
contents let lines = contents.lines().zip(1..);
.lines() for (line, line_number) in lines {
.zip(1..)
.filter_map(|(line, line_number)| {
let line = line.trim(); let line = line.trim();
// Within -start and -end, the tracking issue can be omitted. // Within -start and -end, the tracking issue can be omitted.
match line { match line {
"// no-tracking-issue-start" => { "// no-tracking-issue-start" => {
next_feature_omits_tracking_issue = true; next_feature_omits_tracking_issue = true;
return None; continue;
} }
"// no-tracking-issue-end" => { "// no-tracking-issue-end" => {
next_feature_omits_tracking_issue = false; next_feature_omits_tracking_issue = false;
return None; continue;
} }
_ => {} _ => {}
} }
@ -330,11 +329,11 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features
in_feature_group = true; in_feature_group = true;
prev_names = vec![]; prev_names = vec![];
return None; continue;
} else if line.starts_with(FEATURE_GROUP_END_PREFIX) { } else if line.starts_with(FEATURE_GROUP_END_PREFIX) {
in_feature_group = false; in_feature_group = false;
prev_names = vec![]; prev_names = vec![];
return None; continue;
} }
let mut parts = line.split(','); let mut parts = line.split(',');
@ -343,7 +342,7 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features
Some("incomplete") => Status::Unstable, Some("incomplete") => Status::Unstable,
Some("removed") => Status::Removed, Some("removed") => Status::Removed,
Some("accepted") => Status::Stable, Some("accepted") => Status::Stable,
_ => return None, _ => continue,
}; };
let name = parts.next().unwrap().trim(); let name = parts.next().unwrap().trim();
@ -377,7 +376,7 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features
name, name,
); );
// skip any additional checks for this line // skip any additional checks for this line
return None; continue;
} }
Err(index) => index, Err(index) => index,
}; };
@ -424,9 +423,21 @@ fn collect_lang_features_in(base: &Path, file: &str, bad: &mut bool) -> Features
let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap(); let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap();
Some(s.parse().unwrap()) Some(s.parse().unwrap())
}; };
Some((name.to_owned(), Feature { level, since, has_gate_test: false, tracking_issue })) match features.entry(name.to_owned()) {
}) Entry::Occupied(e) => {
.collect() tidy_error!(
bad,
"{}:{} feature {name} already specified with status '{}'",
path.display(),
line_number,
e.get().level,
);
}
Entry::Vacant(e) => {
e.insert(Feature { level, since, has_gate_test: false, tracking_issue });
}
}
}
} }
fn get_and_check_lib_features( fn get_and_check_lib_features(