From 246f239d9f40f633160f0c18f87a20922d4e77bb Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:06:37 +0200 Subject: Merging debian version 1.65.0+dfsg1-2. Signed-off-by: Daniel Baumann --- compiler/rustc_errors/Cargo.toml | 7 +- .../src/annotate_snippet_emitter_writer.rs | 25 ++-- compiler/rustc_errors/src/diagnostic.rs | 50 +++++-- compiler/rustc_errors/src/diagnostic_builder.rs | 33 ++++- compiler/rustc_errors/src/emitter.rs | 140 +++++------------- compiler/rustc_errors/src/json.rs | 19 ++- compiler/rustc_errors/src/lib.rs | 162 +++++++++++++++------ compiler/rustc_errors/src/translation.rs | 103 +++++++++++++ 8 files changed, 357 insertions(+), 182 deletions(-) create mode 100644 compiler/rustc_errors/src/translation.rs (limited to 'compiler/rustc_errors') diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 7d7e92c52..c36ca11fa 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -13,15 +13,16 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_target = { path = "../rustc_target" } rustc_hir = { path = "../rustc_hir" } rustc_lint_defs = { path = "../rustc_lint_defs" } unicode-width = "0.1.4" atty = "0.2" termcolor = "1.0" -annotate-snippets = "0.8.0" +annotate-snippets = "0.9" termize = "0.1.1" -serde = { version = "1.0.125", features = ["derive"] } +serde = { version = "1.0.125", features = [ "derive" ] } serde_json = "1.0.59" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["handleapi", "synchapi", "winbase"] } +winapi = { version = "0.3", features = [ "handleapi", "synchapi", "winbase" ] } diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 0fcd61d1e..b32fc3c71 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -7,6 +7,7 @@ use crate::emitter::FileWithAnnotatedLines; use crate::snippet::Line; +use crate::translation::Translate; use crate::{ CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic, @@ -32,6 +33,16 @@ pub struct AnnotateSnippetEmitterWriter { macro_backtrace: bool, } +impl Translate for AnnotateSnippetEmitterWriter { + fn fluent_bundle(&self) -> Option<&Lrc> { + self.fluent_bundle.as_ref() + } + + fn fallback_fluent_bundle(&self) -> &FluentBundle { + &**self.fallback_bundle + } +} + impl Emitter for AnnotateSnippetEmitterWriter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, diag: &Diagnostic) { @@ -63,14 +74,6 @@ impl Emitter for AnnotateSnippetEmitterWriter { self.source_map.as_ref() } - fn fluent_bundle(&self) -> Option<&Lrc> { - self.fluent_bundle.as_ref() - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &**self.fallback_bundle - } - fn should_show_explain(&self) -> bool { !self.short_message } @@ -183,7 +186,11 @@ impl AnnotateSnippetEmitterWriter { annotation_type: annotation_type_for_level(*level), }), footer: vec![], - opt: FormatOptions { color: true, anonymized_line_numbers: self.ui_testing }, + opt: FormatOptions { + color: true, + anonymized_line_numbers: self.ui_testing, + margin: None, + }, slices: annotated_files .iter() .map(|(source, line_index, annotations)| { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 17e6c9e95..a774b52c8 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -8,11 +8,14 @@ use rustc_error_messages::FluentValue; use rustc_hir as hir; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_span::edition::LATEST_STABLE_EDITION; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; use rustc_span::{edition::Edition, Span, DUMMY_SP}; +use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; use std::borrow::Cow; use std::fmt; use std::hash::{Hash, Hasher}; +use std::num::ParseIntError; +use std::path::{Path, PathBuf}; /// Error type for `Diagnostic`'s `suggestions` field, indicating that /// `.disable_suggestions()` was called on the `Diagnostic`. @@ -83,10 +86,16 @@ into_diagnostic_arg_using_display!( u64, i128, u128, + std::io::Error, std::num::NonZeroU32, hir::Target, Edition, Ident, + MacroRulesNormalizedIdent, + ParseIntError, + StackProtector, + &TargetTriple, + SplitDebuginfo ); impl IntoDiagnosticArg for bool { @@ -123,12 +132,30 @@ impl IntoDiagnosticArg for String { } } +impl<'a> IntoDiagnosticArg for &'a Path { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + +impl IntoDiagnosticArg for PathBuf { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.display().to_string())) + } +} + impl IntoDiagnosticArg for usize { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Number(self) } } +impl IntoDiagnosticArg for PanicStrategy { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(self.desc().to_string())) + } +} + impl<'source> Into> for DiagnosticArgValue<'source> { fn into(self) -> FluentValue<'source> { match self { @@ -671,19 +698,12 @@ impl Diagnostic { suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { - assert!(!suggestion.is_empty()); - self.push_suggestion(CodeSuggestion { - substitutions: vec![Substitution { - parts: suggestion - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }], - msg: self.subdiagnostic_message_to_diagnostic_message(msg), - style: SuggestionStyle::CompletelyHidden, + self.multipart_suggestion_with_style( + msg, + suggestion, applicability, - }); - self + SuggestionStyle::CompletelyHidden, + ) } /// Prints out a message with a suggested edit of the code. @@ -966,12 +986,12 @@ impl Diagnostic { fn sub_with_highlights>( &mut self, level: Level, - mut message: Vec<(M, Style)>, + message: Vec<(M, Style)>, span: MultiSpan, render_span: Option, ) { let message = message - .drain(..) + .into_iter() .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) .collect(); let sub = SubDiagnostic { level, message, span, render_span }; diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 9e68ee282..7e29dc207 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -12,7 +12,6 @@ use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::thread::panicking; -use tracing::debug; /// Used for emitting structured error messages and other diagnostic information. /// @@ -84,6 +83,13 @@ pub trait EmissionGuarantee: Sized { /// of `Self` without actually performing the emission. #[track_caller] fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self; + + /// Creates a new `DiagnosticBuilder` that will return this type of guarantee. + #[track_caller] + fn make_diagnostic_builder( + handler: &Handler, + msg: impl Into, + ) -> DiagnosticBuilder<'_, Self>; } /// Private module for sealing the `IsError` helper trait. @@ -166,6 +172,15 @@ impl EmissionGuarantee for ErrorGuaranteed { } } } + + fn make_diagnostic_builder( + handler: &Handler, + msg: impl Into, + ) -> DiagnosticBuilder<'_, Self> { + DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>( + handler, msg, + ) + } } impl<'a> DiagnosticBuilder<'a, ()> { @@ -208,6 +223,13 @@ impl EmissionGuarantee for () { DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} } } + + fn make_diagnostic_builder( + handler: &Handler, + msg: impl Into, + ) -> DiagnosticBuilder<'_, Self> { + DiagnosticBuilder::new(handler, Level::Warning(None), msg) + } } impl<'a> DiagnosticBuilder<'a, !> { @@ -247,6 +269,13 @@ impl EmissionGuarantee for ! { // Then fatally error, returning `!` crate::FatalError.raise() } + + fn make_diagnostic_builder( + handler: &Handler, + msg: impl Into, + ) -> DiagnosticBuilder<'_, Self> { + DiagnosticBuilder::new_fatal(handler, msg) + } } /// In general, the `DiagnosticBuilder` uses deref to allow access to @@ -566,7 +595,7 @@ impl Drop for DiagnosticBuilderInner<'_> { ), )); handler.emit_diagnostic(&mut self.diagnostic); - panic!(); + panic!("error was constructed but not emitted"); } } // `.emit()` was previously called, or maybe we're during `.cancel()`. diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 61d953cd6..66fbb8f12 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -14,15 +14,15 @@ use rustc_span::{FileLines, SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; +use crate::translation::Translate; use crate::{ - CodeSuggestion, Diagnostic, DiagnosticArg, DiagnosticId, DiagnosticMessage, FluentBundle, - Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, - SuggestionStyle, + CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler, + LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, SubstitutionHighlight, SuggestionStyle, }; use rustc_lint_defs::pluralize; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentArgs; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -34,7 +34,6 @@ use std::iter; use std::path::Path; use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}; use termcolor::{Buffer, Color, WriteColor}; -use tracing::*; /// Default column width, used in tests and when terminal dimensions cannot be determined. const DEFAULT_COLUMN_WIDTH: usize = 140; @@ -200,7 +199,7 @@ impl Margin { const ANONYMIZED_LINE_NUM: &str = "LL"; /// Emitter trait for emitting errors. -pub trait Emitter { +pub trait Emitter: Translate { /// Emit a structured diagnostic. fn emit_diagnostic(&mut self, diag: &Diagnostic); @@ -231,84 +230,6 @@ pub trait Emitter { fn source_map(&self) -> Option<&Lrc>; - /// 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>; - - /// 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) -> &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::(), - ) - } - - /// 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, ?args); - 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) => { - if let Some(attr) = message.get_attribute(attr) { - attr.value() - } else { - panic!("missing attribute `{attr}` in fluent message `{identifier}`") - } - } - None => { - if let Some(value) = message.value() { - value - } else { - panic!("missing value in fluent message `{identifier}`") - } - } - }; - - let mut err = vec![]; - let translated = bundle.format_pattern(value, Some(&args), &mut err); - trace!(?translated, ?err); - debug_assert!( - err.is_empty(), - "identifier: {:?}, args: {:?}, errors: {:?}", - identifier, - args, - err - ); - translated - } - /// Formats the substitutions of the primary_span /// /// There are a lot of conditions to this method, but in short: @@ -598,11 +519,7 @@ pub trait Emitter { } } -impl Emitter for EmitterWriter { - fn source_map(&self) -> Option<&Lrc> { - self.sm.as_ref() - } - +impl Translate for EmitterWriter { fn fluent_bundle(&self) -> Option<&Lrc> { self.fluent_bundle.as_ref() } @@ -610,6 +527,12 @@ impl Emitter for EmitterWriter { fn fallback_fluent_bundle(&self) -> &FluentBundle { &**self.fallback_bundle } +} + +impl Emitter for EmitterWriter { + fn source_map(&self) -> Option<&Lrc> { + self.sm.as_ref() + } fn emit_diagnostic(&mut self, diag: &Diagnostic) { let fluent_args = self.to_fluent_args(diag.args()); @@ -654,11 +577,7 @@ pub struct SilentEmitter { pub fatal_note: Option, } -impl Emitter for SilentEmitter { - fn source_map(&self) -> Option<&Lrc> { - None - } - +impl Translate for SilentEmitter { fn fluent_bundle(&self) -> Option<&Lrc> { None } @@ -666,6 +585,12 @@ impl Emitter for SilentEmitter { fn fallback_fluent_bundle(&self) -> &FluentBundle { panic!("silent emitter attempted to translate message") } +} + +impl Emitter for SilentEmitter { + fn source_map(&self) -> Option<&Lrc> { + None + } fn emit_diagnostic(&mut self, d: &Diagnostic) { if d.level == Level::Fatal { @@ -1562,7 +1487,7 @@ impl EmitterWriter { ); // Contains the vertical lines' positions for active multiline annotations - let mut multilines = FxHashMap::default(); + let mut multilines = FxIndexMap::default(); // Get the left-side margin to remove it let mut whitespace_margin = usize::MAX; @@ -1779,7 +1704,7 @@ impl EmitterWriter { { notice_capitalization |= only_capitalization; - let has_deletion = parts.iter().any(|p| p.is_deletion()); + let has_deletion = parts.iter().any(|p| p.is_deletion(sm)); let is_multiline = complete.lines().count() > 1; if let Some(span) = span.primary_span() { @@ -1955,16 +1880,23 @@ impl EmitterWriter { let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display; let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display; + // If this addition is _only_ whitespace, then don't trim it, + // or else we're just not rendering anything. + let is_whitespace_addition = part.snippet.trim().is_empty(); + // Do not underline the leading... - let start = part.snippet.len().saturating_sub(part.snippet.trim_start().len()); + let start = if is_whitespace_addition { + 0 + } else { + part.snippet.len().saturating_sub(part.snippet.trim_start().len()) + }; // ...or trailing spaces. Account for substitutions containing unicode // characters. - let sub_len: usize = part - .snippet - .trim() - .chars() - .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1)) - .sum(); + let sub_len: usize = + if is_whitespace_addition { &part.snippet } else { part.snippet.trim() } + .chars() + .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1)) + .sum(); let offset: isize = offsets .iter() @@ -2205,7 +2137,7 @@ impl EmitterWriter { } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] enum DisplaySuggestion { Underline, Diff, diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index b8cd334b4..1680c6acc 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -13,6 +13,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; +use crate::translation::Translate; use crate::DiagnosticId; use crate::{ CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic, @@ -122,6 +123,16 @@ impl JsonEmitter { } } +impl Translate for JsonEmitter { + fn fluent_bundle(&self) -> Option<&Lrc> { + self.fluent_bundle.as_ref() + } + + fn fallback_fluent_bundle(&self) -> &FluentBundle { + &**self.fallback_bundle + } +} + impl Emitter for JsonEmitter { fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) { let data = Diagnostic::from_errors_diagnostic(diag, self); @@ -189,14 +200,6 @@ impl Emitter for JsonEmitter { Some(&self.sm) } - fn fluent_bundle(&self) -> Option<&Lrc> { - self.fluent_bundle.as_ref() - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &**self.fallback_bundle - } - fn should_show_explain(&self) -> bool { !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2409c0b5a..a7b01feeb 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -4,15 +4,14 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(drain_filter)] -#![feature(backtrace)] #![feature(if_let_guard)] +#![feature(adt_const_params)] #![feature(let_chains)] -#![feature(let_else)] +#![cfg_attr(bootstrap, feature(let_else))] #![feature(never_type)] -#![feature(adt_const_params)] +#![feature(result_option_inspect)] #![feature(rustc_attrs)] #![allow(incomplete_features)] -#![allow(rustc::potential_query_instability)] #[macro_use] extern crate rustc_macros; @@ -27,7 +26,7 @@ use Level::*; use emitter::{is_case_difference, Emitter, EmitterWriter}; use registry::Registry; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; @@ -59,6 +58,7 @@ mod lock; pub mod registry; mod snippet; mod styled_buffer; +pub mod translation; pub use snippet::Style; @@ -68,8 +68,8 @@ pub type PResult<'a, T> = Result>; // (See also the comment on `DiagnosticBuilder`'s `diagnostic` field.) #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24); +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64", not(bootstrap)))] +rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] pub enum SuggestionStyle { @@ -150,21 +150,20 @@ pub struct SubstitutionHighlight { impl SubstitutionPart { pub fn is_addition(&self, sm: &SourceMap) -> bool { - !self.snippet.is_empty() - && sm - .span_to_snippet(self.span) - .map_or(self.span.is_empty(), |snippet| snippet.trim().is_empty()) + !self.snippet.is_empty() && !self.replaces_meaningful_content(sm) } - pub fn is_deletion(&self) -> bool { - self.snippet.trim().is_empty() + pub fn is_deletion(&self, sm: &SourceMap) -> bool { + self.snippet.trim().is_empty() && self.replaces_meaningful_content(sm) } pub fn is_replacement(&self, sm: &SourceMap) -> bool { - !self.snippet.is_empty() - && sm - .span_to_snippet(self.span) - .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty()) + !self.snippet.is_empty() && self.replaces_meaningful_content(sm) + } + + fn replaces_meaningful_content(&self, sm: &SourceMap) -> bool { + sm.span_to_snippet(self.span) + .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty()) } } @@ -412,7 +411,7 @@ struct HandlerInner { taught_diagnostics: FxHashSet, /// Used to suggest rustc --explain - emitted_diagnostic_codes: FxHashSet, + emitted_diagnostic_codes: FxIndexSet, /// This set contains a hash of every diagnostic that has been emitted by /// this handler. These hashes is used to avoid emitting the same error @@ -455,9 +454,11 @@ struct HandlerInner { } /// A key denoting where from a diagnostic was stashed. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum StashKey { ItemNoType, + UnderscoreForArrayLengths, + EarlySyntaxWarning, } fn default_track_diagnostic(_: &Diagnostic) {} @@ -625,19 +626,17 @@ impl Handler { /// Stash a given diagnostic with the given `Span` and `StashKey` as the key for later stealing. pub fn stash_diagnostic(&self, span: Span, key: StashKey, diag: Diagnostic) { let mut inner = self.inner.borrow_mut(); - // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic - // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. - // See the PR for a discussion. - inner.stashed_diagnostics.insert((span, key), diag); + inner.stash((span, key), diag); } /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key. pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option> { - self.inner - .borrow_mut() - .stashed_diagnostics - .remove(&(span, key)) - .map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) + let mut inner = self.inner.borrow_mut(); + inner.steal((span, key)).map(|diag| DiagnosticBuilder::new_diagnostic(self, diag)) + } + + pub fn has_stashed_diagnostic(&self, span: Span, key: StashKey) -> bool { + self.inner.borrow().stashed_diagnostics.get(&(span, key)).is_some() } /// Emit all stashed diagnostics. @@ -645,6 +644,15 @@ impl Handler { self.inner.borrow_mut().emit_stashed_diagnostics() } + /// Construct a builder with the `msg` at the level appropriate for the specific `EmissionGuarantee`. + #[rustc_lint_diagnostics] + pub fn struct_diagnostic( + &self, + msg: impl Into, + ) -> DiagnosticBuilder<'_, G> { + G::make_diagnostic_builder(self, msg) + } + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. /// /// Attempting to `.emit()` the builder will only emit if either: @@ -1105,13 +1113,31 @@ impl HandlerInner { /// Emit all stashed diagnostics. fn emit_stashed_diagnostics(&mut self) -> Option { + let has_errors = self.has_errors(); let diags = self.stashed_diagnostics.drain(..).map(|x| x.1).collect::>(); let mut reported = None; for mut diag in diags { + // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { - reported = Some(ErrorGuaranteed(())); + if matches!(diag.level, Level::Error { lint: true }) { + self.lint_err_count -= 1; + } else { + self.err_count -= 1; + } + } else { + if diag.is_force_warn() { + self.warn_count -= 1; + } else { + // Unless they're forced, don't flush stashed warnings when + // there are errors, to avoid causing warning overload. The + // stash would've been stolen already if it were important. + if has_errors { + continue; + } + } } - self.emit_diagnostic(&mut diag); + let reported_this = self.emit_diagnostic(&mut diag); + reported = reported.or(reported_this); } reported } @@ -1225,9 +1251,13 @@ impl HandlerInner { } fn treat_err_as_bug(&self) -> bool { - self.flags - .treat_err_as_bug - .map_or(false, |c| self.err_count() + self.lint_err_count >= c.get()) + self.flags.treat_err_as_bug.map_or(false, |c| { + self.err_count() + self.lint_err_count + self.delayed_bug_count() >= c.get() + }) + } + + fn delayed_bug_count(&self) -> usize { + self.delayed_span_bugs.len() + self.delayed_good_path_bugs.len() } fn print_error_count(&mut self, registry: &Registry) { @@ -1301,9 +1331,47 @@ impl HandlerInner { } } + fn stash(&mut self, key: (Span, StashKey), diagnostic: Diagnostic) { + // Track the diagnostic for counts, but don't panic-if-treat-err-as-bug + // yet; that happens when we actually emit the diagnostic. + if diagnostic.is_error() { + if matches!(diagnostic.level, Level::Error { lint: true }) { + self.lint_err_count += 1; + } else { + self.err_count += 1; + } + } else { + // Warnings are only automatically flushed if they're forced. + if diagnostic.is_force_warn() { + self.warn_count += 1; + } + } + + // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic + // if/when we have a more robust macro-friendly replacement for `(span, key)` as a key. + // See the PR for a discussion. + self.stashed_diagnostics.insert(key, diagnostic); + } + + fn steal(&mut self, key: (Span, StashKey)) -> Option { + let diagnostic = self.stashed_diagnostics.remove(&key)?; + if diagnostic.is_error() { + if matches!(diagnostic.level, Level::Error { lint: true }) { + self.lint_err_count -= 1; + } else { + self.err_count -= 1; + } + } else { + if diagnostic.is_force_warn() { + self.warn_count -= 1; + } + } + Some(diagnostic) + } + #[inline] fn err_count(&self) -> usize { - self.err_count + self.stashed_diagnostics.len() + self.err_count } fn has_errors(&self) -> bool { @@ -1345,7 +1413,9 @@ impl HandlerInner { // 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. - if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) { + if self.flags.treat_err_as_bug.map_or(false, |c| { + self.err_count() + self.lint_err_count + self.delayed_bug_count() + 1 >= c.get() + }) { // FIXME: don't abort here if report_delayed_bugs is off self.span_bug(sp, msg); } @@ -1445,14 +1515,24 @@ impl HandlerInner { if self.treat_err_as_bug() { match ( self.err_count() + self.lint_err_count, + self.delayed_bug_count(), self.flags.treat_err_as_bug.map(|c| c.get()).unwrap_or(0), ) { - (1, 1) => panic!("aborting due to `-Z treat-err-as-bug=1`"), - (0 | 1, _) => {} - (count, as_bug) => panic!( - "aborting after {} errors due to `-Z treat-err-as-bug={}`", - count, as_bug, - ), + (1, 0, 1) => panic!("aborting due to `-Z treat-err-as-bug=1`"), + (0, 1, 1) => panic!("aborting due delayed bug with `-Z treat-err-as-bug=1`"), + (count, delayed_count, as_bug) => { + if delayed_count > 0 { + panic!( + "aborting after {} errors and {} delayed bugs due to `-Z treat-err-as-bug={}`", + count, delayed_count, as_bug, + ) + } else { + panic!( + "aborting after {} errors due to `-Z treat-err-as-bug={}`", + count, as_bug, + ) + } + } } } } diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs new file mode 100644 index 000000000..4f407badb --- /dev/null +++ b/compiler/rustc_errors/src/translation.rs @@ -0,0 +1,103 @@ +use crate::snippet::Style; +use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle}; +use rustc_data_structures::sync::Lrc; +use rustc_error_messages::FluentArgs; +use std::borrow::Cow; + +pub trait Translate { + /// 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>; + + /// 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) -> &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.iter().cloned()) + } + + /// 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::(), + ) + } + + /// 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, ?args); + let (identifier, attr) = match message { + DiagnosticMessage::Str(msg) => return Cow::Borrowed(&msg), + DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + }; + + let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> { + let message = bundle.get_message(&identifier)?; + let value = match attr { + Some(attr) => message.get_attribute(attr)?.value(), + None => message.value()?, + }; + debug!(?message, ?value); + + let mut errs = vec![]; + let translated = bundle.format_pattern(value, Some(&args), &mut errs); + debug!(?translated, ?errs); + Some((translated, errs)) + }; + + self.fluent_bundle() + .and_then(|bundle| translate_with_bundle(bundle)) + // If `translate_with_bundle` returns `None` with the primary bundle, this is likely + // just that the primary bundle doesn't contain the message being translated, so + // proceed to the fallback bundle. + // + // However, when errors are produced from translation, then that means the translation + // is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided). + // + // In debug builds, assert so that compiler devs can spot the broken translation and + // fix it.. + .inspect(|(_, errs)| { + debug_assert!( + errs.is_empty(), + "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", + identifier, + attr, + args, + errs + ); + }) + // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so + // just hide it and try with the fallback bundle. + .filter(|(_, errs)| errs.is_empty()) + .or_else(|| translate_with_bundle(self.fallback_fluent_bundle())) + .map(|(translated, errs)| { + // Always bail out for errors with the fallback bundle. + assert!( + errs.is_empty(), + "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", + identifier, + attr, + args, + errs + ); + translated + }) + .expect("failed to find message in primary or fallback fluent bundles") + } +} -- cgit v1.2.3