@@ -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
}
@@ -1116,13 +1191,15 @@ mod tests {
1116
1191
"\" really long text\" " ,
1117
1192
"module_name" ,
1118
1193
Range :: default ( ) ,
1194
+ String :: new( ) ,
1119
1195
) ,
1120
1196
Lint :: new(
1121
1197
"doc_markdown" ,
1122
1198
"pedantic" ,
1123
1199
"\" single line\" " ,
1124
1200
"module_name" ,
1125
1201
Range :: default ( ) ,
1202
+ String :: new( ) ,
1126
1203
) ,
1127
1204
] ;
1128
1205
assert_eq ! ( expected, result) ;
@@ -1162,20 +1239,23 @@ mod tests {
1162
1239
"\" abc\" " ,
1163
1240
"module_name" ,
1164
1241
Range :: default ( ) ,
1242
+ String :: new( ) ,
1165
1243
) ,
1166
1244
Lint :: new(
1167
1245
"should_assert_eq2" ,
1168
1246
"internal" ,
1169
1247
"\" abc\" " ,
1170
1248
"module_name" ,
1171
1249
Range :: default ( ) ,
1250
+ String :: new( ) ,
1172
1251
) ,
1173
1252
Lint :: new(
1174
1253
"should_assert_eq2" ,
1175
1254
"internal_style" ,
1176
1255
"\" abc\" " ,
1177
1256
"module_name" ,
1178
1257
Range :: default ( ) ,
1258
+ String :: new( ) ,
1179
1259
) ,
1180
1260
] ;
1181
1261
let expected = vec ! [ Lint :: new(
@@ -1184,29 +1264,59 @@ mod tests {
1184
1264
"\" abc\" " ,
1185
1265
"module_name" ,
1186
1266
Range :: default ( ) ,
1267
+ String :: new( ) ,
1187
1268
) ] ;
1188
1269
assert_eq ! ( expected, Lint :: usable_lints( & lints) ) ;
1189
1270
}
1190
1271
1191
1272
#[ test]
1192
1273
fn test_by_lint_group ( ) {
1193
1274
let lints = vec ! [
1194
- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1275
+ Lint :: new(
1276
+ "should_assert_eq" ,
1277
+ "group1" ,
1278
+ "\" abc\" " ,
1279
+ "module_name" ,
1280
+ Range :: default ( ) ,
1281
+ String :: new( ) ,
1282
+ ) ,
1195
1283
Lint :: new(
1196
1284
"should_assert_eq2" ,
1197
1285
"group2" ,
1198
1286
"\" abc\" " ,
1199
1287
"module_name" ,
1200
1288
Range :: default ( ) ,
1289
+ String :: new( ) ,
1290
+ ) ,
1291
+ Lint :: new(
1292
+ "incorrect_match" ,
1293
+ "group1" ,
1294
+ "\" abc\" " ,
1295
+ "module_name" ,
1296
+ Range :: default ( ) ,
1297
+ String :: new( ) ,
1201
1298
) ,
1202
- Lint :: new( "incorrect_match" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1203
1299
] ;
1204
1300
let mut expected: HashMap < String , Vec < Lint > > = HashMap :: new ( ) ;
1205
1301
expected. insert (
1206
1302
"group1" . to_string ( ) ,
1207
1303
vec ! [
1208
- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1209
- Lint :: new( "incorrect_match" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1304
+ Lint :: new(
1305
+ "should_assert_eq" ,
1306
+ "group1" ,
1307
+ "\" abc\" " ,
1308
+ "module_name" ,
1309
+ Range :: default ( ) ,
1310
+ String :: new( ) ,
1311
+ ) ,
1312
+ Lint :: new(
1313
+ "incorrect_match" ,
1314
+ "group1" ,
1315
+ "\" abc\" " ,
1316
+ "module_name" ,
1317
+ Range :: default ( ) ,
1318
+ String :: new( ) ,
1319
+ ) ,
1210
1320
] ,
1211
1321
) ;
1212
1322
expected. insert (
@@ -1217,6 +1327,7 @@ mod tests {
1217
1327
"\" abc\" " ,
1218
1328
"module_name" ,
1219
1329
Range :: default ( ) ,
1330
+ String :: new( ) ,
1220
1331
) ] ,
1221
1332
) ;
1222
1333
assert_eq ! ( expected, Lint :: by_lint_group( lints. into_iter( ) ) ) ;
@@ -1255,9 +1366,30 @@ mod tests {
1255
1366
#[ test]
1256
1367
fn test_gen_lint_group_list ( ) {
1257
1368
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 ( ) ) ,
1369
+ Lint :: new(
1370
+ "abc" ,
1371
+ "group1" ,
1372
+ "\" abc\" " ,
1373
+ "module_name" ,
1374
+ Range :: default ( ) ,
1375
+ String :: new( ) ,
1376
+ ) ,
1377
+ Lint :: new(
1378
+ "should_assert_eq" ,
1379
+ "group1" ,
1380
+ "\" abc\" " ,
1381
+ "module_name" ,
1382
+ Range :: default ( ) ,
1383
+ String :: new( ) ,
1384
+ ) ,
1385
+ Lint :: new(
1386
+ "internal" ,
1387
+ "internal_style" ,
1388
+ "\" abc\" " ,
1389
+ "module_name" ,
1390
+ Range :: default ( ) ,
1391
+ String :: new( ) ,
1392
+ ) ,
1261
1393
] ;
1262
1394
let expected = GENERATED_FILE_COMMENT . to_string ( )
1263
1395
+ & [
0 commit comments