@@ -4,8 +4,9 @@ use crate::utils::{
44use itertools:: Itertools ;
55use std:: collections:: HashSet ;
66use std:: fmt:: Write ;
7+ use std:: fs;
78use std:: ops:: Range ;
8- use std:: path:: { Path , PathBuf } ;
9+ use std:: path:: { self , Path , PathBuf } ;
910use walkdir:: { DirEntry , WalkDir } ;
1011
1112const GENERATED_FILE_COMMENT : & str = "// This file was generated by `cargo dev update_lints`.\n \
@@ -36,123 +37,164 @@ pub fn generate_lint_files(
3637 deprecated : & [ DeprecatedLint ] ,
3738 renamed : & [ RenamedLint ] ,
3839) {
39- FileUpdater :: default ( ) . update_files_checked (
40+ let mut updater = FileUpdater :: default ( ) ;
41+ updater. update_file_checked (
4042 "cargo dev update_lints" ,
4143 update_mode,
42- & mut [
43- (
44- "README.md" ,
45- & mut update_text_region_fn ( "[There are over " , " lints included in this crate!]" , |dst| {
46- write ! ( dst, "{}" , round_to_fifty( lints. len( ) ) ) . unwrap ( ) ;
47- } ) ,
48- ) ,
49- (
50- "book/src/README.md" ,
51- & mut update_text_region_fn ( "[There are over " , " lints included in this crate!]" , |dst| {
52- write ! ( dst, "{}" , round_to_fifty( lints. len( ) ) ) . unwrap ( ) ;
53- } ) ,
54- ) ,
55- (
56- "CHANGELOG.md" ,
57- & mut update_text_region_fn (
58- "<!-- begin autogenerated links to lint list -->\n " ,
59- "<!-- end autogenerated links to lint list -->" ,
60- |dst| {
61- for lint in lints
62- . iter ( )
63- . map ( |l| & * l. name )
64- . chain ( deprecated. iter ( ) . filter_map ( |l| l. name . strip_prefix ( "clippy::" ) ) )
65- . chain ( renamed. iter ( ) . filter_map ( |l| l. old_name . strip_prefix ( "clippy::" ) ) )
66- . sorted ( )
67- {
68- writeln ! ( dst, "[`{lint}`]: {DOCS_LINK}#{lint}" ) . unwrap ( ) ;
69- }
70- } ,
71- ) ,
72- ) ,
73- (
74- "clippy_lints/src/lib.rs" ,
75- & mut update_text_region_fn (
76- "// begin lints modules, do not remove this comment, it's used in `update_lints`\n " ,
77- "// end lints modules, do not remove this comment, it's used in `update_lints`" ,
78- |dst| {
79- for lint_mod in lints. iter ( ) . map ( |l| & l. module ) . sorted ( ) . dedup ( ) {
80- writeln ! ( dst, "mod {lint_mod};" ) . unwrap ( ) ;
81- }
82- } ,
83- ) ,
84- ) ,
85- ( "clippy_lints/src/declared_lints.rs" , & mut |_, src, dst| {
86- dst. push_str ( GENERATED_FILE_COMMENT ) ;
87- dst. push_str ( "pub static LINTS: &[&crate::LintInfo] = &[\n " ) ;
88- for ( module_name, lint_name) in lints. iter ( ) . map ( |l| ( & l. module , l. name . to_uppercase ( ) ) ) . sorted ( ) {
89- writeln ! ( dst, " crate::{module_name}::{lint_name}_INFO," ) . unwrap ( ) ;
90- }
91- dst. push_str ( "];\n " ) ;
92- UpdateStatus :: from_changed ( src != dst)
93- } ) ,
94- ( "clippy_lints/src/deprecated_lints.rs" , & mut |_, src, dst| {
95- let mut searcher = RustSearcher :: new ( src) ;
96- assert ! (
97- searcher. find_token( Token :: Ident ( "declare_with_version" ) )
98- && searcher. find_token( Token :: Ident ( "declare_with_version" ) ) ,
99- "error reading deprecated lints"
100- ) ;
101- dst. push_str ( & src[ ..searcher. pos ( ) as usize ] ) ;
102- dst. push_str ( "! { DEPRECATED(DEPRECATED_VERSION) = [\n " ) ;
103- for lint in deprecated {
104- write ! (
105- dst,
106- " #[clippy::version = \" {}\" ]\n (\" {}\" , \" {}\" ),\n " ,
107- lint. version, lint. name, lint. reason,
108- )
109- . unwrap ( ) ;
44+ "README.md" ,
45+ & mut update_text_region_fn ( "[There are over " , " lints included in this crate!]" , |dst| {
46+ write ! ( dst, "{}" , round_to_fifty( lints. len( ) ) ) . unwrap ( ) ;
47+ } ) ,
48+ ) ;
49+ updater. update_file_checked (
50+ "cargo dev update_lints" ,
51+ update_mode,
52+ "book/src/README.md" ,
53+ & mut update_text_region_fn ( "[There are over " , " lints included in this crate!]" , |dst| {
54+ write ! ( dst, "{}" , round_to_fifty( lints. len( ) ) ) . unwrap ( ) ;
55+ } ) ,
56+ ) ;
57+ updater. update_file_checked (
58+ "cargo dev update_lints" ,
59+ update_mode,
60+ "CHANGELOG.md" ,
61+ & mut update_text_region_fn (
62+ "<!-- begin autogenerated links to lint list -->\n " ,
63+ "<!-- end autogenerated links to lint list -->" ,
64+ |dst| {
65+ for lint in lints
66+ . iter ( )
67+ . map ( |l| & * l. name )
68+ . chain ( deprecated. iter ( ) . filter_map ( |l| l. name . strip_prefix ( "clippy::" ) ) )
69+ . chain ( renamed. iter ( ) . filter_map ( |l| l. old_name . strip_prefix ( "clippy::" ) ) )
70+ . sorted ( )
71+ {
72+ writeln ! ( dst, "[`{lint}`]: {DOCS_LINK}#{lint}" ) . unwrap ( ) ;
11073 }
111- dst. push_str (
112- "]}\n \n \
74+ } ,
75+ ) ,
76+ ) ;
77+ updater. update_file_checked (
78+ "cargo dev update_lints" ,
79+ update_mode,
80+ "clippy_lints/src/deprecated_lints.rs" ,
81+ & mut |_, src, dst| {
82+ let mut searcher = RustSearcher :: new ( src) ;
83+ assert ! (
84+ searcher. find_token( Token :: Ident ( "declare_with_version" ) )
85+ && searcher. find_token( Token :: Ident ( "declare_with_version" ) ) ,
86+ "error reading deprecated lints"
87+ ) ;
88+ dst. push_str ( & src[ ..searcher. pos ( ) as usize ] ) ;
89+ dst. push_str ( "! { DEPRECATED(DEPRECATED_VERSION) = [\n " ) ;
90+ for lint in deprecated {
91+ write ! (
92+ dst,
93+ " #[clippy::version = \" {}\" ]\n (\" {}\" , \" {}\" ),\n " ,
94+ lint. version, lint. name, lint. reason,
95+ )
96+ . unwrap ( ) ;
97+ }
98+ dst. push_str (
99+ "]}\n \n \
113100 #[rustfmt::skip]\n \
114101 declare_with_version! { RENAMED(RENAMED_VERSION) = [\n \
115102 ",
116- ) ;
117- for lint in renamed {
118- write ! (
119- dst,
120- " #[clippy::version = \" {}\" ]\n (\" {}\" , \" {}\" ),\n " ,
121- lint. version, lint. old_name, lint. new_name,
122- )
123- . unwrap ( ) ;
103+ ) ;
104+ for lint in renamed {
105+ write ! (
106+ dst,
107+ " #[clippy::version = \" {}\" ]\n (\" {}\" , \" {}\" ),\n " ,
108+ lint. version, lint. old_name, lint. new_name,
109+ )
110+ . unwrap ( ) ;
111+ }
112+ dst. push_str ( "]}\n " ) ;
113+ UpdateStatus :: from_changed ( src != dst)
114+ } ,
115+ ) ;
116+ updater. update_file_checked (
117+ "cargo dev update_lints" ,
118+ update_mode,
119+ "tests/ui/deprecated.rs" ,
120+ & mut |_, src, dst| {
121+ dst. push_str ( GENERATED_FILE_COMMENT ) ;
122+ for lint in deprecated {
123+ writeln ! ( dst, "#![warn({})] //~ ERROR: lint `{}`" , lint. name, lint. name) . unwrap ( ) ;
124+ }
125+ dst. push_str ( "\n fn main() {}\n " ) ;
126+ UpdateStatus :: from_changed ( src != dst)
127+ } ,
128+ ) ;
129+ updater. update_file_checked (
130+ "cargo dev update_lints" ,
131+ update_mode,
132+ "tests/ui/rename.rs" ,
133+ & mut move |_, src, dst| {
134+ let mut seen_lints = HashSet :: new ( ) ;
135+ dst. push_str ( GENERATED_FILE_COMMENT ) ;
136+ dst. push_str ( "#![allow(clippy::duplicated_attributes)]\n " ) ;
137+ for lint in renamed {
138+ if seen_lints. insert ( & lint. new_name ) {
139+ writeln ! ( dst, "#![allow({})]" , lint. new_name) . unwrap ( ) ;
124140 }
125- dst. push_str ( "]}\n " ) ;
126- UpdateStatus :: from_changed ( src != dst)
127- } ) ,
128- ( "tests/ui/deprecated.rs" , & mut |_, src, dst| {
129- dst. push_str ( GENERATED_FILE_COMMENT ) ;
130- for lint in deprecated {
131- writeln ! ( dst, "#![warn({})] //~ ERROR: lint `{}`" , lint. name, lint. name) . unwrap ( ) ;
141+ }
142+ seen_lints. clear ( ) ;
143+ for lint in renamed {
144+ if seen_lints. insert ( & lint. old_name ) {
145+ writeln ! ( dst, "#![warn({})] //~ ERROR: lint `{}`" , lint. old_name, lint. old_name) . unwrap ( ) ;
132146 }
133- dst. push_str ( "\n fn main() {}\n " ) ;
134- UpdateStatus :: from_changed ( src != dst)
135- } ) ,
136- ( "tests/ui/rename.rs" , & mut move |_, src, dst| {
137- let mut seen_lints = HashSet :: new ( ) ;
138- dst. push_str ( GENERATED_FILE_COMMENT ) ;
139- dst. push_str ( "#![allow(clippy::duplicated_attributes)]\n " ) ;
140- for lint in renamed {
141- if seen_lints. insert ( & lint. new_name ) {
142- writeln ! ( dst, "#![allow({})]" , lint. new_name) . unwrap ( ) ;
147+ }
148+ dst. push_str ( "\n fn main() {}\n " ) ;
149+ UpdateStatus :: from_changed ( src != dst)
150+ } ,
151+ ) ;
152+ for ( crate_name, lints) in lints. iter ( ) . into_group_map_by ( |& l| {
153+ let Some ( path:: Component :: Normal ( name) ) = l. path . components ( ) . next ( ) else {
154+ // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
155+ panic ! ( "internal error: can't read crate name from path `{}`" , l. path. display( ) ) ;
156+ } ;
157+ name
158+ } ) {
159+ updater. update_file_checked (
160+ "cargo dev update_lints" ,
161+ update_mode,
162+ Path :: new ( crate_name) . join ( "src/lib.rs" ) ,
163+ & mut update_text_region_fn (
164+ "// begin lints modules, do not remove this comment, it's used in `update_lints`\n " ,
165+ "// end lints modules, do not remove this comment, it's used in `update_lints`" ,
166+ |dst| {
167+ for lint_mod in lints
168+ . iter ( )
169+ . filter ( |l| !l. module . is_empty ( ) )
170+ . map ( |l| l. module . split_once ( "::" ) . map_or ( & * l. module , |x| x. 0 ) )
171+ . sorted ( )
172+ . dedup ( )
173+ {
174+ writeln ! ( dst, "mod {lint_mod};" ) . unwrap ( ) ;
143175 }
144- }
145- seen_lints. clear ( ) ;
146- for lint in renamed {
147- if seen_lints. insert ( & lint. old_name ) {
148- writeln ! ( dst, "#![warn({})] //~ ERROR: lint `{}`" , lint. old_name, lint. old_name) . unwrap ( ) ;
176+ } ,
177+ ) ,
178+ ) ;
179+ updater. update_file_checked (
180+ "cargo dev update_lints" ,
181+ update_mode,
182+ Path :: new ( crate_name) . join ( "src/declared_lints.rs" ) ,
183+ & mut |_, src, dst| {
184+ dst. push_str ( GENERATED_FILE_COMMENT ) ;
185+ dst. push_str ( "pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n " ) ;
186+ for ( module_path, lint_name) in lints. iter ( ) . map ( |l| ( & l. module , l. name . to_uppercase ( ) ) ) . sorted ( ) {
187+ if module_path. is_empty ( ) {
188+ writeln ! ( dst, " crate::{lint_name}_INFO," ) . unwrap ( ) ;
189+ } else {
190+ writeln ! ( dst, " crate::{module_path}::{lint_name}_INFO," ) . unwrap ( ) ;
149191 }
150192 }
151- dst. push_str ( "\n fn main() {} \n " ) ;
193+ dst. push_str ( "]; \n " ) ;
152194 UpdateStatus :: from_changed ( src != dst)
153- } ) ,
154- ] ,
155- ) ;
195+ } ,
196+ ) ;
197+ }
156198}
157199
158200fn round_to_fifty ( count : usize ) -> usize {
@@ -186,13 +228,25 @@ pub struct RenamedLint {
186228pub fn find_lint_decls ( ) -> Vec < Lint > {
187229 let mut lints = Vec :: with_capacity ( 1000 ) ;
188230 let mut contents = String :: new ( ) ;
189- for ( file, module) in read_src_with_module ( "clippy_lints/src" . as_ref ( ) ) {
190- parse_clippy_lint_decls (
191- file. path ( ) ,
192- File :: open_read_to_cleared_string ( file. path ( ) , & mut contents) ,
193- & module,
194- & mut lints,
195- ) ;
231+ for e in expect_action ( fs:: read_dir ( "." ) , ErrAction :: Read , "." ) {
232+ let e = expect_action ( e, ErrAction :: Read , "." ) ;
233+ if !expect_action ( e. file_type ( ) , ErrAction :: Read , "." ) . is_dir ( ) {
234+ continue ;
235+ }
236+ let Ok ( mut name) = e. file_name ( ) . into_string ( ) else {
237+ continue ;
238+ } ;
239+ if name. starts_with ( "clippy_lints" ) && name != "clippy_lints_internal" {
240+ name. push_str ( "/src" ) ;
241+ for ( file, module) in read_src_with_module ( name. as_ref ( ) ) {
242+ parse_clippy_lint_decls (
243+ file. path ( ) ,
244+ File :: open_read_to_cleared_string ( file. path ( ) , & mut contents) ,
245+ & module,
246+ & mut lints,
247+ ) ;
248+ }
249+ }
196250 }
197251 lints. sort_by ( |lhs, rhs| lhs. name . cmp ( & rhs. name ) ) ;
198252 lints
@@ -204,7 +258,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirE
204258 let e = expect_action ( e, ErrAction :: Read , src_root) ;
205259 let path = e. path ( ) . as_os_str ( ) . as_encoded_bytes ( ) ;
206260 if let Some ( path) = path. strip_suffix ( b".rs" )
207- && let Some ( path) = path. get ( "clippy_lints/src/" . len ( ) ..)
261+ && let Some ( path) = path. get ( src_root . as_os_str ( ) . len ( ) + 1 ..)
208262 {
209263 if path == b"lib" {
210264 Some ( ( e, String :: new ( ) ) )
0 commit comments