Skip to content

Commit 2d04bcd

Browse files
committed
Auto merge of #10989 - Muscraft:add-auto-fix-note, r=weihanglo
add a note that some warnings (and/or errors) can be auto-fixed This adds a note that some warnings (and/or errors) can be auto-fixed by running `cargo --fix`. This only supports `cargo check` as we can't show the same note for `cargo clippy` since it is an external subcommand. The message for the note was taken from [#10976 (comment)](#10976 (comment)).
2 parents 9e0b10f + 8ff8eaa commit 2d04bcd

File tree

4 files changed

+449
-20
lines changed

4 files changed

+449
-20
lines changed

src/cargo/core/compiler/job_queue.rs

Lines changed: 123 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,8 @@ struct DrainState<'cfg> {
129129
messages: Arc<Queue<Message>>,
130130
/// Diagnostic deduplication support.
131131
diag_dedupe: DiagDedupe<'cfg>,
132-
/// Count of warnings, used to print a summary after the job succeeds.
133-
///
134-
/// First value is the total number of warnings, and the second value is
135-
/// the number that were suppressed because they were duplicates of a
136-
/// previous warning.
137-
warning_count: HashMap<JobId, (usize, usize)>,
132+
/// Count of warnings, used to print a summary after the job succeeds
133+
warning_count: HashMap<JobId, WarningCount>,
138134
active: HashMap<JobId, Unit>,
139135
compiled: HashSet<PackageId>,
140136
documented: HashSet<PackageId>,
@@ -170,6 +166,50 @@ struct DrainState<'cfg> {
170166
per_package_future_incompat_reports: Vec<FutureIncompatReportPackage>,
171167
}
172168

169+
/// Count of warnings, used to print a summary after the job succeeds
170+
#[derive(Default)]
171+
pub struct WarningCount {
172+
/// total number of warnings
173+
pub total: usize,
174+
/// number of warnings that were suppressed because they
175+
/// were duplicates of a previous warning
176+
pub duplicates: usize,
177+
/// number of fixable warnings set to `NotAllowed`
178+
/// if any errors have been seen ofr the current
179+
/// target
180+
pub fixable: FixableWarnings,
181+
}
182+
183+
impl WarningCount {
184+
/// If an error is seen this should be called
185+
/// to set `fixable` to `NotAllowed`
186+
fn disallow_fixable(&mut self) {
187+
self.fixable = FixableWarnings::NotAllowed;
188+
}
189+
190+
/// Checks fixable if warnings are allowed
191+
/// fixable warnings are allowed if no
192+
/// errors have been seen for the current
193+
/// target. If an error was seen `fixable`
194+
/// will be `NotAllowed`.
195+
fn fixable_allowed(&self) -> bool {
196+
match &self.fixable {
197+
FixableWarnings::NotAllowed => false,
198+
_ => true,
199+
}
200+
}
201+
}
202+
203+
/// Used to keep track of how many fixable warnings there are
204+
/// and if fixable warnings are allowed
205+
#[derive(Default)]
206+
pub enum FixableWarnings {
207+
NotAllowed,
208+
#[default]
209+
Zero,
210+
Positive(usize),
211+
}
212+
173213
pub struct ErrorsDuringDrain {
174214
pub count: usize,
175215
}
@@ -311,10 +351,12 @@ enum Message {
311351
id: JobId,
312352
level: String,
313353
diag: String,
354+
fixable: bool,
314355
},
315356
WarningCount {
316357
id: JobId,
317358
emitted: bool,
359+
fixable: bool,
318360
},
319361
FixDiagnostic(diagnostic_server::Message),
320362
Token(io::Result<Acquired>),
@@ -363,20 +405,22 @@ impl<'a, 'cfg> JobState<'a, 'cfg> {
363405
Ok(())
364406
}
365407

366-
pub fn emit_diag(&self, level: String, diag: String) -> CargoResult<()> {
408+
pub fn emit_diag(&self, level: String, diag: String, fixable: bool) -> CargoResult<()> {
367409
if let Some(dedupe) = self.output {
368410
let emitted = dedupe.emit_diag(&diag)?;
369411
if level == "warning" {
370412
self.messages.push(Message::WarningCount {
371413
id: self.id,
372414
emitted,
415+
fixable,
373416
});
374417
}
375418
} else {
376419
self.messages.push_bounded(Message::Diagnostic {
377420
id: self.id,
378421
level,
379422
diag,
423+
fixable,
380424
});
381425
}
382426
Ok(())
@@ -679,14 +723,28 @@ impl<'cfg> DrainState<'cfg> {
679723
shell.print_ansi_stderr(err.as_bytes())?;
680724
shell.err().write_all(b"\n")?;
681725
}
682-
Message::Diagnostic { id, level, diag } => {
726+
Message::Diagnostic {
727+
id,
728+
level,
729+
diag,
730+
fixable,
731+
} => {
683732
let emitted = self.diag_dedupe.emit_diag(&diag)?;
684733
if level == "warning" {
685-
self.bump_warning_count(id, emitted);
734+
self.bump_warning_count(id, emitted, fixable);
735+
}
736+
if level == "error" {
737+
let cnts = self.warning_count.entry(id).or_default();
738+
// If there is an error, the `cargo fix` message should not show
739+
cnts.disallow_fixable();
686740
}
687741
}
688-
Message::WarningCount { id, emitted } => {
689-
self.bump_warning_count(id, emitted);
742+
Message::WarningCount {
743+
id,
744+
emitted,
745+
fixable,
746+
} => {
747+
self.bump_warning_count(id, emitted, fixable);
690748
}
691749
Message::FixDiagnostic(msg) => {
692750
self.print.print(&msg)?;
@@ -1127,19 +1185,34 @@ impl<'cfg> DrainState<'cfg> {
11271185
Ok(())
11281186
}
11291187

1130-
fn bump_warning_count(&mut self, id: JobId, emitted: bool) {
1188+
fn bump_warning_count(&mut self, id: JobId, emitted: bool, fixable: bool) {
11311189
let cnts = self.warning_count.entry(id).or_default();
1132-
cnts.0 += 1;
1190+
cnts.total += 1;
11331191
if !emitted {
1134-
cnts.1 += 1;
1192+
cnts.duplicates += 1;
1193+
// Don't add to fixable if it's already been emitted
1194+
} else if fixable {
1195+
// Do not add anything to the fixable warning count if
1196+
// is `NotAllowed` since that indicates there was an
1197+
// error while building this `Unit`
1198+
if cnts.fixable_allowed() {
1199+
cnts.fixable = match cnts.fixable {
1200+
FixableWarnings::NotAllowed => FixableWarnings::NotAllowed,
1201+
FixableWarnings::Zero => FixableWarnings::Positive(1),
1202+
FixableWarnings::Positive(fixable) => FixableWarnings::Positive(fixable + 1),
1203+
};
1204+
}
11351205
}
11361206
}
11371207

11381208
/// Displays a final report of the warnings emitted by a particular job.
11391209
fn report_warning_count(&mut self, config: &Config, id: JobId) {
11401210
let count = match self.warning_count.remove(&id) {
1141-
Some(count) => count,
1142-
None => return,
1211+
// An error could add an entry for a `Unit`
1212+
// with 0 warnings but having fixable
1213+
// warnings be disallowed
1214+
Some(count) if count.total > 0 => count,
1215+
None | Some(_) => return,
11431216
};
11441217
let unit = &self.active[&id];
11451218
let mut message = format!("`{}` ({}", unit.pkg.name(), unit.target.description_named());
@@ -1151,15 +1224,47 @@ impl<'cfg> DrainState<'cfg> {
11511224
message.push_str(" doc");
11521225
}
11531226
message.push_str(") generated ");
1154-
match count.0 {
1227+
match count.total {
11551228
1 => message.push_str("1 warning"),
11561229
n => drop(write!(message, "{} warnings", n)),
11571230
};
1158-
match count.1 {
1231+
match count.duplicates {
11591232
0 => {}
11601233
1 => message.push_str(" (1 duplicate)"),
11611234
n => drop(write!(message, " ({} duplicates)", n)),
11621235
}
1236+
// Only show the `cargo fix` message if its a local `Unit`
1237+
if unit.is_local() && config.nightly_features_allowed {
1238+
// Do not show this if there are any errors or no fixable warnings
1239+
if let FixableWarnings::Positive(fixable) = count.fixable {
1240+
// `cargo fix` doesnt have an option for custom builds
1241+
if !unit.target.is_custom_build() {
1242+
let mut command = {
1243+
let named = unit.target.description_named();
1244+
// if its a lib we need to add the package to fix
1245+
if unit.target.is_lib() {
1246+
format!("{} -p {}", named, unit.pkg.name())
1247+
} else {
1248+
named
1249+
}
1250+
};
1251+
if unit.mode.is_rustc_test()
1252+
&& !(unit.target.is_test() || unit.target.is_bench())
1253+
{
1254+
command.push_str(" --tests");
1255+
}
1256+
let mut suggestions = format!("{} suggestion", fixable);
1257+
if fixable > 1 {
1258+
suggestions.push_str("s")
1259+
}
1260+
drop(write!(
1261+
message,
1262+
" (run `cargo fix --{}` to apply {})",
1263+
command, suggestions
1264+
))
1265+
}
1266+
}
1267+
}
11631268
// Errors are ignored here because it is tricky to handle them
11641269
// correctly, and they aren't important.
11651270
drop(config.shell().warn(message));

src/cargo/core/compiler/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use crate::util::interning::InternedString;
6161
use crate::util::machine_message::{self, Message};
6262
use crate::util::{add_path_args, internal, iter_join_onto, profile};
6363
use cargo_util::{paths, ProcessBuilder, ProcessError};
64+
use rustfix::diagnostics::{Applicability, Diagnostic};
6465

6566
const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
6667

@@ -1420,7 +1421,10 @@ fn on_stderr_line_inner(
14201421
rendered: String,
14211422
message: String,
14221423
level: String,
1424+
// `children: Vec<Diagnostic>` if we need to check them recursively
1425+
children: Vec<Diagnostic>,
14231426
}
1427+
14241428
if let Ok(mut msg) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
14251429
if msg.message.starts_with("aborting due to")
14261430
|| msg.message.ends_with("warning emitted")
@@ -1443,8 +1447,19 @@ fn on_stderr_line_inner(
14431447
.expect("strip should never fail")
14441448
};
14451449
if options.show_diagnostics {
1450+
let machine_applicable: bool = msg
1451+
.children
1452+
.iter()
1453+
.map(|child| {
1454+
child
1455+
.spans
1456+
.iter()
1457+
.filter_map(|span| span.suggestion_applicability)
1458+
.any(|app| app == Applicability::MachineApplicable)
1459+
})
1460+
.any(|b| b);
14461461
count_diagnostic(&msg.level, options);
1447-
state.emit_diag(msg.level, rendered)?;
1462+
state.emit_diag(msg.level, rendered, machine_applicable)?;
14481463
}
14491464
return Ok(true);
14501465
}

0 commit comments

Comments
 (0)