2022-07-13 09:10:39 -05:00
#![ feature(once_cell) ]
2021-09-08 09:31:47 -05:00
#![ cfg_attr(feature = " deny-warnings " , deny(warnings)) ]
#![ warn(rust_2018_idioms, unused_lifetimes) ]
2021-03-12 08:30:50 -06:00
use std ::ffi ::OsStr ;
use std ::path ::PathBuf ;
2022-07-13 09:10:39 -05:00
use std ::sync ::LazyLock ;
2021-03-12 08:30:50 -06:00
use regex ::RegexSet ;
#[ derive(Debug) ]
struct Message {
path : PathBuf ,
bad_lines : Vec < String > ,
}
impl Message {
fn new ( path : PathBuf ) -> Self {
// we don't want the first letter after "error: ", "help: " ... to be capitalized
2022-03-16 07:12:30 -05:00
// also no punctuation (except for "?" ?) at the end of a line
2022-07-13 09:10:39 -05:00
static REGEX_SET : LazyLock < RegexSet > = LazyLock ::new ( | | {
RegexSet ::new ( & [
r "error: [A-Z]" ,
r "help: [A-Z]" ,
r "warning: [A-Z]" ,
r "note: [A-Z]" ,
r "try this: [A-Z]" ,
r "error: .*[.!]$" ,
r "help: .*[.!]$" ,
r "warning: .*[.!]$" ,
r "note: .*[.!]$" ,
r "try this: .*[.!]$" ,
] )
. unwrap ( )
} ) ;
2021-03-12 08:30:50 -06:00
// sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
// we want to ask a question ending in "?"
2022-07-13 09:10:39 -05:00
static EXCEPTIONS_SET : LazyLock < RegexSet > = LazyLock ::new ( | | {
RegexSet ::new ( & [
r "\.\.\.$" ,
r ".*C-like enum variant discriminant is not portable to 32-bit targets" ,
r ".*Intel x86 assembly syntax used" ,
r ".*AT&T x86 assembly syntax used" ,
r "note: Clippy version: .*" ,
r "the compiler unexpectedly panicked. this is a bug." ,
] )
. unwrap ( )
} ) ;
let content : String = std ::fs ::read_to_string ( & path ) . unwrap ( ) ;
2021-03-12 08:30:50 -06:00
let bad_lines = content
. lines ( )
2022-07-13 09:10:39 -05:00
. filter ( | line | REGEX_SET . matches ( line ) . matched_any ( ) )
2021-03-12 08:30:50 -06:00
// ignore exceptions
2022-07-13 09:10:39 -05:00
. filter ( | line | ! EXCEPTIONS_SET . matches ( line ) . matched_any ( ) )
2021-03-12 08:30:50 -06:00
. map ( ToOwned ::to_owned )
. collect ::< Vec < String > > ( ) ;
Message { path , bad_lines }
}
}
#[ test ]
fn lint_message_convention ( ) {
// disable the test inside the rustc test suite
if option_env! ( " RUSTC_TEST_SUITE " ) . is_some ( ) {
return ;
}
// make sure that lint messages:
// * are not capitalized
2022-04-15 13:25:55 -05:00
// * don't have punctuation at the end of the last sentence
2021-03-12 08:30:50 -06:00
// these directories have interesting tests
let test_dirs = [ " ui " , " ui-cargo " , " ui-internal " , " ui-toml " ]
. iter ( )
. map ( PathBuf ::from )
. map ( | p | {
let base = PathBuf ::from ( " tests " ) ;
base . join ( p )
} ) ;
// gather all .stderr files
let tests = test_dirs
. flat_map ( | dir | {
std ::fs ::read_dir ( dir )
. expect ( " failed to read dir " )
. map ( | direntry | direntry . unwrap ( ) . path ( ) )
} )
. filter ( | file | matches! ( file . extension ( ) . map ( OsStr ::to_str ) , Some ( Some ( " stderr " ) ) ) ) ;
// get all files that have any "bad lines" in them
let bad_tests : Vec < Message > = tests
. map ( Message ::new )
. filter ( | message | ! message . bad_lines . is_empty ( ) )
. collect ( ) ;
2021-04-08 10:50:13 -05:00
for message in & bad_tests {
2021-03-12 08:30:50 -06:00
eprintln! (
" error: the test '{}' contained the following nonconforming lines : " ,
message . path . display ( )
) ;
message . bad_lines . iter ( ) . for_each ( | line | eprintln! ( " {} " , line ) ) ;
eprintln! ( " \n \n " ) ;
2021-04-08 10:50:13 -05:00
}
2021-03-12 08:30:50 -06:00
eprintln! (
" \n \n \n Lint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed. "
) ;
eprintln! ( " Check out the rustc-dev-guide for more information: " ) ;
eprintln! ( " https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure \n \n \n " ) ;
assert! ( bad_tests . is_empty ( ) ) ;
}