@@ -24,14 +24,21 @@ use if_chain::if_chain;
24
24
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
25
25
use rustc_hir:: { self as hir, ExprKind , Item , ItemKind , Mutability } ;
26
26
use rustc_lint:: { CheckLintNameResult , LateContext , LateLintPass , LintContext , LintId } ;
27
+ use rustc_middle:: ty:: BorrowKind ;
27
28
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
28
29
use rustc_span:: { sym, Loc , Span , Symbol } ;
30
+ use rustc_trait_selection:: infer:: TyCtxtInferExt ;
31
+ use rustc_typeck:: expr_use_visitor:: { ConsumeMode , Delegate , ExprUseVisitor , PlaceWithHirId } ;
32
+ use rustc_typeck:: hir_ty_to_ty;
29
33
use serde:: Serialize ;
30
- use std:: fs:: OpenOptions ;
34
+ use std:: fs:: { self , OpenOptions } ;
31
35
use std:: io:: prelude:: * ;
36
+ use std:: path:: Path ;
32
37
33
38
use crate :: utils:: internal_lints:: is_lint_ref_type;
34
- use crate :: utils:: { last_path_segment, match_function_call, match_type, paths, span_lint, walk_ptrs_ty_depth} ;
39
+ use crate :: utils:: {
40
+ last_path_segment, match_function_call, match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth,
41
+ } ;
35
42
36
43
/// This is the output file of the lint collector.
37
44
const OUTPUT_FILE : & str = "metadata_collection.json" ;
@@ -91,6 +98,10 @@ impl Drop for MetadataCollector {
91
98
/// You might ask: How hacky is this?
92
99
/// My answer: YES
93
100
fn drop ( & mut self ) {
101
+ if self . lints . is_empty ( ) {
102
+ return ;
103
+ }
104
+
94
105
let mut applicability_info = std:: mem:: take ( & mut self . applicability_into ) ;
95
106
96
107
// Mapping the final data
@@ -99,6 +110,9 @@ impl Drop for MetadataCollector {
99
110
. for_each ( |x| x. applicability = applicability_info. remove ( & x. id ) ) ;
100
111
101
112
// Outputting
113
+ if Path :: new ( OUTPUT_FILE ) . exists ( ) {
114
+ fs:: remove_file ( OUTPUT_FILE ) . unwrap ( ) ;
115
+ }
102
116
let mut file = OpenOptions :: new ( ) . write ( true ) . create ( true ) . open ( OUTPUT_FILE ) . unwrap ( ) ;
103
117
writeln ! ( file, "{}" , serde_json:: to_string_pretty( & self . lints) . unwrap( ) ) . unwrap ( ) ;
104
118
}
@@ -158,6 +172,17 @@ struct ApplicabilityInfo {
158
172
applicabilities : FxHashSet < String > ,
159
173
}
160
174
175
+ fn log_to_file ( msg : & str ) {
176
+ let mut file = OpenOptions :: new ( )
177
+ . write ( true )
178
+ . append ( true )
179
+ . create ( true )
180
+ . open ( "metadata-lint.log" )
181
+ . unwrap ( ) ;
182
+
183
+ write ! ( file, "{}" , msg) . unwrap ( ) ;
184
+ }
185
+
161
186
impl < ' tcx > LateLintPass < ' tcx > for MetadataCollector {
162
187
/// Collecting lint declarations like:
163
188
/// ```rust, ignore
@@ -213,6 +238,20 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector {
213
238
}
214
239
}
215
240
241
+ /// Collecting constant applicability from the actual lint emissions
242
+ ///
243
+ /// Example:
244
+ /// ```rust, ignore
245
+ /// span_lint_and_sugg(
246
+ /// cx,
247
+ /// SOME_LINT,
248
+ /// item.span,
249
+ /// "Le lint message",
250
+ /// "Here comes help:",
251
+ /// "#![allow(clippy::all)]",
252
+ /// Applicability::MachineApplicable, // <-- Extracts this constant value
253
+ /// );
254
+ /// ```
216
255
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
217
256
if let Some ( args) = match_simple_lint_emission ( cx, expr) {
218
257
if let Some ( ( lint_name, mut applicability) ) = extract_emission_info ( cx, args) {
@@ -225,6 +264,73 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector {
225
264
}
226
265
}
227
266
}
267
+
268
+ /// Tracking and hopefully collecting dynamic applicability values
269
+ ///
270
+ /// Example:
271
+ /// ```rust, ignore
272
+ /// // vvv Applicability value to track
273
+ /// let mut applicability = Applicability::MachineApplicable;
274
+ /// // vvv Value Mutation
275
+ /// let suggestion = snippet_with_applicability(cx, expr.span, "_", &mut applicability);
276
+ /// // vvv Emission to link the value to the lint
277
+ /// span_lint_and_sugg(
278
+ /// cx,
279
+ /// SOME_LINT,
280
+ /// expr.span,
281
+ /// "This can be improved",
282
+ /// "try",
283
+ /// suggestion,
284
+ /// applicability,
285
+ /// );
286
+ /// ```
287
+ fn check_local ( & mut self , cx : & LateContext < ' tcx > , local : & ' tcx hir:: Local < ' tcx > ) {
288
+ if let Some ( tc) = cx. maybe_typeck_results ( ) {
289
+ // TODO xFrednet 2021-02-14: support nested applicability (only in tuples)
290
+ let local_ty = if let Some ( ty) = local. ty {
291
+ hir_ty_to_ty ( cx. tcx , ty)
292
+ } else if let Some ( init) = local. init {
293
+ tc. expr_ty ( init)
294
+ } else {
295
+ return ;
296
+ } ;
297
+
298
+ if_chain ! {
299
+ if match_type( cx, local_ty, & paths:: APPLICABILITY ) ;
300
+ if let Some ( body) = get_parent_body( cx, local. hir_id) ;
301
+ then {
302
+ let span = SerializableSpan :: from_span( cx, local. span) ;
303
+ let local_str = crate :: utils:: snippet( cx, local. span, "_" ) ;
304
+ let value_life = format!( "{} -- {}:{}\n " , local_str, span. path. rsplit( '/' ) . next( ) . unwrap_or_default( ) , span. line) ;
305
+ let value_hir_id = local. pat. hir_id;
306
+ let mut tracker = ValueTracker { cx, value_hir_id, value_life} ;
307
+
308
+ cx. tcx. infer_ctxt( ) . enter( |infcx| {
309
+ let body_owner_id = cx. tcx. hir( ) . body_owner_def_id( body. id( ) ) ;
310
+ ExprUseVisitor :: new(
311
+ & mut tracker,
312
+ & infcx,
313
+ body_owner_id,
314
+ cx. param_env,
315
+ cx. typeck_results( )
316
+ )
317
+ . consume_body( body) ;
318
+ } ) ;
319
+
320
+ log_to_file( & tracker. value_life) ;
321
+ lint_collection_error_span( cx, local. span, "Applicability value found" ) ;
322
+ }
323
+ }
324
+ }
325
+ }
326
+ }
327
+
328
+ fn get_parent_body < ' a , ' tcx > ( cx : & ' a LateContext < ' tcx > , id : hir:: HirId ) -> Option < & ' tcx hir:: Body < ' tcx > > {
329
+ let map = cx. tcx . hir ( ) ;
330
+
331
+ map. parent_iter ( id)
332
+ . find_map ( |( parent, _) | map. maybe_body_owned_by ( parent) )
333
+ . map ( |body| map. body ( body) )
228
334
}
229
335
230
336
fn sym_to_string ( sym : Symbol ) -> String {
@@ -262,6 +368,9 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
262
368
None
263
369
}
264
370
371
+ // ==================================================================
372
+ // Lint emission
373
+ // ==================================================================
265
374
fn lint_collection_error_item ( cx : & LateContext < ' _ > , item : & Item < ' _ > , message : & str ) {
266
375
span_lint (
267
376
cx,
@@ -280,13 +389,16 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) {
280
389
) ;
281
390
}
282
391
392
+ // ==================================================================
393
+ // Applicability
394
+ // ==================================================================
283
395
fn match_simple_lint_emission < ' tcx > (
284
396
cx : & LateContext < ' tcx > ,
285
397
expr : & ' tcx hir:: Expr < ' _ > ,
286
398
) -> Option < & ' tcx [ hir:: Expr < ' tcx > ] > {
287
399
LINT_EMISSION_FUNCTIONS
288
400
. iter ( )
289
- . find_map ( |emission_fn| match_function_call ( cx, expr, emission_fn) . map ( |args| args ) )
401
+ . find_map ( |emission_fn| match_function_call ( cx, expr, emission_fn) )
290
402
}
291
403
292
404
/// This returns the lint name and the possible applicability of this emission
@@ -316,3 +428,43 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) -
316
428
)
317
429
} )
318
430
}
431
+
432
+ struct ValueTracker < ' a , ' tcx > {
433
+ cx : & ' a LateContext < ' tcx > ,
434
+ value_hir_id : hir:: HirId ,
435
+ value_life : String ,
436
+ }
437
+
438
+ impl < ' a , ' tcx > ValueTracker < ' a , ' tcx > {
439
+ fn is_value_expr ( & self , expr_id : hir:: HirId ) -> bool {
440
+ match self . cx . tcx . hir ( ) . find ( expr_id) {
441
+ Some ( hir:: Node :: Expr ( expr) ) => path_to_local_id ( expr, self . value_hir_id ) ,
442
+ _ => false ,
443
+ }
444
+ }
445
+ }
446
+
447
+ impl < ' a , ' tcx > Delegate < ' tcx > for ValueTracker < ' a , ' tcx > {
448
+ fn consume ( & mut self , _place_with_id : & PlaceWithHirId < ' tcx > , expr_id : hir:: HirId , _: ConsumeMode ) {
449
+ if self . is_value_expr ( expr_id) {
450
+ // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID
451
+ todo ! ( ) ;
452
+ }
453
+ }
454
+
455
+ fn borrow ( & mut self , _place_with_id : & PlaceWithHirId < ' tcx > , expr_id : hir:: HirId , bk : BorrowKind ) {
456
+ if self . is_value_expr ( expr_id) {
457
+ if let BorrowKind :: MutBorrow = bk {
458
+ // TODO xFrednet 2021-02-17: Save the function
459
+ todo ! ( ) ;
460
+ }
461
+ }
462
+ }
463
+
464
+ fn mutate ( & mut self , _assignee_place : & PlaceWithHirId < ' tcx > , expr_id : hir:: HirId ) {
465
+ if self . is_value_expr ( expr_id) {
466
+ // TODO xFrednet 2021-02-17: Save the new value as a mutation
467
+ todo ! ( ) ;
468
+ }
469
+ }
470
+ }
0 commit comments