Skip to content

Commit 4f854e3

Browse files
Jacherrblyxyas
authored andcommitted
Add support for ICE reporting in lintcheck
1 parent a79db2a commit 4f854e3

File tree

1 file changed

+65
-27
lines changed

1 file changed

+65
-27
lines changed

lintcheck/src/main.rs

+65-27
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ use std::env::consts::EXE_SUFFIX;
1919
use std::fmt::{self, Write as _};
2020
use std::io::{self, ErrorKind};
2121
use std::path::{Path, PathBuf};
22-
use std::process::Command;
22+
use std::process::{Command, ExitStatus};
2323
use std::sync::atomic::{AtomicUsize, Ordering};
2424
use std::time::Duration;
2525
use std::{env, fs, thread};
2626

27-
use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel};
27+
use cargo_metadata::diagnostic::Diagnostic;
2828
use cargo_metadata::Message;
2929
use rayon::prelude::*;
3030
use serde::{Deserialize, Serialize};
@@ -90,16 +90,43 @@ struct Crate {
9090
options: Option<Vec<String>>,
9191
}
9292

93+
/// A single emitted output from clippy being executed on a crate. It may either be a
94+
/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many
95+
/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution).
96+
#[derive(Debug)]
97+
enum ClippyCheckOutput {
98+
ClippyWarning(ClippyWarning),
99+
RustcIce(RustcIce),
100+
}
101+
102+
#[derive(Debug)]
103+
struct RustcIce {
104+
pub crate_name: String,
105+
pub ice_content: String,
106+
}
107+
impl RustcIce {
108+
pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option<Self> {
109+
if status.code().unwrap_or(0) == 101
110+
/* ice exit status */
111+
{
112+
Some(Self {
113+
crate_name: crate_name.to_owned(),
114+
ice_content: stderr.to_owned(),
115+
})
116+
} else {
117+
None
118+
}
119+
}
120+
}
121+
93122
/// A single warning that clippy issued while checking a `Crate`
94123
#[derive(Debug)]
95124
struct ClippyWarning {
96-
crate_name: String,
97125
file: String,
98126
line: usize,
99127
column: usize,
100128
lint_type: String,
101129
message: String,
102-
is_ice: bool,
103130
}
104131

105132
#[allow(unused)]
@@ -124,13 +151,11 @@ impl ClippyWarning {
124151
};
125152

126153
Some(Self {
127-
crate_name: crate_name.to_owned(),
128154
file,
129155
line: span.line_start,
130156
column: span.column_start,
131157
lint_type,
132158
message: diag.message,
133-
is_ice: diag.level == DiagnosticLevel::Ice,
134159
})
135160
}
136161

@@ -311,7 +336,7 @@ impl Crate {
311336
config: &LintcheckConfig,
312337
lint_filter: &[String],
313338
server: &Option<LintcheckServer>,
314-
) -> Vec<ClippyWarning> {
339+
) -> Vec<ClippyCheckOutput> {
315340
// advance the atomic index by one
316341
let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
317342
// "loop" the index within 0..thread_limit
@@ -335,9 +360,9 @@ impl Crate {
335360
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
336361

337362
let mut cargo_clippy_args = if config.fix {
338-
vec!["--fix", "--"]
363+
vec!["--quiet", "--fix", "--"]
339364
} else {
340-
vec!["--", "--message-format=json", "--"]
365+
vec!["--quiet", "--message-format=json", "--"]
341366
};
342367

343368
let mut clippy_args = Vec::<&str>::new();
@@ -428,14 +453,21 @@ impl Crate {
428453
}
429454

430455
// get all clippy warnings and ICEs
431-
let warnings: Vec<ClippyWarning> = Message::parse_stream(stdout.as_bytes())
456+
let mut entries: Vec<ClippyCheckOutput> = Message::parse_stream(stdout.as_bytes())
432457
.filter_map(|msg| match msg {
433458
Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
434459
_ => None,
435460
})
461+
.map(ClippyCheckOutput::ClippyWarning)
436462
.collect();
437463

438-
warnings
464+
if let Some(ice) = RustcIce::from_stderr_and_status(&self.name, *status, &stderr) {
465+
entries.push(ClippyCheckOutput::RustcIce(ice));
466+
} else if !status.success() {
467+
println!("non-ICE bad exit status for {} {}: {}", self.name, self.version, stderr);
468+
}
469+
470+
entries
439471
}
440472
}
441473

@@ -635,7 +667,7 @@ fn main() {
635667
LintcheckServer::spawn(recursive_options)
636668
});
637669

638-
let mut clippy_warnings: Vec<ClippyWarning> = crates
670+
let mut clippy_entries: Vec<ClippyCheckOutput> = crates
639671
.par_iter()
640672
.flat_map(|krate| {
641673
krate.run_clippy_lints(
@@ -651,28 +683,31 @@ fn main() {
651683
.collect();
652684

653685
if let Some(server) = server {
654-
clippy_warnings.extend(server.warnings());
686+
let server_clippy_entries = server.warnings().map(ClippyCheckOutput::ClippyWarning);
687+
688+
clippy_entries.extend(server_clippy_entries);
655689
}
656690

657691
// if we are in --fix mode, don't change the log files, terminate here
658692
if config.fix {
659693
return;
660694
}
661695

662-
// generate some stats
663-
let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
696+
// split up warnings and ices
697+
let mut warnings: Vec<ClippyWarning> = vec![];
698+
let mut raw_ices: Vec<RustcIce> = vec![];
699+
for entry in clippy_entries {
700+
if let ClippyCheckOutput::ClippyWarning(x) = entry {
701+
warnings.push(x);
702+
} else if let ClippyCheckOutput::RustcIce(x) = entry {
703+
raw_ices.push(x);
704+
}
705+
}
664706

665-
// grab crashes/ICEs, save the crate name and the ice message
666-
let ices: Vec<(&String, &String)> = clippy_warnings
667-
.iter()
668-
.filter(|warning| warning.is_ice)
669-
.map(|w| (&w.crate_name, &w.message))
670-
.collect();
707+
// generate some stats
708+
let (stats_formatted, new_stats) = gather_stats(&warnings);
671709

672-
let mut all_msgs: Vec<String> = clippy_warnings
673-
.iter()
674-
.map(|warn| warn.to_output(config.markdown))
675-
.collect();
710+
let mut all_msgs: Vec<String> = warnings.iter().map(|warn| warn.to_output(config.markdown)).collect();
676711
all_msgs.sort();
677712
all_msgs.push("\n\n### Stats:\n\n".into());
678713
all_msgs.push(stats_formatted);
@@ -686,11 +721,14 @@ fn main() {
686721
}
687722
write!(text, "{}", all_msgs.join("")).unwrap();
688723
text.push_str("\n\n### ICEs:\n");
689-
for (cratename, msg) in &ices {
690-
let _: fmt::Result = write!(text, "{cratename}: '{msg}'");
724+
for ice in &raw_ices {
725+
let _: fmt::Result = write!(text, "{}:\n{}\n========================================\n\n", ice.crate_name, ice.ice_content);
691726
}
692727

693728
println!("Writing logs to {}", config.lintcheck_results_path.display());
729+
if !raw_ices.is_empty() {
730+
println!("WARNING: at least one ICE reported, check log file");
731+
}
694732
fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
695733
fs::write(&config.lintcheck_results_path, text).unwrap();
696734

0 commit comments

Comments
 (0)