@@ -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,26 +919,30 @@ 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
+ while let Some ( t) = iter. next ( ) {
927
+ match t. token_kind {
928
+ TokenKind :: LineComment { .. } => {
929
+ let line = t. content . trim_start_matches ( [ '/' , ' ' ] ) ;
930
+ if line. starts_with ( "```" ) {
931
+ docs += "```\n " ;
932
+ } else {
933
+ docs += line;
934
+ docs. push ( '\n' ) ;
935
+ }
936
+ } ,
937
+ TokenKind :: Pound => {
938
+ match_tokens ! ( iter, OpenBracket Ident Colon Colon Ident Eq Literal { ..} CloseBracket Ident ) ;
939
+ break ;
940
+ } ,
941
+ TokenKind :: Ident => {
942
+ break ;
943
+ } ,
944
+ _ => { } ,
945
+ }
875
946
}
876
947
877
948
let ( name, group, desc) = match_tokens ! (
@@ -890,7 +961,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
890
961
..
891
962
} ) = iter. next ( )
892
963
{
893
- lints. push ( Lint :: new ( name, group, desc, module, start..range. end ) ) ;
964
+ lints. push ( Lint :: new ( name, group, desc, module, start..range. end , docs ) ) ;
894
965
}
895
966
}
896
967
}
@@ -1116,13 +1187,15 @@ mod tests {
1116
1187
"\" really long text\" " ,
1117
1188
"module_name" ,
1118
1189
Range :: default ( ) ,
1190
+ String :: new( ) ,
1119
1191
) ,
1120
1192
Lint :: new(
1121
1193
"doc_markdown" ,
1122
1194
"pedantic" ,
1123
1195
"\" single line\" " ,
1124
1196
"module_name" ,
1125
1197
Range :: default ( ) ,
1198
+ String :: new( ) ,
1126
1199
) ,
1127
1200
] ;
1128
1201
assert_eq ! ( expected, result) ;
@@ -1162,20 +1235,23 @@ mod tests {
1162
1235
"\" abc\" " ,
1163
1236
"module_name" ,
1164
1237
Range :: default ( ) ,
1238
+ String :: new( ) ,
1165
1239
) ,
1166
1240
Lint :: new(
1167
1241
"should_assert_eq2" ,
1168
1242
"internal" ,
1169
1243
"\" abc\" " ,
1170
1244
"module_name" ,
1171
1245
Range :: default ( ) ,
1246
+ String :: new( ) ,
1172
1247
) ,
1173
1248
Lint :: new(
1174
1249
"should_assert_eq2" ,
1175
1250
"internal_style" ,
1176
1251
"\" abc\" " ,
1177
1252
"module_name" ,
1178
1253
Range :: default ( ) ,
1254
+ String :: new( ) ,
1179
1255
) ,
1180
1256
] ;
1181
1257
let expected = vec ! [ Lint :: new(
@@ -1184,29 +1260,59 @@ mod tests {
1184
1260
"\" abc\" " ,
1185
1261
"module_name" ,
1186
1262
Range :: default ( ) ,
1263
+ String :: new( ) ,
1187
1264
) ] ;
1188
1265
assert_eq ! ( expected, Lint :: usable_lints( & lints) ) ;
1189
1266
}
1190
1267
1191
1268
#[ test]
1192
1269
fn test_by_lint_group ( ) {
1193
1270
let lints = vec ! [
1194
- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1271
+ Lint :: new(
1272
+ "should_assert_eq" ,
1273
+ "group1" ,
1274
+ "\" abc\" " ,
1275
+ "module_name" ,
1276
+ Range :: default ( ) ,
1277
+ String :: new( ) ,
1278
+ ) ,
1195
1279
Lint :: new(
1196
1280
"should_assert_eq2" ,
1197
1281
"group2" ,
1198
1282
"\" abc\" " ,
1199
1283
"module_name" ,
1200
1284
Range :: default ( ) ,
1285
+ String :: new( ) ,
1286
+ ) ,
1287
+ Lint :: new(
1288
+ "incorrect_match" ,
1289
+ "group1" ,
1290
+ "\" abc\" " ,
1291
+ "module_name" ,
1292
+ Range :: default ( ) ,
1293
+ String :: new( ) ,
1201
1294
) ,
1202
- Lint :: new( "incorrect_match" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1203
1295
] ;
1204
1296
let mut expected: HashMap < String , Vec < Lint > > = HashMap :: new ( ) ;
1205
1297
expected. insert (
1206
1298
"group1" . to_string ( ) ,
1207
1299
vec ! [
1208
- Lint :: new( "should_assert_eq" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1209
- Lint :: new( "incorrect_match" , "group1" , "\" abc\" " , "module_name" , Range :: default ( ) ) ,
1300
+ Lint :: new(
1301
+ "should_assert_eq" ,
1302
+ "group1" ,
1303
+ "\" abc\" " ,
1304
+ "module_name" ,
1305
+ Range :: default ( ) ,
1306
+ String :: new( ) ,
1307
+ ) ,
1308
+ Lint :: new(
1309
+ "incorrect_match" ,
1310
+ "group1" ,
1311
+ "\" abc\" " ,
1312
+ "module_name" ,
1313
+ Range :: default ( ) ,
1314
+ String :: new( ) ,
1315
+ ) ,
1210
1316
] ,
1211
1317
) ;
1212
1318
expected. insert (
@@ -1217,6 +1323,7 @@ mod tests {
1217
1323
"\" abc\" " ,
1218
1324
"module_name" ,
1219
1325
Range :: default ( ) ,
1326
+ String :: new( ) ,
1220
1327
) ] ,
1221
1328
) ;
1222
1329
assert_eq ! ( expected, Lint :: by_lint_group( lints. into_iter( ) ) ) ;
@@ -1255,9 +1362,30 @@ mod tests {
1255
1362
#[ test]
1256
1363
fn test_gen_lint_group_list ( ) {
1257
1364
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 ( ) ) ,
1365
+ Lint :: new(
1366
+ "abc" ,
1367
+ "group1" ,
1368
+ "\" abc\" " ,
1369
+ "module_name" ,
1370
+ Range :: default ( ) ,
1371
+ String :: new( ) ,
1372
+ ) ,
1373
+ Lint :: new(
1374
+ "should_assert_eq" ,
1375
+ "group1" ,
1376
+ "\" abc\" " ,
1377
+ "module_name" ,
1378
+ Range :: default ( ) ,
1379
+ String :: new( ) ,
1380
+ ) ,
1381
+ Lint :: new(
1382
+ "internal" ,
1383
+ "internal_style" ,
1384
+ "\" abc\" " ,
1385
+ "module_name" ,
1386
+ Range :: default ( ) ,
1387
+ String :: new( ) ,
1388
+ ) ,
1261
1389
] ;
1262
1390
let expected = GENERATED_FILE_COMMENT . to_string ( )
1263
1391
+ & [
0 commit comments