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:
commit
d3e24bd457
@ -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
|
||||
|
@ -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
|
||||
|
@ -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!
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
Example of erroneous code:
|
||||
|
||||
```compile_fail
|
||||
```compile_fail,E0590
|
||||
while break {}
|
||||
```
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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`.
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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> { }
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,edition2018
|
||||
```compile_fail,edition2018,E0708
|
||||
#![feature(async_closure)]
|
||||
|
||||
fn main() {
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
@ -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}));
|
||||
}
|
||||
```
|
||||
|
@ -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"); }
|
||||
}
|
||||
```
|
||||
|
@ -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
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user