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

Commit 4fc9603

Browse files
committed
Metadata collection: Rounding up the implementation
1 parent 35844d0 commit 4fc9603

File tree

6 files changed

+88
-5904
lines changed

6 files changed

+88
-5904
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ out
2929

3030
# gh pages docs
3131
util/gh-pages/lints.json
32-
# **/metadata_collection.json
32+
**/metadata_collection.json
3333

3434
# rustfmt backups
3535
*.rs.bk

clippy_lints/src/utils/internal_lints/metadata_collector.rs

Lines changed: 84 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,21 @@
77
//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
88
//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
99
//! a simple mistake)
10-
//!
11-
//! The metadata currently contains:
12-
//! - [x] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303)
13-
//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492)
14-
//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310)
15-
16-
// # Applicability
17-
// - TODO xFrednet 2021-01-17: Find lint emit and collect applicability
18-
// - TODO xFrednet 2021-02-28: 4x weird emission forwarding
19-
// - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name
20-
// - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from
21-
// function.
22-
// - See clippy_lints/src/methods/mod.rs@lint_binary_expr_with_method_call
23-
// - TODO xFrednet 2021-02-28: 2x lint from local from function call
24-
// - See clippy_lints/src/misc.rs@check_binary
25-
// - TODO xFrednet 2021-02-28: 2x lint from local from method call
26-
// - See clippy_lints/src/non_copy_const.rs@lint
10+
2711
// # NITs
2812
// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames
2913

3014
use if_chain::if_chain;
3115
use rustc_data_structures::fx::FxHashMap;
32-
use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, def::DefKind};
16+
use rustc_hir::{
17+
self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
18+
};
3319
use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
3420
use rustc_middle::hir::map::Map;
3521
use rustc_session::{declare_tool_lint, impl_lint_pass};
3622
use rustc_span::{sym, Loc, Span, Symbol};
37-
use serde::Serialize;
23+
use serde::{ser::SerializeStruct, Serialize, Serializer};
24+
use std::collections::BinaryHeap;
3825
use std::fs::{self, OpenOptions};
3926
use std::io::prelude::*;
4027
use std::path::Path;
@@ -47,7 +34,7 @@ use crate::utils::{
4734
/// This is the output file of the lint collector.
4835
const OUTPUT_FILE: &str = "../metadata_collection.json";
4936
/// These lints are excluded from the export.
50-
const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"];
37+
const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"];
5138
/// These groups will be ignored by the lint group matcher. This is useful for collections like
5239
/// `clippy::all`
5340
const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
@@ -107,7 +94,7 @@ declare_clippy_lint! {
10794
/// }
10895
/// ```
10996
pub INTERNAL_METADATA_COLLECTOR,
110-
internal,
97+
internal_warn,
11198
"A busy bee collection metadata about lints"
11299
}
113100

@@ -116,22 +103,28 @@ impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
116103
#[allow(clippy::module_name_repetitions)]
117104
#[derive(Debug, Clone, Default)]
118105
pub struct MetadataCollector {
119-
lints: Vec<LintMetadata>,
106+
/// All collected lints
107+
///
108+
/// We use a Heap here to have the lints added in alphabetic order in the export
109+
lints: BinaryHeap<LintMetadata>,
120110
applicability_into: FxHashMap<String, ApplicabilityInfo>,
121111
}
122112

123113
impl Drop for MetadataCollector {
124114
/// You might ask: How hacky is this?
125115
/// My answer: YES
126116
fn drop(&mut self) {
117+
// The metadata collector gets dropped twice, this makes sure that we only write
118+
// when the list is full
127119
if self.lints.is_empty() {
128120
return;
129121
}
130122

131123
let mut applicability_info = std::mem::take(&mut self.applicability_into);
132124

133125
// Mapping the final data
134-
self.lints
126+
let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
127+
lints
135128
.iter_mut()
136129
.for_each(|x| x.applicability = applicability_info.remove(&x.id));
137130

@@ -140,11 +133,11 @@ impl Drop for MetadataCollector {
140133
fs::remove_file(OUTPUT_FILE).unwrap();
141134
}
142135
let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
143-
writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap();
136+
writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
144137
}
145138
}
146139

147-
#[derive(Debug, Clone, Serialize)]
140+
#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
148141
struct LintMetadata {
149142
id: String,
150143
id_span: SerializableSpan,
@@ -155,6 +148,24 @@ struct LintMetadata {
155148
applicability: Option<ApplicabilityInfo>,
156149
}
157150

151+
// impl Ord for LintMetadata {
152+
// fn cmp(&self, other: &Self) -> Ordering {
153+
// self.id.cmp(&other.id)
154+
// }
155+
// }
156+
//
157+
// impl PartialOrd for LintMetadata {
158+
// fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
159+
// Some(self.cmp(other))
160+
// }
161+
// }
162+
//
163+
// impl PartialEq for LintMetadata {
164+
// fn eq(&self, other: &Self) -> bool {
165+
// self.id == other.id
166+
// }
167+
// }
168+
158169
impl LintMetadata {
159170
fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self {
160171
Self {
@@ -167,7 +178,7 @@ impl LintMetadata {
167178
}
168179
}
169180

170-
#[derive(Debug, Clone, Serialize)]
181+
#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
171182
struct SerializableSpan {
172183
path: String,
173184
line: usize,
@@ -194,25 +205,30 @@ impl SerializableSpan {
194205
}
195206
}
196207

197-
#[derive(Debug, Clone, Default, Serialize)]
208+
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
198209
struct ApplicabilityInfo {
199210
/// Indicates if any of the lint emissions uses multiple spans. This is related to
200211
/// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
201212
/// currently not be applied automatically.
202-
is_multi_suggestion: bool,
203-
applicability: Option<String>,
204-
}
205-
206-
#[allow(dead_code)]
207-
fn log_to_file(msg: &str) {
208-
let mut file = OpenOptions::new()
209-
.write(true)
210-
.append(true)
211-
.create(true)
212-
.open("metadata-lint.log")
213-
.unwrap();
214-
215-
write!(file, "{}", msg).unwrap();
213+
is_multi_part_suggestion: bool,
214+
applicability: Option<usize>,
215+
}
216+
217+
impl Serialize for ApplicabilityInfo {
218+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
219+
where
220+
S: Serializer,
221+
{
222+
let index = self.applicability.unwrap_or_default();
223+
224+
let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
225+
s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
226+
s.serialize_field(
227+
"applicability",
228+
&paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
229+
)?;
230+
s.end()
231+
}
216232
}
217233

218234
impl<'hir> LateLintPass<'hir> for MetadataCollector {
@@ -266,16 +282,20 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
266282
/// ```
267283
fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
268284
if let Some(args) = match_lint_emission(cx, expr) {
269-
let mut emission_info = extract_complex_emission_info(cx, args);
285+
let mut emission_info = extract_emission_info(cx, args);
270286
if emission_info.is_empty() {
271-
lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it");
287+
// See:
288+
// - src/misc.rs:734:9
289+
// - src/methods/mod.rs:3545:13
290+
// - src/methods/mod.rs:3496:13
291+
// We are basically unable to resolve the lint name it self.
272292
return;
273293
}
274294

275295
for (lint_name, applicability, is_multi_part) in emission_info.drain(..) {
276296
let app_info = self.applicability_into.entry(lint_name).or_default();
277297
app_info.applicability = applicability;
278-
app_info.is_multi_suggestion = is_multi_part;
298+
app_info.is_multi_part_suggestion = is_multi_part;
279299
}
280300
}
281301
}
@@ -289,7 +309,7 @@ fn sym_to_string(sym: Symbol) -> String {
289309
}
290310

291311
fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
292-
extract_attr_docs(item).or_else(|| {
312+
extract_attr_docs(cx, item).or_else(|| {
293313
lint_collection_error_item(cx, item, "could not collect the lint documentation");
294314
None
295315
})
@@ -305,8 +325,10 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<St
305325
/// ```
306326
///
307327
/// Would result in `Hello world!\n=^.^=\n`
308-
fn extract_attr_docs(item: &Item<'_>) -> Option<String> {
309-
item.attrs
328+
fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
329+
cx.tcx
330+
.hir()
331+
.attrs(item.hir_id())
310332
.iter()
311333
.filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string()))
312334
.reduce(|mut acc, sym| {
@@ -357,15 +379,6 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s
357379
);
358380
}
359381

360-
fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) {
361-
span_lint(
362-
cx,
363-
INTERNAL_METADATA_COLLECTOR,
364-
span,
365-
&format!("Metadata collection error: {}", message),
366-
);
367-
}
368-
369382
// ==================================================================
370383
// Applicability
371384
// ==================================================================
@@ -377,11 +390,15 @@ fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>)
377390
.find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
378391
}
379392

380-
fn extract_complex_emission_info<'hir>(
393+
fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
394+
a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
395+
}
396+
397+
fn extract_emission_info<'hir>(
381398
cx: &LateContext<'hir>,
382399
args: &'hir [hir::Expr<'hir>],
383-
) -> Vec<(String, Option<String>, bool)> {
384-
let mut lints= Vec::new();
400+
) -> Vec<(String, Option<usize>, bool)> {
401+
let mut lints = Vec::new();
385402
let mut applicability = None;
386403
let mut multi_part = false;
387404

@@ -401,7 +418,10 @@ fn extract_complex_emission_info<'hir>(
401418
}
402419
}
403420

404-
lints.drain(..).map(|lint_name| (lint_name, applicability.clone(), multi_part)).collect()
421+
lints
422+
.drain(..)
423+
.map(|lint_name| (lint_name, applicability, multi_part))
424+
.collect()
405425
}
406426

407427
/// Resolves the possible lints that this expression could reference
@@ -412,7 +432,7 @@ fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<Str
412432
}
413433

414434
/// This function tries to resolve the linked applicability to the given expression.
415-
fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<String> {
435+
fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
416436
let mut resolver = ApplicabilityResolver::new(cx);
417437
resolver.visit_expr(expr);
418438
resolver.complete()
@@ -457,7 +477,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
457477
if_chain! {
458478
if let ExprKind::Path(qpath) = &expr.kind;
459479
if let QPath::Resolved(_, path) = qpath;
460-
480+
461481
let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr));
462482
if match_type(self.cx, expr_ty, &paths::LINT);
463483
then {
@@ -492,15 +512,11 @@ impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
492512
}
493513

494514
fn add_new_index(&mut self, new_index: usize) {
495-
self.applicability_index = self
496-
.applicability_index
497-
.map_or(new_index, |old_index| old_index.min(new_index))
498-
.into();
515+
self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
499516
}
500517

501-
fn complete(self) -> Option<String> {
518+
fn complete(self) -> Option<usize> {
502519
self.applicability_index
503-
.map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string())
504520
}
505521
}
506522

clippy_utils/src/paths.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
99
pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
1010
#[cfg(feature = "metadata-collector-lint")]
1111
pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
12-
["rustc_lint_defs", "Applicability", "MachineApplicable"],
13-
["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
14-
["rustc_lint_defs", "Applicability", "HasPlaceholders"],
1512
["rustc_lint_defs", "Applicability", "Unspecified"],
13+
["rustc_lint_defs", "Applicability", "HasPlaceholders"],
14+
["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
15+
["rustc_lint_defs", "Applicability", "MachineApplicable"],
1616
];
1717
#[cfg(feature = "metadata-collector-lint")]
1818
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];

0 commit comments

Comments
 (0)