Skip to content

Commit a13ceca

Browse files
committed
Require compile-fail tests for new lang features
Its non trivial to test lang feature gates, and people forget to add such tests. So we extend the features lint of the tidy tool to ensure that all new lang features contain a new compile-fail test. Of course, one could drop this requirement and just grep all tests in run-pass for #![feature(abc)] and then run this test again, removing the mention, requiring that it fails. But this only tests for the existence of a compilation failure. Manual tests ensure that also the correct lines spawn the error, and also test the actual error message. For library features, it makes no sense to require such a test, as here code is used that is generic for all library features.
1 parent e8b6d3c commit a13ceca

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

src/tools/tidy/src/features.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//! * The set of library features is disjoint from the set of language features
1717
//! * Library features have at most one stability level
1818
//! * Library features have at most one `since` value
19+
//! * All stability attributes have tests to ensure they are actually stable/unstable
1920
2021
use std::collections::HashMap;
2122
use std::fmt;
@@ -42,10 +43,11 @@ impl fmt::Display for Status {
4243
struct Feature {
4344
level: Status,
4445
since: String,
46+
has_gate_test: bool,
4547
}
4648

4749
pub fn check(path: &Path, bad: &mut bool) {
48-
let features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
50+
let mut features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
4951
assert!(!features.is_empty());
5052
let mut lib_features = HashMap::<String, Feature>::new();
5153

@@ -106,10 +108,77 @@ pub fn check(path: &Path, bad: &mut bool) {
106108
Feature {
107109
level: level,
108110
since: since.to_owned(),
111+
has_gate_test: false,
109112
});
110113
}
111114
});
112115

116+
super::walk(&path.join("test/compile-fail"),
117+
&mut |path| super::filter_dirs(path),
118+
&mut |file| {
119+
let filename = file.file_name().unwrap().to_string_lossy();
120+
if !filename.ends_with(".rs") || filename == "features.rs" ||
121+
filename == "diagnostic_list.rs" {
122+
return;
123+
}
124+
125+
contents.truncate(0);
126+
t!(t!(File::open(&file), &file).read_to_string(&mut contents));
127+
128+
for (i, line) in contents.lines().enumerate() {
129+
let mut err = |msg: &str| {
130+
println!("{}:{}: {}", file.display(), i + 1, msg);
131+
*bad = true;
132+
};
133+
134+
let gate_test_str = "gate-test-";
135+
136+
if !line.contains(gate_test_str) {
137+
continue;
138+
}
139+
140+
let feature_name = match line.find(gate_test_str) {
141+
Some(i) => {
142+
&line[i+gate_test_str.len()..line[i+1..].find(' ').unwrap_or(line.len())]
143+
},
144+
None => continue,
145+
};
146+
let found_feature = features.get_mut(feature_name)
147+
.map(|v| { v.has_gate_test = true; () })
148+
.is_some();
149+
150+
let found_lib_feature = features.get_mut(feature_name)
151+
.map(|v| { v.has_gate_test = true; () })
152+
.is_some();
153+
154+
if !(found_feature || found_lib_feature) {
155+
err(&format!("gate-test test found referencing a nonexistent feature '{}'",
156+
feature_name));
157+
}
158+
}
159+
});
160+
161+
// Only check the number of lang features.
162+
// Obligatory testing for library features is dumb.
163+
let gate_untested = features.iter()
164+
.filter(|&(_, f)| f.level == Status::Unstable)
165+
.filter(|&(_, f)| !f.has_gate_test)
166+
.count();
167+
168+
// FIXME get this number down to zero.
169+
let gate_untested_expected = 98;
170+
171+
if gate_untested != gate_untested_expected {
172+
print!("Expected {} gate untested features, but found {}. ",
173+
gate_untested_expected, gate_untested);
174+
if gate_untested < gate_untested_expected {
175+
println!("Did you forget to reduce the expected number?");
176+
} else {
177+
println!("Did you forget to add a gate test for your new feature?");
178+
}
179+
*bad = true;
180+
}
181+
113182
if *bad {
114183
return;
115184
}
@@ -162,6 +231,7 @@ fn collect_lang_features(path: &Path) -> HashMap<String, Feature> {
162231
Feature {
163232
level: level,
164233
since: since.to_owned(),
234+
has_gate_test: false,
165235
}))
166236
})
167237
.collect()

0 commit comments

Comments
 (0)