diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index aa2865a75f9ae..2279ed8595408 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -329,6 +329,8 @@ pub struct HandlerFlags { /// show macro backtraces even for non-local macros. /// (rustc: see `-Z external-macro-backtrace`) pub external_macro_backtrace: bool, + /// If true, identical diagnostics are reported only once. + pub deduplicate_diagnostics: bool, } impl Drop for HandlerInner { @@ -736,16 +738,17 @@ impl HandlerInner { self.emitted_diagnostic_codes.insert(code.clone()); } - let diagnostic_hash = { + let already_emitted = |this: &mut Self| { use std::hash::Hash; let mut hasher = StableHasher::new(); diagnostic.hash(&mut hasher); - hasher.finish() + let diagnostic_hash = hasher.finish(); + !this.emitted_diagnostics.insert(diagnostic_hash) }; - // Only emit the diagnostic if we haven't already emitted an equivalent - // one: - if self.emitted_diagnostics.insert(diagnostic_hash) { + // Only emit the diagnostic if we've been asked to deduplicate and + // haven't already emitted an equivalent diagnostic. + if !(self.flags.deduplicate_diagnostics && already_emitted(self)) { self.emitter.emit_diagnostic(diagnostic); if diagnostic.is_error() { self.deduplicated_err_count += 1; diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs index 164803d92b127..b6b22e298ca62 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -20,7 +20,7 @@ use rustc_span::source_map::{FileName, FilePathMapping}; use rustc_span::symbol::{sym, Symbol}; use rustc_errors::emitter::HumanReadableErrorType; -use rustc_errors::{ColorConfig, FatalError, Handler}; +use rustc_errors::{ColorConfig, FatalError, Handler, HandlerFlags}; use getopts; @@ -597,6 +597,17 @@ impl DebuggingOptions { pub fn ui_testing(&self) -> bool { self.ui_testing.unwrap_or(false) } + + pub fn diagnostic_handler_flags(&self, can_emit_warnings: bool) -> HandlerFlags { + HandlerFlags { + can_emit_warnings, + treat_err_as_bug: self.treat_err_as_bug, + dont_buffer_diagnostics: self.dont_buffer_diagnostics, + report_delayed_bugs: self.report_delayed_bugs, + external_macro_backtrace: self.external_macro_backtrace, + deduplicate_diagnostics: self.deduplicate_diagnostics.unwrap_or(true), + } + } } // The type of entry function, so users can have their own entry functions diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 7e44ef7d4a433..656c1b019b2e2 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -946,4 +946,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, insert_sideeffect: bool = (false, parse_bool, [TRACKED], "fix undefined behavior when a thread doesn't eventually make progress \ (such as entering an empty infinite loop) by inserting llvm.sideeffect"), + deduplicate_diagnostics: Option = (None, parse_opt_bool, [UNTRACKED], + "deduplicate identical diagnostics"), } diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index 28acbd5713f12..15911db46fe0b 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -941,15 +941,8 @@ pub fn build_session_with_source_map( .last() .unwrap_or(false); let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow); - let can_emit_warnings = !(warnings_allow || cap_lints_allow); - let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; - let dont_buffer_diagnostics = sopts.debugging_opts.dont_buffer_diagnostics; - let report_delayed_bugs = sopts.debugging_opts.report_delayed_bugs; - - let external_macro_backtrace = sopts.debugging_opts.external_macro_backtrace; - let write_dest = match diagnostics_output { DiagnosticOutput::Default => None, DiagnosticOutput::Raw(write) => Some(write), @@ -958,14 +951,7 @@ pub fn build_session_with_source_map( let diagnostic_handler = rustc_errors::Handler::with_emitter_and_flags( emitter, - rustc_errors::HandlerFlags { - can_emit_warnings, - treat_err_as_bug, - report_delayed_bugs, - dont_buffer_diagnostics, - external_macro_backtrace, - ..Default::default() - }, + sopts.debugging_opts.diagnostic_handler_flags(can_emit_warnings), ); build_session_(sopts, local_crate_source_file, diagnostic_handler, source_map, lint_caps) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 4a65a9f431a44..0af25efc04234 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -269,12 +269,7 @@ impl Options { let codegen_options = build_codegen_options(matches, error_format); let debugging_options = build_debugging_options(matches, error_format); - let diag = new_handler( - error_format, - None, - debugging_options.treat_err_as_bug, - debugging_options.ui_testing(), - ); + let diag = new_handler(error_format, None, &debugging_options); // check for deprecated options check_deprecated_options(&matches, &diag); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index cb22039327e07..f53f3a75e095c 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -35,7 +35,7 @@ use crate::html::render::RenderInfo; use crate::passes; -pub use rustc::session::config::{CodegenOptions, Input, Options}; +pub use rustc::session::config::{CodegenOptions, DebuggingOptions, Input, Options}; pub use rustc::session::search_paths::SearchPath; pub type ExternalPaths = FxHashMap, clean::TypeKind)>; @@ -170,12 +170,8 @@ impl<'tcx> DocContext<'tcx> { pub fn new_handler( error_format: ErrorOutputType, source_map: Option>, - treat_err_as_bug: Option, - ui_testing: bool, + debugging_opts: &DebuggingOptions, ) -> errors::Handler { - // rustdoc doesn't override (or allow to override) anything from this that is relevant here, so - // stick to the defaults - let sessopts = Options::default(); let emitter: Box = match error_format { ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); @@ -184,34 +180,25 @@ pub fn new_handler( color_config, source_map.map(|cm| cm as _), short, - sessopts.debugging_opts.teach, - sessopts.debugging_opts.terminal_width, + debugging_opts.teach, + debugging_opts.terminal_width, false, ) - .ui_testing(ui_testing), + .ui_testing(debugging_opts.ui_testing()), ) } ErrorOutputType::Json { pretty, json_rendered } => { let source_map = source_map.unwrap_or_else(|| { - Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping())) + Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) }); Box::new( JsonEmitter::stderr(None, source_map, pretty, json_rendered, false) - .ui_testing(ui_testing), + .ui_testing(debugging_opts.ui_testing()), ) } }; - errors::Handler::with_emitter_and_flags( - emitter, - errors::HandlerFlags { - can_emit_warnings: true, - treat_err_as_bug, - report_delayed_bugs: false, - external_macro_backtrace: false, - ..Default::default() - }, - ) + errors::Handler::with_emitter_and_flags(emitter, debugging_opts.diagnostic_handler_flags(true)) } pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions) { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 998b28b8807d3..c8a32306194df 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -445,12 +445,7 @@ fn main_args(args: &[String]) -> i32 { } fn main_options(options: config::Options) -> i32 { - let diag = core::new_handler( - options.error_format, - None, - options.debugging_options.treat_err_as_bug, - options.debugging_options.ui_testing(), - ); + let diag = core::new_handler(options.error_format, None, &options.debugging_options); match (options.should_test, options.markdown_input()) { (true, true) => return markdown::test(options, &diag), @@ -463,12 +458,7 @@ fn main_options(options: config::Options) -> i32 { // need to move these items separately because we lose them by the time the closure is called, // but we can't crates the Handler ahead of time because it's not Send - let diag_opts = ( - options.error_format, - options.debugging_options.treat_err_as_bug, - options.debugging_options.ui_testing(), - options.edition, - ); + let diag_opts = (options.error_format, options.edition, options.debugging_options.clone()); let show_coverage = options.show_coverage; rust_input(options, move |out| { if show_coverage { @@ -479,8 +469,8 @@ fn main_options(options: config::Options) -> i32 { let Output { krate, renderinfo, renderopts } = out; info!("going to format"); - let (error_format, treat_err_as_bug, ui_testing, edition) = diag_opts; - let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing); + let (error_format, edition, debugging_options) = diag_opts; + let diag = core::new_handler(error_format, None, &debugging_options); match html::render::run(krate, renderopts, renderinfo, &diag, edition) { Ok(_) => rustc_driver::EXIT_SUCCESS, Err(e) => { diff --git a/src/test/ui/consts/miri_unleashed/mutable_const2.stderr b/src/test/ui/consts/miri_unleashed/mutable_const2.stderr index 2212b7d75d21e..3493b7c54c43b 100644 --- a/src/test/ui/consts/miri_unleashed/mutable_const2.stderr +++ b/src/test/ui/consts/miri_unleashed/mutable_const2.stderr @@ -10,7 +10,7 @@ error: internal compiler error: mutable allocation in constant LL | const MUTABLE_BEHIND_RAW: *mut i32 = &UnsafeCell::new(42) as *const _ as *mut _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:345:17 +thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:347:17 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. error: internal compiler error: unexpected panic diff --git a/src/test/ui/deduplicate-diagnostics.deduplicate.stderr b/src/test/ui/deduplicate-diagnostics.deduplicate.stderr new file mode 100644 index 0000000000000..1acfce506229f --- /dev/null +++ b/src/test/ui/deduplicate-diagnostics.deduplicate.stderr @@ -0,0 +1,8 @@ +error: cannot find derive macro `Unresolved` in this scope + --> $DIR/deduplicate-diagnostics.rs:4:10 + | +LL | #[derive(Unresolved)] + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/deduplicate-diagnostics.duplicate.stderr b/src/test/ui/deduplicate-diagnostics.duplicate.stderr new file mode 100644 index 0000000000000..325da3b5d915b --- /dev/null +++ b/src/test/ui/deduplicate-diagnostics.duplicate.stderr @@ -0,0 +1,14 @@ +error: cannot find derive macro `Unresolved` in this scope + --> $DIR/deduplicate-diagnostics.rs:4:10 + | +LL | #[derive(Unresolved)] + | ^^^^^^^^^^ + +error: cannot find derive macro `Unresolved` in this scope + --> $DIR/deduplicate-diagnostics.rs:4:10 + | +LL | #[derive(Unresolved)] + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/deduplicate-diagnostics.rs b/src/test/ui/deduplicate-diagnostics.rs new file mode 100644 index 0000000000000..4a1f503d757a5 --- /dev/null +++ b/src/test/ui/deduplicate-diagnostics.rs @@ -0,0 +1,8 @@ +// revisions: duplicate deduplicate +//[duplicate] compile-flags: -Z deduplicate-diagnostics=no + +#[derive(Unresolved)] //~ ERROR cannot find derive macro `Unresolved` in this scope + //[duplicate]~| ERROR cannot find derive macro `Unresolved` in this scope +struct S; + +fn main() {}