Skip to content

Commit edc6b00

Browse files
committed
Auto merge of rust-lang#13064 - Alexendoo:lintcheck-shrink-json, r=xFrednet
Reduce the size of lintcheck JSON output Saves about 80% of the size by picking out what we need rather than serialising the whole diagnostic r? `@xFrednet` changelog: none
2 parents 2ad8cdc + eac1aab commit edc6b00

File tree

5 files changed

+61
-76
lines changed

5 files changed

+61
-76
lines changed

lintcheck/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ clap = { version = "4.4", features = ["derive", "env"] }
1616
crossbeam-channel = "0.5.6"
1717
diff = "0.1.13"
1818
flate2 = "1.0"
19+
itertools = "0.12"
1920
rayon = "1.5.1"
2021
serde = { version = "1.0", features = ["derive"] }
2122
serde_json = "1.0.85"

lintcheck/src/driver.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use std::{env, mem};
1111
fn run_clippy(addr: &str) -> Option<i32> {
1212
let driver_info = DriverInfo {
1313
package_name: env::var("CARGO_PKG_NAME").ok()?,
14-
crate_name: env::var("CARGO_CRATE_NAME").ok()?,
15-
version: env::var("CARGO_PKG_VERSION").ok()?,
1614
};
1715

1816
let mut stream = BufReader::new(TcpStream::connect(addr).unwrap());

lintcheck/src/json.rs

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,71 @@
1-
use std::collections::HashMap;
2-
use std::fmt::Write;
31
use std::fs;
4-
use std::hash::Hash;
52
use std::path::Path;
63

4+
use itertools::EitherOrBoth;
5+
use serde::{Deserialize, Serialize};
6+
77
use crate::ClippyWarning;
88

9-
/// Creates the log file output for [`crate::config::OutputFormat::Json`]
10-
pub(crate) fn output(clippy_warnings: &[ClippyWarning]) -> String {
11-
serde_json::to_string(&clippy_warnings).unwrap()
9+
#[derive(Deserialize, Serialize)]
10+
struct LintJson {
11+
lint: String,
12+
file_name: String,
13+
byte_pos: (u32, u32),
14+
rendered: String,
1215
}
1316

14-
fn load_warnings(path: &Path) -> Vec<ClippyWarning> {
15-
let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()));
16-
17-
serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display()))
17+
impl LintJson {
18+
fn key(&self) -> impl Ord + '_ {
19+
(self.file_name.as_str(), self.byte_pos, self.lint.as_str())
20+
}
1821
}
1922

20-
/// Group warnings by their primary span location + lint name
21-
fn create_map(warnings: &[ClippyWarning]) -> HashMap<impl Eq + Hash + '_, Vec<&ClippyWarning>> {
22-
let mut map = HashMap::<_, Vec<_>>::with_capacity(warnings.len());
23-
24-
for warning in warnings {
25-
let span = warning.span();
26-
let key = (&warning.lint_type, &span.file_name, span.byte_start, span.byte_end);
23+
/// Creates the log file output for [`crate::config::OutputFormat::Json`]
24+
pub(crate) fn output(clippy_warnings: Vec<ClippyWarning>) -> String {
25+
let mut lints: Vec<LintJson> = clippy_warnings
26+
.into_iter()
27+
.map(|warning| {
28+
let span = warning.span();
29+
LintJson {
30+
file_name: span.file_name.clone(),
31+
byte_pos: (span.byte_start, span.byte_end),
32+
lint: warning.lint,
33+
rendered: warning.diag.rendered.unwrap(),
34+
}
35+
})
36+
.collect();
37+
lints.sort_by(|a, b| a.key().cmp(&b.key()));
38+
serde_json::to_string(&lints).unwrap()
39+
}
2740

28-
map.entry(key).or_default().push(warning);
29-
}
41+
fn load_warnings(path: &Path) -> Vec<LintJson> {
42+
let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()));
3043

31-
map
44+
serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display()))
3245
}
3346

34-
fn print_warnings(title: &str, warnings: &[&ClippyWarning]) {
47+
fn print_warnings(title: &str, warnings: &[LintJson]) {
3548
if warnings.is_empty() {
3649
return;
3750
}
3851

3952
println!("### {title}");
4053
println!("```");
4154
for warning in warnings {
42-
print!("{}", warning.diag);
55+
print!("{}", warning.rendered);
4356
}
4457
println!("```");
4558
}
4659

47-
fn print_changed_diff(changed: &[(&[&ClippyWarning], &[&ClippyWarning])]) {
48-
fn render(warnings: &[&ClippyWarning]) -> String {
49-
let mut rendered = String::new();
50-
for warning in warnings {
51-
write!(&mut rendered, "{}", warning.diag).unwrap();
52-
}
53-
rendered
54-
}
55-
60+
fn print_changed_diff(changed: &[(LintJson, LintJson)]) {
5661
if changed.is_empty() {
5762
return;
5863
}
5964

6065
println!("### Changed");
6166
println!("```diff");
62-
for &(old, new) in changed {
63-
let old_rendered = render(old);
64-
let new_rendered = render(new);
65-
66-
for change in diff::lines(&old_rendered, &new_rendered) {
67+
for (old, new) in changed {
68+
for change in diff::lines(&old.rendered, &new.rendered) {
6769
use diff::Result::{Both, Left, Right};
6870

6971
match change {
@@ -86,26 +88,19 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path) {
8688
let old_warnings = load_warnings(old_path);
8789
let new_warnings = load_warnings(new_path);
8890

89-
let old_map = create_map(&old_warnings);
90-
let new_map = create_map(&new_warnings);
91-
9291
let mut added = Vec::new();
9392
let mut removed = Vec::new();
9493
let mut changed = Vec::new();
9594

96-
for (key, new) in &new_map {
97-
if let Some(old) = old_map.get(key) {
98-
if old != new {
99-
changed.push((old.as_slice(), new.as_slice()));
100-
}
101-
} else {
102-
added.extend(new);
103-
}
104-
}
105-
106-
for (key, old) in &old_map {
107-
if !new_map.contains_key(key) {
108-
removed.extend(old);
95+
for change in itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key())) {
96+
match change {
97+
EitherOrBoth::Both(old, new) => {
98+
if old.rendered != new.rendered {
99+
changed.push((old, new));
100+
}
101+
},
102+
EitherOrBoth::Left(old) => removed.push(old),
103+
EitherOrBoth::Right(new) => added.push(new),
109104
}
110105
}
111106

lintcheck/src/main.rs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use std::{env, fs, thread};
3838
use cargo_metadata::diagnostic::{Diagnostic, DiagnosticSpan};
3939
use cargo_metadata::Message;
4040
use rayon::prelude::*;
41-
use serde::{Deserialize, Serialize};
41+
use serde::Deserialize;
4242
use walkdir::{DirEntry, WalkDir};
4343

4444
const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
@@ -142,19 +142,17 @@ impl RustcIce {
142142
}
143143

144144
/// A single warning that clippy issued while checking a `Crate`
145-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
145+
#[derive(Debug)]
146146
struct ClippyWarning {
147-
crate_name: String,
148-
crate_version: String,
149-
lint_type: String,
147+
lint: String,
150148
diag: Diagnostic,
151149
}
152150

153151
#[allow(unused)]
154152
impl ClippyWarning {
155-
fn new(mut diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option<Self> {
156-
let lint_type = diag.code.clone()?.code;
157-
if !(lint_type.contains("clippy") || diag.message.contains("clippy"))
153+
fn new(mut diag: Diagnostic) -> Option<Self> {
154+
let lint = diag.code.clone()?.code;
155+
if !(lint.contains("clippy") || diag.message.contains("clippy"))
158156
|| diag.message.contains("could not read cargo metadata")
159157
{
160158
return None;
@@ -164,12 +162,7 @@ impl ClippyWarning {
164162
let rendered = diag.rendered.as_mut().unwrap();
165163
*rendered = strip_ansi_escapes::strip_str(&rendered);
166164

167-
Some(Self {
168-
crate_name: crate_name.to_owned(),
169-
crate_version: crate_version.to_owned(),
170-
lint_type,
171-
diag,
172-
})
165+
Some(Self { lint, diag })
173166
}
174167

175168
fn span(&self) -> &DiagnosticSpan {
@@ -181,15 +174,15 @@ impl ClippyWarning {
181174
let mut file = span.file_name.clone();
182175
let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end);
183176
match format {
184-
OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.diag.message),
177+
OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint, self.diag.message),
185178
OutputFormat::Markdown => {
186179
if file.starts_with("target") {
187180
file.insert_str(0, "../");
188181
}
189182

190183
let mut output = String::from("| ");
191184
write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap();
192-
write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.diag.message).unwrap();
185+
write!(output, r#" | `{:<50}` | "{}" |"#, self.lint, self.diag.message).unwrap();
193186
output.push('\n');
194187
output
195188
},
@@ -473,7 +466,7 @@ impl Crate {
473466
// get all clippy warnings and ICEs
474467
let mut entries: Vec<ClippyCheckOutput> = Message::parse_stream(stdout.as_bytes())
475468
.filter_map(|msg| match msg {
476-
Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
469+
Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message),
477470
_ => None,
478471
})
479472
.map(ClippyCheckOutput::ClippyWarning)
@@ -572,7 +565,7 @@ fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>)
572565
let mut counter: HashMap<&String, usize> = HashMap::new();
573566
warnings
574567
.iter()
575-
.for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1);
568+
.for_each(|wrn| *counter.entry(&wrn.lint).or_insert(0) += 1);
576569

577570
// collect into a tupled list for sorting
578571
let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();
@@ -754,7 +747,7 @@ fn lintcheck(config: LintcheckConfig) {
754747
panic!("Some crates ICEd");
755748
}
756749

757-
json::output(&warnings)
750+
json::output(warnings)
758751
},
759752
};
760753

lintcheck/src/recursive.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ use serde::{Deserialize, Serialize};
1919
#[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)]
2020
pub(crate) struct DriverInfo {
2121
pub package_name: String,
22-
pub crate_name: String,
23-
pub version: String,
2422
}
2523

2624
pub(crate) fn serialize_line<T, W>(value: &T, writer: &mut W)
@@ -65,7 +63,7 @@ fn process_stream(
6563
let messages = stderr
6664
.lines()
6765
.filter_map(|json_msg| serde_json::from_str::<Diagnostic>(json_msg).ok())
68-
.filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version));
66+
.filter_map(ClippyWarning::new);
6967

7068
for message in messages {
7169
sender.send(message).unwrap();

0 commit comments

Comments
 (0)