Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 68d702f

Browse files
committed
Metadata collection lint: Start Applicability value tracking
1 parent 060e0e9 commit 68d702f

File tree

1 file changed

+155
-3
lines changed

1 file changed

+155
-3
lines changed

clippy_lints/src/utils/internal_lints/metadata_collector.rs

Lines changed: 155 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,21 @@ use if_chain::if_chain;
2424
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2525
use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability};
2626
use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
27+
use rustc_middle::ty::BorrowKind;
2728
use rustc_session::{declare_tool_lint, impl_lint_pass};
2829
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;
2933
use serde::Serialize;
30-
use std::fs::OpenOptions;
34+
use std::fs::{self, OpenOptions};
3135
use std::io::prelude::*;
36+
use std::path::Path;
3237

3338
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+
};
3542

3643
/// This is the output file of the lint collector.
3744
const 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+
161186
impl<'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

230336
fn 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+
// ==================================================================
265374
fn 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+
// ==================================================================
283395
fn 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

Comments
 (0)