diff options
Diffstat (limited to 'compiler/rustc_errors')
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 65 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic_impls.rs | 155 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 63 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 286 |
4 files changed, 342 insertions, 227 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index c186d5b284f..5d345e788e9 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -38,7 +38,7 @@ pub enum DiagArgValue { Str(Cow<'static, str>), // This gets converted to a `FluentNumber`, which is an `f64`. An `i32` // safely fits in an `f64`. Any integers bigger than that will be converted - // to strings in `into_diagnostic_arg` and stored using the `Str` variant. + // to strings in `into_diag_arg` and stored using the `Str` variant. Number(i32), StrListSepByAnd(Vec<Cow<'static, str>>), } @@ -112,48 +112,48 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError { /// When implemented manually, it should be generic over the emission /// guarantee, i.e.: /// ```ignore (fragment) -/// impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for Foo { ... } +/// impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Foo { ... } /// ``` /// rather than being specific: /// ```ignore (fragment) -/// impl<'a> IntoDiagnostic<'a> for Bar { ... } // the default type param is `ErrorGuaranteed` -/// impl<'a> IntoDiagnostic<'a, ()> for Baz { ... } +/// impl<'a> Diagnostic<'a> for Bar { ... } // the default type param is `ErrorGuaranteed` +/// impl<'a> Diagnostic<'a, ()> for Baz { ... } /// ``` /// There are two reasons for this. /// - A diagnostic like `Foo` *could* be emitted at any level -- `level` is -/// passed in to `into_diagnostic` from outside. Even if in practice it is +/// passed in to `into_diag` from outside. Even if in practice it is /// always emitted at a single level, we let the diagnostic creation/emission /// site determine the level (by using `create_err`, `emit_warn`, etc.) -/// rather than the `IntoDiagnostic` impl. +/// rather than the `Diagnostic` impl. /// - Derived impls are always generic, and it's good for the hand-written /// impls to be consistent with them. -#[rustc_diagnostic_item = "IntoDiagnostic"] -pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { +#[rustc_diagnostic_item = "Diagnostic"] +pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { /// Write out as a diagnostic out of `DiagCtxt`. #[must_use] - fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G>; + fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G>; } -impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T> +impl<'a, T, G> Diagnostic<'a, G> for Spanned<T> where - T: IntoDiagnostic<'a, G>, + T: Diagnostic<'a, G>, G: EmissionGuarantee, { - fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { - self.node.into_diagnostic(dcx, level).with_span(self.span) + fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> { + self.node.into_diag(dcx, level).with_span(self.span) } } -/// Converts a value of a type into a `DiagArg` (typically a field of an `IntoDiagnostic` struct). +/// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct). /// Implemented as a custom trait rather than `From` so that it is implemented on the type being /// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to /// implement this. -pub trait IntoDiagnosticArg { - fn into_diagnostic_arg(self) -> DiagArgValue; +pub trait IntoDiagArg { + fn into_diag_arg(self) -> DiagArgValue; } -impl IntoDiagnosticArg for DiagArgValue { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for DiagArgValue { + fn into_diag_arg(self) -> DiagArgValue { self } } @@ -170,31 +170,32 @@ impl Into<FluentValue<'static>> for DiagArgValue { /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. -#[rustc_diagnostic_item = "AddToDiagnostic"] -pub trait AddToDiagnostic +#[rustc_diagnostic_item = "Subdiagnostic"] +pub trait Subdiagnostic where Self: Sized, { /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diagnostic<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { - self.add_to_diagnostic_with(diag, |_, m| m); + fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { + self.add_to_diag_with(diag, |_, m| m); } /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used /// (to optionally perform eager translation). - fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( self, diag: &mut Diag<'_, G>, f: F, ); } -pub trait SubdiagMessageOp<G> = Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage; +pub trait SubdiagMessageOp<G: EmissionGuarantee> = + Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage; /// Trait implemented by lint types. This should not be implemented manually. Instead, use /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. -#[rustc_diagnostic_item = "DecorateLint"] -pub trait DecorateLint<'a, G: EmissionGuarantee> { +#[rustc_diagnostic_item = "LintDiagnostic"] +pub trait LintDiagnostic<'a, G: EmissionGuarantee> { /// Decorate and emit a lint. fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>); @@ -419,8 +420,8 @@ impl DiagInner { self.children.push(sub); } - pub(crate) fn arg(&mut self, name: impl Into<DiagArgName>, arg: impl IntoDiagnosticArg) { - self.args.insert(name.into(), arg.into_diagnostic_arg()); + pub(crate) fn arg(&mut self, name: impl Into<DiagArgName>, arg: impl IntoDiagArg) { + self.args.insert(name.into(), arg.into_diag_arg()); } /// Fields used for Hash, and PartialEq trait. @@ -482,7 +483,7 @@ pub struct Subdiag { /// - The `EmissionGuarantee`, which determines the type returned from `emit`. /// /// Each constructed `Diag` must be consumed by a function such as `emit`, -/// `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a `Diag` +/// `cancel`, `delay_as_bug`, or `into_diag`. A panic occurrs if a `Diag` /// is dropped without being consumed by one of these functions. /// /// If there is some state in a downstream crate you would like to access in @@ -1194,9 +1195,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn subdiagnostic( &mut self, dcx: &crate::DiagCtxt, - subdiagnostic: impl AddToDiagnostic, + subdiagnostic: impl Subdiagnostic, ) -> &mut Self { - subdiagnostic.add_to_diagnostic_with(self, |diag, msg| { + subdiagnostic.add_to_diag_with(self, |diag, msg| { let args = diag.args.iter(); let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); dcx.eagerly_translate(msg, args) @@ -1243,7 +1244,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { pub fn arg( &mut self, name: impl Into<DiagArgName>, - arg: impl IntoDiagnosticArg, + arg: impl IntoDiagArg, ) -> &mut Self { self.deref_mut().arg(name, arg); self diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 839bd65e4a6..6c0551848d6 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,8 +1,8 @@ use crate::diagnostic::DiagLocation; -use crate::{fluent_generated as fluent, AddToDiagnostic}; +use crate::{fluent_generated as fluent, Subdiagnostic}; use crate::{ - Diag, DiagArgValue, DiagCtxt, EmissionGuarantee, ErrCode, IntoDiagnostic, IntoDiagnosticArg, - Level, SubdiagMessageOp, + Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level, + SubdiagMessageOp, }; use rustc_ast as ast; use rustc_ast_pretty::pprust; @@ -22,9 +22,9 @@ use std::process::ExitStatus; pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display); -impl IntoDiagnosticArg for DiagArgFromDisplay<'_> { - fn into_diagnostic_arg(self) -> DiagArgValue { - self.0.to_string().into_diagnostic_arg() +impl IntoDiagArg for DiagArgFromDisplay<'_> { + fn into_diag_arg(self) -> DiagArgValue { + self.0.to_string().into_diag_arg() } } @@ -40,34 +40,35 @@ impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> { } } -impl<'a, T: Clone + IntoDiagnosticArg> IntoDiagnosticArg for &'a T { - fn into_diagnostic_arg(self) -> DiagArgValue { - self.clone().into_diagnostic_arg() +impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { + fn into_diag_arg(self) -> DiagArgValue { + self.clone().into_diag_arg() } } -macro_rules! into_diagnostic_arg_using_display { +#[macro_export] +macro_rules! into_diag_arg_using_display { ($( $ty:ty ),+ $(,)?) => { $( - impl IntoDiagnosticArg for $ty { - fn into_diagnostic_arg(self) -> DiagArgValue { - self.to_string().into_diagnostic_arg() + impl IntoDiagArg for $ty { + fn into_diag_arg(self) -> DiagArgValue { + self.to_string().into_diag_arg() } } )+ } } -macro_rules! into_diagnostic_arg_for_number { +macro_rules! into_diag_arg_for_number { ($( $ty:ty ),+ $(,)?) => { $( - impl IntoDiagnosticArg for $ty { - fn into_diagnostic_arg(self) -> DiagArgValue { + impl IntoDiagArg for $ty { + fn into_diag_arg(self) -> DiagArgValue { // Convert to a string if it won't fit into `Number`. if let Ok(n) = TryInto::<i32>::try_into(self) { DiagArgValue::Number(n) } else { - self.to_string().into_diagnostic_arg() + self.to_string().into_diag_arg() } } } @@ -75,7 +76,7 @@ macro_rules! into_diagnostic_arg_for_number { } } -into_diagnostic_arg_using_display!( +into_diag_arg_using_display!( ast::ParamKindOrd, std::io::Error, Box<dyn std::error::Error>, @@ -92,10 +93,10 @@ into_diagnostic_arg_using_display!( ErrCode, ); -into_diagnostic_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); +into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); -impl IntoDiagnosticArg for bool { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for bool { + fn into_diag_arg(self) -> DiagArgValue { if self { DiagArgValue::Str(Cow::Borrowed("true")) } else { @@ -104,64 +105,64 @@ impl IntoDiagnosticArg for bool { } } -impl IntoDiagnosticArg for char { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for char { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(format!("{self:?}"))) } } -impl IntoDiagnosticArg for Vec<char> { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for Vec<char> { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::StrListSepByAnd( self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(), ) } } -impl IntoDiagnosticArg for Symbol { - fn into_diagnostic_arg(self) -> DiagArgValue { - self.to_ident_string().into_diagnostic_arg() +impl IntoDiagArg for Symbol { + fn into_diag_arg(self) -> DiagArgValue { + self.to_ident_string().into_diag_arg() } } -impl<'a> IntoDiagnosticArg for &'a str { - fn into_diagnostic_arg(self) -> DiagArgValue { - self.to_string().into_diagnostic_arg() +impl<'a> IntoDiagArg for &'a str { + fn into_diag_arg(self) -> DiagArgValue { + self.to_string().into_diag_arg() } } -impl IntoDiagnosticArg for String { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for String { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self)) } } -impl<'a> IntoDiagnosticArg for Cow<'a, str> { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl<'a> IntoDiagArg for Cow<'a, str> { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.into_owned())) } } -impl<'a> IntoDiagnosticArg for &'a Path { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl<'a> IntoDiagArg for &'a Path { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.display().to_string())) } } -impl IntoDiagnosticArg for PathBuf { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for PathBuf { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.display().to_string())) } } -impl IntoDiagnosticArg for PanicStrategy { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for PanicStrategy { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.desc().to_string())) } } -impl IntoDiagnosticArg for hir::ConstContext { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for hir::ConstContext { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Borrowed(match self { hir::ConstContext::ConstFn => "const_fn", hir::ConstContext::Static(_) => "static", @@ -170,58 +171,58 @@ impl IntoDiagnosticArg for hir::ConstContext { } } -impl IntoDiagnosticArg for ast::Expr { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for ast::Expr { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) } } -impl IntoDiagnosticArg for ast::Path { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for ast::Path { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) } } -impl IntoDiagnosticArg for ast::token::Token { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for ast::token::Token { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(pprust::token_to_string(&self)) } } -impl IntoDiagnosticArg for ast::token::TokenKind { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for ast::token::TokenKind { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(pprust::token_kind_to_string(&self)) } } -impl IntoDiagnosticArg for type_ir::FloatTy { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for type_ir::FloatTy { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Borrowed(self.name_str())) } } -impl IntoDiagnosticArg for std::ffi::CString { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for std::ffi::CString { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) } } -impl IntoDiagnosticArg for rustc_data_structures::small_c_str::SmallCStr { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) } } -impl IntoDiagnosticArg for ast::Visibility { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for ast::Visibility { + fn into_diag_arg(self) -> DiagArgValue { let s = pprust::vis_to_string(&self); let s = s.trim_end().to_string(); DiagArgValue::Str(Cow::Owned(s)) } } -impl IntoDiagnosticArg for rustc_lint_defs::Level { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for rustc_lint_defs::Level { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) } } @@ -235,22 +236,22 @@ impl From<Vec<Symbol>> for DiagSymbolList { } } -impl IntoDiagnosticArg for DiagSymbolList { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for DiagSymbolList { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::StrListSepByAnd( self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(), ) } } -impl<Id> IntoDiagnosticArg for hir::def::Res<Id> { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl<Id> IntoDiagArg for hir::def::Res<Id> { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Borrowed(self.descr())) } } -impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetDataLayoutErrors<'_> { - fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { +impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { + fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { match self { TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => { Diag::new(dcx, level, fluent::errors_target_invalid_address_space) @@ -297,8 +298,8 @@ pub struct SingleLabelManySpans { pub spans: Vec<Span>, pub label: &'static str, } -impl AddToDiagnostic for SingleLabelManySpans { - fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( +impl Subdiagnostic for SingleLabelManySpans { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( self, diag: &mut Diag<'_, G>, _: F, @@ -315,20 +316,20 @@ pub struct ExpectedLifetimeParameter { pub count: usize, } -impl IntoDiagnosticArg for DiagLocation { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for DiagLocation { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::from(self.to_string())) } } -impl IntoDiagnosticArg for Backtrace { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for Backtrace { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::from(self.to_string())) } } -impl IntoDiagnosticArg for Level { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for Level { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::from(self.to_string())) } } @@ -342,8 +343,8 @@ pub struct IndicateAnonymousLifetime { pub suggestion: String, } -impl IntoDiagnosticArg for type_ir::ClosureKind { - fn into_diagnostic_arg(self) -> DiagArgValue { +impl IntoDiagArg for type_ir::ClosureKind { + fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(self.as_str().into()) } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 57b8df52f4b..6ce3fa3535d 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -541,6 +541,7 @@ pub struct SilentEmitter { pub fallback_bundle: LazyFallbackBundle, pub fatal_dcx: DiagCtxt, pub fatal_note: Option<String>, + pub emit_fatal_diagnostic: bool, } impl Translate for SilentEmitter { @@ -561,7 +562,7 @@ impl Emitter for SilentEmitter { } fn emit_diagnostic(&mut self, mut diag: DiagInner) { - if diag.level == Level::Fatal { + if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); } @@ -1512,7 +1513,9 @@ impl HumanEmitter { for line_idx in 0..annotated_file.lines.len() { let file = annotated_file.file.clone(); let line = &annotated_file.lines[line_idx]; - if let Some(source_string) = file.get_line(line.line_index - 1) { + if let Some(source_string) = + line.line_index.checked_sub(1).and_then(|l| file.get_line(l)) + { let leading_whitespace = source_string .chars() .take_while(|c| c.is_whitespace()) @@ -1552,7 +1555,10 @@ impl HumanEmitter { for line in &annotated_file.lines { max_line_len = max( max_line_len, - annotated_file.file.get_line(line.line_index - 1).map_or(0, |s| s.len()), + line.line_index + .checked_sub(1) + .and_then(|l| annotated_file.file.get_line(l)) + .map_or(0, |s| s.len()), ); for ann in &line.annotations { span_right_margin = max(span_right_margin, ann.start_col.display); @@ -1638,6 +1644,27 @@ impl HumanEmitter { *style, ); } + if let Some(line) = annotated_file.lines.get(line_idx) { + for ann in &line.annotations { + if let AnnotationType::MultilineStart(pos) = ann.annotation_type + { + // In the case where we have elided the entire start of the + // multispan because those lines were empty, we still need + // to draw the `|`s across the `...`. + draw_multiline_line( + &mut buffer, + last_buffer_line_num, + width_offset, + pos, + if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }, + ); + } + } + } } else if line_idx_delta == 2 { let unannotated_line = annotated_file .file @@ -1665,6 +1692,24 @@ impl HumanEmitter { *style, ); } + if let Some(line) = annotated_file.lines.get(line_idx) { + for ann in &line.annotations { + if let AnnotationType::MultilineStart(pos) = ann.annotation_type + { + draw_multiline_line( + &mut buffer, + last_buffer_line_num, + width_offset, + pos, + if ann.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }, + ); + } + } + } } } @@ -2086,7 +2131,7 @@ impl HumanEmitter { } if !self.short_message { for child in children { - assert!(child.level.can_be_top_or_sub().1); + assert!(child.level.can_be_subdiag()); let span = &child.span; if let Err(err) = self.emit_messages_default_inner( span, @@ -2417,7 +2462,15 @@ impl FileWithAnnotatedLines { // the beginning doesn't have an underline, but the current logic seems to be // working correctly. let middle = min(ann.line_start + 4, ann.line_end); - for line in ann.line_start + 1..middle { + // We'll show up to 4 lines past the beginning of the multispan start. + // We will *not* include the tail of lines that are only whitespace. + let until = (ann.line_start..middle) + .rev() + .filter_map(|line| file.get_line(line - 1).map(|s| (line + 1, s))) + .find(|(_, s)| !s.trim().is_empty()) + .map(|(line, _)| line) + .unwrap_or(ann.line_start); + for line in ann.line_start + 1..until { // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`). add_annotation_to_file(&mut output, file.clone(), line, ann.as_line()); } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 76b44f73f47..8d6b22a9fa9 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -7,7 +7,6 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(bootstrap, feature(min_specialization))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -37,9 +36,9 @@ extern crate self as rustc_errors; pub use codes::*; pub use diagnostic::{ - AddToDiagnostic, BugAbort, DecorateLint, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, - DiagInner, DiagStyledString, EmissionGuarantee, FatalAbort, IntoDiagnostic, IntoDiagnosticArg, - StringPart, Subdiag, SubdiagMessageOp, + BugAbort, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, DiagInner, DiagStyledString, + Diagnostic, EmissionGuarantee, FatalAbort, IntoDiagArg, LintDiagnostic, StringPart, Subdiag, + SubdiagMessageOp, Subdiagnostic, }; pub use diagnostic_impls::{ DiagArgFromDisplay, DiagSymbolList, ExpectedLifetimeParameter, IndicateAnonymousLifetime, @@ -103,9 +102,9 @@ pub type PResult<'a, T> = Result<T, PErr<'a>>; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] @@ -442,8 +441,8 @@ struct DiagCtxtInner { emitter: Box<DynEmitter>, /// Must we produce a diagnostic to justify the use of the expensive - /// `trimmed_def_paths` function? - must_produce_diag: bool, + /// `trimmed_def_paths` function? Backtrace is the location of the call. + must_produce_diag: Option<Backtrace>, /// Has this diagnostic context printed any diagnostics? (I.e. has /// `self.emitter.emit_diagnostic()` been called? @@ -526,12 +525,15 @@ pub enum StashKey { UndeterminedMacroResolution, } -fn default_track_diagnostic(diag: DiagInner, f: &mut dyn FnMut(DiagInner)) { +fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { (*f)(diag) } -pub static TRACK_DIAGNOSTIC: AtomicRef<fn(DiagInner, &mut dyn FnMut(DiagInner))> = - AtomicRef::new(&(default_track_diagnostic as _)); +/// Diagnostics emitted by `DiagCtxtInner::emit_diagnostic` are passed through this function. Used +/// for tracking by incremental, to replay diagnostics as necessary. +pub static TRACK_DIAGNOSTIC: AtomicRef< + fn(DiagInner, &mut dyn FnMut(DiagInner) -> Option<ErrorGuaranteed>) -> Option<ErrorGuaranteed>, +> = AtomicRef::new(&(default_track_diagnostic as _)); #[derive(Copy, Clone, Default)] pub struct DiagCtxtFlags { @@ -572,10 +574,11 @@ impl Drop for DiagCtxtInner { } if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() { - if self.must_produce_diag { + if let Some(backtrace) = &self.must_produce_diag { panic!( - "must_produce_diag: trimmed_def_paths called but no diagnostics emitted; \ - use `DelayDm` for lints or `with_no_trimmed_paths` for debugging" + "must_produce_diag: `trimmed_def_paths` called but no diagnostics emitted; \ + use `DelayDm` for lints or `with_no_trimmed_paths` for debugging. \ + called at: {backtrace}" ); } } @@ -609,12 +612,18 @@ impl DiagCtxt { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } - pub fn make_silent(&mut self, fallback_bundle: LazyFallbackBundle, fatal_note: Option<String>) { + pub fn make_silent( + &mut self, + fallback_bundle: LazyFallbackBundle, + fatal_note: Option<String>, + emit_fatal_diagnostic: bool, + ) { self.wrap_emitter(|old_dcx| { Box::new(emitter::SilentEmitter { fallback_bundle, fatal_dcx: DiagCtxt { inner: Lock::new(old_dcx) }, fatal_note, + emit_fatal_diagnostic, }) }); } @@ -721,7 +730,7 @@ impl DiagCtxt { *delayed_bugs = Default::default(); *deduplicated_err_count = 0; *deduplicated_warn_count = 0; - *must_produce_diag = false; + *must_produce_diag = None; *has_printed = false; *suppressed_expected_diag = false; *taught_diagnostics = Default::default(); @@ -768,13 +777,10 @@ impl DiagCtxt { format!("invalid level in `stash_diagnostic`: {:?}", diag.level), ); } - Error => { - // This `unchecked_error_guaranteed` is valid. It is where the - // `ErrorGuaranteed` for stashed errors originates. See - // `DiagCtxtInner::drop`. - #[allow(deprecated)] - Some(ErrorGuaranteed::unchecked_error_guaranteed()) - } + // We delay a bug here so that `-Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs` + // can be used to create a backtrace at the stashing site insted of whenever the + // diagnostic context is dropped and thus delayed bugs are emitted. + Error => Some(self.span_delayed_bug(span, "stashing {key:?}")), DelayedBug => return self.inner.borrow_mut().emit_diagnostic(diag), ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow | Expect(_) => None, @@ -1091,8 +1097,13 @@ impl DiagCtxt { /// Used when trimmed_def_paths is called and we must produce a diagnostic /// to justify its cost. + #[track_caller] pub fn set_must_produce_diag(&self) { - self.inner.borrow_mut().must_produce_diag = true; + assert!( + self.inner.borrow().must_produce_diag.is_none(), + "should only need to collect a backtrace once" + ); + self.inner.borrow_mut().must_produce_diag = Some(Backtrace::capture()); } } @@ -1134,12 +1145,12 @@ impl DiagCtxt { } #[track_caller] - pub fn create_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> { - bug.into_diagnostic(self, Bug) + pub fn create_bug<'a>(&'a self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> { + bug.into_diag(self, Bug) } #[track_caller] - pub fn emit_bug<'a>(&'a self, bug: impl IntoDiagnostic<'a, BugAbort>) -> ! { + pub fn emit_bug<'a>(&'a self, bug: impl Diagnostic<'a, BugAbort>) -> ! { self.create_bug(bug).emit() } @@ -1174,29 +1185,26 @@ impl DiagCtxt { #[track_caller] pub fn create_fatal<'a>( &'a self, - fatal: impl IntoDiagnostic<'a, FatalAbort>, + fatal: impl Diagnostic<'a, FatalAbort>, ) -> Diag<'a, FatalAbort> { - fatal.into_diagnostic(self, Fatal) + fatal.into_diag(self, Fatal) } #[track_caller] - pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, FatalAbort>) -> ! { + pub fn emit_fatal<'a>(&'a self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! { self.create_fatal(fatal).emit() } #[track_caller] pub fn create_almost_fatal<'a>( &'a self, - fatal: impl IntoDiagnostic<'a, FatalError>, + fatal: impl Diagnostic<'a, FatalError>, ) -> Diag<'a, FatalError> { - fatal.into_diagnostic(self, Fatal) + fatal.into_diag(self, Fatal) } #[track_caller] - pub fn emit_almost_fatal<'a>( - &'a self, - fatal: impl IntoDiagnostic<'a, FatalError>, - ) -> FatalError { + pub fn emit_almost_fatal<'a>(&'a self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError { self.create_almost_fatal(fatal).emit() } @@ -1234,12 +1242,12 @@ impl DiagCtxt { } #[track_caller] - pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> Diag<'a> { - err.into_diagnostic(self, Error) + pub fn create_err<'a>(&'a self, err: impl Diagnostic<'a>) -> Diag<'a> { + err.into_diag(self, Error) } #[track_caller] - pub fn emit_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed { + pub fn emit_err<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed { self.create_err(err).emit() } @@ -1297,12 +1305,12 @@ impl DiagCtxt { } #[track_caller] - pub fn create_warn<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) -> Diag<'a, ()> { - warning.into_diagnostic(self, Warning) + pub fn create_warn<'a>(&'a self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { + warning.into_diag(self, Warning) } #[track_caller] - pub fn emit_warn<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) { + pub fn emit_warn<'a>(&'a self, warning: impl Diagnostic<'a, ()>) { self.create_warn(warning).emit() } @@ -1335,12 +1343,12 @@ impl DiagCtxt { } #[track_caller] - pub fn create_note<'a>(&'a self, note: impl IntoDiagnostic<'a, ()>) -> Diag<'a, ()> { - note.into_diagnostic(self, Note) + pub fn create_note<'a>(&'a self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> { + note.into_diag(self, Note) } #[track_caller] - pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, ()>) { + pub fn emit_note<'a>(&'a self, note: impl Diagnostic<'a, ()>) { self.create_note(note).emit() } @@ -1387,7 +1395,7 @@ impl DiagCtxtInner { deduplicated_err_count: 0, deduplicated_warn_count: 0, emitter, - must_produce_diag: false, + must_produce_diag: None, has_printed: false, suppressed_expected_diag: false, taught_diagnostics: Default::default(), @@ -1422,74 +1430,103 @@ impl DiagCtxtInner { // Return value is only `Some` if the level is `Error` or `DelayedBug`. fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option<ErrorGuaranteed> { - assert!(diagnostic.level.can_be_top_or_sub().0); - - if let Some(expectation_id) = diagnostic.level.get_expectation_id() { - // The `LintExpectationId` can be stable or unstable depending on when it was created. - // Diagnostics created before the definition of `HirId`s are unstable and can not yet - // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by - // a stable one by the `LintLevelsBuilder`. - if let LintExpectationId::Unstable { .. } = expectation_id { - self.unstable_expect_diagnostics.push(diagnostic); - return None; - } - self.suppressed_expected_diag = true; - self.fulfilled_expectations.insert(expectation_id.normalize()); - } - if diagnostic.has_future_breakage() { // Future breakages aren't emitted if they're `Level::Allow`, // but they still need to be constructed and stashed below, // so they'll trigger the must_produce_diag check. - self.suppressed_expected_diag = true; + assert!(matches!(diagnostic.level, Error | Warning | Allow)); self.future_breakage_diagnostics.push(diagnostic.clone()); } - // Note that because this comes before the `match` below, - // `-Zeagerly-emit-delayed-bugs` continues to work even after we've - // issued an error and stopped recording new delayed bugs. - if diagnostic.level == DelayedBug && self.flags.eagerly_emit_delayed_bugs { - diagnostic.level = Error; - } - + // We call TRACK_DIAGNOSTIC with an empty closure for the cases that + // return early *and* have some kind of side-effect, except where + // noted. match diagnostic.level { - // This must come after the possible promotion of `DelayedBug` to - // `Error` above. - Fatal | Error if self.treat_next_err_as_bug() => { - diagnostic.level = Bug; + Bug => {} + Fatal | Error => { + if self.treat_next_err_as_bug() { + // `Fatal` and `Error` can be promoted to `Bug`. + diagnostic.level = Bug; + } } DelayedBug => { - // If we have already emitted at least one error, we don't need - // to record the delayed bug, because it'll never be used. - return if let Some(guar) = self.has_errors() { - Some(guar) + // Note that because we check these conditions first, + // `-Zeagerly-emit-delayed-bugs` and `-Ztreat-err-as-bug` + // continue to work even after we've issued an error and + // stopped recording new delayed bugs. + if self.flags.eagerly_emit_delayed_bugs { + // `DelayedBug` can be promoted to `Error` or `Bug`. + if self.treat_next_err_as_bug() { + diagnostic.level = Bug; + } else { + diagnostic.level = Error; + } } else { - let backtrace = std::backtrace::Backtrace::capture(); - // This `unchecked_error_guaranteed` is valid. It is where the - // `ErrorGuaranteed` for delayed bugs originates. See - // `DiagCtxtInner::drop`. - #[allow(deprecated)] - let guar = ErrorGuaranteed::unchecked_error_guaranteed(); - self.delayed_bugs - .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar)); - Some(guar) - }; + // If we have already emitted at least one error, we don't need + // to record the delayed bug, because it'll never be used. + return if let Some(guar) = self.has_errors() { + Some(guar) + } else { + // No `TRACK_DIAGNOSTIC` call is needed, because the + // incremental session is deleted if there is a delayed + // bug. This also saves us from cloning the diagnostic. + let backtrace = std::backtrace::Backtrace::capture(); + // This `unchecked_error_guaranteed` is valid. It is where the + // `ErrorGuaranteed` for delayed bugs originates. See + // `DiagCtxtInner::drop`. + #[allow(deprecated)] + let guar = ErrorGuaranteed::unchecked_error_guaranteed(); + self.delayed_bugs + .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar)); + Some(guar) + }; + } + } + ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect` + Warning => { + if !self.flags.can_emit_warnings { + // We are not emitting warnings. + if diagnostic.has_future_breakage() { + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + } + return None; + } } - Warning if !self.flags.can_emit_warnings => { + Note | Help | FailureNote => {} + OnceNote | OnceHelp => panic!("bad level: {:?}", diagnostic.level), + Allow => { + // Nothing emitted for allowed lints. if diagnostic.has_future_breakage() { - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; } return None; } - Allow | Expect(_) => { - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); - return None; + Expect(expect_id) | ForceWarning(Some(expect_id)) => { + // Diagnostics created before the definition of `HirId`s are + // unstable and can not yet be stored. Instead, they are + // buffered until the `LintExpectationId` is replaced by a + // stable one by the `LintLevelsBuilder`. + if let LintExpectationId::Unstable { .. } = expect_id { + // We don't call TRACK_DIAGNOSTIC because we wait for the + // unstable ID to be updated, whereupon the diagnostic will + // be passed into this method again. + self.unstable_expect_diagnostics.push(diagnostic); + return None; + } + self.fulfilled_expectations.insert(expect_id.normalize()); + if let Expect(_) = diagnostic.level { + // Nothing emitted here for expected lints. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + return None; + } } - _ => {} } - let mut guaranteed = None; - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |mut diagnostic| { + TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| { if let Some(code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code); } @@ -1552,17 +1589,17 @@ impl DiagCtxtInner { // `ErrorGuaranteed` for errors and lint errors originates. #[allow(deprecated)] let guar = ErrorGuaranteed::unchecked_error_guaranteed(); - guaranteed = Some(guar); if is_lint { self.lint_err_guars.push(guar); } else { self.err_guars.push(guar); } self.panic_if_treat_err_as_bug(); + Some(guar) + } else { + None } - }); - - guaranteed + }) } fn treat_err_as_bug(&self) -> bool { @@ -1863,23 +1900,13 @@ impl Level { matches!(*self, FailureNote) } - pub fn get_expectation_id(&self) -> Option<LintExpectationId> { - match self { - Expect(id) | ForceWarning(Some(id)) => Some(*id), - _ => None, - } - } - - // Can this level be used in a top-level diagnostic message and/or a - // subdiagnostic message? - fn can_be_top_or_sub(&self) -> (bool, bool) { + // Can this level be used in a subdiagnostic message? + fn can_be_subdiag(&self) -> bool { match self { Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow - | Expect(_) => (true, false), - - Warning | Note | Help => (true, true), + | Expect(_) => false, - OnceNote | OnceHelp => (false, true), + Warning | Note | Help | OnceNote | OnceHelp => true, } } } @@ -1924,6 +1951,39 @@ pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( } } +/// Grammatical tool for displaying messages to end users in a nice form. +/// +/// Returns "an" if the given string starts with a vowel, and "a" otherwise. +pub fn a_or_an(s: &str) -> &'static str { + let mut chars = s.chars(); + let Some(mut first_alpha_char) = chars.next() else { + return "a"; + }; + if first_alpha_char == '`' { + let Some(next) = chars.next() else { + return "a"; + }; + first_alpha_char = next; + } + if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) { + "an" + } else { + "a" + } +} + +/// Grammatical tool for displaying messages to end users in a nice form. +/// +/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c" +pub fn display_list_with_comma_and<T: std::fmt::Display>(v: &[T]) -> String { + match v.len() { + 0 => "".to_string(), + 1 => v[0].to_string(), + 2 => format!("{} and {}", v[0], v[1]), + _ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])), + } +} + #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum TerminalUrl { No, |
