@@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder;
33use indoc:: writedoc;
44use itertools:: Itertools ;
55use rustc_lexer:: { tokenize, unescape, LiteralKind , TokenKind } ;
6- use std:: collections:: { HashMap , HashSet } ;
6+ use std:: collections:: { BTreeSet , HashMap , HashSet } ;
77use std:: ffi:: OsStr ;
88use std:: fmt:: Write ;
99use std:: fs:: { self , OpenOptions } ;
@@ -124,6 +124,8 @@ fn generate_lint_files(
124124 let content = gen_lint_group_list ( "all" , all_group_lints) ;
125125 process_file ( "clippy_lints/src/lib.register_all.rs" , update_mode, & content) ;
126126
127+ update_docs ( update_mode, & usable_lints) ;
128+
127129 for ( lint_group, lints) in Lint :: by_lint_group ( usable_lints. into_iter ( ) . chain ( internal_lints) ) {
128130 let content = gen_lint_group_list ( & lint_group, lints. iter ( ) ) ;
129131 process_file (
@@ -140,6 +142,62 @@ fn generate_lint_files(
140142 process_file ( "tests/ui/rename.rs" , update_mode, & content) ;
141143}
142144
145+ fn update_docs ( update_mode : UpdateMode , usable_lints : & [ Lint ] ) {
146+ replace_region_in_file ( update_mode, Path :: new ( "src/docs.rs" ) , "docs! {\n " , "\n }\n " , |res| {
147+ for name in usable_lints. iter ( ) . map ( |lint| lint. name . clone ( ) ) . sorted ( ) {
148+ writeln ! ( res, r#" "{name}","# ) . unwrap ( ) ;
149+ }
150+ } ) ;
151+
152+ if update_mode == UpdateMode :: Check {
153+ let mut extra = BTreeSet :: new ( ) ;
154+ let mut lint_names = usable_lints
155+ . iter ( )
156+ . map ( |lint| lint. name . clone ( ) )
157+ . collect :: < BTreeSet < _ > > ( ) ;
158+ for file in std:: fs:: read_dir ( "src/docs" ) . unwrap ( ) {
159+ let filename = file. unwrap ( ) . file_name ( ) . into_string ( ) . unwrap ( ) ;
160+ if let Some ( name) = filename. strip_suffix ( ".txt" ) {
161+ if !lint_names. remove ( name) {
162+ extra. insert ( name. to_string ( ) ) ;
163+ }
164+ }
165+ }
166+
167+ let failed = print_lint_names ( "extra lint docs:" , & extra) | print_lint_names ( "missing lint docs:" , & lint_names) ;
168+
169+ if failed {
170+ exit_with_failure ( ) ;
171+ }
172+ } else {
173+ if std:: fs:: remove_dir_all ( "src/docs" ) . is_err ( ) {
174+ eprintln ! ( "could not remove src/docs directory" ) ;
175+ }
176+ if std:: fs:: create_dir ( "src/docs" ) . is_err ( ) {
177+ eprintln ! ( "could not recreate src/docs directory" ) ;
178+ }
179+ }
180+ for lint in usable_lints {
181+ process_file (
182+ Path :: new ( "src/docs" ) . join ( lint. name . clone ( ) + ".txt" ) ,
183+ update_mode,
184+ & lint. documentation ,
185+ ) ;
186+ }
187+ }
188+
189+ fn print_lint_names ( header : & str , lints : & BTreeSet < String > ) -> bool {
190+ if lints. is_empty ( ) {
191+ return false ;
192+ }
193+ println ! ( "{}" , header) ;
194+ for lint in lints. iter ( ) . sorted ( ) {
195+ println ! ( " {}" , lint) ;
196+ }
197+ println ! ( ) ;
198+ true
199+ }
200+
143201pub fn print_lints ( ) {
144202 let ( lint_list, _, _) = gather_all ( ) ;
145203 let usable_lints = Lint :: usable_lints ( & lint_list) ;
@@ -589,17 +647,26 @@ struct Lint {
589647 desc : String ,
590648 module : String ,
591649 declaration_range : Range < usize > ,
650+ documentation : String ,
592651}
593652
594653impl Lint {
595654 #[ must_use]
596- fn new ( name : & str , group : & str , desc : & str , module : & str , declaration_range : Range < usize > ) -> Self {
655+ fn new (
656+ name : & str ,
657+ group : & str ,
658+ desc : & str ,
659+ module : & str ,
660+ declaration_range : Range < usize > ,
661+ documentation : String ,
662+ ) -> Self {
597663 Self {
598664 name : name. to_lowercase ( ) ,
599665 group : group. into ( ) ,
600666 desc : remove_line_splices ( desc) ,
601667 module : module. into ( ) ,
602668 declaration_range,
669+ documentation,
603670 }
604671 }
605672
@@ -852,27 +919,35 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
852919 } | token_kind == & TokenKind :: Ident && * content == "declare_clippy_lint" ,
853920 ) {
854921 let start = range. start ;
855-
856- let mut iter = iter
857- . by_ref ( )
858- . filter ( |t| !matches ! ( t. token_kind, TokenKind :: Whitespace | TokenKind :: LineComment { .. } ) ) ;
922+ let mut docs = String :: with_capacity ( 128 ) ;
923+ let mut iter = iter. by_ref ( ) . filter ( |t| !matches ! ( t. token_kind, TokenKind :: Whitespace ) ) ;
859924 // matches `!{`
860925 match_tokens ! ( iter, Bang OpenBrace ) ;
861- match iter. next ( ) {
862- // #[clippy::version = "version"] pub
863- Some ( LintDeclSearchResult {
864- token_kind : TokenKind :: Pound ,
865- ..
866- } ) => {
867- match_tokens ! ( iter, OpenBracket Ident Colon Colon Ident Eq Literal { ..} CloseBracket Ident ) ;
868- } ,
869- // pub
870- Some ( LintDeclSearchResult {
871- token_kind : TokenKind :: Ident ,
872- ..
873- } ) => ( ) ,
874- _ => continue ,
926+ let mut in_code = false ;
927+ while let Some ( t) = iter. next ( ) {
928+ match t. token_kind {
929+ TokenKind :: LineComment { .. } => {
930+ if let Some ( line) = t. content . strip_prefix ( "/// " ) . or_else ( || t. content . strip_prefix ( "///" ) ) {
931+ if line. starts_with ( "```" ) {
932+ docs += "```\n " ;
933+ in_code = !in_code;
934+ } else if !( in_code && line. starts_with ( "# " ) ) {
935+ docs += line;
936+ docs. push ( '\n' ) ;
937+ }
938+ }
939+ } ,
940+ TokenKind :: Pound => {
941+ match_tokens ! ( iter, OpenBracket Ident Colon Colon Ident Eq Literal { ..} CloseBracket Ident ) ;
942+ break ;
943+ } ,
944+ TokenKind :: Ident => {
945+ break ;
946+ } ,
947+ _ => { } ,
948+ }
875949 }
950+ docs. pop ( ) ; // remove final newline
876951
877952 let ( name, group, desc) = match_tokens ! (
878953 iter,
@@ -890,7 +965,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
890965 ..
891966 } ) = iter. next ( )
892967 {
893- lints. push ( Lint :: new ( name, group, desc, module, start..range. end ) ) ;
968+ lints. push ( Lint :: new ( name, group, desc, module, start..range. end , docs ) ) ;
894969 }
895970 }
896971}
@@ -977,7 +1052,11 @@ fn remove_line_splices(s: &str) -> String {
9771052 . and_then ( |s| s. strip_suffix ( '"' ) )
9781053 . unwrap_or_else ( || panic ! ( "expected quoted string, found `{}`" , s) ) ;
9791054 let mut res = String :: with_capacity ( s. len ( ) ) ;
980- unescape:: unescape_literal ( s, unescape:: Mode :: Str , & mut |range, _| res. push_str ( & s[ range] ) ) ;
1055+ unescape:: unescape_literal ( s, unescape:: Mode :: Str , & mut |range, ch| {
1056+ if ch. is_ok ( ) {
1057+ res. push_str ( & s[ range] ) ;
1058+ }
1059+ } ) ;
9811060 res
9821061}
9831062
@@ -1116,13 +1195,15 @@ mod tests {
11161195 "\" really long text\" " ,
11171196 "module_name" ,
11181197 Range :: default ( ) ,
1198+ String :: new( ) ,
11191199 ) ,
11201200 Lint :: new(
11211201 "doc_markdown" ,
11221202 "pedantic" ,
11231203 "\" single line\" " ,
11241204 "module_name" ,
11251205 Range :: default ( ) ,
1206+ String :: new( ) ,
11261207 ) ,
11271208 ] ;
11281209 assert_eq ! ( expected, result) ;
@@ -1162,20 +1243,23 @@ mod tests {
11621243 "\" abc\" " ,
11631244 "module_name" ,
11641245 Range :: default ( ) ,
1246+ String :: new( ) ,
11651247 ) ,
11661248 Lint :: new(
11671249 "should_assert_eq2" ,
11681250 "internal" ,
11691251 "\" abc\" " ,
11701252 "module_name" ,
11711253 Range :: default ( ) ,
1254+ String :: new( ) ,
11721255 ) ,
11731256 Lint :: new(
11741257 "should_assert_eq2" ,
11751258 "internal_style" ,
11761259 "\" abc\" " ,
11771260 "module_name" ,
11781261 Range :: default ( ) ,
1262+ String :: new( ) ,
11791263 ) ,
11801264 ] ;
11811265 let expected = vec ! [ Lint :: new(
@@ -1184,29 +1268,59 @@ mod tests {
11841268 "\" abc\" " ,
11851269 "module_name" ,
11861270 Range :: default ( ) ,
1271+ String :: new( ) ,
11871272 ) ] ;
11881273 assert_eq ! ( expected, Lint :: usable_lints( & lints) ) ;
11891274 }
11901275
11911276 #[ test]
11921277 fn test_by_lint_group ( ) {
11931278 let lints = vec ! [
1194- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1279+ Lint :: new(
1280+ "should_assert_eq" ,
1281+ "group1" ,
1282+ "\" abc\" " ,
1283+ "module_name" ,
1284+ Range :: default ( ) ,
1285+ String :: new( ) ,
1286+ ) ,
11951287 Lint :: new(
11961288 "should_assert_eq2" ,
11971289 "group2" ,
11981290 "\" abc\" " ,
11991291 "module_name" ,
12001292 Range :: default ( ) ,
1293+ String :: new( ) ,
1294+ ) ,
1295+ Lint :: new(
1296+ "incorrect_match" ,
1297+ "group1" ,
1298+ "\" abc\" " ,
1299+ "module_name" ,
1300+ Range :: default ( ) ,
1301+ String :: new( ) ,
12011302 ) ,
1202- Lint :: new( "incorrect_match" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
12031303 ] ;
12041304 let mut expected: HashMap < String , Vec < Lint > > = HashMap :: new ( ) ;
12051305 expected. insert (
12061306 "group1" . to_string ( ) ,
12071307 vec ! [
1208- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1209- Lint :: new( "incorrect_match" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1308+ Lint :: new(
1309+ "should_assert_eq" ,
1310+ "group1" ,
1311+ "\" abc\" " ,
1312+ "module_name" ,
1313+ Range :: default ( ) ,
1314+ String :: new( ) ,
1315+ ) ,
1316+ Lint :: new(
1317+ "incorrect_match" ,
1318+ "group1" ,
1319+ "\" abc\" " ,
1320+ "module_name" ,
1321+ Range :: default ( ) ,
1322+ String :: new( ) ,
1323+ ) ,
12101324 ] ,
12111325 ) ;
12121326 expected. insert (
@@ -1217,6 +1331,7 @@ mod tests {
12171331 "\" abc\" " ,
12181332 "module_name" ,
12191333 Range :: default ( ) ,
1334+ String :: new( ) ,
12201335 ) ] ,
12211336 ) ;
12221337 assert_eq ! ( expected, Lint :: by_lint_group( lints. into_iter( ) ) ) ;
@@ -1255,9 +1370,30 @@ mod tests {
12551370 #[ test]
12561371 fn test_gen_lint_group_list ( ) {
12571372 let lints = vec ! [
1258- Lint :: new( "abc" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1259- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1260- Lint :: new( "internal" , "internal_style" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1373+ Lint :: new(
1374+ "abc" ,
1375+ "group1" ,
1376+ "\" abc\" " ,
1377+ "module_name" ,
1378+ Range :: default ( ) ,
1379+ String :: new( ) ,
1380+ ) ,
1381+ Lint :: new(
1382+ "should_assert_eq" ,
1383+ "group1" ,
1384+ "\" abc\" " ,
1385+ "module_name" ,
1386+ Range :: default ( ) ,
1387+ String :: new( ) ,
1388+ ) ,
1389+ Lint :: new(
1390+ "internal" ,
1391+ "internal_style" ,
1392+ "\" abc\" " ,
1393+ "module_name" ,
1394+ Range :: default ( ) ,
1395+ String :: new( ) ,
1396+ ) ,
12611397 ] ;
12621398 let expected = GENERATED_FILE_COMMENT . to_string ( )
12631399 + & [
0 commit comments