@@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder;
3
3
use indoc:: writedoc;
4
4
use itertools:: Itertools ;
5
5
use rustc_lexer:: { tokenize, unescape, LiteralKind , TokenKind } ;
6
- use std:: collections:: { HashMap , HashSet } ;
6
+ use std:: collections:: { BTreeSet , HashMap , HashSet } ;
7
7
use std:: ffi:: OsStr ;
8
8
use std:: fmt:: Write ;
9
9
use std:: fs:: { self , OpenOptions } ;
@@ -124,6 +124,8 @@ fn generate_lint_files(
124
124
let content = gen_lint_group_list ( "all" , all_group_lints) ;
125
125
process_file ( "clippy_lints/src/lib.register_all.rs" , update_mode, & content) ;
126
126
127
+ update_docs ( update_mode, & usable_lints) ;
128
+
127
129
for ( lint_group, lints) in Lint :: by_lint_group ( usable_lints. into_iter ( ) . chain ( internal_lints) ) {
128
130
let content = gen_lint_group_list ( & lint_group, lints. iter ( ) ) ;
129
131
process_file (
@@ -140,6 +142,62 @@ fn generate_lint_files(
140
142
process_file ( "tests/ui/rename.rs" , update_mode, & content) ;
141
143
}
142
144
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
+
143
201
pub fn print_lints ( ) {
144
202
let ( lint_list, _, _) = gather_all ( ) ;
145
203
let usable_lints = Lint :: usable_lints ( & lint_list) ;
@@ -589,17 +647,26 @@ struct Lint {
589
647
desc : String ,
590
648
module : String ,
591
649
declaration_range : Range < usize > ,
650
+ documentation : String ,
592
651
}
593
652
594
653
impl Lint {
595
654
#[ 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 {
597
663
Self {
598
664
name : name. to_lowercase ( ) ,
599
665
group : group. into ( ) ,
600
666
desc : remove_line_splices ( desc) ,
601
667
module : module. into ( ) ,
602
668
declaration_range,
669
+ documentation,
603
670
}
604
671
}
605
672
@@ -852,27 +919,35 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
852
919
} | token_kind == & TokenKind :: Ident && * content == "declare_clippy_lint" ,
853
920
) {
854
921
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 ) ) ;
859
924
// matches `!{`
860
925
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
+ }
875
949
}
950
+ docs. pop ( ) ; // remove final newline
876
951
877
952
let ( name, group, desc) = match_tokens ! (
878
953
iter,
@@ -890,7 +965,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
890
965
..
891
966
} ) = iter. next ( )
892
967
{
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 ) ) ;
894
969
}
895
970
}
896
971
}
@@ -1120,13 +1195,15 @@ mod tests {
1120
1195
"\" really long text\" " ,
1121
1196
"module_name" ,
1122
1197
Range :: default ( ) ,
1198
+ String :: new( ) ,
1123
1199
) ,
1124
1200
Lint :: new(
1125
1201
"doc_markdown" ,
1126
1202
"pedantic" ,
1127
1203
"\" single line\" " ,
1128
1204
"module_name" ,
1129
1205
Range :: default ( ) ,
1206
+ String :: new( ) ,
1130
1207
) ,
1131
1208
] ;
1132
1209
assert_eq ! ( expected, result) ;
@@ -1166,20 +1243,23 @@ mod tests {
1166
1243
"\" abc\" " ,
1167
1244
"module_name" ,
1168
1245
Range :: default ( ) ,
1246
+ String :: new( ) ,
1169
1247
) ,
1170
1248
Lint :: new(
1171
1249
"should_assert_eq2" ,
1172
1250
"internal" ,
1173
1251
"\" abc\" " ,
1174
1252
"module_name" ,
1175
1253
Range :: default ( ) ,
1254
+ String :: new( ) ,
1176
1255
) ,
1177
1256
Lint :: new(
1178
1257
"should_assert_eq2" ,
1179
1258
"internal_style" ,
1180
1259
"\" abc\" " ,
1181
1260
"module_name" ,
1182
1261
Range :: default ( ) ,
1262
+ String :: new( ) ,
1183
1263
) ,
1184
1264
] ;
1185
1265
let expected = vec ! [ Lint :: new(
@@ -1188,29 +1268,59 @@ mod tests {
1188
1268
"\" abc\" " ,
1189
1269
"module_name" ,
1190
1270
Range :: default ( ) ,
1271
+ String :: new( ) ,
1191
1272
) ] ;
1192
1273
assert_eq ! ( expected, Lint :: usable_lints( & lints) ) ;
1193
1274
}
1194
1275
1195
1276
#[ test]
1196
1277
fn test_by_lint_group ( ) {
1197
1278
let lints = vec ! [
1198
- 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
+ ) ,
1199
1287
Lint :: new(
1200
1288
"should_assert_eq2" ,
1201
1289
"group2" ,
1202
1290
"\" abc\" " ,
1203
1291
"module_name" ,
1204
1292
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( ) ,
1205
1302
) ,
1206
- Lint :: new( "incorrect_match" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1207
1303
] ;
1208
1304
let mut expected: HashMap < String , Vec < Lint > > = HashMap :: new ( ) ;
1209
1305
expected. insert (
1210
1306
"group1" . to_string ( ) ,
1211
1307
vec ! [
1212
- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1213
- 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
+ ) ,
1214
1324
] ,
1215
1325
) ;
1216
1326
expected. insert (
@@ -1221,6 +1331,7 @@ mod tests {
1221
1331
"\" abc\" " ,
1222
1332
"module_name" ,
1223
1333
Range :: default ( ) ,
1334
+ String :: new( ) ,
1224
1335
) ] ,
1225
1336
) ;
1226
1337
assert_eq ! ( expected, Lint :: by_lint_group( lints. into_iter( ) ) ) ;
@@ -1259,9 +1370,30 @@ mod tests {
1259
1370
#[ test]
1260
1371
fn test_gen_lint_group_list ( ) {
1261
1372
let lints = vec ! [
1262
- Lint :: new( "abc" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1263
- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1264
- 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
+ ) ,
1265
1397
] ;
1266
1398
let expected = GENERATED_FILE_COMMENT . to_string ( )
1267
1399
+ & [
0 commit comments