@@ -24,14 +24,21 @@ use if_chain::if_chain;
2424use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
2525use rustc_hir:: { self as hir, ExprKind , Item , ItemKind , Mutability } ;
2626use rustc_lint:: { CheckLintNameResult , LateContext , LateLintPass , LintContext , LintId } ;
27+ use rustc_middle:: ty:: BorrowKind ;
2728use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
2829use 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;
2933use serde:: Serialize ;
30- use std:: fs:: OpenOptions ;
34+ use std:: fs:: { self , OpenOptions } ;
3135use std:: io:: prelude:: * ;
36+ use std:: path:: Path ;
3237
3338use 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+ } ;
3542
3643/// This is the output file of the lint collector.
3744const OUTPUT_FILE : & str = "metadata_collection.json" ;
@@ -91,6 +98,10 @@ impl Drop for MetadataCollector {
9198 /// You might ask: How hacky is this?
9299 /// My answer: YES
93100 fn drop ( & mut self ) {
101+ if self . lints . is_empty ( ) {
102+ return ;
103+ }
104+
94105 let mut applicability_info = std:: mem:: take ( & mut self . applicability_into ) ;
95106
96107 // Mapping the final data
@@ -99,6 +110,9 @@ impl Drop for MetadataCollector {
99110 . for_each ( |x| x. applicability = applicability_info. remove ( & x. id ) ) ;
100111
101112 // Outputting
113+ if Path :: new ( OUTPUT_FILE ) . exists ( ) {
114+ fs:: remove_file ( OUTPUT_FILE ) . unwrap ( ) ;
115+ }
102116 let mut file = OpenOptions :: new ( ) . write ( true ) . create ( true ) . open ( OUTPUT_FILE ) . unwrap ( ) ;
103117 writeln ! ( file, "{}" , serde_json:: to_string_pretty( & self . lints) . unwrap( ) ) . unwrap ( ) ;
104118 }
@@ -158,6 +172,17 @@ struct ApplicabilityInfo {
158172 applicabilities : FxHashSet < String > ,
159173}
160174
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+
161186impl < ' tcx > LateLintPass < ' tcx > for MetadataCollector {
162187 /// Collecting lint declarations like:
163188 /// ```rust, ignore
@@ -213,6 +238,20 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector {
213238 }
214239 }
215240
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+ /// ```
216255 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
217256 if let Some ( args) = match_simple_lint_emission ( cx, expr) {
218257 if let Some ( ( lint_name, mut applicability) ) = extract_emission_info ( cx, args) {
@@ -225,6 +264,73 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector {
225264 }
226265 }
227266 }
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) )
228334}
229335
230336fn sym_to_string ( sym : Symbol ) -> String {
@@ -262,6 +368,9 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
262368 None
263369}
264370
371+ // ==================================================================
372+ // Lint emission
373+ // ==================================================================
265374fn lint_collection_error_item ( cx : & LateContext < ' _ > , item : & Item < ' _ > , message : & str ) {
266375 span_lint (
267376 cx,
@@ -280,13 +389,16 @@ fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) {
280389 ) ;
281390}
282391
392+ // ==================================================================
393+ // Applicability
394+ // ==================================================================
283395fn match_simple_lint_emission < ' tcx > (
284396 cx : & LateContext < ' tcx > ,
285397 expr : & ' tcx hir:: Expr < ' _ > ,
286398) -> Option < & ' tcx [ hir:: Expr < ' tcx > ] > {
287399 LINT_EMISSION_FUNCTIONS
288400 . 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) )
290402}
291403
292404/// 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<'_>]) -
316428 )
317429 } )
318430}
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