Rollup merge of #71350 - GuillaumeGomez:error-code-explanation-extra-check, r=oli-obk

Error code explanation extra check

r? @Mark-Simulacrum
This commit is contained in:
Dylan DPC 2020-04-22 23:19:20 +02:00 committed by GitHub
commit d3e24bd457
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 231 additions and 84 deletions

View File

@ -2,12 +2,14 @@ External C functions are allowed to be variadic. However, a variadic function
takes a minimum number of arguments. For example, consider C's variadic `printf`
function:
```
```compile_fail,E0060
use std::os::raw::{c_char, c_int};
extern "C" {
fn printf(_: *const c_char, ...) -> c_int;
}
unsafe { printf(); } // error!
```
Using this declaration, it must be called with at least one argument, so

View File

@ -2,7 +2,7 @@ A pattern was declared as an argument in a foreign function declaration.
Erroneous code example:
```compile_fail
```compile_fail,E0130
extern {
fn foo((a, b): (u32, u32)); // error: patterns aren't allowed in foreign
// function declarations

View File

@ -2,7 +2,7 @@ A negative implementation was marked as unsafe.
Erroneous code example:
```compile_fail
```compile_fail,E0198
struct Foo;
unsafe impl !Clone for Foo { } // error!

View File

@ -1,5 +1,15 @@
Inherent associated types were part of [RFC 195] but are not yet implemented.
See [the tracking issue][iss8995] for the status of this implementation.
Erroneous code example:
```compile_fail,E0202
struct Foo;
impl Foo {
type Bar = isize; // error!
}
```
[RFC 195]: https://github.com/rust-lang/rfcs/blob/master/text/0195-associated-items.md
[iss8995]: https://github.com/rust-lang/rust/issues/8995

View File

@ -3,15 +3,11 @@ message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
```compile_fail,E0230
#![feature(rustc_attrs)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{B}>`"] // error
trait BadAnnotation<A> {}
```
There will be an error about `bool` not implementing `Index<u8>`, followed by a

View File

@ -3,15 +3,11 @@ message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
```compile_fail,E0231
#![feature(rustc_attrs)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{}>`"] // error!
trait BadAnnotation<A> {}
```
there will be an error about `bool` not implementing `Index<u8>`, followed by a

View File

@ -3,15 +3,11 @@ message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail
```compile_fail,E0232
#![feature(rustc_attrs)]
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
trait Index<Idx> { /* ... */ }
foo(true); // `bool` does not implement `Index<u8>`
#[rustc_on_unimplemented(lorem="")] // error!
trait BadAnnotation {}
```
there will be an error about `bool` not implementing `Index<u8>`, followed by a

View File

@ -4,7 +4,7 @@ You tried to supply a type which doesn't implement some trait in a location
which expected that trait. This error typically occurs when working with
`Fn`-based types. Erroneous code example:
```compile-fail
```compile_fail
fn foo<F: Fn(usize)>(x: F) { }
fn main() {

View File

@ -3,27 +3,27 @@ attempted to `pub use` a type or value that was not itself public.
Erroneous code example:
```compile_fail
mod foo {
const X: u32 = 1;
```compile_fail,E0364
mod a {
fn foo() {}
mod a {
pub use super::foo; // error!
}
}
pub use foo::X;
fn main() {}
```
The solution to this problem is to ensure that the items that you are
re-exporting are themselves marked with `pub`:
```
mod foo {
pub const X: u32 = 1;
mod a {
pub fn foo() {} // ok!
mod a {
pub use super::foo;
}
}
pub use foo::X;
fn main() {}
```
See the [Use Declarations][use-declarations] section of the reference for

View File

@ -3,7 +3,7 @@ or a newtype wrapper around a pointer.
Erroneous code example:
```compile-fail,E0378
```compile_fail,E0378
#![feature(dispatch_from_dyn)]
use std::ops::DispatchFromDyn;

View File

@ -3,7 +3,7 @@
Example of erroneous code:
```compile_fail
```compile_fail,E0590
while break {}
```

View File

@ -3,5 +3,17 @@ instantiated from outside of the defining crate as it has been marked
as `non_exhaustive` and as such more fields/variants may be added in
future that could cause adverse side effects for this code.
Erroneous code example:
```ignore (it only works cross-crate)
#[non_exhaustive]
pub struct NormalStruct {
pub first_field: u16,
pub second_field: u16,
}
let ns = NormalStruct { first_field: 640, second_field: 480 }; // error!
```
It is recommended that you look for a `new` function or equivalent in the
crate's documentation.

View File

@ -2,17 +2,17 @@ A closure or generator was constructed that references its own type.
Erroneous example:
```compile-fail,E0644
```compile_fail,E0644
fn fix<F>(f: &F)
where F: Fn(&F)
{
f(&f);
f(&f);
}
fn main() {
fix(&|y| {
// Here, when `x` is called, the parameter `y` is equal to `x`.
});
fix(&|y| {
// Here, when `x` is called, the parameter `y` is equal to `x`.
});
}
```

View File

@ -2,7 +2,7 @@ An unstable feature was used.
Erroneous code example:
```compile_fail,E658
```compile_fail,E0658
#[repr(u128)] // error: use of unstable library feature 'repr128'
enum Foo {
Bar(u64),

View File

@ -1,5 +1,17 @@
Cannot convert inline assembly operand to a single LLVM value.
Erroneous code example:
```compile_fail,E0669
#![feature(llvm_asm)]
fn main() {
unsafe {
llvm_asm!("" :: "r"("")); // error!
}
}
```
This error usually happens when trying to pass in a value to an input inline
assembly operand that is actually a pair of values. In particular, this can
happen when trying to pass in a slice, for instance a `&str`. In Rust, these

View File

@ -3,7 +3,7 @@ generator can be constructed.
Erroneous code example:
```edition2018,compile-fail,E0698
```edition2018,compile_fail,E0698
async fn bar<T>() -> () {}
async fn foo() {

View File

@ -3,7 +3,7 @@ appear within the `impl Trait` itself.
Erroneous code example:
```compile-fail,E0700
```compile_fail,E0700
use std::cell::Cell;
trait Trait<'a> { }

View File

@ -2,7 +2,7 @@
Erroneous code example:
```compile_fail,edition2018
```compile_fail,edition2018,E0708
#![feature(async_closure)]
fn main() {

View File

@ -1,5 +1,19 @@
A `#[marker]` trait contained an associated item.
Erroneous code example:
```compile_fail,E0714
#![feature(marker_trait_attr)]
#![feature(associated_type_defaults)]
#[marker]
trait MarkerConst {
const N: usize; // error!
}
fn main() {}
```
The items of marker traits cannot be overridden, so there's no need to have them
when they cannot be changed per-type anyway. If you wanted them for ergonomic
reasons, consider making an extension trait instead.

View File

@ -1,5 +1,24 @@
An `impl` for a `#[marker]` trait tried to override an associated item.
Erroneous code example:
```compile_fail,E0715
#![feature(marker_trait_attr)]
#[marker]
trait Marker {
const N: usize = 0;
fn do_something() {}
}
struct OverrideConst;
impl Marker for OverrideConst { // error!
const N: usize = 1;
}
fn main() {}
```
Because marker traits are allowed to have multiple implementations for the same
type, it's not allowed to override anything in those implementations, as it
would be ambiguous which override should actually be used.

View File

@ -2,14 +2,16 @@ A `yield` clause was used in an `async` context.
Example of erroneous code:
```compile_fail
```compile_fail,E0727,edition2018
#![feature(generators)]
let generator = || {
async {
yield;
}
};
fn main() {
let generator = || {
async {
yield;
}
};
}
```
Here, the `yield` keyword is used in an `async` block,
@ -17,10 +19,12 @@ which is not yet supported.
To fix this error, you have to move `yield` out of the `async` block:
```
```edition2018
#![feature(generators)]
let generator = || {
yield;
};
fn main() {
let generator = || {
yield;
};
}
```

View File

@ -1,5 +1,18 @@
An `enum` with a discriminant must specify a `#[repr(inttype)]`.
Erroneous code example:
```compile_fail,E0732
#![feature(arbitrary_enum_discriminant)]
enum Enum { // error!
Unit = 1,
Tuple() = 2,
Struct{} = 3,
}
# fn main() {}
```
A `#[repr(inttype)]` must be provided on an `enum` if it has a non-unit
variant with a discriminant, or where there are both unit variants with
discriminants and non-unit variants. This restriction ensures that there
@ -23,7 +36,9 @@ fn discriminant(v : &Enum) -> u8 {
unsafe { *(v as *const Enum as *const u8) }
}
assert_eq!(3, discriminant(&Enum::Unit));
assert_eq!(2, discriminant(&Enum::Tuple(5)));
assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11}));
fn main() {
assert_eq!(3, discriminant(&Enum::Unit));
assert_eq!(2, discriminant(&Enum::Tuple(5)));
assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11}));
}
```

View File

@ -1 +1,16 @@
A `union` cannot have fields with destructors.
Erroneous code example:
```compile_fail,E0740
union Test {
a: A, // error!
}
#[derive(Debug)]
struct A(i32);
impl Drop for A {
fn drop(&mut self) { println!("A"); }
}
```

View File

@ -3,16 +3,13 @@ Control-flow expressions are not allowed inside a const context.
At the moment, `if` and `match`, as well as the looping constructs `for`,
`while`, and `loop`, are forbidden inside a `const`, `static`, or `const fn`.
```compile_fail,E0658
```compile_fail,E0744
const _: i32 = {
let mut x = 0;
loop {
x += 1;
if x == 4 {
break;
}
for i in 0..4 { // error!
x += i;
}
x
};
```

View File

@ -15,21 +15,57 @@ const WHITELIST: &[&str] = &[
"E0727", "E0729",
];
// Some error codes don't have any tests apparently...
const IGNORE_EXPLANATION_CHECK: &[&str] =
&["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750", "E0751"];
fn check_error_code_explanation(
f: &str,
error_codes: &mut HashMap<String, bool>,
err_code: String,
) {
) -> bool {
let mut invalid_compile_fail_format = false;
let mut found_error_code = false;
for line in f.lines() {
let s = line.trim();
if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
error_codes.insert(err_code, true);
return;
if s.starts_with("```") {
if s.contains("compile_fail") && s.contains('E') {
if !found_error_code {
error_codes.insert(err_code.clone(), true);
found_error_code = true;
}
} else if s.contains("compile-fail") {
invalid_compile_fail_format = true;
}
} else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
error_codes.get_mut(&err_code).map(|x| *x = true);
return;
if !found_error_code {
error_codes.get_mut(&err_code).map(|x| *x = true);
found_error_code = true;
}
}
}
invalid_compile_fail_format
}
fn check_if_error_code_is_test_in_explanation(f: &str, err_code: &String) -> bool {
let mut can_be_ignored = false;
for line in f.lines() {
let s = line.trim();
if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
return true;
}
if s.starts_with("```") {
if s.contains("compile_fail") && s.contains(err_code) {
return true;
} else if s.contains("(") {
// It's very likely that we can't actually make it fail compilation...
can_be_ignored = true;
}
}
}
can_be_ignored
}
macro_rules! some_or_continue {
@ -41,7 +77,12 @@ macro_rules! some_or_continue {
};
}
fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &Path) {
fn extract_error_codes(
f: &str,
error_codes: &mut HashMap<String, bool>,
path: &Path,
errors: &mut Vec<String>,
) {
let mut reached_no_explanation = false;
for line in f.lines() {
@ -55,10 +96,26 @@ fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>, path: &
// Now we extract the tests from the markdown file!
let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1));
let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
let path = some_or_continue!(path.parent()).join(md_file_name);
let path = some_or_continue!(path.parent())
.join(md_file_name)
.canonicalize()
.expect("failed to canonicalize error explanation file path");
match read_to_string(&path) {
Ok(content) => {
check_error_code_explanation(&content, error_codes, err_code);
if !IGNORE_EXPLANATION_CHECK.contains(&err_code.as_str())
&& !check_if_error_code_is_test_in_explanation(&content, &err_code)
{
errors.push(format!(
"`{}` doesn't use its own error code in compile_fail example",
path.display(),
));
}
if check_error_code_explanation(&content, error_codes, err_code) {
errors.push(format!(
"`{}` uses invalid tag `compile-fail` instead of `compile_fail`",
path.display(),
));
}
}
Err(e) => {
eprintln!("Couldn't read `{}`: {}", path.display(), e);
@ -94,22 +151,24 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, boo
}
pub fn check(path: &Path, bad: &mut bool) {
let mut errors = Vec::new();
println!("Checking which error codes lack tests...");
let mut error_codes: HashMap<String, bool> = HashMap::new();
super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
let file_name = entry.file_name();
if file_name == "error_codes.rs" {
extract_error_codes(contents, &mut error_codes, entry.path());
extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors);
} else if entry.path().extension() == Some(OsStr::new("stderr")) {
extract_error_codes_from_tests(contents, &mut error_codes);
}
});
println!("Found {} error codes", error_codes.len());
if errors.is_empty() {
println!("Found {} error codes", error_codes.len());
let mut errors = Vec::new();
for (err_code, nb) in &error_codes {
if !*nb && !WHITELIST.contains(&err_code.as_str()) {
errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
for (err_code, nb) in &error_codes {
if !*nb && !WHITELIST.contains(&err_code.as_str()) {
errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
}
}
}
errors.sort();