From 2ee0409f323574af15c5d75dbfc42b1721720de8 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 19 Feb 2024 15:07:08 +0000 Subject: [PATCH] errors: share `SilentEmitter` between rustc and rustfmt Signed-off-by: David Wood --- compiler/rustc_errors/src/emitter.rs | 32 ++----- compiler/rustc_errors/src/lib.rs | 95 +++++++++++++++----- compiler/rustc_interface/src/interface.rs | 14 +-- compiler/rustc_session/src/parse.rs | 18 ++-- src/tools/rustfmt/src/parse/session.rs | 73 ++++++--------- src/tools/rustfmt/tests/rustfmt/main.rs | 2 +- src/tools/rustfmt/tests/target/issue_6082.rs | 5 ++ 7 files changed, 131 insertions(+), 108 deletions(-) create mode 100644 src/tools/rustfmt/tests/target/issue_6082.rs diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 293d8f01e67..57b8df52f4b 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -10,7 +10,6 @@ use rustc_span::source_map::SourceMap; use rustc_span::{FileLines, FileName, SourceFile, Span}; -use crate::error::TranslateError; use crate::snippet::{ Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString, }; @@ -539,18 +538,9 @@ impl Emitter for HumanEmitter { /// Fatal diagnostics are forwarded to `fatal_dcx` to avoid silent /// failures of rustc, as witnessed e.g. in issue #89358. pub struct SilentEmitter { + pub fallback_bundle: LazyFallbackBundle, pub fatal_dcx: DiagCtxt, - pub fatal_note: String, -} - -pub fn silent_translate<'a>(message: &'a DiagMessage) -> Result, TranslateError<'_>> { - match message { - DiagMessage::Str(msg) | DiagMessage::Translated(msg) => Ok(Cow::Borrowed(msg)), - DiagMessage::FluentIdentifier(identifier, _) => { - // Any value works here. - Ok(identifier.clone()) - } - } + pub fatal_note: Option, } impl Translate for SilentEmitter { @@ -559,17 +549,9 @@ impl Translate for SilentEmitter { } fn fallback_fluent_bundle(&self) -> &FluentBundle { - panic!("silent emitter attempted to translate message") - } - - // Override `translate_message` for the silent emitter because eager translation of - // subdiagnostics result in a call to this. - fn translate_message<'a>( - &'a self, - message: &'a DiagMessage, - _: &'a FluentArgs<'_>, - ) -> Result, TranslateError<'_>> { - silent_translate(message) + // Ideally this field wouldn't be necessary and the fallback bundle in `fatal_dcx` would be + // used but the lock prevents this. + &self.fallback_bundle } } @@ -580,7 +562,9 @@ impl Emitter for SilentEmitter { fn emit_diagnostic(&mut self, mut diag: DiagInner) { if diag.level == Level::Fatal { - diag.sub(Level::Note, self.fatal_note.clone(), MultiSpan::new()); + if let Some(fatal_note) = &self.fatal_note { + diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); + } self.fatal_dcx.emit_diagnostic(diag); } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0a6d66f52da..0caa8fa9e8a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -63,7 +63,7 @@ use emitter::{is_case_difference, DynEmitter, Emitter}; use registry::Registry; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; -use rustc_data_structures::sync::Lock; +use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::AtomicRef; use rustc_lint_defs::LintExpectationId; use rustc_span::source_map::SourceMap; @@ -606,29 +606,54 @@ impl DiagCtxt { } pub fn new(emitter: Box) -> Self { - Self { - inner: Lock::new(DiagCtxtInner { - flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, - err_guars: Vec::new(), - lint_err_guars: Vec::new(), - delayed_bugs: Vec::new(), - deduplicated_err_count: 0, - deduplicated_warn_count: 0, - emitter, - must_produce_diag: false, - has_printed: false, - suppressed_expected_diag: false, - taught_diagnostics: Default::default(), - emitted_diagnostic_codes: Default::default(), - emitted_diagnostics: Default::default(), - stashed_diagnostics: Default::default(), - future_breakage_diagnostics: Vec::new(), - check_unstable_expect_diagnostics: false, - unstable_expect_diagnostics: Vec::new(), - fulfilled_expectations: Default::default(), - ice_file: None, - }), + Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } + } + + pub fn make_silent(&mut self, fallback_bundle: LazyFallbackBundle, fatal_note: Option) { + self.wrap_emitter(|old_dcx| { + Box::new(emitter::SilentEmitter { + fallback_bundle, + fatal_dcx: DiagCtxt { inner: Lock::new(old_dcx) }, + fatal_note, + }) + }); + } + + fn wrap_emitter(&mut self, f: F) + where + F: FnOnce(DiagCtxtInner) -> Box, + { + // A empty type that implements `Emitter` so that a `DiagCtxtInner` can be constructed + // to temporarily swap in place of the real one, which will be used in constructing + // its replacement. + struct FalseEmitter; + + impl Emitter for FalseEmitter { + fn emit_diagnostic(&mut self, _: DiagInner) { + unimplemented!("false emitter must only used during `wrap_emitter`") + } + + fn source_map(&self) -> Option<&Lrc> { + unimplemented!("false emitter must only used during `wrap_emitter`") + } } + + impl translation::Translate for FalseEmitter { + fn fluent_bundle(&self) -> Option<&Lrc> { + unimplemented!("false emitter must only used during `wrap_emitter`") + } + + fn fallback_fluent_bundle(&self) -> &FluentBundle { + unimplemented!("false emitter must only used during `wrap_emitter`") + } + } + + let mut inner = self.inner.borrow_mut(); + let mut prev_dcx = DiagCtxtInner::new(Box::new(FalseEmitter)); + std::mem::swap(&mut *inner, &mut prev_dcx); + let new_emitter = f(prev_dcx); + let mut new_dcx = DiagCtxtInner::new(new_emitter); + std::mem::swap(&mut *inner, &mut new_dcx); } /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`. @@ -1345,6 +1370,30 @@ impl DiagCtxt { // `DiagCtxt::foo()` just borrows `inner` and forwards a call to // `DiagCtxtInner::foo`. impl DiagCtxtInner { + fn new(emitter: Box) -> Self { + Self { + flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() }, + err_guars: Vec::new(), + lint_err_guars: Vec::new(), + delayed_bugs: Vec::new(), + deduplicated_err_count: 0, + deduplicated_warn_count: 0, + emitter, + must_produce_diag: false, + has_printed: false, + suppressed_expected_diag: false, + taught_diagnostics: Default::default(), + emitted_diagnostic_codes: Default::default(), + emitted_diagnostics: Default::default(), + stashed_diagnostics: Default::default(), + future_breakage_diagnostics: Vec::new(), + check_unstable_expect_diagnostics: false, + unstable_expect_diagnostics: Vec::new(), + fulfilled_expectations: Default::default(), + ice_file: None, + } + } + /// Emit all stashed diagnostics. fn emit_stashed_diagnostics(&mut self) -> Option { let mut guar = None; diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 3a2bbed19be..b2ec8e53429 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -45,9 +45,10 @@ pub struct Compiler { pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { - let psess = ParseSess::with_silent_emitter(format!( - "this error occurred on the command line: `--cfg={s}`" - )); + let psess = ParseSess::with_silent_emitter( + vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], + format!("this error occurred on the command line: `--cfg={s}`"), + ); let filename = FileName::cfg_spec_source_code(&s); macro_rules! error { @@ -107,9 +108,10 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec) -> CheckCfg { let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() }; for s in specs { - let psess = ParseSess::with_silent_emitter(format!( - "this error occurred on the command line: `--check-cfg={s}`" - )); + let psess = ParseSess::with_silent_emitter( + vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], + format!("this error occurred on the command line: `--check-cfg={s}`"), + ); let filename = FileName::cfg_spec_source_code(&s); macro_rules! error { diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 25b5422f322..506bd5d5dbd 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -265,14 +265,20 @@ impl ParseSess { } } - pub fn with_silent_emitter(fatal_note: String) -> Self { - let fallback_bundle = fallback_fluent_bundle(Vec::new(), false); + pub fn with_silent_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self { + let fallback_bundle = fallback_fluent_bundle(locale_resources, false); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = - Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)); + let emitter = Box::new(HumanEmitter::new( + stderr_destination(ColorConfig::Auto), + fallback_bundle.clone(), + )); let fatal_dcx = DiagCtxt::new(emitter); - let dcx = - DiagCtxt::new(Box::new(SilentEmitter { fatal_dcx, fatal_note })).disable_warnings(); + let dcx = DiagCtxt::new(Box::new(SilentEmitter { + fallback_bundle, + fatal_dcx, + fatal_note: Some(fatal_note), + })) + .disable_warnings(); ParseSess::with_dcx(dcx, sm) } diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 11af9860513..60a89a57536 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -1,9 +1,8 @@ -use std::borrow::Cow; use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; -use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter}; +use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter, SilentEmitter}; use rustc_errors::translation::Translate; use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; @@ -28,41 +27,6 @@ pub(crate) struct ParseSess { can_reset_errors: Lrc, } -/// Emitter which discards every error. -struct SilentEmitter; - -impl Translate for SilentEmitter { - fn fluent_bundle(&self) -> Option<&Lrc> { - None - } - - fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { - panic!("silent emitter attempted to translate a diagnostic"); - } - - // Override `translate_message` for the silent emitter because eager translation of - // subdiagnostics result in a call to this. - fn translate_message<'a>( - &'a self, - message: &'a rustc_errors::DiagMessage, - _: &'a rustc_errors::translation::FluentArgs<'_>, - ) -> Result, rustc_errors::error::TranslateError<'_>> { - rustc_errors::emitter::silent_translate(message) - } -} - -impl Emitter for SilentEmitter { - fn source_map(&self) -> Option<&Lrc> { - None - } - - fn emit_diagnostic(&mut self, _diag: DiagInner) {} -} - -fn silent_emitter() -> Box { - Box::new(SilentEmitter {}) -} - /// Emit errors against every files expect ones specified in the `ignore_path_set`. struct SilentOnIgnoredFilesEmitter { ignore_path_set: IntoDynSyncSend>, @@ -143,17 +107,23 @@ fn default_dcx( ColorConfig::Never }; - let emitter = if hide_parse_errors { - silent_emitter() + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + let emitter = Box::new( + HumanEmitter::new(stderr_destination(emit_color), fallback_bundle.clone()) + .sm(Some(source_map.clone())), + ); + + let emitter: Box = if hide_parse_errors { + Box::new(SilentEmitter { + fallback_bundle, + fatal_dcx: DiagCtxt::new(emitter), + fatal_note: None, + }) } else { - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - Box::new( - HumanEmitter::new(stderr_destination(emit_color), fallback_bundle) - .sm(Some(source_map.clone())), - ) + emitter }; DiagCtxt::new(Box::new(SilentOnIgnoredFilesEmitter { has_non_ignorable_parser_errors: false, @@ -232,7 +202,14 @@ impl ParseSess { } pub(crate) fn set_silent_emitter(&mut self) { - self.raw_psess.dcx = DiagCtxt::new(silent_emitter()); + // Ideally this invocation wouldn't be necessary and the fallback bundle in + // `self.parse_sess.dcx` could be used, but the lock in `DiagCtxt` prevents this. + // See `::fallback_fluent_bundle`. + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), + false, + ); + self.raw_psess.dcx.make_silent(fallback_bundle, None); } pub(crate) fn span_to_filename(&self, span: Span) -> FileName { diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 7dcf7c8416e..e66fad1e7fa 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -176,7 +176,7 @@ fn rustfmt_emits_error_on_line_overflow_true() { #[test] #[allow(non_snake_case)] fn dont_emit_ICE() { - let files = ["tests/target/issue_5728.rs", "tests/target/issue_5729.rs"]; + let files = ["tests/target/issue_5728.rs", "tests/target/issue_5729.rs", "tests/target/issue_6082.rs"]; for file in files { let args = [file]; diff --git a/src/tools/rustfmt/tests/target/issue_6082.rs b/src/tools/rustfmt/tests/target/issue_6082.rs new file mode 100644 index 00000000000..58e512c710e --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue_6082.rs @@ -0,0 +1,5 @@ +macro_rules! test { + ($T:ident, $b:lifetime) => { + Box<$T<$b>> + }; +}