@ -1,9 +1,9 @@
use core ::fmt ::Write ;
use itertools ::Itertools ;
use r egex::Regex ;
use r ustc_lexer::{ tokenize , unescape , LiteralKind , TokenKind } ;
use std ::collections ::HashMap ;
use std ::ffi ::OsStr ;
use std ::fs ;
use std ::lazy ::SyncLazy ;
use std ::path ::Path ;
use walkdir ::WalkDir ;
@ -13,35 +13,7 @@
// Use that command to update this file and do not edit by hand.\n\
// Manual edits will be overwritten.\n\n";
static DEC_CLIPPY_LINT_RE : SyncLazy < Regex > = SyncLazy ::new ( | | {
Regex ::new (
r #" (?x)
declare_clippy_lint ! \ s * [ \ { ( ]
( ? :\ s + ///.*)*
( ? :\ s * \ #\ [ clippy ::version \ s * = \ s * " [^ " ] * " \ ])?
\ s + pub \ s + ( ? P < name > [ A - Z_ ] [ A - Z_0 - 9 ] * ) \ s * , \ s *
( ? P < cat > [ a - z_ ] + ) \ s * , \ s *
" (?P<desc>(?:[^ " \ \ ] + | \ \ ( ? s ) . ( ? - s ) ) * ) " \ s*[})]
" #,
)
. unwrap ( )
} ) ;
static DEC_DEPRECATED_LINT_RE : SyncLazy < Regex > = SyncLazy ::new ( | | {
Regex ::new (
r #" (?x)
declare_deprecated_lint ! \ s * [ { ( ] \ s *
( ? :\ s + ///.*)*
( ? :\ s * \ #\ [ clippy ::version \ s * = \ s * " [^ " ] * " \ ])?
\ s + pub \ s + ( ? P < name > [ A - Z_ ] [ A - Z_0 - 9 ] * ) \ s * , \ s *
" (?P<desc>(?:[^ " \ \ ] + | \ \ ( ? s ) . ( ? - s ) ) * ) " \ s*[})]
" #,
)
. unwrap ( )
} ) ;
static NL_ESCAPE_RE : SyncLazy < Regex > = SyncLazy ::new ( | | Regex ::new ( r # "\\\n\s*"# ) . unwrap ( ) ) ;
static DOCS_LINK : & str = " https://rust-lang.github.io/rust-clippy/master/index.html " ;
const DOCS_LINK : & str = " https://rust-lang.github.io/rust-clippy/master/index.html " ;
#[ derive(Clone, Copy, PartialEq) ]
pub enum UpdateMode {
@ -60,60 +32,52 @@ pub enum UpdateMode {
/// Panics if a file path could not read from or then written to
#[ allow(clippy::too_many_lines) ]
pub fn run ( update_mode : UpdateMode ) {
let lint_list : Vec < Lint > = gather_all ( ) . collect ( ) ;
let ( lints , deprecated_lints ) = gather_all ( ) ;
let internal_lints = Lint ::internal_lints ( & lint_list ) ;
let deprecated_lints = Lint ::deprecated_lints ( & lint_list ) ;
let usable_lints = Lint ::usable_lints ( & lint_list ) ;
let internal_lints = Lint ::internal_lints ( & lints ) ;
let usable_lints = Lint ::usable_lints ( & lints ) ;
let mut sorted_usable_lints = usable_lints . clone ( ) ;
sorted_usable_lints . sort_by_key ( | lint | lint . name . clone ( ) ) ;
let usable_lint_count = round_to_fifty ( usable_lints . len ( ) ) ;
let mut file_change = false ;
file_change | = replace_region_in_file (
replace_region_in_file (
update_mode ,
Path ::new ( " README.md " ) ,
& format! (
r # "\[There are over \d+ lints included in this crate!\]\({}\)"# ,
DOCS_LINK
) ,
" " ,
true ,
update_mode = = UpdateMode ::Change ,
| | {
vec! [ format! (
" [There are over {} lints included in this crate!]({}) " ,
usable_lint_count , DOCS_LINK
) ]
" [There are over " ,
" lints included in this crate!] " ,
| res | {
write! ( res , " {} " , round_to_fifty ( usable_lints . len ( ) ) ) . unwrap ( ) ;
} ,
)
. changed ;
) ;
file_change | = replace_region_in_file (
replace_region_in_file (
update_mode ,
Path ::new ( " CHANGELOG.md " ) ,
" <!-- begin autogenerated links to lint list --> " ,
" <!-- begin autogenerated links to lint list --> \n " ,
" <!-- end autogenerated links to lint list --> " ,
false ,
update_mode = = UpdateMode ::Change ,
| | gen_changelog_lint_list ( usable_lints . iter ( ) . chain ( deprecated_lints . iter ( ) ) ) ,
)
. changed ;
| res | {
for lint in usable_lints
. iter ( )
. map ( | l | & l . name )
. chain ( deprecated_lints . iter ( ) . map ( | l | & l . name ) )
. sorted ( )
{
writeln! ( res , " [`{}`]: {}#{} " , lint , DOCS_LINK , lint ) . unwrap ( ) ;
}
} ,
) ;
// This has to be in lib.rs, otherwise rustfmt doesn't work
file_change | = replace_region_in_file (
replace_region_in_file (
update_mode ,
Path ::new ( " clippy_lints/src/lib.rs " ) ,
" begin lints modules " ,
" end lints modules " ,
false ,
update_mode = = UpdateMode ::Change ,
| | gen_modules_list ( usable_lints . iter ( ) ) ,
)
. changed ;
if file_change & & update_mode = = UpdateMode ::Check {
exit_with_failure ( ) ;
}
" // begin lints modules, do not remove this comment, it’ s used in `update_lints` \n " ,
" // end lints modules, do not remove this comment, it’ s used in `update_lints` " ,
| res | {
for lint_mod in usable_lints . iter ( ) . map ( | l | & l . module ) . unique ( ) . sorted ( ) {
writeln! ( res , " mod {}; " , lint_mod ) . unwrap ( ) ;
}
} ,
) ;
process_file (
" clippy_lints/src/lib.register_lints.rs " ,
@ -123,7 +87,7 @@ pub fn run(update_mode: UpdateMode) {
process_file (
" clippy_lints/src/lib.deprecated.rs " ,
update_mode ,
& gen_deprecated ( deprecated_lints . iter ( ) ) ,
& gen_deprecated ( & deprecated_lints ) ,
) ;
let all_group_lints = usable_lints . iter ( ) . filter ( | l | {
@ -146,15 +110,12 @@ pub fn run(update_mode: UpdateMode) {
}
pub fn print_lints ( ) {
let lint_list : Vec < Lint > = gather_all ( ) . collect ( ) ;
let ( lint_list , _ ) = gather_all ( ) ;
let usable_lints = Lint ::usable_lints ( & lint_list ) ;
let usable_lint_count = usable_lints . len ( ) ;
let grouped_by_lint_group = Lint ::by_lint_group ( usable_lints . into_iter ( ) ) ;
for ( lint_group , mut lints ) in grouped_by_lint_group {
if lint_group = = " Deprecated " {
continue ;
}
println! ( " \n ## {} " , lint_group ) ;
lints . sort_by_key ( | l | l . name . clone ( ) ) ;
@ -198,19 +159,17 @@ struct Lint {
name : String ,
group : String ,
desc : String ,
deprecation : Option < String > ,
module : String ,
}
impl Lint {
#[ must_use ]
fn new ( name : & str , group : & str , desc : & str , deprecation: Option < & str > , module: & str ) -> Self {
fn new ( name : & str , group : & str , desc : & str , module: & str ) -> Self {
Self {
name : name . to_lowercase ( ) ,
group : group . to_string ( ) ,
desc : NL_ESCAPE_RE . replace ( & desc . replace ( " \\ \" " , " \" " ) , " " ) . to_string ( ) ,
deprecation : deprecation . map ( ToString ::to_string ) ,
module : module . to_string ( ) ,
group : group . into ( ) ,
desc : remove_line_splices ( desc ) ,
module : module . into ( ) ,
}
}
@ -219,7 +178,7 @@ fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &
fn usable_lints ( lints : & [ Self ] ) -> Vec < Self > {
lints
. iter ( )
. filter ( | l | l . deprecation . is_none ( ) & & ! l . group . starts_with ( " internal " ) )
. filter ( | l | ! l . group . starts_with ( " internal " ) )
. cloned ( )
. collect ( )
}
@ -230,12 +189,6 @@ fn internal_lints(lints: &[Self]) -> Vec<Self> {
lints . iter ( ) . filter ( | l | l . group = = " internal " ) . cloned ( ) . collect ( )
}
/// Returns all deprecated lints
#[ must_use ]
fn deprecated_lints ( lints : & [ Self ] ) -> Vec < Self > {
lints . iter ( ) . filter ( | l | l . deprecation . is_some ( ) ) . cloned ( ) . collect ( )
}
/// Returns the lints in a `HashMap`, grouped by the different lint groups
#[ must_use ]
fn by_lint_group ( lints : impl Iterator < Item = Self > ) -> HashMap < String , Vec < Self > > {
@ -243,6 +196,20 @@ fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>
}
}
#[ derive(Clone, PartialEq, Debug) ]
struct DeprecatedLint {
name : String ,
reason : String ,
}
impl DeprecatedLint {
fn new ( name : & str , reason : & str ) -> Self {
Self {
name : name . to_lowercase ( ) ,
reason : remove_line_splices ( reason ) ,
}
}
}
/// Generates the code for registering a group
fn gen_lint_group_list < ' a > ( group_name : & str , lints : impl Iterator < Item = & ' a Lint > ) -> String {
let mut details : Vec < _ > = lints . map ( | l | ( & l . module , l . name . to_uppercase ( ) ) ) . collect ( ) ;
@ -262,32 +229,12 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lin
output
}
/// Generates the module declarations for `lints`
#[ must_use ]
fn gen_modules_list < ' a > ( lints : impl Iterator < Item = & ' a Lint > ) -> Vec < String > {
lints
. map ( | l | & l . module )
. unique ( )
. map ( | module | format! ( " mod {} ; " , module ) )
. sorted ( )
. collect ::< Vec < String > > ( )
}
/// Generates the list of lint links at the bottom of the CHANGELOG
#[ must_use ]
fn gen_changelog_lint_list < ' a > ( lints : impl Iterator < Item = & ' a Lint > ) -> Vec < String > {
lints
. sorted_by_key ( | l | & l . name )
. map ( | l | format! ( " [` {} `]: {} # {} " , l . name , DOCS_LINK , l . name ) )
. collect ( )
}
/// Generates the `register_removed` code
#[ must_use ]
fn gen_deprecated < ' a > ( lints : impl Iterator < Item = & ' a Lint > ) -> String {
fn gen_deprecated ( lints : & [ DeprecatedLint ] ) -> String {
let mut output = GENERATED_FILE_COMMENT . to_string ( ) ;
output . push_str ( " { \n " ) ;
for Lint { name , deprecation , .. } in lints {
for lint in lints {
output . push_str ( & format! (
concat! (
" store.register_removed( \n " ,
@ -295,8 +242,7 @@ fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
" \" {} \" , \n " ,
" ); \n "
) ,
name ,
deprecation . as_ref ( ) . expect ( " `lints` are deprecated " )
lint . name , lint . reason ,
) ) ;
}
output . push_str ( " } \n " ) ;
@ -330,61 +276,133 @@ fn gen_register_lint_list<'a>(
output
}
/// Gathers all files in `src/clippy_lints` and gathers all lints inside
fn gather_all ( ) -> impl Iterator < Item = Lint > {
lint_files ( ) . flat_map ( | f | gather_from_file ( & f ) )
}
/// Gathers all lints defined in `clippy_lints/src`
fn gather_all ( ) -> ( Vec < Lint > , Vec < DeprecatedLint > ) {
let mut lints = Vec ::with_capacity ( 1000 ) ;
let mut deprecated_lints = Vec ::with_capacity ( 50 ) ;
let root_path = clippy_project_root ( ) . join ( " clippy_lints/src " ) ;
fn gather_from_file ( dir_entry : & walkdir ::DirEntry ) -> impl Iterator < Item = Lint > {
let content = fs ::read_to_string ( dir_entry . path ( ) ) . unwrap ( ) ;
let path = dir_entry . path ( ) ;
let filename = path . file_stem ( ) . unwrap ( ) ;
let path_buf = path . with_file_name ( filename ) ;
let mut rel_path = path_buf
. strip_prefix ( clippy_project_root ( ) . join ( " clippy_lints/src " ) )
. expect ( " only files in `clippy_lints/src` should be looked at " ) ;
// If the lints are stored in mod.rs, we get the module name from
// the containing directory:
if filename = = " mod " {
rel_path = rel_path . parent ( ) . unwrap ( ) ;
}
let module = rel_path
. components ( )
. map ( | c | c . as_os_str ( ) . to_str ( ) . unwrap ( ) )
. collect ::< Vec < _ > > ( )
. join ( " :: " ) ;
parse_contents ( & content , & module )
}
fn parse_contents ( content : & str , module : & str ) -> impl Iterator < Item = Lint > {
let lints = DEC_CLIPPY_LINT_RE
. captures_iter ( content )
. map ( | m | Lint ::new ( & m [ " name " ] , & m [ " cat " ] , & m [ " desc " ] , None , module ) ) ;
let deprecated = DEC_DEPRECATED_LINT_RE
. captures_iter ( content )
. map ( | m | Lint ::new ( & m [ " name " ] , " Deprecated " , & m [ " desc " ] , Some ( & m [ " desc " ] ) , module ) ) ;
// Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
lints . chain ( deprecated ) . collect ::< Vec < Lint > > ( ) . into_iter ( )
}
/// Collects all .rs files in the `clippy_lints/src` directory
fn lint_files ( ) -> impl Iterator < Item = walkdir ::DirEntry > {
// We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
// Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
let path = clippy_project_root ( ) . join ( " clippy_lints/src " ) ;
WalkDir ::new ( path )
for ( rel_path , file ) in WalkDir ::new ( & root_path )
. into_iter ( )
. filter_map( Result ::ok )
. map ( Result ::unwrap )
. filter ( | f | f . path ( ) . extension ( ) = = Some ( OsStr ::new ( " rs " ) ) )
. map ( | f | ( f . path ( ) . strip_prefix ( & root_path ) . unwrap ( ) . to_path_buf ( ) , f ) )
{
let path = file . path ( ) ;
let contents =
fs ::read_to_string ( path ) . unwrap_or_else ( | e | panic! ( " Cannot read from ` {} `: {} " , path . display ( ) , e ) ) ;
let module = rel_path
. components ( )
. map ( | c | c . as_os_str ( ) . to_str ( ) . unwrap ( ) )
. collect ::< Vec < _ > > ( )
. join ( " :: " ) ;
// If the lints are stored in mod.rs, we get the module name from
// the containing directory:
let module = if let Some ( module ) = module . strip_suffix ( " ::mod.rs " ) {
module
} else {
module . strip_suffix ( " .rs " ) . unwrap_or ( & module )
} ;
if module = = " deprecated_lints " {
parse_deprecated_contents ( & contents , & mut deprecated_lints ) ;
} else {
parse_contents ( & contents , module , & mut lints ) ;
}
}
( lints , deprecated_lints )
}
/// Whether a file has had its text changed or not
#[ derive(PartialEq, Debug) ]
struct FileChange {
changed : bool ,
new_lines : String ,
macro_rules ! match_tokens {
( $iter :ident , $( $token :ident $( { $( $fields :tt ) * } ) ? $( ( $capture :ident ) ) ? ) * ) = > {
{
$( $( let $capture = ) ? if let Some ( ( TokenKind ::$token $( { $( $fields ) * } ) ? , _x ) ) = $iter . next ( ) {
_x
} else {
continue ;
} ; ) *
#[ allow(clippy::unused_unit) ]
{ ( $( $( $capture , ) ? ) * ) }
}
}
}
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
fn parse_contents ( contents : & str , module : & str , lints : & mut Vec < Lint > ) {
let mut offset = 0 usize ;
let mut iter = tokenize ( contents ) . map ( | t | {
let range = offset .. offset + t . len ;
offset = range . end ;
( t . kind , & contents [ range ] )
} ) ;
while iter . any ( | ( kind , s ) | kind = = TokenKind ::Ident & & s = = " declare_clippy_lint " ) {
let mut iter = iter
. by_ref ( )
. filter ( | & ( kind , _ ) | ! matches! ( kind , TokenKind ::Whitespace | TokenKind ::LineComment { .. } ) ) ;
// matches `!{`
match_tokens! ( iter , Bang OpenBrace ) ;
match iter . next ( ) {
// #[clippy::version = "version"] pub
Some ( ( TokenKind ::Pound , _ ) ) = > {
match_tokens! ( iter , OpenBracket Ident Colon Colon Ident Eq Literal { .. } CloseBracket Ident ) ;
} ,
// pub
Some ( ( TokenKind ::Ident , _ ) ) = > ( ) ,
_ = > continue ,
}
let ( name , group , desc ) = match_tokens! (
iter ,
// LINT_NAME
Ident ( name ) Comma
// group,
Ident ( group ) Comma
// "description" }
Literal { kind : LiteralKind ::Str { .. } , .. } ( desc ) CloseBrace
) ;
lints . push ( Lint ::new ( name , group , desc , module ) ) ;
}
}
/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
fn parse_deprecated_contents ( contents : & str , lints : & mut Vec < DeprecatedLint > ) {
let mut offset = 0 usize ;
let mut iter = tokenize ( contents ) . map ( | t | {
let range = offset .. offset + t . len ;
offset = range . end ;
( t . kind , & contents [ range ] )
} ) ;
while iter . any ( | ( kind , s ) | kind = = TokenKind ::Ident & & s = = " declare_deprecated_lint " ) {
let mut iter = iter
. by_ref ( )
. filter ( | & ( kind , _ ) | ! matches! ( kind , TokenKind ::Whitespace | TokenKind ::LineComment { .. } ) ) ;
let ( name , reason ) = match_tokens! (
iter ,
// !{
Bang OpenBrace
// #[clippy::version = "version"]
Pound OpenBracket Ident Colon Colon Ident Eq Literal { .. } CloseBracket
// pub LINT_NAME,
Ident Ident ( name ) Comma
// "description"
Literal { kind : LiteralKind ::Str { .. } , .. } ( reason )
// }
CloseBrace
) ;
lints . push ( DeprecatedLint ::new ( name , reason ) ) ;
}
}
/// Removes the line splices and surrounding quotes from a string literal
fn remove_line_splices ( s : & str ) -> String {
let s = s
. strip_prefix ( '"' )
. and_then ( | s | s . strip_suffix ( '"' ) )
. unwrap_or_else ( | | panic! ( " expected quoted string, found ` {} ` " , s ) ) ;
let mut res = String ::with_capacity ( s . len ( ) ) ;
unescape ::unescape_literal ( s , unescape ::Mode ::Str , & mut | range , _ | res . push_str ( & s [ range ] ) ) ;
res
}
/// Replaces a region in a file delimited by two lines matching regexes.
@ -396,144 +414,49 @@ struct FileChange {
/// # Panics
///
/// Panics if the path could not read or then written
fn replace_region_in_file < F > (
fn replace_region_in_file (
update_mode : UpdateMode ,
path : & Path ,
start : & str ,
end : & str ,
replace_start : bool ,
write_back : bool ,
replacements : F ,
) -> FileChange
where
F : FnOnce ( ) -> Vec < String > ,
{
let contents = fs ::read_to_string ( path ) . unwrap_or_else ( | e | panic! ( " Cannot read from {} : {} " , path . display ( ) , e ) ) ;
let file_change = replace_region_in_text ( & contents , start , end , replace_start , replacements ) ;
write_replacement : impl FnMut ( & mut String ) ,
) {
let contents = fs ::read_to_string ( path ) . unwrap_or_else ( | e | panic! ( " Cannot read from ` {} `: {} " , path . display ( ) , e ) ) ;
let new_contents = match replace_region_in_text ( & contents , start , end , write_replacement ) {
Ok ( x ) = > x ,
Err ( delim ) = > panic! ( " Couldn't find ` {} ` in file ` {} ` " , delim , path . display ( ) ) ,
} ;
if write_back {
if let Err ( e ) = fs ::write ( path , file_change . new_lines . as_bytes ( ) ) {
panic! ( " Cannot write to {} : {} " , path . display ( ) , e ) ;
}
}
file_change
}
/// Replaces a region in a text delimited by two lines matching regexes.
///
/// * `text` is the input text on which you want to perform the replacement
/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
/// delimiter line is never replaced.
/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
///
/// If you want to perform the replacement on files instead of already parsed text,
/// use `replace_region_in_file`.
///
/// # Example
///
/// ```ignore
/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
/// let result =
/// replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
/// vec!["a different".to_string(), "text".to_string()]
/// })
/// .new_lines;
/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
/// ```
///
/// # Panics
///
/// Panics if start or end is not valid regex
fn replace_region_in_text < F > ( text : & str , start : & str , end : & str , replace_start : bool , replacements : F ) -> FileChange
where
F : FnOnce ( ) -> Vec < String > ,
{
let replace_it = replacements ( ) ;
let mut in_old_region = false ;
let mut found = false ;
let mut new_lines = vec! [ ] ;
let start = Regex ::new ( start ) . unwrap ( ) ;
let end = Regex ::new ( end ) . unwrap ( ) ;
for line in text . lines ( ) {
if in_old_region {
if end . is_match ( line ) {
in_old_region = false ;
new_lines . extend ( replace_it . clone ( ) ) ;
new_lines . push ( line . to_string ( ) ) ;
match update_mode {
UpdateMode ::Check if contents ! = new_contents = > exit_with_failure ( ) ,
UpdateMode ::Check = > ( ) ,
UpdateMode ::Change = > {
if let Err ( e ) = fs ::write ( path , new_contents . as_bytes ( ) ) {
panic! ( " Cannot write to ` {} `: {} " , path . display ( ) , e ) ;
}
} else if start . is_match ( line ) {
if ! replace_start {
new_lines . push ( line . to_string ( ) ) ;
}
in_old_region = true ;
found = true ;
} else {
new_lines . push ( line . to_string ( ) ) ;
}
} ,
}
if ! found {
// This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
// given text or file. Most likely this is an error on the programmer's side and the Regex
// is incorrect.
eprintln! ( " error: regex \n {:?} \n doesn't match. You may have to update it. " , start ) ;
std ::process ::exit ( 1 ) ;
}
let mut new_lines = new_lines . join ( " \n " ) ;
if text . ends_with ( '\n' ) {
new_lines . push ( '\n' ) ;
}
let changed = new_lines ! = text ;
FileChange { changed , new_lines }
}
#[ test ]
fn test_parse_contents ( ) {
let result : Vec < Lint > = parse_contents (
r #"
declare_clippy_lint! {
#[ clippy::version = " Hello Clippy! " ]
pub PTR_ARG ,
style ,
" really long \
text "
}
/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
/// were found, or the missing delimiter if not.
fn replace_region_in_text < ' a > (
text : & str ,
start : & ' a str ,
end : & ' a str ,
mut write_replacement : impl FnMut ( & mut String ) ,
) -> Result < String , & ' a str > {
let ( text_start , rest ) = text . split_once ( start ) . ok_or ( start ) ? ;
let ( _ , text_end ) = rest . split_once ( end ) . ok_or ( end ) ? ;
declare_clippy_lint! {
#[ clippy::version = " Test version " ]
pub DOC_MARKDOWN ,
pedantic,
" single line "
}
let mut res = String ::with_capacity ( text . len ( ) + 4096 ) ;
res . push_str ( text_start ) ;
res . push_str ( start ) ;
write_replacement ( & mut res ) ;
res . push_str ( end ) ;
res . push_str ( text_end ) ;
/// some doc comment
declare_deprecated_lint! {
#[ clippy::version = " I'm a version " ]
pub SHOULD_ASSERT_EQ ,
" `assert!()` will be more flexible with RFC 2011 "
}
" #,
" module_name " ,
)
. collect ( ) ;
let expected = vec! [
Lint ::new ( " ptr_arg " , " style " , " really long text " , None , " module_name " ) ,
Lint ::new ( " doc_markdown " , " pedantic " , " single line " , None , " module_name " ) ,
Lint ::new (
" should_assert_eq " ,
" Deprecated " ,
" `assert!()` will be more flexible with RFC 2011 " ,
Some ( " `assert!()` will be more flexible with RFC 2011 " ) ,
" module_name " ,
) ,
] ;
assert_eq! ( expected , result ) ;
Ok ( res )
}
#[ cfg(test) ]
@ -541,55 +464,65 @@ mod tests {
use super ::* ;
#[ test ]
fn test_replace_region ( ) {
let text = " \n abc \n 123 \n 789 \n def \n ghi " ;
let expected = FileChange {
changed : true ,
new_lines : " \n abc \n hello world \n def \n ghi " . to_string ( ) ,
} ;
let result = replace_region_in_text ( text , r # "^\s*abc$"# , r # "^\s*def"# , false , | | {
vec! [ " hello world " . to_string ( ) ]
} ) ;
fn test_parse_contents ( ) {
static CONTENTS : & str = r #"
declare_clippy_lint! {
#[ clippy::version = " Hello Clippy! " ]
pub PTR_ARG ,
style ,
" really long \
text "
}
declare_clippy_lint! {
#[ clippy::version = " Test version " ]
pub DOC_MARKDOWN ,
pedantic ,
" single line "
}
" #;
let mut result = Vec ::new ( ) ;
parse_contents ( CONTENTS , " module_name " , & mut result ) ;
let expected = vec! [
Lint ::new ( " ptr_arg " , " style " , " \" really long text \" " , " module_name " ) ,
Lint ::new ( " doc_markdown " , " pedantic " , " \" single line \" " , " module_name " ) ,
] ;
assert_eq! ( expected , result ) ;
}
#[ test ]
fn test_replace_region_with_start ( ) {
let text = " \n abc \n 123 \n 789 \n def \n ghi " ;
let expected = FileChange {
changed : true ,
new_lines : " \n hello world \n def \n ghi " . to_string ( ) ,
} ;
let result = replace_region_in_text ( text , r # "^\s*abc$"# , r # "^\s*def"# , true , | | {
vec! [ " hello world " . to_string ( ) ]
} ) ;
assert_eq! ( expected , result ) ;
}
fn test_parse_deprecated_contents ( ) {
static DEPRECATED_CONTENTS : & str = r #"
/// some doc comment
declare_deprecated_lint! {
#[ clippy::version = " I'm a version " ]
pub SHOULD_ASSERT_EQ ,
" `assert!()` will be more flexible with RFC 2011 "
}
" #;
#[ test ]
fn test_replace_region_no_changes ( ) {
let text = " 123 \n 456 \n 789 " ;
let expected = FileChange {
changed : false ,
new_lines : " 123 \n 456 \n 789 " . to_string ( ) ,
} ;
let result = replace_region_in_text ( text , r # "^\s*123$"# , r # "^\s*456"# , false , Vec ::new ) ;
let mut result = Vec ::new ( ) ;
parse_deprecated_contents ( DEPRECATED_CONTENTS , & mut result ) ;
let expected = vec! [ DeprecatedLint ::new (
" should_assert_eq " ,
" \" `assert!()` will be more flexible with RFC 2011 \" " ,
) ] ;
assert_eq! ( expected , result ) ;
}
#[ test ]
fn test_usable_lints ( ) {
let lints = vec! [
Lint ::new ( " should_assert_eq " , " Deprecated " , " abc " , Some ( " Reason " ) , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " Not Deprecated " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " internal " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " internal_style " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " Not Deprecated " , " \" abc \" " , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " internal " , " \" abc \" " , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " internal_style " , " \" abc \" " , " module_name " ) ,
] ;
let expected = vec! [ Lint ::new (
" should_assert_eq2 " ,
" Not Deprecated " ,
" abc " ,
None ,
" \" abc \" " ,
" module_name " ,
) ] ;
assert_eq! ( expected , Lint ::usable_lints ( & lints ) ) ;
@ -598,55 +531,30 @@ fn test_usable_lints() {
#[ test ]
fn test_by_lint_group ( ) {
let lints = vec! [
Lint ::new ( " should_assert_eq " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " group2 " , " abc " , None , " module_name " ) ,
Lint ::new ( " incorrect_match " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq " , " group1 " , " \" abc \" " , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " group2 " , " \" abc \" " , " module_name " ) ,
Lint ::new ( " incorrect_match " , " group1 " , " \" abc \" " , " module_name " ) ,
] ;
let mut expected : HashMap < String , Vec < Lint > > = HashMap ::new ( ) ;
expected . insert (
" group1 " . to_string ( ) ,
vec! [
Lint ::new ( " should_assert_eq " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " incorrect_match " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq " , " group1 " , " \" abc \" " , " module_name " ) ,
Lint ::new ( " incorrect_match " , " group1 " , " \" abc \" " , " module_name " ) ,
] ,
) ;
expected . insert (
" group2 " . to_string ( ) ,
vec! [ Lint ::new ( " should_assert_eq2 " , " group2 " , " abc " , None , " module_name " ) ] ,
vec! [ Lint ::new ( " should_assert_eq2 " , " group2 " , " \" abc \" " , " module_name " ) ] ,
) ;
assert_eq! ( expected , Lint ::by_lint_group ( lints . into_iter ( ) ) ) ;
}
#[ test ]
fn test_gen_changelog_lint_list ( ) {
let lints = vec! [
Lint ::new ( " should_assert_eq " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq2 " , " group2 " , " abc " , None , " module_name " ) ,
] ;
let expected = vec! [
format! ( " [`should_assert_eq`]: {} #should_assert_eq " , DOCS_LINK ) ,
format! ( " [`should_assert_eq2`]: {} #should_assert_eq2 " , DOCS_LINK ) ,
] ;
assert_eq! ( expected , gen_changelog_lint_list ( lints . iter ( ) ) ) ;
}
#[ test ]
fn test_gen_deprecated ( ) {
let lints = vec! [
Lint ::new (
" should_assert_eq " ,
" group1 " ,
" abc " ,
Some ( " has been superseded by should_assert_eq2 " ) ,
" module_name " ,
) ,
Lint ::new (
" another_deprecated " ,
" group2 " ,
" abc " ,
Some ( " will be removed " ) ,
" module_name " ,
) ,
DeprecatedLint ::new ( " should_assert_eq " , " \" has been superseded by should_assert_eq2 \" " ) ,
DeprecatedLint ::new ( " another_deprecated " , " \" will be removed \" " ) ,
] ;
let expected = GENERATED_FILE_COMMENT . to_string ( )
@ -665,32 +573,15 @@ fn test_gen_deprecated() {
. join ( " \n " )
+ " \n " ;
assert_eq! ( expected , gen_deprecated ( lints . iter ( ) ) ) ;
}
#[ test ]
#[ should_panic ]
fn test_gen_deprecated_fail ( ) {
let lints = vec! [ Lint ::new ( " should_assert_eq2 " , " group2 " , " abc " , None , " module_name " ) ] ;
let _deprecated_lints = gen_deprecated ( lints . iter ( ) ) ;
}
#[ test ]
fn test_gen_modules_list ( ) {
let lints = vec! [
Lint ::new ( " should_assert_eq " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " incorrect_stuff " , " group3 " , " abc " , None , " another_module " ) ,
] ;
let expected = vec! [ " mod another_module; " . to_string ( ) , " mod module_name; " . to_string ( ) ] ;
assert_eq! ( expected , gen_modules_list ( lints . iter ( ) ) ) ;
assert_eq! ( expected , gen_deprecated ( & lints ) ) ;
}
#[ test ]
fn test_gen_lint_group_list ( ) {
let lints = vec! [
Lint ::new ( " abc " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " should_assert_eq " , " group1 " , " abc " , None , " module_name " ) ,
Lint ::new ( " internal " , " internal_style " , " abc " , None , " module_name " ) ,
Lint ::new ( " abc " , " group1 " , " \" abc \" " , " module_name " ) ,
Lint ::new ( " should_assert_eq " , " group1 " , " \" abc \" " , " module_name " ) ,
Lint ::new ( " internal " , " internal_style " , " \" abc \" " , " module_name " ) ,
] ;
let expected = GENERATED_FILE_COMMENT . to_string ( )
+ & [