Skip to content

Commit eecff6d

Browse files
committed
Auto merge of #12439 - Jacherr:issue-12185, r=blyxyas
fix ice reporting in lintcheck Fixes #12185 This PR fixes the lack of reported ICEs within lintcheck by modifying the way in which data is collected from each crate being linted. Instead of lintcheck only reading `stdout` for warnings, it now also reads `stderr` for any potential ICE (although admittedly, it is not the cleanest method of doing so). If it is detected, it parses the ICE into its message and backtrace separately, and then adds them to the list of outputs via clippy. Once all outputs are collected, the formatter then proceeds to generate the file as normal. Note that this PR also has a couple of side effects: - When clippy fails to process a package, but said failure is not an ICE, the `stderr` will be sent to the console; - Instead of `ClippyWarning` being the primary struct for everything reported, there is now `ClippyCheckOutput`, an enum which splits the outputs into warnings and ICEs. changelog: none
2 parents 08dac85 + 42d0970 commit eecff6d

File tree

1 file changed

+69
-27
lines changed

1 file changed

+69
-27
lines changed

lintcheck/src/main.rs

+69-27
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ use std::env::consts::EXE_SUFFIX;
2626
use std::fmt::{self, Write as _};
2727
use std::io::{self, ErrorKind};
2828
use std::path::{Path, PathBuf};
29-
use std::process::Command;
29+
use std::process::{Command, ExitStatus};
3030
use std::sync::atomic::{AtomicUsize, Ordering};
3131
use std::time::Duration;
3232
use std::{env, fs, thread};
3333

34-
use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel};
34+
use cargo_metadata::diagnostic::Diagnostic;
3535
use cargo_metadata::Message;
3636
use rayon::prelude::*;
3737
use serde::{Deserialize, Serialize};
@@ -97,16 +97,43 @@ struct Crate {
9797
options: Option<Vec<String>>,
9898
}
9999

100+
/// A single emitted output from clippy being executed on a crate. It may either be a
101+
/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many
102+
/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution).
103+
#[derive(Debug)]
104+
enum ClippyCheckOutput {
105+
ClippyWarning(ClippyWarning),
106+
RustcIce(RustcIce),
107+
}
108+
109+
#[derive(Debug)]
110+
struct RustcIce {
111+
pub crate_name: String,
112+
pub ice_content: String,
113+
}
114+
impl RustcIce {
115+
pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option<Self> {
116+
if status.code().unwrap_or(0) == 101
117+
/* ice exit status */
118+
{
119+
Some(Self {
120+
crate_name: crate_name.to_owned(),
121+
ice_content: stderr.to_owned(),
122+
})
123+
} else {
124+
None
125+
}
126+
}
127+
}
128+
100129
/// A single warning that clippy issued while checking a `Crate`
101130
#[derive(Debug)]
102131
struct ClippyWarning {
103-
crate_name: String,
104132
file: String,
105133
line: usize,
106134
column: usize,
107135
lint_type: String,
108136
message: String,
109-
is_ice: bool,
110137
}
111138

112139
#[allow(unused)]
@@ -131,13 +158,11 @@ impl ClippyWarning {
131158
};
132159

133160
Some(Self {
134-
crate_name: crate_name.to_owned(),
135161
file,
136162
line: span.line_start,
137163
column: span.column_start,
138164
lint_type,
139165
message: diag.message,
140-
is_ice: diag.level == DiagnosticLevel::Ice,
141166
})
142167
}
143168

@@ -318,7 +343,7 @@ impl Crate {
318343
config: &LintcheckConfig,
319344
lint_filter: &[String],
320345
server: &Option<LintcheckServer>,
321-
) -> Vec<ClippyWarning> {
346+
) -> Vec<ClippyCheckOutput> {
322347
// advance the atomic index by one
323348
let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
324349
// "loop" the index within 0..thread_limit
@@ -342,9 +367,9 @@ impl Crate {
342367
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
343368

344369
let mut cargo_clippy_args = if config.fix {
345-
vec!["--fix", "--"]
370+
vec!["--quiet", "--fix", "--"]
346371
} else {
347-
vec!["--", "--message-format=json", "--"]
372+
vec!["--quiet", "--message-format=json", "--"]
348373
};
349374

350375
let mut clippy_args = Vec::<&str>::new();
@@ -435,14 +460,21 @@ impl Crate {
435460
}
436461

437462
// get all clippy warnings and ICEs
438-
let warnings: Vec<ClippyWarning> = Message::parse_stream(stdout.as_bytes())
463+
let mut entries: Vec<ClippyCheckOutput> = Message::parse_stream(stdout.as_bytes())
439464
.filter_map(|msg| match msg {
440465
Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
441466
_ => None,
442467
})
468+
.map(ClippyCheckOutput::ClippyWarning)
443469
.collect();
444470

445-
warnings
471+
if let Some(ice) = RustcIce::from_stderr_and_status(&self.name, *status, &stderr) {
472+
entries.push(ClippyCheckOutput::RustcIce(ice));
473+
} else if !status.success() {
474+
println!("non-ICE bad exit status for {} {}: {}", self.name, self.version, stderr);
475+
}
476+
477+
entries
446478
}
447479
}
448480

@@ -642,7 +674,7 @@ fn main() {
642674
LintcheckServer::spawn(recursive_options)
643675
});
644676

645-
let mut clippy_warnings: Vec<ClippyWarning> = crates
677+
let mut clippy_entries: Vec<ClippyCheckOutput> = crates
646678
.par_iter()
647679
.flat_map(|krate| {
648680
krate.run_clippy_lints(
@@ -658,28 +690,31 @@ fn main() {
658690
.collect();
659691

660692
if let Some(server) = server {
661-
clippy_warnings.extend(server.warnings());
693+
let server_clippy_entries = server.warnings().map(ClippyCheckOutput::ClippyWarning);
694+
695+
clippy_entries.extend(server_clippy_entries);
662696
}
663697

664698
// if we are in --fix mode, don't change the log files, terminate here
665699
if config.fix {
666700
return;
667701
}
668702

669-
// generate some stats
670-
let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
703+
// split up warnings and ices
704+
let mut warnings: Vec<ClippyWarning> = vec![];
705+
let mut raw_ices: Vec<RustcIce> = vec![];
706+
for entry in clippy_entries {
707+
if let ClippyCheckOutput::ClippyWarning(x) = entry {
708+
warnings.push(x);
709+
} else if let ClippyCheckOutput::RustcIce(x) = entry {
710+
raw_ices.push(x);
711+
}
712+
}
671713

672-
// grab crashes/ICEs, save the crate name and the ice message
673-
let ices: Vec<(&String, &String)> = clippy_warnings
674-
.iter()
675-
.filter(|warning| warning.is_ice)
676-
.map(|w| (&w.crate_name, &w.message))
677-
.collect();
714+
// generate some stats
715+
let (stats_formatted, new_stats) = gather_stats(&warnings);
678716

679-
let mut all_msgs: Vec<String> = clippy_warnings
680-
.iter()
681-
.map(|warn| warn.to_output(config.markdown))
682-
.collect();
717+
let mut all_msgs: Vec<String> = warnings.iter().map(|warn| warn.to_output(config.markdown)).collect();
683718
all_msgs.sort();
684719
all_msgs.push("\n\n### Stats:\n\n".into());
685720
all_msgs.push(stats_formatted);
@@ -693,11 +728,18 @@ fn main() {
693728
}
694729
write!(text, "{}", all_msgs.join("")).unwrap();
695730
text.push_str("\n\n### ICEs:\n");
696-
for (cratename, msg) in &ices {
697-
let _: fmt::Result = write!(text, "{cratename}: '{msg}'");
731+
for ice in &raw_ices {
732+
let _: fmt::Result = write!(
733+
text,
734+
"{}:\n{}\n========================================\n\n",
735+
ice.crate_name, ice.ice_content
736+
);
698737
}
699738

700739
println!("Writing logs to {}", config.lintcheck_results_path.display());
740+
if !raw_ices.is_empty() {
741+
println!("WARNING: at least one ICE reported, check log file");
742+
}
701743
fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
702744
fs::write(&config.lintcheck_results_path, text).unwrap();
703745

0 commit comments

Comments
 (0)