diff options
author | David Wood <david.wood@huawei.com> | 2022-03-26 07:27:43 +0000 |
---|---|---|
committer | David Wood <david.wood@huawei.com> | 2022-04-05 07:01:02 +0100 |
commit | 7f91697b5035f8620df4de47057024c3539b55a6 (patch) | |
tree | a7f6e303b30ec5b3d675c362d474e1de0f3419ad | |
parent | c45f29595df6f6a178b7998bc33c76099f3c12b7 (diff) | |
download | rust-7f91697b5035f8620df4de47057024c3539b55a6.tar.gz |
errors: implement fallback diagnostic translation
This commit updates the signatures of all diagnostic functions to accept
types that can be converted into a `DiagnosticMessage`. This enables
existing diagnostic calls to continue to work as before and Fluent
identifiers to be provided. The `SessionDiagnostic` derive just
generates normal diagnostic calls, so these APIs had to be modified to
accept Fluent identifiers.
In addition, loading of the "fallback" Fluent bundle, which contains the
built-in English messages, has been implemented.
Each diagnostic now has "arguments" which correspond to variables in the
Fluent messages (necessary to render a Fluent message) but no API for
adding arguments has been added yet. Therefore, diagnostics (that do not
require interpolation) can be converted to use Fluent identifiers and
will be output as before.
46 files changed, 920 insertions, 294 deletions
diff --git a/Cargo.lock b/Cargo.lock index 34b200af522..7983b79d5d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1239,6 +1239,50 @@ dependencies = [ ] [[package]] +name = "fluent" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +dependencies = [ + "thiserror", +] + +[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1783,6 +1827,26 @@ dependencies = [ ] [[package]] +name = "intl-memoizer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf" +dependencies = [ + "tinystr", + "unic-langid", +] + +[[package]] name = "itertools" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2813,6 +2877,12 @@ dependencies = [ ] [[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] name = "proc-macro2" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3649,9 +3719,13 @@ version = "0.0.0" name = "rustc_error_messages" version = "0.0.0" dependencies = [ + "fluent", + "rustc_data_structures", "rustc_macros", "rustc_serialize", "rustc_span", + "tracing", + "unic-langid", ] [[package]] @@ -4586,6 +4660,12 @@ dependencies = [ ] [[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + +[[package]] name = "semver" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5117,6 +5197,12 @@ dependencies = [ ] [[package]] +name = "tinystr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1" + +[[package]] name = "tinyvec" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5275,6 +5361,15 @@ dependencies = [ ] [[package]] +name = "type-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +dependencies = [ + "rustc-hash", +] + +[[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5329,6 +5424,49 @@ dependencies = [ ] [[package]] +name = "unic-langid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5" +dependencies = [ + "unic-langid-impl", + "unic-langid-macros", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d" +dependencies = [ + "tinystr", +] + +[[package]] +name = "unic-langid-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5" +dependencies = [ + "proc-macro-hack", + "tinystr", + "unic-langid-impl", + "unic-langid-macros-impl", +] + +[[package]] +name = "unic-langid-macros-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f" +dependencies = [ + "proc-macro-hack", + "quote", + "syn", + "unic-langid-impl", +] + +[[package]] name = "unic-ucd-version" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index c9395492c9e..723adb8da1b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -109,7 +109,7 @@ impl RegionName { *span, format!("lifetime `{}` represents this closure's body", self), ); - diag.note(¬e); + diag.note(note); } RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( span, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 8aa18b8e37c..92c4ab7eb86 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1707,23 +1707,33 @@ impl SharedEmitter { impl Emitter for SharedEmitter { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { + let fluent_args = self.to_fluent_args(diag.args()); drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: diag.message().to_string(), + msg: self.translate_messages(&diag.message, &fluent_args).to_string(), code: diag.code.clone(), lvl: diag.level(), }))); for child in &diag.children { drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: child.message().to_string(), + msg: self.translate_messages(&child.message, &fluent_args).to_string(), code: None, lvl: child.level, }))); } drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); } + fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } + + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + panic!("shared emitter attempted to translate a diagnostic"); + } } impl SharedEmitterMain { @@ -1754,9 +1764,9 @@ impl SharedEmitterMain { let msg = msg.strip_prefix("error: ").unwrap_or(&msg); let mut err = match level { - Level::Error { lint: false } => sess.struct_err(&msg).forget_guarantee(), - Level::Warning => sess.struct_warn(&msg), - Level::Note => sess.struct_note_without_error(&msg), + Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(), + Level::Warning => sess.struct_warn(msg), + Level::Note => sess.struct_note_without_error(msg), _ => bug!("Invalid inline asm diagnostic level"), }; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 69f96d07f90..c95c59904d1 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1172,9 +1172,11 @@ static DEFAULT_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + /// When `install_ice_hook` is called, this function will be called as the panic /// hook. pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + fallback_bundle, false, false, None, @@ -1209,7 +1211,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { } for note in &xs { - handler.note_without_error(note); + handler.note_without_error(note.as_ref()); } // If backtraces are enabled, also print the query stack diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 194a11dd3ea..abd565cd529 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -7,6 +7,10 @@ edition = "2021" doctest = false [dependencies] +fluent = "0.16.0" +rustc_data_structures = { path = "../rustc_data_structures" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_macros = { path = "../rustc_macros" } +tracing = "0.1" +unic-langid = { version = "0.9.0", features = ["macros"] } diff --git a/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl b/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl new file mode 100644 index 00000000000..e89fa586d2a --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl @@ -0,0 +1,2 @@ +parser-struct-literal-body-without-path = struct literal body without path + .suggestion = you might have forgotten to add the struct literal inside the block diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 57d4a4c1f5e..105bf1413de 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -1,31 +1,75 @@ +use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; +use std::borrow::Cow; +use tracing::debug; + +pub use fluent::{FluentArgs, FluentValue}; + +static FALLBACK_FLUENT_RESOURCE: &'static str = include_str!("../locales/en-US/diagnostics.ftl"); + +pub type FluentBundle = fluent::FluentBundle<fluent::FluentResource>; + +/// Return the default `FluentBundle` with standard en-US diagnostic messages. +pub fn fallback_fluent_bundle() -> Lrc<FluentBundle> { + let fallback_resource = fluent::FluentResource::try_new(FALLBACK_FLUENT_RESOURCE.to_string()) + .expect("failed to parse ftl resource"); + debug!(?fallback_resource); + let mut fallback_bundle = FluentBundle::new(vec![unic_langid::langid!("en-US")]); + fallback_bundle.add_resource(fallback_resource).expect("failed to add resource to bundle"); + let fallback_bundle = Lrc::new(fallback_bundle); + fallback_bundle +} + +/// Identifier for the Fluent message/attribute corresponding to a diagnostic message. +type FluentId = Cow<'static, str>; /// Abstraction over a message in a diagnostic to support both translatable and non-translatable /// diagnostic messages. +/// +/// Intended to be removed once diagnostics are entirely translatable. #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum DiagnosticMessage { /// Non-translatable diagnostic message. + // FIXME(davidtwco): can a `Cow<'static, str>` be used here? Str(String), /// Identifier for a Fluent message corresponding to the diagnostic message. - FluentIdentifier(String), + FluentIdentifier(FluentId, Option<FluentId>), } impl DiagnosticMessage { - /// Convert `DiagnosticMessage` to a `&str`. - pub fn as_str(&self) -> &str { + /// Returns the `String` contained within the `DiagnosticMessage::Str` variant, assuming that + /// this diagnostic message is of the legacy, non-translatable variety. Panics if this + /// assumption does not hold. + /// + /// Don't use this - it exists to support some places that do comparison with diagnostic + /// strings. + pub fn expect_str(&self) -> &str { match self { - DiagnosticMessage::Str(msg) => msg, - DiagnosticMessage::FluentIdentifier(..) => unimplemented!(), + DiagnosticMessage::Str(s) => s, + _ => panic!("expected non-translatable diagnostic message"), } } - /// Convert `DiagnosticMessage` to an owned `String`. - pub fn to_string(self) -> String { - match self { - DiagnosticMessage::Str(msg) => msg, - DiagnosticMessage::FluentIdentifier(..) => unimplemented!(), - } + /// Create a `DiagnosticMessage` for the provided Fluent identifier. + pub fn fluent(id: impl Into<Cow<'static, str>>) -> Self { + DiagnosticMessage::FluentIdentifier(id.into(), None) + } + + /// Create a `DiagnosticMessage` for the provided Fluent identifier and attribute. + pub fn fluent_attr( + id: impl Into<Cow<'static, str>>, + attr: impl Into<Cow<'static, str>>, + ) -> Self { + DiagnosticMessage::FluentIdentifier(id.into(), Some(attr.into())) + } +} + +/// `From` impl that enables existing diagnostic calls to functions which now take +/// `impl Into<DiagnosticMessage>` to continue to work as before. +impl<S: Into<String>> From<S> for DiagnosticMessage { + fn from(s: S) -> Self { + DiagnosticMessage::Str(s.into()) } } @@ -72,12 +116,8 @@ impl MultiSpan { MultiSpan { primary_spans: vec, span_labels: vec![] } } - pub fn push_span_label(&mut self, span: Span, label: String) { - self.span_labels.push((span, DiagnosticMessage::Str(label))); - } - - pub fn push_span_message(&mut self, span: Span, message: DiagnosticMessage) { - self.span_labels.push((span, message)); + pub fn push_span_label(&mut self, span: Span, label: impl Into<DiagnosticMessage>) { + self.span_labels.push((span, label.into())); } /// Selects the first primary span (if any). diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 330c3d218fc..4dc99b8dd0f 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -7,16 +7,22 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; -use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Emitter, Level, MultiSpan, SubDiagnostic}; +use crate::{ + CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, Level, + MultiSpan, Style, SubDiagnostic, +}; use annotate_snippets::display_list::{DisplayList, FormatOptions}; use annotate_snippets::snippet::*; use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; use rustc_span::source_map::SourceMap; use rustc_span::SourceFile; /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitterWriter { source_map: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, + /// If true, hides the longer explanation text short_message: bool, /// If true, will normalize line numbers with `LL` to prevent noise in UI test diffs. @@ -28,8 +34,10 @@ pub struct AnnotateSnippetEmitterWriter { impl Emitter for AnnotateSnippetEmitterWriter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, diag: &Diagnostic) { + let fluent_args = self.to_fluent_args(diag.args()); + let mut children = diag.children.clone(); - let (mut primary_span, suggestions) = self.primary_span_formatted(&diag); + let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); self.fix_multispans_in_extern_macros_and_render_macro_backtrace( &self.source_map, @@ -41,7 +49,8 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.emit_messages_default( &diag.level, - diag.message().to_string(), + &diag.message, + &fluent_args, &diag.code, &primary_span, &children, @@ -53,6 +62,14 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.source_map.as_ref() } + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + &self.fallback_bundle + } + fn should_show_explain(&self) -> bool { !self.short_message } @@ -82,10 +99,11 @@ fn annotation_type_for_level(level: Level) -> AnnotationType { impl AnnotateSnippetEmitterWriter { pub fn new( source_map: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, macro_backtrace: bool, ) -> Self { - Self { source_map, short_message, ui_testing: false, macro_backtrace } + Self { source_map, fallback_bundle, short_message, ui_testing: false, macro_backtrace } } /// Allows to modify `Self` to enable or disable the `ui_testing` flag. @@ -99,12 +117,14 @@ impl AnnotateSnippetEmitterWriter { fn emit_messages_default( &mut self, level: &Level, - message: String, + messages: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, code: &Option<DiagnosticId>, msp: &MultiSpan, _children: &[SubDiagnostic], _suggestions: &[CodeSuggestion], ) { + let message = self.translate_messages(messages, args); if let Some(source_map) = &self.source_map { // Make sure our primary file comes first let primary_lo = if let Some(ref primary_span) = msp.primary_span().as_ref() { @@ -120,8 +140,7 @@ impl AnnotateSnippetEmitterWriter { // should be done if it happens return; }; - let mut annotated_files = - FileWithAnnotatedLines::collect_annotations(msp, &self.source_map); + let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp); if let Ok(pos) = annotated_files.binary_search_by(|x| x.file.name.cmp(&primary_lo.file.name)) { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index f2c0e2701b3..4a140eaf0b5 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -4,10 +4,12 @@ use crate::{ SuggestionStyle, ToolMetadata, }; use rustc_data_structures::stable_map::FxHashMap; +use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_serialize::json::Json; use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::{Span, DUMMY_SP}; +use std::borrow::Cow; use std::fmt; use std::hash::{Hash, Hasher}; @@ -16,6 +18,28 @@ use std::hash::{Hash, Hasher}; #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub struct SuggestionsDisabled; +/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of +/// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of +/// diagnostic emission. +pub type DiagnosticArg<'source> = (Cow<'source, str>, DiagnosticArgValue<'source>); + +/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted +/// to a `FluentValue` by the emitter to be used in diagnostic translation. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] +pub enum DiagnosticArgValue<'source> { + Str(Cow<'source, str>), + Number(usize), +} + +impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> { + fn into(self) -> FluentValue<'source> { + match self { + DiagnosticArgValue::Str(s) => From::from(s), + DiagnosticArgValue::Number(n) => From::from(n), + } + } +} + #[must_use] #[derive(Clone, Debug, Encodable, Decodable)] pub struct Diagnostic { @@ -28,6 +52,7 @@ pub struct Diagnostic { pub span: MultiSpan, pub children: Vec<SubDiagnostic>, pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, + args: Vec<DiagnosticArg<'static>>, /// This is not used for highlighting or rendering any error message. Rather, it can be used /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of @@ -103,18 +128,23 @@ impl StringPart { } impl Diagnostic { - pub fn new(level: Level, message: &str) -> Self { + pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self { Diagnostic::new_with_code(level, None, message) } - pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self { + pub fn new_with_code<M: Into<DiagnosticMessage>>( + level: Level, + code: Option<DiagnosticId>, + message: M, + ) -> Self { Diagnostic { level, - message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)], + message: vec![(message.into(), Style::NoStyle)], code, span: MultiSpan::new(), children: vec![], suggestions: Ok(vec![]), + args: vec![], sort_span: DUMMY_SP, is_lint: false, } @@ -232,7 +262,7 @@ impl Diagnostic { self.set_span(after); for span_label in before.span_labels() { if let Some(label) = span_label.label { - self.span.push_span_message(after, label); + self.span.push_span_label(after, label); } } self @@ -326,52 +356,67 @@ impl Diagnostic { } /// Add a note attached to this diagnostic. - pub fn note(&mut self, msg: &str) -> &mut Self { + pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::Note, msg, MultiSpan::new(), None); self } - pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self { + pub fn highlighted_note<M: Into<DiagnosticMessage>>( + &mut self, + msg: Vec<(M, Style)>, + ) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None); self } /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - pub fn note_once(&mut self, msg: &str) -> &mut Self { + pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::OnceNote, msg, MultiSpan::new(), None); self } /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_note<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::Note, msg, sp.into(), None); self } /// Prints the span with a note above it. /// This is like [`Diagnostic::note()`], but it gets its own span. - pub fn span_note_once<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_note_once<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::OnceNote, msg, sp.into(), None); self } /// Add a warning attached to this diagnostic. - pub fn warn(&mut self, msg: &str) -> &mut Self { + pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new(), None); self } /// Prints the span with a warning above it. /// This is like [`Diagnostic::warn()`], but it gets its own span. - pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_warn<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::Warning, msg, sp.into(), None); self } /// Add a help message attached to this diagnostic. - pub fn help(&mut self, msg: &str) -> &mut Self { + pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new(), None); self } @@ -384,7 +429,11 @@ impl Diagnostic { /// Prints the span with some help above it. /// This is like [`Diagnostic::help()`], but it gets its own span. - pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self { + pub fn span_help<S: Into<MultiSpan>>( + &mut self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> &mut Self { self.sub(Level::Help, msg, sp.into(), None); self } @@ -420,7 +469,7 @@ impl Diagnostic { /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -436,7 +485,7 @@ impl Diagnostic { /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion_verbose( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -450,7 +499,7 @@ impl Diagnostic { /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn multipart_suggestion_with_style( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, style: SuggestionStyle, @@ -463,7 +512,7 @@ impl Diagnostic { .map(|(span, snippet)| SubstitutionPart { snippet, span }) .collect(), }], - msg: DiagnosticMessage::Str(msg.to_owned()), + msg: msg.into(), style, applicability, tool_metadata: Default::default(), @@ -479,7 +528,7 @@ impl Diagnostic { /// improve understandability. pub fn tool_only_multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { @@ -491,7 +540,7 @@ impl Diagnostic { .map(|(span, snippet)| SubstitutionPart { snippet, span }) .collect(), }], - msg: DiagnosticMessage::Str(msg.to_owned()), + msg: msg.into(), style: SuggestionStyle::CompletelyHidden, applicability, tool_metadata: Default::default(), @@ -519,7 +568,7 @@ impl Diagnostic { pub fn span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -537,7 +586,7 @@ impl Diagnostic { pub fn span_suggestion_with_style( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, style: SuggestionStyle, @@ -546,7 +595,7 @@ impl Diagnostic { substitutions: vec![Substitution { parts: vec![SubstitutionPart { snippet: suggestion, span: sp }], }], - msg: DiagnosticMessage::Str(msg.to_owned()), + msg: msg.into(), style, applicability, tool_metadata: Default::default(), @@ -558,7 +607,7 @@ impl Diagnostic { pub fn span_suggestion_verbose( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -577,7 +626,7 @@ impl Diagnostic { pub fn span_suggestions( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = String>, applicability: Applicability, ) -> &mut Self { @@ -589,7 +638,7 @@ impl Diagnostic { .collect(); self.push_suggestion(CodeSuggestion { substitutions, - msg: DiagnosticMessage::Str(msg.to_owned()), + msg: msg.into(), style: SuggestionStyle::ShowCode, applicability, tool_metadata: Default::default(), @@ -601,7 +650,7 @@ impl Diagnostic { /// See also [`Diagnostic::span_suggestion()`]. pub fn multipart_suggestions( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = Vec<(Span, String)>>, applicability: Applicability, ) -> &mut Self { @@ -614,7 +663,7 @@ impl Diagnostic { .collect(), }) .collect(), - msg: DiagnosticMessage::Str(msg.to_owned()), + msg: msg.into(), style: SuggestionStyle::ShowCode, applicability, tool_metadata: Default::default(), @@ -628,7 +677,7 @@ impl Diagnostic { pub fn span_suggestion_short( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -651,7 +700,7 @@ impl Diagnostic { pub fn span_suggestion_hidden( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -672,7 +721,7 @@ impl Diagnostic { pub fn tool_only_span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self { @@ -690,13 +739,13 @@ impl Diagnostic { /// the suggestion in a tool-specific way, as it may not even directly involve Rust code. pub fn tool_only_suggestion_with_metadata( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, applicability: Applicability, tool_metadata: Json, ) { self.push_suggestion(CodeSuggestion { substitutions: vec![], - msg: DiagnosticMessage::Str(msg.to_owned()), + msg: msg.into(), style: SuggestionStyle::CompletelyHidden, applicability, tool_metadata: ToolMetadata::new(tool_metadata), @@ -730,13 +779,13 @@ impl Diagnostic { self.code.clone() } - pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self { - self.message[0] = (DiagnosticMessage::Str(msg.into()), Style::NoStyle); + pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self { + self.message[0] = (msg.into(), Style::NoStyle); self } - pub fn message(&self) -> DiagnosticMessage { - DiagnosticMessage::Str(self.message.iter().map(|i| i.0.as_str()).collect::<String>()) + pub fn args(&self) -> &[DiagnosticArg<'static>] { + &self.args } pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> { @@ -750,13 +799,13 @@ impl Diagnostic { pub fn sub( &mut self, level: Level, - message: &str, + message: impl Into<DiagnosticMessage>, span: MultiSpan, render_span: Option<MultiSpan>, ) { let sub = SubDiagnostic { level, - message: vec![(DiagnosticMessage::Str(message.to_owned()), Style::NoStyle)], + message: vec![(message.into(), Style::NoStyle)], span, render_span, }; @@ -765,14 +814,14 @@ impl Diagnostic { /// Convenience function for internal use, clients should use one of the /// public methods above. - fn sub_with_highlights( + fn sub_with_highlights<M: Into<DiagnosticMessage>>( &mut self, level: Level, - mut message: Vec<(String, Style)>, + mut message: Vec<(M, Style)>, span: MultiSpan, render_span: Option<MultiSpan>, ) { - let message = message.drain(..).map(|m| (DiagnosticMessage::Str(m.0), m.1)).collect(); + let message = message.drain(..).map(|m| (m.0.into(), m.1)).collect(); let sub = SubDiagnostic { level, message, span, render_span }; self.children.push(sub); } @@ -813,13 +862,3 @@ impl PartialEq for Diagnostic { self.keys() == other.keys() } } - -impl SubDiagnostic { - pub fn message(&self) -> DiagnosticMessage { - DiagnosticMessage::Str(self.message.iter().map(|i| i.0.as_str()).collect::<String>()) - } - - pub fn styled_message(&self) -> &Vec<(DiagnosticMessage, Style)> { - &self.message - } -} diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 4a7e252edb2..9669941fd08 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,4 +1,4 @@ -use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString, ErrorGuaranteed}; +use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed}; use crate::{Handler, Level, MultiSpan, StashKey}; use rustc_lint_defs::Applicability; @@ -99,7 +99,10 @@ mod sealed_level_is_error { impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new_guaranteeing_error<const L: Level>(handler: &'a Handler, message: &str) -> Self + crate fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>( + handler: &'a Handler, + message: M, + ) -> Self where (): sealed_level_is_error::IsError<L>, { @@ -163,7 +166,11 @@ impl EmissionGuarantee for ErrorGuaranteed { impl<'a> DiagnosticBuilder<'a, ()> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new(handler: &'a Handler, level: Level, message: &str) -> Self { + crate fn new<M: Into<DiagnosticMessage>>( + handler: &'a Handler, + level: Level, + message: M, + ) -> Self { let diagnostic = Diagnostic::new_with_code(level, None, message); Self::new_diagnostic(handler, diagnostic) } @@ -201,7 +208,7 @@ impl EmissionGuarantee for () { impl<'a> DiagnosticBuilder<'a, !> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. - crate fn new_fatal(handler: &'a Handler, message: &str) -> Self { + crate fn new_fatal(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message); Self::new_diagnostic_fatal(handler, diagnostic) } @@ -346,7 +353,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { } // Take the `Diagnostic` by replacing it with a dummy. - let dummy = Diagnostic::new(Level::Allow, ""); + let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::Str("".to_string())); let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy); // Disable the ICE on `Drop`. @@ -434,25 +441,25 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { found: DiagnosticStyledString, ) -> &mut Self); - forward!(pub fn note(&mut self, msg: &str) -> &mut Self); - forward!(pub fn note_once(&mut self, msg: &str) -> &mut Self); + forward!(pub fn note(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); + forward!(pub fn note_once(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); forward!(pub fn span_note( &mut self, sp: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> &mut Self); forward!(pub fn span_note_once( &mut self, sp: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> &mut Self); - forward!(pub fn warn(&mut self, msg: &str) -> &mut Self); + forward!(pub fn warn(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); forward!(pub fn span_warn(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> &mut Self); - forward!(pub fn help(&mut self, msg: &str) -> &mut Self); + forward!(pub fn help(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self); forward!(pub fn span_help( &mut self, sp: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> &mut Self); forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self); forward!(pub fn set_is_lint(&mut self,) -> &mut Self); @@ -461,67 +468,67 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { forward!(pub fn multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self); forward!(pub fn multipart_suggestion_verbose( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self); forward!(pub fn tool_only_multipart_suggestion( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestions( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = String>, applicability: Applicability, ) -> &mut Self); forward!(pub fn multipart_suggestions( &mut self, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestions: impl Iterator<Item = Vec<(Span, String)>>, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_short( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_verbose( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn span_suggestion_hidden( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); forward!(pub fn tool_only_span_suggestion( &mut self, sp: Span, - msg: &str, + msg: impl Into<DiagnosticMessage>, suggestion: String, applicability: Applicability, ) -> &mut Self); @@ -547,7 +554,9 @@ impl Drop for DiagnosticBuilderInner<'_> { if !panicking() { handler.emit_diagnostic(&mut Diagnostic::new( Level::Bug, - "the following error was constructed but not emitted", + DiagnosticMessage::Str( + "the following error was constructed but not emitted".to_string(), + ), )); handler.emit_diagnostic(&mut self.diagnostic); panic!(); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 881938ec85f..43bcaa646f3 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -15,14 +15,15 @@ use rustc_span::{SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; use crate::{ - CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Handler, Level, MultiSpan, - SubDiagnostic, SubstitutionHighlight, SuggestionStyle, + CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle, + Handler, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle, }; use rustc_lint_defs::pluralize; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; use rustc_span::hygiene::{ExpnKind, MacroKind}; use std::borrow::Cow; use std::cmp::{max, min, Reverse}; @@ -58,13 +59,23 @@ impl HumanReadableErrorType { self, dst: Box<dyn Write + Send>, source_map: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, teach: bool, terminal_width: Option<usize>, macro_backtrace: bool, ) -> EmitterWriter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); - EmitterWriter::new(dst, source_map, short, teach, color, terminal_width, macro_backtrace) + EmitterWriter::new( + dst, + source_map, + fallback_bundle, + short, + teach, + color, + terminal_width, + macro_backtrace, + ) } } @@ -212,6 +223,68 @@ pub trait Emitter { fn source_map(&self) -> Option<&Lrc<SourceMap>>; + /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no + /// language was requested by the user then this will be `None` and `fallback_fluent_bundle` + /// should be used. + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>>; + + /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler. + /// Used when the user has not requested a specific language or when a localized diagnostic is + /// unavailable for the requested locale. + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle>; + + /// Convert diagnostic arguments (a rustc internal type that exists to implement + /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. + /// + /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then + /// passed around as a reference thereafter. + fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { + FromIterator::from_iter(args.to_vec().drain(..)) + } + + /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. + fn translate_messages( + &self, + messages: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, + ) -> Cow<'_, str> { + Cow::Owned( + messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(), + ) + } + + /// Convert a `DiagnosticMessage` to a string, performing translation if necessary. + fn translate_message<'a>( + &'a self, + message: &'a DiagnosticMessage, + args: &'a FluentArgs<'_>, + ) -> Cow<'_, str> { + trace!(?message); + let (identifier, attr) = match message { + DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg), + DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + }; + + let bundle = match self.fluent_bundle() { + Some(bundle) if bundle.has_message(&identifier) => bundle, + _ => self.fallback_fluent_bundle(), + }; + + let message = bundle.get_message(&identifier).expect("missing diagnostic in fluent bundle"); + let value = match attr { + Some(attr) => { + message.get_attribute(attr).expect("missing attribute in fluent message").value() + } + None => message.value().expect("missing value in fluent message"), + }; + + let mut err = vec![]; + let translated = bundle.format_pattern(value, Some(&args), &mut err); + trace!(?translated, ?err); + debug_assert!(err.is_empty()); + translated + } + /// Formats the substitutions of the primary_span /// /// There are a lot of conditions to this method, but in short: @@ -225,10 +298,12 @@ pub trait Emitter { fn primary_span_formatted<'a>( &mut self, diag: &'a Diagnostic, + fluent_args: &FluentArgs<'_>, ) -> (MultiSpan, &'a [CodeSuggestion]) { let mut primary_span = diag.span.clone(); let suggestions = diag.suggestions.as_ref().map_or(&[][..], |suggestions| &suggestions[..]); if let Some((sugg, rest)) = suggestions.split_first() { + let msg = self.translate_message(&sugg.msg, fluent_args); if rest.is_empty() && // ^ if there is only one suggestion // don't display multi-suggestions as labels @@ -236,7 +311,7 @@ pub trait Emitter { // don't display multipart suggestions as labels sugg.substitutions[0].parts.len() == 1 && // don't display long messages as labels - sugg.msg.as_str().split_whitespace().count() < 10 && + msg.split_whitespace().count() < 10 && // don't display multiline suggestions as labels !sugg.substitutions[0].parts[0].snippet.contains('\n') && ![ @@ -252,12 +327,12 @@ pub trait Emitter { let msg = if substitution.is_empty() || sugg.style.hide_inline() { // This substitution is only removal OR we explicitly don't want to show the // code inline (`hide_inline`). Therefore, we don't show the substitution. - format!("help: {}", sugg.msg.as_str()) + format!("help: {}", &msg) } else { // Show the default suggestion text with the substitution format!( "help: {}{}: `{}`", - sugg.msg.as_str(), + &msg, if self .source_map() .map(|sm| is_case_difference( @@ -492,9 +567,19 @@ impl Emitter for EmitterWriter { self.sm.as_ref() } + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + &self.fallback_bundle + } + fn emit_diagnostic(&mut self, diag: &Diagnostic) { + let fluent_args = self.to_fluent_args(diag.args()); + let mut children = diag.children.clone(); - let (mut primary_span, suggestions) = self.primary_span_formatted(&diag); + let (mut primary_span, suggestions) = self.primary_span_formatted(&diag, &fluent_args); debug!("emit_diagnostic: suggestions={:?}", suggestions); self.fix_multispans_in_extern_macros_and_render_macro_backtrace( @@ -507,7 +592,8 @@ impl Emitter for EmitterWriter { self.emit_messages_default( &diag.level, - &diag.styled_message(), + &diag.message, + &fluent_args, &diag.code, &primary_span, &children, @@ -536,6 +622,15 @@ impl Emitter for SilentEmitter { fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } + + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + panic!("silent emitter attempted to translate message") + } + fn emit_diagnostic(&mut self, d: &Diagnostic) { if d.level == Level::Fatal { let mut d = d.clone(); @@ -591,6 +686,7 @@ impl ColorConfig { pub struct EmitterWriter { dst: Destination, sm: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, ui_testing: bool, @@ -610,6 +706,7 @@ impl EmitterWriter { pub fn stderr( color_config: ColorConfig, source_map: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, terminal_width: Option<usize>, @@ -619,6 +716,7 @@ impl EmitterWriter { EmitterWriter { dst, sm: source_map, + fallback_bundle, short_message, teach, ui_testing: false, @@ -630,6 +728,7 @@ impl EmitterWriter { pub fn new( dst: Box<dyn Write + Send>, source_map: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, short_message: bool, teach: bool, colored: bool, @@ -639,6 +738,7 @@ impl EmitterWriter { EmitterWriter { dst: Raw(dst, colored), sm: source_map, + fallback_bundle, short_message, teach, ui_testing: false, @@ -1177,6 +1277,7 @@ impl EmitterWriter { &self, buffer: &mut StyledBuffer, msg: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, padding: usize, label: &str, override_style: Option<Style>, @@ -1229,7 +1330,7 @@ impl EmitterWriter { // very *weird* formats // see? for &(ref text, ref style) in msg.iter() { - let text = text.as_str(); + let text = self.translate_message(text, args); let lines = text.split('\n').collect::<Vec<_>>(); if lines.len() > 1 { for (i, line) in lines.iter().enumerate() { @@ -1240,7 +1341,7 @@ impl EmitterWriter { buffer.append(line_number, line, style_or_override(*style, override_style)); } } else { - buffer.append(line_number, text, style_or_override(*style, override_style)); + buffer.append(line_number, &text, style_or_override(*style, override_style)); } } } @@ -1249,6 +1350,7 @@ impl EmitterWriter { &mut self, msp: &MultiSpan, msg: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, code: &Option<DiagnosticId>, level: &Level, max_line_num_len: usize, @@ -1267,7 +1369,7 @@ impl EmitterWriter { buffer.append(0, level.to_str(), Style::MainHeaderMsg); buffer.append(0, ": ", Style::NoStyle); } - self.msg_to_buffer(&mut buffer, msg, max_line_num_len, "note", None); + self.msg_to_buffer(&mut buffer, msg, args, max_line_num_len, "note", None); } else { let mut label_width = 0; // The failure note level itself does not provide any useful diagnostic information @@ -1288,9 +1390,9 @@ impl EmitterWriter { label_width += 2; } for &(ref text, _) in msg.iter() { - let text = text.as_str(); + let text = self.translate_message(text, args); // Account for newlines to align output to its label. - for (line, text) in normalize_whitespace(text).lines().enumerate() { + for (line, text) in normalize_whitespace(&text).lines().enumerate() { buffer.append( 0 + line, &format!( @@ -1304,7 +1406,7 @@ impl EmitterWriter { } } - let mut annotated_files = FileWithAnnotatedLines::collect_annotations(msp, &self.sm); + let mut annotated_files = FileWithAnnotatedLines::collect_annotations(self, args, msp); // Make sure our primary file comes first let (primary_lo, sm) = if let (Some(sm), Some(ref primary_span)) = @@ -1588,6 +1690,7 @@ impl EmitterWriter { fn emit_suggestion_default( &mut self, suggestion: &CodeSuggestion, + args: &FluentArgs<'_>, level: &Level, max_line_num_len: usize, ) -> io::Result<()> { @@ -1614,6 +1717,7 @@ impl EmitterWriter { self.msg_to_buffer( &mut buffer, &[(suggestion.msg.to_owned(), Style::NoStyle)], + args, max_line_num_len, "suggestion", Some(Style::HeaderMsg), @@ -1855,6 +1959,7 @@ impl EmitterWriter { &mut self, level: &Level, message: &[(DiagnosticMessage, Style)], + args: &FluentArgs<'_>, code: &Option<DiagnosticId>, span: &MultiSpan, children: &[SubDiagnostic], @@ -1867,7 +1972,7 @@ impl EmitterWriter { num_decimal_digits(n) }; - match self.emit_message_default(span, message, code, level, max_line_num_len, false) { + match self.emit_message_default(span, message, args, code, level, max_line_num_len, false) { Ok(()) => { if !children.is_empty() || suggestions.iter().any(|s| s.style != SuggestionStyle::CompletelyHidden) @@ -1890,7 +1995,8 @@ impl EmitterWriter { let span = child.render_span.as_ref().unwrap_or(&child.span); if let Err(err) = self.emit_message_default( &span, - &child.styled_message(), + &child.message, + args, &None, &child.level, max_line_num_len, @@ -1906,6 +2012,7 @@ impl EmitterWriter { if let Err(e) = self.emit_message_default( &MultiSpan::new(), &[(sugg.msg.to_owned(), Style::HeaderMsg)], + args, &None, &Level::Help, max_line_num_len, @@ -1914,7 +2021,7 @@ impl EmitterWriter { panic!("failed to emit error: {}", e); } } else if let Err(e) = - self.emit_suggestion_default(sugg, &Level::Help, max_line_num_len) + self.emit_suggestion_default(sugg, args, &Level::Help, max_line_num_len) { panic!("failed to emit error: {}", e); }; @@ -1940,8 +2047,9 @@ impl FileWithAnnotatedLines { /// Preprocess all the annotations so that they are grouped by file and by line number /// This helps us quickly iterate over the whole message (including secondary file spans) pub fn collect_annotations( + emitter: &dyn Emitter, + args: &FluentArgs<'_>, msp: &MultiSpan, - source_map: &Option<Lrc<SourceMap>>, ) -> Vec<FileWithAnnotatedLines> { fn add_annotation_to_file( file_vec: &mut Vec<FileWithAnnotatedLines>, @@ -1976,7 +2084,7 @@ impl FileWithAnnotatedLines { let mut output = vec![]; let mut multiline_annotations = vec![]; - if let Some(ref sm) = source_map { + if let Some(ref sm) = emitter.source_map() { for span_label in msp.span_labels() { if span_label.span.is_dummy() { continue; @@ -2003,7 +2111,10 @@ impl FileWithAnnotatedLines { start_col: lo.col_display, end_col: hi.col_display, is_primary: span_label.is_primary, - label: span_label.label.map(|m| m.to_string()), + label: span_label + .label + .as_ref() + .map(|m| emitter.translate_message(m, args).to_string()), overlaps_exactly: false, }; multiline_annotations.push((lo.file, ml)); @@ -2012,7 +2123,10 @@ impl FileWithAnnotatedLines { start_col: lo.col_display, end_col: hi.col_display, is_primary: span_label.is_primary, - label: span_label.label.map(|m| m.to_string()), + label: span_label + .label + .as_ref() + .map(|m| emitter.translate_message(m, args).to_string()), annotation_type: AnnotationType::Singleline, }; add_annotation_to_file(&mut output, lo.file, lo.line, ann); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index b93ab70e1f3..3e7bcf2d37e 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -15,10 +15,11 @@ use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; use crate::DiagnosticId; use crate::ToolMetadata; -use crate::{CodeSuggestion, MultiSpan, SpanLabel, SubDiagnostic}; +use crate::{CodeSuggestion, FluentBundle, MultiSpan, SpanLabel, SubDiagnostic}; use rustc_lint_defs::Applicability; use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; use rustc_span::hygiene::ExpnData; use rustc_span::Span; use std::io::{self, Write}; @@ -36,6 +37,7 @@ pub struct JsonEmitter { dst: Box<dyn Write + Send>, registry: Option<Registry>, sm: Lrc<SourceMap>, + fallback_bundle: Lrc<FluentBundle>, pretty: bool, ui_testing: bool, json_rendered: HumanReadableErrorType, @@ -47,6 +49,7 @@ impl JsonEmitter { pub fn stderr( registry: Option<Registry>, source_map: Lrc<SourceMap>, + fallback_bundle: Lrc<FluentBundle>, pretty: bool, json_rendered: HumanReadableErrorType, terminal_width: Option<usize>, @@ -56,6 +59,7 @@ impl JsonEmitter { dst: Box::new(io::BufWriter::new(io::stderr())), registry, sm: source_map, + fallback_bundle, pretty, ui_testing: false, json_rendered, @@ -67,6 +71,7 @@ impl JsonEmitter { pub fn basic( pretty: bool, json_rendered: HumanReadableErrorType, + fallback_bundle: Lrc<FluentBundle>, terminal_width: Option<usize>, macro_backtrace: bool, ) -> JsonEmitter { @@ -74,6 +79,7 @@ impl JsonEmitter { JsonEmitter::stderr( None, Lrc::new(SourceMap::new(file_path_mapping)), + fallback_bundle, pretty, json_rendered, terminal_width, @@ -85,6 +91,7 @@ impl JsonEmitter { dst: Box<dyn Write + Send>, registry: Option<Registry>, source_map: Lrc<SourceMap>, + fallback_bundle: Lrc<FluentBundle>, pretty: bool, json_rendered: HumanReadableErrorType, terminal_width: Option<usize>, @@ -94,6 +101,7 @@ impl JsonEmitter { dst, registry, sm: source_map, + fallback_bundle, pretty, ui_testing: false, json_rendered, @@ -173,6 +181,14 @@ impl Emitter for JsonEmitter { Some(&self.sm) } + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<FluentBundle> { + &self.fallback_bundle + } + fn should_show_explain(&self) -> bool { !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) } @@ -345,14 +361,18 @@ struct UnusedExterns<'a, 'b, 'c> { impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { - let sugg = diag.suggestions.iter().flatten().map(|sugg| Diagnostic { - message: sugg.msg.clone().to_string(), - code: None, - level: "help", - spans: DiagnosticSpan::from_suggestion(sugg, je), - children: vec![], - rendered: None, - tool_metadata: sugg.tool_metadata.clone(), + let args = je.to_fluent_args(diag.args()); + let sugg = diag.suggestions.iter().flatten().map(|sugg| { + let translated_message = je.translate_message(&sugg.msg, &args); + Diagnostic { + message: translated_message.to_string(), + code: None, + level: "help", + spans: DiagnosticSpan::from_suggestion(sugg, &args, je), + children: vec![], + rendered: None, + tool_metadata: sugg.tool_metadata.clone(), + } }); // generate regular command line output and store it in the json @@ -375,6 +395,7 @@ impl Diagnostic { .new_emitter( Box::new(buf), Some(je.sm.clone()), + je.fallback_bundle.clone(), false, je.terminal_width, je.macro_backtrace, @@ -384,15 +405,16 @@ impl Diagnostic { let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); let output = String::from_utf8(output).unwrap(); + let translated_message = je.translate_messages(&diag.message, &args); Diagnostic { - message: diag.message().to_string(), + message: translated_message.to_string(), code: DiagnosticCode::map_opt_string(diag.code.clone(), je), level: diag.level.to_str(), - spans: DiagnosticSpan::from_multispan(&diag.span, je), + spans: DiagnosticSpan::from_multispan(&diag.span, &args, je), children: diag .children .iter() - .map(|c| Diagnostic::from_sub_diagnostic(c, je)) + .map(|c| Diagnostic::from_sub_diagnostic(c, &args, je)) .chain(sugg) .collect(), rendered: Some(output), @@ -400,16 +422,21 @@ impl Diagnostic { } } - fn from_sub_diagnostic(diag: &SubDiagnostic, je: &JsonEmitter) -> Diagnostic { + fn from_sub_diagnostic( + diag: &SubDiagnostic, + args: &FluentArgs<'_>, + je: &JsonEmitter, + ) -> Diagnostic { + let translated_message = je.translate_messages(&diag.message, args); Diagnostic { - message: diag.message().to_string(), + message: translated_message.to_string(), code: None, level: diag.level.to_str(), spans: diag .render_span .as_ref() - .map(|sp| DiagnosticSpan::from_multispan(sp, je)) - .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, je)), + .map(|sp| DiagnosticSpan::from_multispan(sp, args, je)) + .unwrap_or_else(|| DiagnosticSpan::from_multispan(&diag.span, args, je)), children: vec![], rendered: None, tool_metadata: ToolMetadata::default(), @@ -421,12 +448,13 @@ impl DiagnosticSpan { fn from_span_label( span: SpanLabel, suggestion: Option<(&String, Applicability)>, + args: &FluentArgs<'_>, je: &JsonEmitter, ) -> DiagnosticSpan { Self::from_span_etc( span.span, span.is_primary, - span.label.map(|m| m.to_string()), + span.label.as_ref().map(|m| je.translate_message(m, args)).map(|m| m.to_string()), suggestion, je, ) @@ -492,14 +520,22 @@ impl DiagnosticSpan { } } - fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec<DiagnosticSpan> { + fn from_multispan( + msp: &MultiSpan, + args: &FluentArgs<'_>, + je: &JsonEmitter, + ) -> Vec<DiagnosticSpan> { msp.span_labels() .into_iter() - .map(|span_str| Self::from_span_label(span_str, None, je)) + .map(|span_str| Self::from_span_label(span_str, None, args, je)) .collect() } - fn from_suggestion(suggestion: &CodeSuggestion, je: &JsonEmitter) -> Vec<DiagnosticSpan> { + fn from_suggestion( + suggestion: &CodeSuggestion, + args: &FluentArgs<'_>, + je: &JsonEmitter, + ) -> Vec<DiagnosticSpan> { suggestion .substitutions .iter() @@ -510,6 +546,7 @@ impl DiagnosticSpan { DiagnosticSpan::from_span_label( span_label, Some((&suggestion_inner.snippet, suggestion.applicability)), + args, je, ) }) diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index ed01afe6e30..fa0ccd65d06 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -39,12 +39,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); + let fallback_bundle = crate::fallback_fluent_bundle(); let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), None, sm, + fallback_bundle, true, HumanReadableErrorType::Short(ColorConfig::Never), None, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 5e770f7d5ae..04a0a9f7b73 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -31,7 +31,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; -pub use rustc_error_messages::{DiagnosticMessage, MultiSpan, SpanLabel}; +pub use rustc_error_messages::{ + fallback_fluent_bundle, DiagnosticMessage, FluentBundle, MultiSpan, SpanLabel, +}; pub use rustc_lint_defs::{pluralize, Applicability}; use rustc_serialize::json::Json; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -402,7 +404,9 @@ impl fmt::Display for ExplicitBug { impl error::Error for ExplicitBug {} -pub use diagnostic::{Diagnostic, DiagnosticId, DiagnosticStyledString, SubDiagnostic}; +pub use diagnostic::{ + Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticStyledString, SubDiagnostic, +}; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee}; use std::backtrace::Backtrace; @@ -540,10 +544,12 @@ impl Handler { can_emit_warnings: bool, treat_err_as_bug: Option<NonZeroUsize>, sm: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, ) -> Self { Self::with_tty_emitter_and_flags( color_config, sm, + fallback_bundle, HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, ) } @@ -551,11 +557,13 @@ impl Handler { pub fn with_tty_emitter_and_flags( color_config: ColorConfig, sm: Option<Lrc<SourceMap>>, + fallback_bundle: Lrc<FluentBundle>, flags: HandlerFlags, ) -> Self { let emitter = Box::new(EmitterWriter::stderr( color_config, sm, + fallback_bundle, false, false, None, @@ -660,7 +668,7 @@ impl Handler { pub fn struct_span_warn( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_warn(msg); result.set_span(span); @@ -671,7 +679,7 @@ impl Handler { pub fn struct_span_allow( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_allow(msg); result.set_span(span); @@ -683,7 +691,7 @@ impl Handler { pub fn struct_span_warn_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ()> { let mut result = self.struct_span_warn(span, msg); @@ -696,17 +704,21 @@ impl Handler { /// Attempting to `.emit()` the builder will only emit if either: /// * `can_emit_warnings` is `true` /// * `is_force_warn` was set in `DiagnosticId::Lint` - pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Warning, msg) } /// Construct a builder at the `Allow` level with the `msg`. - pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Allow, msg) } /// Construct a builder at the `Expect` level with the `msg`. - pub fn struct_expect(&self, msg: &str, id: LintExpectationId) -> DiagnosticBuilder<'_, ()> { + pub fn struct_expect( + &self, + msg: impl Into<DiagnosticMessage>, + id: LintExpectationId, + ) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Expect(id), msg) } @@ -714,7 +726,7 @@ impl Handler { pub fn struct_span_err( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_err(msg); result.set_span(span); @@ -725,7 +737,7 @@ impl Handler { pub fn struct_span_err_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_span_err(span, msg); @@ -735,20 +747,23 @@ impl Handler { /// Construct a builder at the `Error` level with the `msg`. // FIXME: This method should be removed (every error should have an associated error code). - pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - DiagnosticBuilder::new_guaranteeing_error::<{ Level::Error { lint: false } }>(self, msg) + pub fn struct_err( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg) } /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. #[doc(hidden)] - pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_err_lint(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Error { lint: true }, msg) } /// Construct a builder at the `Error` level with the `msg` and the `code`. pub fn struct_err_with_code( &self, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let mut result = self.struct_err(msg); @@ -760,7 +775,7 @@ impl Handler { pub fn struct_span_fatal( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, !> { let mut result = self.struct_fatal(msg); result.set_span(span); @@ -771,7 +786,7 @@ impl Handler { pub fn struct_span_fatal_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, !> { let mut result = self.struct_span_fatal(span, msg); @@ -780,21 +795,24 @@ impl Handler { } /// Construct a builder at the `Error` level with the `msg`. - pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, !> { + pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { DiagnosticBuilder::new_fatal(self, msg) } /// Construct a builder at the `Help` level with the `msg`. - pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Help, msg) } /// Construct a builder at the `Note` level with the `msg`. - pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_note_without_error( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { DiagnosticBuilder::new(self, Level::Note, msg) } - pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: &str) -> ! { + pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.emit_diag_at_span(Diagnostic::new(Fatal, msg), span); FatalError.raise() } @@ -802,80 +820,106 @@ impl Handler { pub fn span_fatal_with_code( &self, span: impl Into<MultiSpan>, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> ! { self.emit_diag_at_span(Diagnostic::new_with_code(Fatal, Some(code), msg), span); FatalError.raise() } - pub fn span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed { + pub fn span_err( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.emit_diag_at_span(Diagnostic::new(Error { lint: false }, msg), span).unwrap() } - pub fn span_err_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) { + pub fn span_err_with_code( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { self.emit_diag_at_span( Diagnostic::new_with_code(Error { lint: false }, Some(code), msg), span, ); } - pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: &str) { + pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { self.emit_diag_at_span(Diagnostic::new(Warning, msg), span); } - pub fn span_warn_with_code(&self, span: impl Into<MultiSpan>, msg: &str, code: DiagnosticId) { + pub fn span_warn_with_code( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { self.emit_diag_at_span(Diagnostic::new_with_code(Warning, Some(code), msg), span); } - pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ! { + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.inner.borrow_mut().span_bug(span, msg) } #[track_caller] - pub fn delay_span_bug(&self, span: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed { + pub fn delay_span_bug( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.inner.borrow_mut().delay_span_bug(span, msg) } // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's // where the explanation of what "good path" is (also, it should be renamed). - pub fn delay_good_path_bug(&self, msg: &str) { + pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) { self.inner.borrow_mut().delay_good_path_bug(msg) } - pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: &str) { + pub fn span_bug_no_panic(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) { self.emit_diag_at_span(Diagnostic::new(Bug, msg), span); } - pub fn span_note_without_error(&self, span: impl Into<MultiSpan>, msg: &str) { + pub fn span_note_without_error( + &self, + span: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) { self.emit_diag_at_span(Diagnostic::new(Note, msg), span); } - pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn span_note_diag( + &self, + span: Span, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { let mut db = DiagnosticBuilder::new(self, Note, msg); db.set_span(span); db } // NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread - pub fn fatal(&self, msg: &str) -> FatalError { + pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> FatalError { self.inner.borrow_mut().fatal(msg) } - pub fn err(&self, msg: &str) -> ErrorGuaranteed { + pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.inner.borrow_mut().err(msg) } - pub fn warn(&self, msg: &str) { + pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { let mut db = DiagnosticBuilder::new(self, Warning, msg); db.emit(); } - pub fn note_without_error(&self, msg: &str) { + pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) { DiagnosticBuilder::new(self, Note, msg).emit(); } - pub fn bug(&self, msg: &str) -> ! { + pub fn bug(&self, msg: impl Into<DiagnosticMessage>) -> ! { self.inner.borrow_mut().bug(msg) } @@ -1145,7 +1189,10 @@ impl HandlerInner { match (errors.len(), warnings.len()) { (0, 0) => return, - (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(Level::Warning, &warnings)), + (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new( + Level::Warning, + DiagnosticMessage::Str(warnings.to_owned()), + )), (_, 0) => { let _ = self.fatal(&errors); } @@ -1220,7 +1267,7 @@ impl HandlerInner { } } - fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ! { + fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); panic::panic_any(ExplicitBug); } @@ -1230,7 +1277,11 @@ impl HandlerInner { } #[track_caller] - fn delay_span_bug(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> ErrorGuaranteed { + fn delay_span_bug( + &mut self, + sp: impl Into<MultiSpan>, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. // FIXME: Would be nice to increment err_count in a more coherent way. @@ -1246,7 +1297,7 @@ impl HandlerInner { // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's // where the explanation of what "good path" is (also, it should be renamed). - fn delay_good_path_bug(&mut self, msg: &str) { + fn delay_good_path_bug(&mut self, msg: impl Into<DiagnosticMessage>) { let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); if self.flags.report_delayed_bugs { self.emit_diagnostic(&mut diagnostic); @@ -1255,33 +1306,37 @@ impl HandlerInner { self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); } - fn failure(&mut self, msg: &str) { + fn failure(&mut self, msg: impl Into<DiagnosticMessage>) { self.emit_diagnostic(&mut Diagnostic::new(FailureNote, msg)); } - fn fatal(&mut self, msg: &str) -> FatalError { + fn fatal(&mut self, msg: impl Into<DiagnosticMessage>) -> FatalError { self.emit(Fatal, msg); FatalError } - fn err(&mut self, msg: &str) -> ErrorGuaranteed { + fn err(&mut self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.emit(Error { lint: false }, msg) } /// Emit an error; level should be `Error` or `Fatal`. - fn emit(&mut self, level: Level, msg: &str) -> ErrorGuaranteed { + fn emit(&mut self, level: Level, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { if self.treat_err_as_bug() { self.bug(msg); } self.emit_diagnostic(&mut Diagnostic::new(level, msg)).unwrap() } - fn bug(&mut self, msg: &str) -> ! { + fn bug(&mut self, msg: impl Into<DiagnosticMessage>) -> ! { self.emit_diagnostic(&mut Diagnostic::new(Bug, msg)); panic::panic_any(ExplicitBug); } - fn flush_delayed(&mut self, bugs: impl IntoIterator<Item = Diagnostic>, explanation: &str) { + fn flush_delayed( + &mut self, + bugs: impl IntoIterator<Item = Diagnostic>, + explanation: impl Into<DiagnosticMessage> + Copy, + ) { let mut no_bugs = true; for mut bug in bugs { if no_bugs { diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 1f90b24a8cc..0e20e0970f4 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -250,7 +250,7 @@ fn check_binders( if let Some(prev_info) = binders.get(&name) { // 1. The meta-variable is already bound in the current LHS: This is an error. let mut span = MultiSpan::from_span(span); - span.push_span_label(prev_info.span, "previous declaration".into()); + span.push_span_label(prev_info.span, "previous declaration"); buffer_lint(sess, span, node_id, "duplicate matcher binding"); } else if get_binder_info(macros, binders, name).is_none() { // 2. The meta-variable is free: This is a binder. @@ -622,7 +622,7 @@ fn ops_is_prefix( for (i, binder) in binder_ops.iter().enumerate() { if i >= occurrence_ops.len() { let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition".into()); + span.push_span_label(binder.span, "expected repetition"); let message = &format!("variable '{}' is still repeating at this depth", name); buffer_lint(sess, span, node_id, message); return; @@ -630,8 +630,8 @@ fn ops_is_prefix( let occurrence = &occurrence_ops[i]; if occurrence.op != binder.op { let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition".into()); - span.push_span_label(occurrence.span, "conflicting repetition".into()); + span.push_span_label(binder.span, "expected repetition"); + span.push_span_label(occurrence.span, "conflicting repetition"); let message = "meta-variable repeats with different Kleene operator"; buffer_lint(sess, span, node_id, message); return; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 2ed11204ef6..7fef87ed977 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -69,7 +69,7 @@ fn emit_frag_parse_err( kind: AstFragmentKind, ) { // FIXME(davidtwco): avoid depending on the error message text - if parser.token == token::Eof && e.message().as_str().ends_with(", found `<eof>`") { + if parser.token == token::Eof && e.message[0].0.expect_str().ends_with(", found `<eof>`") { if !e.span.is_dummy() { // early end of macro arm (#52866) e.replace_span_with(parser.sess.source_map().next_point(parser.token.span)); @@ -78,7 +78,7 @@ fn emit_frag_parse_err( e.message[0] = ( rustc_errors::DiagnosticMessage::Str(format!( "macro expansion ends with an incomplete expression: {}", - msg.0.as_str().replace(", found `<eof>`", ""), + msg.0.expect_str().replace(", found `<eof>`", ""), )), msg.1, ); diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 2949ca716b2..52a656e1d1c 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -128,13 +128,15 @@ fn parse_ident<'sess>( sess: &'sess ParseSess, span: Span, ) -> PResult<'sess, Ident> { - let err_fn = |msg| sess.span_diagnostic.struct_span_err(span, msg); if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt { if let Some((elem, false)) = token.ident() { return Ok(elem); } let token_str = pprust::token_to_string(&token); - let mut err = err_fn(&format!("expected identifier, found `{}`", &token_str)); + let mut err = sess.span_diagnostic.struct_span_err( + span, + &format!("expected identifier, found `{}`", &token_str) + ); err.span_suggestion( token.span, &format!("try removing `{}`", &token_str), @@ -143,7 +145,7 @@ fn parse_ident<'sess>( ); return Err(err); } - Err(err_fn("expected identifier")) + Err(sess.span_diagnostic.struct_span_err(span, "expected identifier")) } /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index d9b00f8f3ec..65128b91770 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -127,6 +127,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & create_default_session_if_not_set_then(|_| { let output = Arc::new(Mutex::new(Vec::new())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); @@ -142,6 +143,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & let emitter = EmitterWriter::new( Box::new(Shared { data: output.clone() }), Some(source_map.clone()), + fallback_bundle, false, false, false, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 5793aedef1e..f25215fe813 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2075,7 +2075,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { diag.span_suggestion( span, - msg, + *msg, format!("{}.as_ref()", snippet), Applicability::MachineApplicable, ); diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index cd20cb3db99..80500f3fe65 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -41,8 +41,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); // FIXME: we should point at the lifetime let mut multi_span: MultiSpan = vec![binding_span].into(); - multi_span - .push_span_label(binding_span, "introduces a `'static` lifetime requirement".into()); + multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement"); err.span_note(multi_span, "because this has an unmet lifetime requirement"); note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span)); if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 5dcac7f56cc..baea3e8285a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, Region}; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { - let mut label_or_note = |span, msg| { + let mut label_or_note = |span, msg: &str| { let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count(); let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index fd414dd5bc7..e6f9246d732 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1571,7 +1571,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { lint.build("bounds on generic parameters are not enforced in type aliases"); let msg = "the bound will not be checked when the type alias is used, \ and should be removed"; - err.multipart_suggestion(&msg, suggestion, Applicability::MachineApplicable); + err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); if !suggested_changing_assoc_types { TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); suggested_changing_assoc_types = true; diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 74fef0be9e9..67f5aa0540f 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -37,7 +37,7 @@ fn emit_unfulfilled_expectation_lint( |diag| { let mut diag = diag.build("this lint expectation is unfulfilled"); if let Some(rationale) = expectation.reason { - diag.note(&rationale.as_str()); + diag.note(rationale.as_str()); } if expectation.is_unfulfilled_lint_expectations { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index c95905b9b18..14ac30987b3 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -154,7 +154,7 @@ fn lint_overflowing_range_endpoint<'tcx>( let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); err.span_suggestion( parent_expr.span, - &"use an inclusive range instead", + "use an inclusive range instead", suggestion, Applicability::MachineApplicable, ); @@ -399,7 +399,7 @@ fn lint_uint_literal<'tcx>( lint.build("only `u8` can be cast into `char`") .span_suggestion( par_e.span, - &"use a `char` literal instead", + "use a `char` literal instead", format!("'\\u{{{:X}}}'", lit_val), Applicability::MachineApplicable, ) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 2999db79785..ed264045170 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -18,7 +18,9 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Diagnostic, EmissionGuarantee, ErrorGuaranteed}; -use rustc_errors::{Applicability, DiagnosticBuilder, Handler, MultiSpan, PResult}; +use rustc_errors::{ + Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult, +}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; @@ -273,12 +275,12 @@ impl<'a> Parser<'a> { pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, - m: &str, + m: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { self.sess.span_diagnostic.struct_span_err(sp, m) } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> ! { + pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<DiagnosticMessage>) -> ! { self.sess.span_diagnostic.span_bug(sp, m) } @@ -584,16 +586,22 @@ impl<'a> Parser<'a> { // field: value, // } } err.delay_as_bug(); - self.struct_span_err(expr.span, "struct literal body without path") - .multipart_suggestion( - "you might have forgotten to add the struct literal inside the block", - vec![ - (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()), - (expr.span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MaybeIncorrect, - ) - .emit(); + self.struct_span_err( + expr.span, + DiagnosticMessage::fluent("parser-struct-literal-body-without-path"), + ) + .multipart_suggestion( + DiagnosticMessage::fluent_attr( + "parser-struct-literal-body-without-path", + "suggestion", + ), + vec![ + (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MaybeIncorrect, + ) + .emit(); self.restore_snapshot(snapshot); let mut tail = self.mk_block( vec![self.mk_stmt_err(expr.span)], @@ -1752,7 +1760,7 @@ impl<'a> Parser<'a> { let mut primary_span: MultiSpan = primary_span.into(); for span_label in err.span.span_labels() { if let Some(label) = span_label.label { - primary_span.push_span_message(span_label.span, label); + primary_span.push_span_label(span_label.span, label); } } err.set_span(primary_span); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7c407cbe465..7978a1a7f5f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1703,7 +1703,7 @@ impl<'a> Parser<'a> { if matches!(expr.kind, ExprKind::Err) { let mut err = self .diagnostic() - .struct_span_err(self.token.span, &"invalid interpolated expression"); + .struct_span_err(self.token.span, "invalid interpolated expression"); err.downgrade_to_delayed_bug(); return err; } @@ -1820,7 +1820,7 @@ impl<'a> Parser<'a> { } else if let Some(fixed) = fix_base_capitalisation(suf) { let msg = "invalid base prefix for number literal"; - self.struct_span_err(span, &msg) + self.struct_span_err(span, msg) .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") .span_suggestion( span, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index fd4b00de392..5bf6f22b5d0 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1011,7 +1011,7 @@ impl<'a> Parser<'a> { let current_qual_sp = current_qual_sp.to(sp_start); if let Ok(current_qual) = self.span_to_snippet(current_qual_sp) { // FIXME(davidtwco): avoid depending on the error message text - if err.message().as_str() == "expected `{`, found keyword `unsafe`" { + if err.message[0].0.expect_str() == "expected `{`, found keyword `unsafe`" { let invalid_qual_sp = self.token.uninterpolated_span(); let invalid_qual = self.span_to_snippet(invalid_qual_sp).unwrap(); diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index bd4e1ddd25c..700290d67a4 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -620,8 +620,8 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D } else { sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd)) - .note(&"Please follow the instructions below to create a bug report with the provided information") - .note(&"See <https://github.com/rust-lang/rust/issues/84970> for more information") + .note("Please follow the instructions below to create a bug report with the provided information") + .note("See <https://github.com/rust-lang/rust/issues/84970> for more information") .emit(); panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index c9a6bb542c0..609dbd1fe1b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1341,7 +1341,7 @@ impl<'a> Resolver<'a> { let def_span = self.session.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); if !first && binding.vis.is_public() { - note_span.push_span_label(def_span, "consider importing it directly".into()); + note_span.push_span_label(def_span, "consider importing it directly"); } err.span_note(note_span, &msg); } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index add499701af..02abdbaa983 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -739,7 +739,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() { for message in note { - diag.note(&message); + diag.note(message); } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 920605a7463..1e943f0e44a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1140,7 +1140,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } err.span_suggestion( span, - &"use this syntax instead", + "use this syntax instead", path_str.to_string(), Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 42bf6ff2a98..d5053034ed8 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -431,6 +431,20 @@ where } } +impl<'a, S: Encoder> Encodable<S> for Cow<'a, str> { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + let val: &str = self; + val.encode(s) + } +} + +impl<'a, D: Decoder> Decodable<D> for Cow<'a, str> { + fn decode(d: &mut D) -> Cow<'static, str> { + let v: String = Decodable::decode(d); + Cow::Owned(v) + } +} + impl<S: Encoder, T: Encodable<S>> Encodable<S> for Option<T> { fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_option(|s| match *self { diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 5a93303ad55..49c1a1be201 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -8,7 +8,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; use rustc_errors::{ - error_code, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, + error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, + ErrorGuaranteed, MultiSpan, }; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; use rustc_span::edition::Edition; @@ -173,8 +174,15 @@ pub struct ParseSess { impl ParseSess { /// Used for testing. pub fn new(file_path_mapping: FilePathMapping) -> Self { + let fallback_bundle = fallback_fluent_bundle(); let sm = Lrc::new(SourceMap::new(file_path_mapping)); - let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, None, Some(sm.clone())); + let handler = Handler::with_tty_emitter( + ColorConfig::Auto, + true, + None, + Some(sm.clone()), + fallback_bundle, + ); ParseSess::with_span_handler(handler, sm) } @@ -203,8 +211,10 @@ impl ParseSess { } pub fn with_silent_emitter(fatal_note: Option<String>) -> Self { + let fallback_bundle = fallback_fluent_bundle(); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fatal_handler = Handler::with_tty_emitter(ColorConfig::Auto, false, None, None); + let fatal_handler = + Handler::with_tty_emitter(ColorConfig::Auto, false, None, None, fallback_bundle); let handler = Handler::with_emitter( false, None, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 321ab6af6e5..9a11f537c25 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -19,7 +19,10 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; -use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, + FluentBundle, MultiSpan, +}; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; @@ -279,34 +282,34 @@ impl Session { pub fn struct_span_warn<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn(sp, msg) } pub fn struct_span_warn_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_warn_with_code(sp, msg, code) } - pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_warn(msg) } pub fn struct_span_allow<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_span_allow(sp, msg) } - pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_allow(msg) } pub fn struct_expect( &self, - msg: &str, + msg: impl Into<DiagnosticMessage>, id: lint::LintExpectationId, ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_expect(msg, id) @@ -314,25 +317,28 @@ impl Session { pub fn struct_span_err<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_span_err(sp, msg) } pub fn struct_span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_span_err_with_code(sp, msg, code) } // FIXME: This method should be removed (every error should have an associated error code). - pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorGuaranteed> { + pub fn struct_err( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_err(msg) } pub fn struct_err_with_code( &self, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { self.diagnostic().struct_err_with_code(msg, code) @@ -340,50 +346,64 @@ impl Session { pub fn struct_span_fatal<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal(sp, msg) } pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_span_fatal_with_code(sp, msg, code) } - pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, !> { + pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { self.diagnostic().struct_fatal(msg) } - pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! { + pub fn span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().span_fatal(sp, msg) } pub fn span_fatal_with_code<S: Into<MultiSpan>>( &self, sp: S, - msg: &str, + msg: impl Into<DiagnosticMessage>, code: DiagnosticId, ) -> ! { self.diagnostic().span_fatal_with_code(sp, msg, code) } - pub fn fatal(&self, msg: &str) -> ! { + pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! { self.diagnostic().fatal(msg).raise() } - pub fn span_err_or_warn<S: Into<MultiSpan>>(&self, is_warning: bool, sp: S, msg: &str) { + pub fn span_err_or_warn<S: Into<MultiSpan>>( + &self, + is_warning: bool, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) { if is_warning { self.span_warn(sp, msg); } else { self.span_err(sp, msg); } } - pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ErrorGuaranteed { + pub fn span_err<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.diagnostic().span_err(sp, msg) } - pub fn span_err_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { - self.diagnostic().span_err_with_code(sp, &msg, code) + pub fn span_err_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { + self.diagnostic().span_err_with_code(sp, msg, code) } - pub fn err(&self, msg: &str) -> ErrorGuaranteed { + pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.diagnostic().err(msg) } pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { @@ -423,25 +443,34 @@ impl Session { Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) } } - pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) { self.diagnostic().span_warn(sp, msg) } - pub fn span_warn_with_code<S: Into<MultiSpan>>(&self, sp: S, msg: &str, code: DiagnosticId) { + pub fn span_warn_with_code<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + code: DiagnosticId, + ) { self.diagnostic().span_warn_with_code(sp, msg, code) } - pub fn warn(&self, msg: &str) { + pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { self.diagnostic().warn(msg) } /// Delay a span_bug() call until abort_if_errors() #[track_caller] - pub fn delay_span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ErrorGuaranteed { + pub fn delay_span_bug<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) -> ErrorGuaranteed { self.diagnostic().delay_span_bug(sp, msg) } /// Used for code paths of expensive computations that should only take place when /// warnings or errors are emitted. If no messages are emitted ("good path"), then /// it's likely a bug. - pub fn delay_good_path_bug(&self, msg: &str) { + pub fn delay_good_path_bug(&self, msg: impl Into<DiagnosticMessage>) { if self.opts.debugging_opts.print_type_sizes || self.opts.debugging_opts.query_dep_graph || self.opts.debugging_opts.dump_mir.is_some() @@ -455,13 +484,20 @@ impl Session { self.diagnostic().delay_good_path_bug(msg) } - pub fn note_without_error(&self, msg: &str) { + pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) { self.diagnostic().note_without_error(msg) } - pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) { + pub fn span_note_without_error<S: Into<MultiSpan>>( + &self, + sp: S, + msg: impl Into<DiagnosticMessage>, + ) { self.diagnostic().span_note_without_error(sp, msg) } - pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { + pub fn struct_note_without_error( + &self, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_note_without_error(msg) } @@ -1033,6 +1069,7 @@ fn default_emitter( sopts: &config::Options, registry: rustc_errors::registry::Registry, source_map: Lrc<SourceMap>, + fallback_bundle: Lrc<FluentBundle>, emitter_dest: Option<Box<dyn Write + Send>>, ) -> Box<dyn Emitter + sync::Send> { let macro_backtrace = sopts.debugging_opts.macro_backtrace; @@ -1041,14 +1078,19 @@ fn default_emitter( let (short, color_config) = kind.unzip(); if let HumanReadableErrorType::AnnotateSnippet(_) = kind { - let emitter = - AnnotateSnippetEmitterWriter::new(Some(source_map), short, macro_backtrace); + let emitter = AnnotateSnippetEmitterWriter::new( + Some(source_map), + fallback_bundle, + short, + macro_backtrace, + ); Box::new(emitter.ui_testing(sopts.debugging_opts.ui_testing)) } else { let emitter = match dst { None => EmitterWriter::stderr( color_config, Some(source_map), + fallback_bundle, short, sopts.debugging_opts.teach, sopts.debugging_opts.terminal_width, @@ -1057,6 +1099,7 @@ fn default_emitter( Some(dst) => EmitterWriter::new( dst, Some(source_map), + fallback_bundle, short, false, // no teach messages when writing to a buffer false, // no colors when writing to a buffer @@ -1071,6 +1114,7 @@ fn default_emitter( JsonEmitter::stderr( Some(registry), source_map, + fallback_bundle, pretty, json_rendered, sopts.debugging_opts.terminal_width, @@ -1083,6 +1127,7 @@ fn default_emitter( dst, Some(registry), source_map, + fallback_bundle, pretty, json_rendered, sopts.debugging_opts.terminal_width, @@ -1152,7 +1197,10 @@ pub fn build_session( sopts.file_path_mapping(), hash_kind, )); - let emitter = default_emitter(&sopts, registry, source_map.clone(), write_dest); + + let fallback_bundle = fallback_fluent_bundle(); + let emitter = + default_emitter(&sopts, registry, source_map.clone(), fallback_bundle.clone(), write_dest); let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( emitter, @@ -1385,13 +1433,22 @@ pub enum IncrCompSession { } fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler { + let fallback_bundle = fallback_fluent_bundle(); let emitter: Box<dyn Emitter + sync::Send> = match output { config::ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); - Box::new(EmitterWriter::stderr(color_config, None, short, false, None, false)) + Box::new(EmitterWriter::stderr( + color_config, + None, + fallback_bundle, + short, + false, + None, + false, + )) } config::ErrorOutputType::Json { pretty, json_rendered } => { - Box::new(JsonEmitter::basic(pretty, json_rendered, None, false)) + Box::new(JsonEmitter::basic(pretty, json_rendered, fallback_bundle, None, false)) } }; rustc_errors::Handler::with_emitter(true, None, emitter) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index e4ccd21246e..8dac56120e6 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -597,7 +597,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { Some(format!("{}", name)) } _ => { - err.note(&msg); + err.note(msg); None } } @@ -2481,7 +2481,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .opt_associated_item(trait_item_def_id) .and_then(|i| self.tcx.opt_item_name(i.container.id())) { - assoc_span.push_span_label(ident.span, "in this trait".into()); + assoc_span.push_span_label(ident.span, "in this trait"); } err.span_note(assoc_span, &msg); } @@ -2506,7 +2506,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .opt_associated_item(trait_item_def_id) .and_then(|i| self.tcx.opt_item_name(i.container.id())) { - assoc_span.push_span_label(ident.span, "in this trait".into()); + assoc_span.push_span_label(ident.span, "in this trait"); } err.span_note(assoc_span, &msg); } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index a31ae25aea0..84958136cac 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -169,10 +169,7 @@ fn lint_object_unsafe_trait( let node = tcx.hir().get_if_local(trait_def_id); let mut spans = MultiSpan::from_span(span); if let Some(hir::Node::Item(item)) = node { - spans.push_span_label( - item.ident.span, - "this trait cannot be made into an object...".into(), - ); + spans.push_span_label(item.ident.span, "this trait cannot be made into an object..."); spans.push_span_label(span, format!("...because {}", violation.error_msg())); } else { spans.push_span_label( diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index bd64e2b03ce..44b6b4b62ce 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -143,6 +143,7 @@ crate fn new_handler( source_map: Option<Lrc<source_map::SourceMap>>, debugging_opts: &DebuggingOptions, ) -> rustc_errors::Handler { + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); let emitter: Box<dyn Emitter + sync::Send> = match error_format { ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); @@ -150,6 +151,7 @@ crate fn new_handler( EmitterWriter::stderr( color_config, source_map.map(|sm| sm as _), + fallback_bundle, short, debugging_opts.teach, debugging_opts.terminal_width, @@ -166,6 +168,7 @@ crate fn new_handler( JsonEmitter::stderr( None, source_map, + fallback_bundle, pretty, json_rendered, debugging_opts.terminal_width, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c4201e22212..fc079619786 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -537,12 +537,28 @@ crate fn make_test( // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - supports_color = - EmitterWriter::stderr(ColorConfig::Auto, None, false, false, Some(80), false) - .supports_color(); - - let emitter = - EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + supports_color = EmitterWriter::stderr( + ColorConfig::Auto, + None, + fallback_bundle.clone(), + false, + false, + Some(80), + false, + ) + .supports_color(); + + let emitter = EmitterWriter::new( + box io::sink(), + None, + fallback_bundle, + false, + false, + false, + None, + false, + ); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let handler = Handler::with_emitter(false, None, box emitter); diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 636018dbb22..10f1fc05404 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -32,7 +32,8 @@ struct SyntaxChecker<'a, 'tcx> { impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { fn check_rust_syntax(&self, item: &clean::Item, dox: &str, code_block: RustCodeBlock) { let buffer = Lrc::new(Lock::new(Buffer::default())); - let emitter = BufferEmitter { buffer: Lrc::clone(&buffer) }; + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle }; let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let handler = Handler::with_emitter(false, None, Box::new(emitter)); @@ -171,12 +172,14 @@ struct Buffer { struct BufferEmitter { buffer: Lrc<Lock<Buffer>>, + fallback_bundle: Lrc<rustc_errors::FluentBundle>, } impl Emitter for BufferEmitter { fn emit_diagnostic(&mut self, diag: &Diagnostic) { let mut buffer = self.buffer.borrow_mut(); - buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.as_str())); + // FIXME(davidtwco): need to support translation here eventually + buffer.messages.push(format!("error from rustc: {}", diag.message[0].0.expect_str())); if diag.is_error() { buffer.has_errors = true; } @@ -185,4 +188,12 @@ impl Emitter for BufferEmitter { fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } + + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + &self.fallback_bundle + } } diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs index acb3c917d62..cc354b50afa 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs @@ -130,8 +130,8 @@ fn check_arm<'tcx>( &msg, |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); - help_span.push_span_label(binding_span, "replace this binding".into()); - help_span.push_span_label(inner_then_pat.span, "with this pattern".into()); + help_span.push_span_label(binding_span, "replace this binding"); + help_span.push_span_label(inner_then_pat.span, "with this pattern"); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index d67c03714e3..b836363b31b 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -621,7 +621,17 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); + let emitter = EmitterWriter::new( + Box::new(io::sink()), + None, + fallback_bundle, + false, + false, + false, + None, + false, + ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs index c7772e483ad..ddaffc75188 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -102,7 +102,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(method_name.ident.span); - span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); + span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index ecc9acf4445..06209bfe7b0 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -148,7 +148,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { - cx.tcx.sess.span_err(span, &err); + cx.tcx.sess.span_err(span, err.as_ref()); } } else { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 5eb7b0f0521..d29d07da7b0 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -235,11 +235,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - &snippet_opt(cx, span) + snippet_opt(cx, span) .map_or( "change the call to".into(), |x| Cow::from(format!("change `{}` to", x)), - ), + ) + .as_ref(), suggestion.into(), Applicability::Unspecified, ); @@ -264,11 +265,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - &snippet_opt(cx, span) + snippet_opt(cx, span) .map_or( "change the call to".into(), |x| Cow::from(format!("change `{}` to", x)) - ), + ) + .as_ref(), suggestion.into(), Applicability::Unspecified, ); diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 855a6a6ef6a..bfce787af5e 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -165,9 +165,11 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, + fallback_bundle, false, false, None, @@ -191,7 +193,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { ]; for note in &xs { - handler.note_without_error(note); + handler.note_without_error(note.as_ref()); } // If backtraces are enabled, also print the query stack diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 7f11cddd882..4563dbf6c16 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -33,6 +33,12 @@ impl Emitter for SilentEmitter { None } fn emit_diagnostic(&mut self, _db: &Diagnostic) {} + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + panic!("silent emitter attempted to translate a diagnostic"); + } } fn silent_emitter() -> Box<dyn Emitter + Send> { @@ -82,6 +88,14 @@ impl Emitter for SilentOnIgnoredFilesEmitter { } self.handle_non_ignoreable_error(db); } + + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + self.emitter.fluent_bundle() + } + + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + self.emitter.fallback_fluent_bundle() + } } fn default_handler( @@ -100,9 +114,11 @@ fn default_handler( let emitter = if hide_parse_errors { silent_emitter() } else { + let fallback_bundle = rustc_errors::fallback_fluent_bundle(); Box::new(EmitterWriter::stderr( color_cfg, Some(source_map.clone()), + fallback_bundle, false, false, None, @@ -329,6 +345,12 @@ mod tests { fn emit_diagnostic(&mut self, _db: &Diagnostic) { self.num_emitted_errors.fetch_add(1, Ordering::Release); } + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + fn fallback_fluent_bundle(&self) -> &Lrc<rustc_errors::FluentBundle> { + panic!("test emitter attempted to translate a diagnostic"); + } } fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic { |