1- use crate :: utils:: { FileUpdater , UpdateMode , UpdateStatus , update_text_region_fn} ;
1+ use crate :: utils:: { File , FileAction , FileUpdater , UpdateMode , UpdateStatus , panic_file, update_text_region_fn} ;
2+ use core:: str;
23use itertools:: Itertools ;
34use rustc_lexer:: { LiteralKind , TokenKind , tokenize} ;
45use rustc_literal_escaper:: { Mode , unescape_unicode} ;
56use std:: collections:: { HashMap , HashSet } ;
6- use std:: ffi:: OsStr ;
77use std:: fmt:: Write ;
8- use std:: fs;
8+ use std:: fs:: OpenOptions ;
99use std:: ops:: Range ;
1010use std:: path:: Path ;
1111use walkdir:: { DirEntry , WalkDir } ;
@@ -26,8 +26,11 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht
2626///
2727/// Panics if a file path could not read from or then written to
2828pub fn update ( update_mode : UpdateMode ) {
29- let ( lints, deprecated_lints, renamed_lints) = gather_all ( ) ;
30- generate_lint_files ( update_mode, & lints, & deprecated_lints, & renamed_lints) ;
29+ let lints = find_lint_decls ( ) ;
30+ let DeprecatedLints {
31+ renamed, deprecated, ..
32+ } = read_deprecated_lints ( ) ;
33+ generate_lint_files ( update_mode, & lints, & deprecated, & renamed) ;
3134}
3235
3336pub fn generate_lint_files (
@@ -36,8 +39,6 @@ pub fn generate_lint_files(
3639 deprecated : & [ DeprecatedLint ] ,
3740 renamed : & [ RenamedLint ] ,
3841) {
39- let mut lints = lints. to_owned ( ) ;
40- lints. sort_by ( |lhs, rhs| lhs. name . cmp ( & rhs. name ) ) ;
4142 FileUpdater :: default ( ) . update_files_checked (
4243 "cargo dev update_lints" ,
4344 update_mode,
@@ -107,7 +108,7 @@ pub fn generate_lint_files(
107108}
108109
109110pub fn print_lints ( ) {
110- let ( lints, _ , _ ) = gather_all ( ) ;
111+ let lints = find_lint_decls ( ) ;
111112 let lint_count = lints. len ( ) ;
112113 let grouped_by_lint_group = Lint :: by_lint_group ( lints. into_iter ( ) ) ;
113114
@@ -205,40 +206,54 @@ pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str,
205206 }
206207}
207208
208- /// Gathers all lints defined in `clippy_lints/src`
209+ /// Finds all lint declarations (`declare_clippy_lint!`)
209210#[ must_use]
210- pub fn gather_all ( ) -> ( Vec < Lint > , Vec < DeprecatedLint > , Vec < RenamedLint > ) {
211+ pub fn find_lint_decls ( ) -> Vec < Lint > {
211212 let mut lints = Vec :: with_capacity ( 1000 ) ;
212- let mut deprecated_lints = Vec :: with_capacity ( 50 ) ;
213- let mut renamed_lints = Vec :: with_capacity ( 50 ) ;
214-
215- for file in clippy_lints_src_files ( ) {
216- let path = file. path ( ) ;
217- let contents =
218- fs:: read_to_string ( path) . unwrap_or_else ( |e| panic ! ( "Cannot read from `{}`: {e}" , path. display( ) ) ) ;
219- let module = path. as_os_str ( ) . to_str ( ) . unwrap ( ) [ "clippy_lints/src/" . len ( ) ..] . replace ( [ '/' , '\\' ] , "::" ) ;
220-
221- // If the lints are stored in mod.rs, we get the module name from
222- // the containing directory:
223- let module = if let Some ( module) = module. strip_suffix ( "::mod.rs" ) {
224- module
225- } else {
226- module. strip_suffix ( ".rs" ) . unwrap_or ( & module)
227- } ;
228-
229- if module == "deprecated_lints" {
230- parse_deprecated_contents ( & contents, & mut deprecated_lints, & mut renamed_lints) ;
231- } else {
232- parse_contents ( & contents, module, & mut lints) ;
233- }
213+ let mut contents = String :: new ( ) ;
214+ for ( file, module) in read_src_with_module ( "clippy_lints/src" . as_ref ( ) ) {
215+ parse_clippy_lint_decls (
216+ File :: open_read_to_cleared_string ( file. path ( ) , & mut contents) ,
217+ & module,
218+ & mut lints,
219+ ) ;
234220 }
235- ( lints, deprecated_lints, renamed_lints)
221+ lints. sort_by ( |lhs, rhs| lhs. name . cmp ( & rhs. name ) ) ;
222+ lints
236223}
237224
238- pub fn clippy_lints_src_files ( ) -> impl Iterator < Item = DirEntry > {
239- let iter = WalkDir :: new ( "clippy_lints/src" ) . into_iter ( ) ;
240- iter. map ( Result :: unwrap)
241- . filter ( |f| f. path ( ) . extension ( ) == Some ( OsStr :: new ( "rs" ) ) )
225+ /// Reads the source files from the given root directory
226+ fn read_src_with_module ( src_root : & Path ) -> impl use < ' _ > + Iterator < Item = ( DirEntry , String ) > {
227+ WalkDir :: new ( src_root) . into_iter ( ) . filter_map ( move |e| {
228+ let e = match e {
229+ Ok ( e) => e,
230+ Err ( ref e) => panic_file ( e, FileAction :: Read , src_root) ,
231+ } ;
232+ let path = e. path ( ) . as_os_str ( ) . as_encoded_bytes ( ) ;
233+ if let Some ( path) = path. strip_suffix ( b".rs" )
234+ && let Some ( path) = path. get ( "clippy_lints/src/" . len ( ) ..)
235+ {
236+ if path == b"lib" {
237+ Some ( ( e, String :: new ( ) ) )
238+ } else {
239+ let path = if let Some ( path) = path. strip_suffix ( b"mod" )
240+ && let Some ( path) = path. strip_suffix ( b"/" ) . or_else ( || path. strip_suffix ( b"\\ " ) )
241+ {
242+ path
243+ } else {
244+ path
245+ } ;
246+ if let Ok ( path) = str:: from_utf8 ( path) {
247+ let path = path. replace ( [ '/' , '\\' ] , "::" ) ;
248+ Some ( ( e, path) )
249+ } else {
250+ None
251+ }
252+ }
253+ } else {
254+ None
255+ }
256+ } )
242257}
243258
244259macro_rules! match_tokens {
@@ -266,7 +281,7 @@ pub(crate) struct LintDeclSearchResult<'a> {
266281}
267282
268283/// Parse a source file looking for `declare_clippy_lint` macro invocations.
269- fn parse_contents ( contents : & str , module : & str , lints : & mut Vec < Lint > ) {
284+ fn parse_clippy_lint_decls ( contents : & str , module : & str , lints : & mut Vec < Lint > ) {
270285 let mut offset = 0usize ;
271286 let mut iter = tokenize ( contents) . map ( |t| {
272287 let range = offset..offset + t. len as usize ;
@@ -333,15 +348,40 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
333348 }
334349}
335350
336- /// Parse a source file looking for `declare_deprecated_lint` macro invocations.
337- fn parse_deprecated_contents ( contents : & str , deprecated : & mut Vec < DeprecatedLint > , renamed : & mut Vec < RenamedLint > ) {
338- let Some ( ( _, contents) ) = contents. split_once ( "\n declare_with_version! { DEPRECATED" ) else {
339- return ;
340- } ;
341- let Some ( ( deprecated_src, renamed_src) ) = contents. split_once ( "\n declare_with_version! { RENAMED" ) else {
342- return ;
351+ pub struct DeprecatedLints {
352+ pub file : File < ' static > ,
353+ pub contents : String ,
354+ pub deprecated : Vec < DeprecatedLint > ,
355+ pub renamed : Vec < RenamedLint > ,
356+ pub deprecated_end : u32 ,
357+ pub renamed_end : u32 ,
358+ }
359+
360+ #[ must_use]
361+ #[ expect( clippy:: cast_possible_truncation) ]
362+ pub fn read_deprecated_lints ( ) -> DeprecatedLints {
363+ let mut res = DeprecatedLints {
364+ file : File :: open (
365+ "clippy_lints/src/deprecated_lints.rs" ,
366+ OpenOptions :: new ( ) . read ( true ) . write ( true ) ,
367+ ) ,
368+ contents : String :: new ( ) ,
369+ deprecated : Vec :: with_capacity ( 30 ) ,
370+ renamed : Vec :: with_capacity ( 80 ) ,
371+ deprecated_end : 0 ,
372+ renamed_end : 0 ,
343373 } ;
344374
375+ res. file . read_append_to_string ( & mut res. contents ) ;
376+
377+ let ( _, contents) = res. contents . split_once ( "\n declare_with_version! { DEPRECATED" ) . unwrap ( ) ;
378+ let ( deprecated_src, contents) = contents. split_once ( "\n ]}" ) . unwrap ( ) ;
379+ res. deprecated_end = ( res. contents . len ( ) - contents. len ( ) - 2 ) as u32 ;
380+
381+ let ( _, contents) = contents. split_once ( "\n declare_with_version! { RENAMED" ) . unwrap ( ) ;
382+ let ( renamed_src, contents) = contents. split_once ( "\n ]}" ) . unwrap ( ) ;
383+ res. renamed_end = ( res. contents . len ( ) - contents. len ( ) - 2 ) as u32 ;
384+
345385 for line in deprecated_src. lines ( ) {
346386 let mut offset = 0usize ;
347387 let mut iter = tokenize ( line) . map ( |t| {
@@ -362,7 +402,7 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint
362402 // "new_name"),
363403 Whitespace Literal { kind: LiteralKind :: Str { ..} , ..} ( reason) CloseParen Comma
364404 ) ;
365- deprecated. push ( DeprecatedLint :: new ( name, reason) ) ;
405+ res . deprecated . push ( DeprecatedLint :: new ( name, reason) ) ;
366406 }
367407 for line in renamed_src. lines ( ) {
368408 let mut offset = 0usize ;
@@ -384,8 +424,10 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint
384424 // "new_name"),
385425 Whitespace Literal { kind: LiteralKind :: Str { ..} , ..} ( new_name) CloseParen Comma
386426 ) ;
387- renamed. push ( RenamedLint :: new ( old_name, new_name) ) ;
427+ res . renamed . push ( RenamedLint :: new ( old_name, new_name) ) ;
388428 }
429+
430+ res
389431}
390432
391433/// Removes the line splices and surrounding quotes from a string literal
@@ -411,7 +453,7 @@ mod tests {
411453 use super :: * ;
412454
413455 #[ test]
414- fn test_parse_contents ( ) {
456+ fn test_parse_clippy_lint_decls ( ) {
415457 static CONTENTS : & str = r#"
416458 declare_clippy_lint! {
417459 #[clippy::version = "Hello Clippy!"]
@@ -429,7 +471,7 @@ mod tests {
429471 }
430472 "# ;
431473 let mut result = Vec :: new ( ) ;
432- parse_contents ( CONTENTS , "module_name" , & mut result) ;
474+ parse_clippy_lint_decls ( CONTENTS , "module_name" , & mut result) ;
433475 for r in & mut result {
434476 r. declaration_range = Range :: default ( ) ;
435477 }
0 commit comments