Skip to content

Commit 74f2b40

Browse files
committed
Auto merge of #7896 - ehuss:internal-errors, r=alexcrichton
Rework internal errors. This changes the behavior of "internal" errors, which were previously hidden and only displayed with `--verbose`. The changes here: - `internal` now means "a cargo bug", and is always displayed and requests that the user file a bug report. - Added `VerboseError` to signify an error that should only be displayed with `--verbose`. This is only used in one place, to display the `rustc` compiler command if the compiler exited with a normal error code (i.e. not a crash). - Audited the uses of internal errors, and promoted some to normal errors, as they seemed to contain useful information, but weren't necessarily bugs in cargo. - Added some details to some errors. - Sometimes the "run with --verbose" message wasn't being printed when I think it should. This happened when rustc failed while other rustc processes were running. Another case was with `cargo install` installing multiple packages, and one of them fails.
2 parents 914e31d + 0d44a82 commit 74f2b40

File tree

21 files changed

+215
-136
lines changed

21 files changed

+215
-136
lines changed

crates/cargo-test-support/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,7 @@ fn substitute_macros(input: &str) -> String {
16451645
("[FINISHED]", " Finished"),
16461646
("[ERROR]", "error:"),
16471647
("[WARNING]", "warning:"),
1648+
("[NOTE]", "note:"),
16481649
("[DOCUMENTING]", " Documenting"),
16491650
("[FRESH]", " Fresh"),
16501651
("[UPDATING]", " Updating"),
@@ -1666,7 +1667,6 @@ fn substitute_macros(input: &str) -> String {
16661667
("[IGNORED]", " Ignored"),
16671668
("[INSTALLED]", " Installed"),
16681669
("[REPLACED]", " Replaced"),
1669-
("[NOTE]", " Note"),
16701670
];
16711671
let mut result = input.to_owned();
16721672
for &(pat, subst) in &macros {

src/cargo/core/compiler/context/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use jobserver::Client;
99
use crate::core::compiler::{self, compilation, Unit};
1010
use crate::core::PackageId;
1111
use crate::util::errors::{CargoResult, CargoResultExt};
12-
use crate::util::{internal, profile, Config};
12+
use crate::util::{profile, Config};
1313

1414
use super::build_plan::BuildPlan;
1515
use super::custom_build::{self, BuildDeps, BuildScriptOutputs, BuildScripts};
@@ -313,11 +313,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
313313
self.files_mut()
314314
.host
315315
.prepare()
316-
.chain_err(|| internal("couldn't prepare build directories"))?;
316+
.chain_err(|| "couldn't prepare build directories")?;
317317
for target in self.files.as_mut().unwrap().target.values_mut() {
318318
target
319319
.prepare()
320-
.chain_err(|| internal("couldn't prepare build directories"))?;
320+
.chain_err(|| "couldn't prepare build directories")?;
321321
}
322322

323323
self.compilation.host_deps_output = self.files_mut().host.deps().to_path_buf();

src/cargo/core/compiler/custom_build.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,8 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
292292
//
293293
// If we have an old build directory, then just move it into place,
294294
// otherwise create it!
295-
paths::create_dir_all(&script_out_dir).chain_err(|| {
296-
internal(
297-
"failed to create script output directory for \
298-
build command",
299-
)
300-
})?;
295+
paths::create_dir_all(&script_out_dir)
296+
.chain_err(|| "failed to create script output directory for build command")?;
301297

302298
// For all our native lib dependencies, pick up their metadata to pass
303299
// along to this custom build command. We're also careful to augment our

src/cargo/core/compiler/job_queue.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ use super::job::{
7171
use super::timings::Timings;
7272
use super::{BuildContext, BuildPlan, CompileMode, Context, Unit};
7373
use crate::core::{PackageId, TargetKind};
74-
use crate::handle_error;
7574
use crate::util;
7675
use crate::util::diagnostic_server::{self, DiagnosticPrinter};
7776
use crate::util::{internal, profile, CargoResult, CargoResultExt, ProcessBuilder};
@@ -531,7 +530,7 @@ impl<'a, 'cfg> DrainState<'a, 'cfg> {
531530
self.emit_warnings(Some(msg), &unit, cx)?;
532531

533532
if !self.active.is_empty() {
534-
handle_error(&e, &mut *cx.bcx.config.shell());
533+
crate::display_error(&e, &mut *cx.bcx.config.shell());
535534
cx.bcx.config.shell().warn(
536535
"build failed, waiting for other \
537536
jobs to finish...",

src/cargo/core/compiler/mod.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub use crate::core::compiler::unit::{Unit, UnitInterner};
4444
use crate::core::manifest::TargetSourcePath;
4545
use crate::core::profiles::{Lto, PanicStrategy, Profile};
4646
use crate::core::{Edition, Feature, InternedString, PackageId, Target};
47-
use crate::util::errors::{self, CargoResult, CargoResultExt, Internal, ProcessError};
47+
use crate::util::errors::{self, CargoResult, CargoResultExt, ProcessError, VerboseError};
4848
use crate::util::machine_message::Message;
4949
use crate::util::{self, machine_message, ProcessBuilder};
5050
use crate::util::{internal, join_paths, paths, profile};
@@ -262,15 +262,15 @@ fn rustc<'a, 'cfg>(
262262
}
263263
}
264264

265-
fn internal_if_simple_exit_code(err: Error) -> Error {
265+
fn verbose_if_simple_exit_code(err: Error) -> Error {
266266
// If a signal on unix (`code == None`) or an abnormal termination
267267
// on Windows (codes like `0xC0000409`), don't hide the error details.
268268
match err
269269
.downcast_ref::<ProcessError>()
270270
.as_ref()
271271
.and_then(|perr| perr.exit.and_then(|e| e.code()))
272272
{
273-
Some(n) if errors::is_simple_exit_code(n) => Internal::new(err).into(),
273+
Some(n) if errors::is_simple_exit_code(n) => VerboseError::new(err).into(),
274274
_ => err,
275275
}
276276
}
@@ -288,7 +288,7 @@ fn rustc<'a, 'cfg>(
288288
&mut |line| on_stdout_line(state, line, package_id, &target),
289289
&mut |line| on_stderr_line(state, line, package_id, &target, &mut output_options),
290290
)
291-
.map_err(internal_if_simple_exit_code)
291+
.map_err(verbose_if_simple_exit_code)
292292
.chain_err(|| format!("could not compile `{}`.", name))?;
293293
}
294294

@@ -302,8 +302,7 @@ fn rustc<'a, 'cfg>(
302302
.replace(&real_name, &crate_name),
303303
);
304304
if src.exists() && src.file_name() != dst.file_name() {
305-
fs::rename(&src, &dst)
306-
.chain_err(|| internal(format!("could not rename crate {:?}", src)))?;
305+
fs::rename(&src, &dst).chain_err(|| format!("could not rename crate {:?}", src))?;
307306
}
308307
}
309308

src/cargo/core/compiler/output_depinfo.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResul
4444
};
4545
relpath
4646
.to_str()
47-
.ok_or_else(|| internal("path not utf-8"))
47+
.ok_or_else(|| internal(format!("path `{:?}` not utf-8", relpath)))
4848
.map(|f| f.replace(" ", "\\ "))
4949
}
5050

@@ -119,7 +119,7 @@ pub fn output_depinfo<'a, 'b>(cx: &mut Context<'a, 'b>, unit: &Unit<'a>) -> Carg
119119
.resolve_path(bcx.config)
120120
.as_os_str()
121121
.to_str()
122-
.ok_or_else(|| internal("build.dep-info-basedir path not utf-8"))?
122+
.ok_or_else(|| anyhow::format_err!("build.dep-info-basedir path not utf-8"))?
123123
.to_string();
124124
Some(basedir_string.as_str())
125125
}

src/cargo/core/shell.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ impl Shell {
228228
}
229229
}
230230

231+
/// Prints a cyan 'note' message.
232+
pub fn note<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> {
233+
self.print(&"note", Some(&message), Cyan, false)
234+
}
235+
231236
/// Updates the verbosity of the shell.
232237
pub fn set_verbosity(&mut self, verbosity: Verbosity) {
233238
self.verbosity = verbosity;

src/cargo/lib.rs

Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use log::debug;
3737
use serde::ser;
3838
use std::fmt;
3939

40-
pub use crate::util::errors::Internal;
40+
pub use crate::util::errors::{InternalError, VerboseError};
4141
pub use crate::util::{CargoResult, CliError, CliResult, Config};
4242

4343
pub const CARGO_ENV: &str = "CARGO";
@@ -106,64 +106,60 @@ pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
106106
}
107107
}
108108

109-
let CliError {
110-
error,
111-
exit_code,
112-
unknown,
113-
} = err;
114-
// `exit_code` of 0 means non-fatal error (e.g., docopt version info).
115-
let fatal = exit_code != 0;
116-
117-
let hide = unknown && shell.verbosity() != Verbose;
118-
109+
let CliError { error, exit_code } = err;
119110
if let Some(error) = error {
120-
if hide {
121-
drop(shell.error("An unknown error occurred"))
122-
} else if fatal {
123-
drop(shell.error(&error))
124-
} else {
125-
println!("{}", error);
126-
}
127-
128-
if !handle_cause(&error, shell) || hide {
129-
drop(writeln!(
130-
shell.err(),
131-
"\nTo learn more, run the command again \
132-
with --verbose."
133-
));
134-
}
111+
display_error(&error, shell);
135112
}
136113

137114
std::process::exit(exit_code)
138115
}
139116

140-
pub fn handle_error(err: &Error, shell: &mut Shell) {
141-
debug!("handle_error; err={:?}", err);
142-
143-
let _ignored_result = shell.error(err);
144-
handle_cause(err, shell);
117+
/// Displays an error, and all its causes, to stderr.
118+
pub fn display_error(err: &Error, shell: &mut Shell) {
119+
debug!("display_error; err={:?}", err);
120+
let has_verbose = _display_error(err, shell);
121+
if has_verbose {
122+
drop(writeln!(
123+
shell.err(),
124+
"\nTo learn more, run the command again with --verbose."
125+
));
126+
}
127+
if err
128+
.chain()
129+
.any(|e| e.downcast_ref::<InternalError>().is_some())
130+
{
131+
drop(shell.note("this is an unexpected cargo internal error"));
132+
drop(
133+
shell.note(
134+
"we would appreciate a bug report: https://github.com/rust-lang/cargo/issues/",
135+
),
136+
);
137+
drop(shell.note(format!("{}", version())));
138+
// Once backtraces are stabilized, this should print out a backtrace
139+
// if it is available.
140+
}
145141
}
146142

147-
fn handle_cause(cargo_err: &Error, shell: &mut Shell) -> bool {
148-
fn print(error: &str, shell: &mut Shell) {
149-
drop(writeln!(shell.err(), "\nCaused by:"));
150-
drop(writeln!(shell.err(), " {}", error));
143+
fn _display_error(err: &Error, shell: &mut Shell) -> bool {
144+
let verbosity = shell.verbosity();
145+
let is_verbose = |e: &(dyn std::error::Error + 'static)| -> bool {
146+
verbosity != Verbose && e.downcast_ref::<VerboseError>().is_some()
147+
};
148+
// Generally the top error shouldn't be verbose, but check it anyways.
149+
if is_verbose(err.as_ref()) {
150+
return true;
151151
}
152-
153-
let verbose = shell.verbosity();
154-
155-
// The first error has already been printed to the shell.
156-
for err in cargo_err.chain().skip(1) {
152+
drop(shell.error(&err));
153+
for cause in err.chain().skip(1) {
157154
// If we're not in verbose mode then print remaining errors until one
158-
// marked as `Internal` appears.
159-
if verbose != Verbose && err.downcast_ref::<Internal>().is_some() {
160-
return false;
155+
// marked as `VerboseError` appears.
156+
if is_verbose(cause) {
157+
return true;
161158
}
162-
163-
print(&err.to_string(), shell);
159+
drop(writeln!(shell.err(), "\nCaused by:"));
160+
drop(writeln!(shell.err(), " {}", cause));
164161
}
165-
166-
true
162+
false
167163
}
168164

169165
pub fn version() -> VersionInfo {

src/cargo/ops/cargo_install.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub fn install(
8282
) {
8383
Ok(()) => succeeded.push(krate),
8484
Err(e) => {
85-
crate::handle_error(&e, &mut opts.config.shell());
85+
crate::display_error(&e, &mut opts.config.shell());
8686
failed.push(krate)
8787
}
8888
}

src/cargo/ops/cargo_new.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,11 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> {
362362
}
363363

364364
pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<()> {
365+
// This is here just as a random location to exercise the internal error handling.
366+
if std::env::var_os("__CARGO_TEST_INTERNAL_ERROR").is_some() {
367+
return Err(crate::util::internal("internal error test"));
368+
}
369+
365370
let path = &opts.path;
366371

367372
if fs::metadata(&path.join("Cargo.toml")).is_ok() {

0 commit comments

Comments
 (0)