1
Fork 0

Auto merge of #38914 - est31:tidy-gate-tests, r=nikomatsakis

Make tidy check for lang gate tests

Add gate tests to the checks that tidy performs. Excerpt from the commit message of the main commit:

    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.

The tidy lint extension now checks the compile-fail test suite for occurences of "gate-test-X" where X is a feature. Alternatively, it also accepts file names with the form "feature-gate-X.rs". If a lang feature is found that has no such check, we emit a tidy error.

I've applied the markings to all tests I could find in the test suite. I left a small (20 elements) whitelist of features that right now have no gate test, or where I couldn't find one. Once this PR gets merged, I'd like to close issue #22820 and open a new one on suggestion of @nikomatsakis to track the removal of all elements from that whitelist (already have a draft). Writing such a small test can be a good opportunity for a first contribution, so I won't touch it (let others have the fun xD).

cc @brson , @pnkfelix (they both discussed about this in the issue linked above).
This commit is contained in:
bors 2017-01-14 06:43:03 +00:00
commit 6fe23719fe
53 changed files with 225 additions and 20 deletions

View file

@ -45,6 +45,10 @@ whole, instead of just a few lines inside the test.
* `should-fail` indicates that the test should fail; used for "meta testing",
where we test the compiletest program itself to check that it will generate
errors in appropriate scenarios. This header is ignored for pretty-printer tests.
* `gate-test-X` where `X` is a feature marks the test as "gate test" for feature X.
Such tests are supposed to ensure that the compiler errors when usage of a gated
feature is attempted without the proper `#![feature(X)]` tag.
Each unstable lang feature is required to have a gate test.
## Revisions

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-asm
fn main() {
unsafe {
asm!(""); //~ ERROR inline assembly is not stable enough

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-asm
fn main() {
unsafe {
println!("{}", asm!("")); //~ ERROR inline assembly is not stable

View file

@ -16,6 +16,8 @@
// using `rustc_attrs` feature. There is a separate compile-fail/ test
// ensuring that the attribute feature-gating works in this context.)
// gate-test-generic_param_attrs
#![feature(rustc_attrs)]
#![allow(dead_code)]

View file

@ -10,6 +10,8 @@
// Verifies all possible restrictions for statics values.
// gate-test-drop_types_in_const
#![feature(box_syntax)]
use std::marker;

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-concat_idents
const XY_1: i32 = 10;
fn main() {

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-concat_idents
const XY_1: i32 = 10;
fn main() {

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-const_fn
// Test use of const fn without feature gate.
const fn foo() -> usize { 0 } //~ ERROR const fn is unstable

View file

@ -8,6 +8,10 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-intrinsics
// gate-test-platform_intrinsics
// gate-test-abi_vectorcall
// Functions
extern "rust-intrinsic" fn f1() {} //~ ERROR intrinsics are subject to change
extern "platform-intrinsic" fn f2() {} //~ ERROR platform intrinsics are experimental

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-advanced_slice_patterns
#![feature(slice_patterns)]
fn main() {

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-allow_internal_unstable
macro_rules! bar {
() => {
// more layers don't help:

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-associated_type_defaults
trait Foo {
type Bar = u8; //~ ERROR associated type defaults are unstable
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-box_syntax
// Check that `box EXPR` is feature-gated.
//
// See also feature-gate-placement-expr.rs

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-box_patterns
fn main() {
let box x = Box::new('c'); //~ ERROR box pattern syntax is experimental
println!("x: {}", x);

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-dropck_parametricity
// Ensure that attempts to use the unsafe attribute are feature-gated.
// Example adapted from RFC 1238 text (just left out the feature gate).

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-dropck_eyepatch
// Check that `may_dangle` is rejected if `dropck_eyepatch` feature gate is absent.
#![feature(generic_param_attrs)]

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-placement_in_syntax
// Check that `in PLACE { EXPR }` is feature-gated.
//
// See also feature-gate-box-expr.rs

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-associated_consts
trait MyTrait {
const C: bool;
//~^ associated constants are experimental

View file

@ -10,6 +10,8 @@
// Check that literals in attributes don't parse without the feature gate.
// gate-test-attr_literals
#![feature(rustc_attrs)]
#![allow(dead_code)]
#![allow(unused_variables)]

View file

@ -10,6 +10,8 @@
// Test that the use of the box syntax is gated by `box_syntax` feature gate.
// gate-test-box_syntax
fn main() {
let x = box 3;
//~^ ERROR box expression syntax is experimental; you can call `Box::new` instead.

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-concat_idents
fn main() {
concat_idents!(a, b); //~ ERROR `concat_idents` is not stable enough
}

View file

@ -11,6 +11,8 @@
// Test that `#[link_args]` attribute is gated by `link_args`
// feature gate.
// gate-test-link_args
#[link_args = "aFdEfSeVEEE"]
extern {}
//~^ ERROR the `link_args` attribute is not portable across platforms

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-link_llvm_intrinsics
extern {
#[link_name = "llvm.sqrt.f32"]
fn sqrt(x: f32) -> f32;

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-naked_functions
#[naked]
//~^ the `#[naked]` attribute is an experimental feature
fn naked() {}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-no_core
#![no_core] //~ ERROR no_core is experimental
fn main() {}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-non_ascii_idents
extern crate core as bäz; //~ ERROR non-ascii idents
use föö::bar; //~ ERROR non-ascii idents

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-plugin_registrar
// Test that `#[plugin_registrar]` attribute is gated by `plugin_registrar`
// feature gate.

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-target_feature
#[target_feature = "+sse2"]
//~^ the `#[target_feature]` attribute is an experimental feature
fn foo() {}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-thread_local
// Test that `#[thread_local]` attribute is gated by `thread_local`
// feature gate.
//

View file

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-trace_macros
fn main() {
trace_macros!(true); //~ ERROR: `trace_macros` is not stable

View file

@ -7,6 +7,9 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-i128_type
fn test1() -> i128 { //~ ERROR 128-bit type is unstable
0
}

View file

@ -7,6 +7,9 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-i128_type
fn test2() {
0i128; //~ ERROR 128-bit integers are not stable
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-conservative_impl_trait
fn foo() -> impl Fn() { || {} }
//~^ ERROR `impl Trait` is experimental

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-link_cfg
#[link(name = "foo", cfg(foo))]
//~^ ERROR: is feature gated
extern {}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-linkage
extern {
#[linkage = "extern_weak"] static foo: isize;
//~^ ERROR: the `linkage` attribute is experimental and not portable

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-log_syntax
fn main() {
log_syntax!() //~ ERROR `log_syntax!` is not stable enough
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-log_syntax
fn main() {
println!("{}", log_syntax!()); //~ ERROR `log_syntax!` is not stable
}

View file

@ -10,6 +10,8 @@
// Test that ! errors when used in illegal positions with feature(never_type) disabled
// gate-test-never_type
trait Foo {
type Wub;
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-no_core
#![no_core] //~ ERROR no_core is experimental
fn main() {}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-relaxed_adts
struct S(u8);
fn main() {

View file

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-needs_panic_runtime
// gate-test-panic_runtime
#![panic_runtime] //~ ERROR: is an experimental feature
#![needs_panic_runtime] //~ ERROR: is an experimental feature

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-pub_restricted
pub(crate) //~ ERROR experimental
mod foo {}

View file

@ -14,6 +14,8 @@
// revisions: with_gate no_gate
// gate-test-structural_match
#![allow(dead_code)]
#![deny(future_incompatible)]
#![feature(rustc_attrs)]

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-custom_derive
#[derive_Clone]
//~^ ERROR attributes of the form `#[derive_*]` are reserved
struct Test;

View file

@ -10,6 +10,8 @@
// Check that specialization must be ungated to use the `default` keyword
// gate-test-specialization
trait Foo {
fn foo(&self);
}

View file

@ -10,6 +10,8 @@
// Check that writing an overlapping impl is not allow unless specialization is ungated.
// gate-test-specialization
trait Foo {
fn foo(&self);
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-drop_types_in_const
#![feature(box_syntax)]
static mut a: Box<isize> = box 3;

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-struct_field_attributes
struct Foo {
present: (),
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-type_ascription
// Type ascription is feature gated
fn main() {

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-abi_unadjusted
extern "unadjusted" fn foo() {
//~^ ERROR: unadjusted ABI is an implementation detail and perma-unstable
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-untagged_unions
union U { //~ ERROR unions are unstable and possibly buggy
a: u8,
}

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// gate-test-windows_subsystem
#![windows_subsystem = "console"]
//~^ ERROR: the windows subsystem attribute is currently unstable

View file

@ -16,6 +16,7 @@
//! * The set of library features is disjoint from the set of language features
//! * Library features have at most one stability level
//! * Library features have at most one `since` value
//! * All unstable lang features have tests to ensure they are actually unstable
use std::collections::HashMap;
use std::fmt;
@ -26,6 +27,7 @@ use std::path::Path;
#[derive(PartialEq)]
enum Status {
Stable,
Removed,
Unstable,
}
@ -34,27 +36,22 @@ impl fmt::Display for Status {
let as_str = match *self {
Status::Stable => "stable",
Status::Unstable => "unstable",
Status::Removed => "removed",
};
fmt::Display::fmt(as_str, f)
}
}
struct Feature {
name: String,
level: Status,
since: String,
}
struct LibFeature {
level: Status,
since: String,
has_gate_test: bool,
}
pub fn check(path: &Path, bad: &mut bool) {
let features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
let mut features = collect_lang_features(&path.join("libsyntax/feature_gate.rs"));
assert!(!features.is_empty());
let mut lib_features = HashMap::<String, LibFeature>::new();
let mut lib_features = HashMap::<String, Feature>::new();
let mut contents = String::new();
super::walk(path,
@ -97,7 +94,7 @@ pub fn check(path: &Path, bad: &mut bool) {
None => "None",
};
if features.iter().any(|f| f.name == feature_name) {
if features.contains_key(feature_name) {
err("duplicating a lang feature");
}
if let Some(ref s) = lib_features.get(feature_name) {
@ -110,21 +107,105 @@ pub fn check(path: &Path, bad: &mut bool) {
continue;
}
lib_features.insert(feature_name.to_owned(),
LibFeature {
Feature {
level: level,
since: since.to_owned(),
has_gate_test: false,
});
}
});
super::walk(&path.join("test/compile-fail"),
&mut |path| super::filter_dirs(path),
&mut |file| {
let filename = file.file_name().unwrap().to_string_lossy();
if !filename.ends_with(".rs") || filename == "features.rs" ||
filename == "diagnostic_list.rs" {
return;
}
let filen_underscore = filename.replace("-","_").replace(".rs","");
test_filen_gate(&filen_underscore, &mut features);
contents.truncate(0);
t!(t!(File::open(&file), &file).read_to_string(&mut contents));
for (i, line) in contents.lines().enumerate() {
let mut err = |msg: &str| {
println!("{}:{}: {}", file.display(), i + 1, msg);
*bad = true;
};
let gate_test_str = "gate-test-";
if !line.contains(gate_test_str) {
continue;
}
let feature_name = match line.find(gate_test_str) {
Some(i) => {
&line[i+gate_test_str.len()..line[i+1..].find(' ').unwrap_or(line.len())]
},
None => continue,
};
let found_feature = features.get_mut(feature_name)
.map(|v| { v.has_gate_test = true; () })
.is_some();
let found_lib_feature = features.get_mut(feature_name)
.map(|v| { v.has_gate_test = true; () })
.is_some();
if !(found_feature || found_lib_feature) {
err(&format!("gate-test test found referencing a nonexistent feature '{}'",
feature_name));
}
}
});
// FIXME get this whitelist empty.
let whitelist = vec![
"abi_ptx", "simd", "safe_suggestion", "macro_reexport",
"more_struct_aliases", "static_recursion", "reflect",
"quote", "cfg_target_has_atomic", "custom_attribute",
"default_type_parameter_fallback", "pushpop_unsafe",
"use_extern_macros", "staged_api", "const_indexing",
"unboxed_closures", "stmt_expr_attributes",
"cfg_target_thread_local", "unwind_attributes",
"inclusive_range_syntax"
];
// Only check the number of lang features.
// Obligatory testing for library features is dumb.
let gate_untested = features.iter()
.filter(|&(_, f)| f.level == Status::Unstable)
.filter(|&(_, f)| !f.has_gate_test)
.filter(|&(n, _)| !whitelist.contains(&n.as_str()))
.collect::<Vec<_>>();
for &(name, _) in gate_untested.iter() {
println!("Expected a gate test for the feature '{}'.", name);
println!("Hint: create a file named 'feature-gate-{}.rs' in the compile-fail\
\n test suite, with its failures due to missing usage of\
\n #![feature({})].", name, name);
println!("Hint: If you already have such a test and don't want to rename it,\
\n you can also add a // gate-test-{} line to the test file.",
name);
}
if gate_untested.len() > 0 {
println!("Found {} features without a gate test.", gate_untested.len());
*bad = true;
}
if *bad {
return;
}
let mut lines = Vec::new();
for feature in features {
for (name, feature) in features.iter() {
lines.push(format!("{:<32} {:<8} {:<12} {:<8}",
feature.name,
name,
"lang",
feature.level,
feature.since));
@ -150,7 +231,20 @@ fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
.map(|(i, j)| &line[i..j])
}
fn collect_lang_features(path: &Path) -> Vec<Feature> {
fn test_filen_gate(filen_underscore: &str,
features: &mut HashMap<String, Feature>) -> bool {
if filen_underscore.starts_with("feature_gate") {
for (n, f) in features.iter_mut() {
if filen_underscore == format!("feature_gate_{}", n) {
f.has_gate_test = true;
return true;
}
}
}
return false;
}
fn collect_lang_features(path: &Path) -> HashMap<String, Feature> {
let mut contents = String::new();
t!(t!(File::open(path)).read_to_string(&mut contents));
@ -159,17 +253,18 @@ fn collect_lang_features(path: &Path) -> Vec<Feature> {
let mut parts = line.trim().split(",");
let level = match parts.next().map(|l| l.trim().trim_left_matches('(')) {
Some("active") => Status::Unstable,
Some("removed") => Status::Unstable,
Some("removed") => Status::Removed,
Some("accepted") => Status::Stable,
_ => return None,
};
let name = parts.next().unwrap().trim();
let since = parts.next().unwrap().trim().trim_matches('"');
Some(Feature {
name: name.to_owned(),
level: level,
since: since.to_owned(),
})
Some((name.to_owned(),
Feature {
level: level,
since: since.to_owned(),
has_gate_test: false,
}))
})
.collect()
}