2
2
//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
3
3
//!
4
4
//! This module and therefor the entire lint is guarded by a feature flag called
5
- //! `internal_metadata_lint`
5
+ //! `metadata-collector-lint`
6
+ //!
7
+ //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
8
+ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
9
+ //! a simple mistake)
6
10
//!
7
11
//! The metadata currently contains:
8
- //! - [ ] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303)
12
+ //! - [x ] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303)
9
13
//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492)
10
14
//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310)
11
15
17
21
// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames
18
22
19
23
use if_chain:: if_chain;
20
- use rustc_hir:: { ExprKind , Item , ItemKind , Mutability } ;
24
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
25
+ use rustc_hir:: { self as hir, ExprKind , Item , ItemKind , Mutability } ;
21
26
use rustc_lint:: { CheckLintNameResult , LateContext , LateLintPass , LintContext , LintId } ;
22
27
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
23
- use rustc_span:: { sym, Loc , Span } ;
28
+ use rustc_span:: { sym, Loc , Span , Symbol } ;
24
29
use serde:: Serialize ;
25
30
use std:: fs:: OpenOptions ;
26
31
use std:: io:: prelude:: * ;
27
32
28
33
use crate :: utils:: internal_lints:: is_lint_ref_type;
29
- use crate :: utils:: span_lint;
34
+ use crate :: utils:: { last_path_segment , match_function_call , match_type , paths , span_lint, walk_ptrs_ty_depth } ;
30
35
36
+ /// This is the output file of the lint collector.
31
37
const OUTPUT_FILE : & str = "metadata_collection.json" ;
38
+ /// These lints are excluded from the export.
32
39
const BLACK_LISTED_LINTS : [ & str ; 2 ] = [ "lint_author" , "deep_code_inspection" ] ;
33
40
41
+ // TODO xFrednet 2021-02-15: `span_lint_and_then` & `span_lint_hir_and_then` requires special
42
+ // handling
43
+ #[ rustfmt:: skip]
44
+ const LINT_EMISSION_FUNCTIONS : [ & [ & str ] ; 5 ] = [
45
+ & [ "clippy_lints" , "utils" , "diagnostics" , "span_lint" ] ,
46
+ & [ "clippy_lints" , "utils" , "diagnostics" , "span_lint_and_help" ] ,
47
+ & [ "clippy_lints" , "utils" , "diagnostics" , "span_lint_and_note" ] ,
48
+ & [ "clippy_lints" , "utils" , "diagnostics" , "span_lint_hir" ] ,
49
+ & [ "clippy_lints" , "utils" , "diagnostics" , "span_lint_and_sugg" ] ,
50
+ ] ;
51
+
34
52
declare_clippy_lint ! {
35
53
/// **What it does:** Collects metadata about clippy lints for the website.
36
54
///
@@ -66,12 +84,21 @@ impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
66
84
#[ derive( Debug , Clone , Default ) ]
67
85
pub struct MetadataCollector {
68
86
lints : Vec < LintMetadata > ,
87
+ applicability_into : FxHashMap < String , ApplicabilityInfo > ,
69
88
}
70
89
71
90
impl Drop for MetadataCollector {
91
+ /// You might ask: How hacky is this?
92
+ /// My answer: YES
72
93
fn drop ( & mut self ) {
73
- // You might ask: How hacky is this?
74
- // My answer: YES
94
+ let mut applicability_info = std:: mem:: take ( & mut self . applicability_into ) ;
95
+
96
+ // Mapping the final data
97
+ self . lints
98
+ . iter_mut ( )
99
+ . for_each ( |x| x. applicability = applicability_info. remove ( & x. id ) ) ;
100
+
101
+ // Outputting
75
102
let mut file = OpenOptions :: new ( ) . write ( true ) . create ( true ) . open ( OUTPUT_FILE ) . unwrap ( ) ;
76
103
writeln ! ( file, "{}" , serde_json:: to_string_pretty( & self . lints) . unwrap( ) ) . unwrap ( ) ;
77
104
}
@@ -83,6 +110,21 @@ struct LintMetadata {
83
110
id_span : SerializableSpan ,
84
111
group : String ,
85
112
docs : String ,
113
+ /// This field is only used in the output and will only be
114
+ /// mapped shortly before the actual output.
115
+ applicability : Option < ApplicabilityInfo > ,
116
+ }
117
+
118
+ impl LintMetadata {
119
+ fn new ( id : String , id_span : SerializableSpan , group : String , docs : String ) -> Self {
120
+ Self {
121
+ id,
122
+ id_span,
123
+ group,
124
+ docs,
125
+ applicability : None ,
126
+ }
127
+ }
86
128
}
87
129
88
130
#[ derive( Debug , Clone , Serialize ) ]
@@ -101,12 +143,31 @@ impl SerializableSpan {
101
143
102
144
Self {
103
145
path : format ! ( "{}" , loc. file. name) ,
104
- line : 1 ,
146
+ line : loc . line ,
105
147
}
106
148
}
107
149
}
108
150
151
+ #[ derive( Debug , Clone , Default , Serialize ) ]
152
+ struct ApplicabilityInfo {
153
+ /// Indicates if any of the lint emissions uses multiple spans. This is related to
154
+ /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
155
+ /// currently not be applied automatically.
156
+ has_multi_suggestion : bool ,
157
+ /// These are all the available applicability values for the lint suggestions
158
+ applicabilities : FxHashSet < String > ,
159
+ }
160
+
109
161
impl < ' tcx > LateLintPass < ' tcx > for MetadataCollector {
162
+ /// Collecting lint declarations like:
163
+ /// ```rust, ignore
164
+ /// declare_clippy_lint! {
165
+ /// /// **What it does:** Something IDK.
166
+ /// pub SOME_LINT,
167
+ /// internal,
168
+ /// "Who am I?"
169
+ /// }
170
+ /// ```
110
171
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
111
172
if_chain ! {
112
173
if let ItemKind :: Static ( ref ty, Mutability :: Not , body_id) = item. kind;
@@ -115,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector {
115
176
if let ExprKind :: AddrOf ( _, _, ref inner_exp) = expr. kind;
116
177
if let ExprKind :: Struct ( _, _, _) = inner_exp. kind;
117
178
then {
118
- let lint_name = item. ident. name. as_str ( ) . to_string ( ) . to_ascii_lowercase( ) ;
179
+ let lint_name = sym_to_string ( item. ident. name) . to_ascii_lowercase( ) ;
119
180
if BLACK_LISTED_LINTS . contains( & lint_name. as_str( ) ) {
120
181
return ;
121
182
}
@@ -126,33 +187,50 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector {
126
187
if let Some ( group_some) = get_lint_group( cx, lint_lst[ 0 ] ) {
127
188
group = group_some;
128
189
} else {
129
- lint_collection_error ( cx, item, "Unable to determine lint group" ) ;
190
+ lint_collection_error_item ( cx, item, "Unable to determine lint group" ) ;
130
191
return ;
131
192
}
132
193
} else {
133
- lint_collection_error ( cx, item, "Unable to find lint in lint_store" ) ;
194
+ lint_collection_error_item ( cx, item, "Unable to find lint in lint_store" ) ;
134
195
return ;
135
196
}
136
197
137
198
let docs: String ;
138
199
if let Some ( docs_some) = extract_attr_docs( item) {
139
200
docs = docs_some;
140
201
} else {
141
- lint_collection_error ( cx, item, "could not collect the lint documentation" ) ;
202
+ lint_collection_error_item ( cx, item, "could not collect the lint documentation" ) ;
142
203
return ;
143
204
} ;
144
205
145
- self . lints. push( LintMetadata {
146
- id : lint_name,
147
- id_span : SerializableSpan :: from_item( cx, item) ,
206
+ self . lints. push( LintMetadata :: new (
207
+ lint_name,
208
+ SerializableSpan :: from_item( cx, item) ,
148
209
group,
149
210
docs,
211
+ ) ) ;
212
+ }
213
+ }
214
+ }
215
+
216
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
217
+ if let Some ( args) = match_simple_lint_emission ( cx, expr) {
218
+ if let Some ( ( lint_name, mut applicability) ) = extract_emission_info ( cx, args) {
219
+ let app_info = self . applicability_into . entry ( lint_name) . or_default ( ) ;
220
+ applicability. drain ( ..) . for_each ( |x| {
221
+ app_info. applicabilities . insert ( x) ;
150
222
} ) ;
223
+ } else {
224
+ lint_collection_error_span ( cx, expr. span , "I found this but I can't get the lint or applicability" ) ;
151
225
}
152
226
}
153
227
}
154
228
}
155
229
230
+ fn sym_to_string ( sym : Symbol ) -> String {
231
+ sym. as_str ( ) . to_string ( )
232
+ }
233
+
156
234
/// This function collects all documentation that has been added to an item using
157
235
/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
158
236
///
@@ -166,23 +244,11 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector {
166
244
fn extract_attr_docs ( item : & Item < ' _ > ) -> Option < String > {
167
245
item. attrs
168
246
. iter ( )
169
- . filter_map ( |ref x| x. doc_str ( ) )
170
- . fold ( None , |acc, sym| {
171
- let mut doc_str = sym. as_str ( ) . to_string ( ) ;
172
- doc_str. push ( '\n' ) ;
173
-
174
- #[ allow( clippy:: option_if_let_else) ] // See clippy#6737
175
- if let Some ( mut x) = acc {
176
- x. push_str ( & doc_str) ;
177
- Some ( x)
178
- } else {
179
- Some ( doc_str)
180
- }
181
-
182
- // acc.map_or(Some(doc_str), |mut x| {
183
- // x.push_str(&doc_str);
184
- // Some(x)
185
- // })
247
+ . filter_map ( |ref x| x. doc_str ( ) . map ( |sym| sym. as_str ( ) . to_string ( ) ) )
248
+ . reduce ( |mut acc, sym| {
249
+ acc. push_str ( & sym) ;
250
+ acc. push ( '\n' ) ;
251
+ acc
186
252
} )
187
253
}
188
254
@@ -196,11 +262,57 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
196
262
None
197
263
}
198
264
199
- fn lint_collection_error ( cx : & LateContext < ' _ > , item : & Item < ' _ > , message : & str ) {
265
+ fn lint_collection_error_item ( cx : & LateContext < ' _ > , item : & Item < ' _ > , message : & str ) {
200
266
span_lint (
201
267
cx,
202
268
INTERNAL_METADATA_COLLECTOR ,
203
269
item. ident . span ,
204
270
& format ! ( "Metadata collection error for `{}`: {}" , item. ident. name, message) ,
205
271
) ;
206
272
}
273
+
274
+ fn lint_collection_error_span ( cx : & LateContext < ' _ > , span : Span , message : & str ) {
275
+ span_lint (
276
+ cx,
277
+ INTERNAL_METADATA_COLLECTOR ,
278
+ span,
279
+ & format ! ( "Metadata collection error: {}" , message) ,
280
+ ) ;
281
+ }
282
+
283
+ fn match_simple_lint_emission < ' tcx > (
284
+ cx : & LateContext < ' tcx > ,
285
+ expr : & ' tcx hir:: Expr < ' _ > ,
286
+ ) -> Option < & ' tcx [ hir:: Expr < ' tcx > ] > {
287
+ LINT_EMISSION_FUNCTIONS
288
+ . iter ( )
289
+ . find_map ( |emission_fn| match_function_call ( cx, expr, emission_fn) . map ( |args| args) )
290
+ }
291
+
292
+ /// This returns the lint name and the possible applicability of this emission
293
+ fn extract_emission_info < ' tcx > ( cx : & LateContext < ' tcx > , args : & [ hir:: Expr < ' _ > ] ) -> Option < ( String , Vec < String > ) > {
294
+ let mut lint_name = None ;
295
+ let mut applicability = None ;
296
+
297
+ for arg in args {
298
+ let ( arg_ty, _) = walk_ptrs_ty_depth ( cx. typeck_results ( ) . expr_ty ( & arg) ) ;
299
+
300
+ if match_type ( cx, arg_ty, & paths:: LINT ) {
301
+ // If we found the lint arg, extract the lint name
302
+ if let ExprKind :: Path ( ref lint_path) = arg. kind {
303
+ lint_name = Some ( last_path_segment ( lint_path) . ident . name )
304
+ }
305
+ } else if match_type ( cx, arg_ty, & paths:: APPLICABILITY ) {
306
+ if let ExprKind :: Path ( ref path) = arg. kind {
307
+ applicability = Some ( last_path_segment ( path) . ident . name )
308
+ }
309
+ }
310
+ }
311
+
312
+ lint_name. map ( |lint_name| {
313
+ (
314
+ sym_to_string ( lint_name) . to_ascii_lowercase ( ) ,
315
+ applicability. map ( sym_to_string) . map_or_else ( Vec :: new, |x| vec ! [ x] ) ,
316
+ )
317
+ } )
318
+ }
0 commit comments