diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /compiler/rustc_errors | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_errors')
-rw-r--r-- | compiler/rustc_errors/Cargo.toml | 1 | ||||
-rw-r--r-- | compiler/rustc_errors/messages.ftl | 22 | ||||
-rw-r--r-- | compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs | 20 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 10 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic_impls.rs | 69 | ||||
-rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 271 | ||||
-rw-r--r-- | compiler/rustc_errors/src/json.rs | 40 | ||||
-rw-r--r-- | compiler/rustc_errors/src/json/tests.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_errors/src/lib.rs | 182 | ||||
-rw-r--r-- | compiler/rustc_errors/src/markdown/parse.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_errors/src/markdown/term.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_errors/src/markdown/tests/term.rs | 2 |
13 files changed, 334 insertions, 293 deletions
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index e8bcd7c11..faab9f09d 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -25,6 +25,7 @@ annotate-snippets = "0.9" termize = "0.1.1" serde = { version = "1.0.125", features = [ "derive" ] } serde_json = "1.0.59" +derive_setters = "0.1.6" [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/compiler/rustc_errors/messages.ftl b/compiler/rustc_errors/messages.ftl index 8e8223c3c..d68dba0be 100644 --- a/compiler/rustc_errors/messages.ftl +++ b/compiler/rustc_errors/messages.ftl @@ -1,3 +1,25 @@ +errors_delayed_at_with_newline = + delayed at {$emitted_at} + {$note} + +errors_delayed_at_without_newline = + delayed at {$emitted_at} - {$note} + +errors_expected_lifetime_parameter = + expected lifetime {$count -> + [1] parameter + *[other] parameters + } + +errors_indicate_anonymous_lifetime = + indicate the anonymous {$count -> + [1] lifetime + *[other] lifetimes + } + +errors_invalid_flushed_delayed_diagnostic_level = + `flushed_delayed` got diagnostic with level {$level}, instead of the expected `DelayedBug` + errors_target_inconsistent_architecture = inconsistent target specification: "data-layout" claims architecture is {$dl}-endian, while "target-endian" is `{$target}` diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 9872b3bda..a88fba6da 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -157,10 +157,8 @@ impl AnnotateSnippetEmitterWriter { { annotated_files.swap(0, pos); } - // owned: line source, line index, annotations - type Owned = (String, usize, Vec<crate::snippet::Annotation>); - let filename = source_map.filename_for_diagnostics(&primary_lo.file.name); - let origin = filename.to_string_lossy(); + // owned: file name, line source, line index, annotations + type Owned = (String, String, usize, Vec<crate::snippet::Annotation>); let annotated_files: Vec<Owned> = annotated_files .into_iter() .flat_map(|annotated_file| { @@ -169,7 +167,15 @@ impl AnnotateSnippetEmitterWriter { .lines .into_iter() .map(|line| { - (source_string(file.clone(), &line), line.line_index, line.annotations) + // Ensure the source file is present before we try + // to load a string from it. + source_map.ensure_source_file_source_present(file.clone()); + ( + format!("{}", source_map.filename_for_diagnostics(&file.name)), + source_string(file.clone(), &line), + line.line_index, + line.annotations, + ) }) .collect::<Vec<Owned>>() }) @@ -192,11 +198,11 @@ impl AnnotateSnippetEmitterWriter { }, slices: annotated_files .iter() - .map(|(source, line_index, annotations)| { + .map(|(file_name, source, line_index, annotations)| { Slice { source, line_start: *line_index, - origin: Some(&origin), + origin: Some(&file_name), // FIXME(#59346): Not really sure when `fold` should be true or false fold: false, annotations: annotations diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index ed0d06ed0..a96e317df 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -420,13 +420,13 @@ impl Diagnostic { let expected_label = if expected_label.is_empty() { "expected".to_string() } else { - format!("expected {}", expected_label) + format!("expected {expected_label}") }; let found_label = found_label.to_string(); let found_label = if found_label.is_empty() { "found".to_string() } else { - format!("found {}", found_label) + format!("found {found_label}") }; let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { (expected_label.len() - found_label.len(), 0) @@ -439,13 +439,13 @@ impl Diagnostic { StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), })); - msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); + msg.push((format!("`{expected_extra}\n"), Style::NoStyle)); msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle)); msg.extend(found.0.iter().map(|x| match *x { StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), })); - msg.push((format!("`{}", found_extra), Style::NoStyle)); + msg.push((format!("`{found_extra}"), Style::NoStyle)); // For now, just attach these as notes. self.highlighted_note(msg); @@ -454,7 +454,7 @@ impl Diagnostic { pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ - (format!("`{}` from trait: `", name), Style::NoStyle), + (format!("`{name}` from trait: `"), Style::NoStyle), (signature, Style::Highlight), ("`".to_string(), Style::NoStyle), ]); diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 08ff2cfba..5e23ae655 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -536,7 +536,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { } }; - if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() { + if handler.inner.lock().flags.dont_buffer_diagnostics + || handler.inner.lock().flags.treat_err_as_bug.is_some() + { self.emit(); return None; } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 10fe7fc74..a170e3a89 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,3 +1,4 @@ +use crate::diagnostic::DiagnosticLocation; use crate::{fluent_generated as fluent, AddToDiagnostic}; use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg}; use rustc_ast as ast; @@ -10,6 +11,7 @@ use rustc_span::Span; use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; use rustc_type_ir as type_ir; +use std::backtrace::Backtrace; use std::borrow::Cow; use std::fmt; use std::num::ParseIntError; @@ -102,7 +104,7 @@ impl IntoDiagnosticArg for bool { impl IntoDiagnosticArg for char { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) + DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}"))) } } @@ -164,6 +166,12 @@ impl IntoDiagnosticArg for hir::ConstContext { } } +impl IntoDiagnosticArg for ast::Expr { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) + } +} + impl IntoDiagnosticArg for ast::Path { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) @@ -311,3 +319,62 @@ pub enum LabelKind { Label, Help, } + +#[derive(Subdiagnostic)] +#[label(errors_expected_lifetime_parameter)] +pub struct ExpectedLifetimeParameter { + #[primary_span] + pub span: Span, + pub count: usize, +} + +#[derive(Subdiagnostic)] +#[note(errors_delayed_at_with_newline)] +pub struct DelayedAtWithNewline { + #[primary_span] + pub span: Span, + pub emitted_at: DiagnosticLocation, + pub note: Backtrace, +} +#[derive(Subdiagnostic)] +#[note(errors_delayed_at_without_newline)] +pub struct DelayedAtWithoutNewline { + #[primary_span] + pub span: Span, + pub emitted_at: DiagnosticLocation, + pub note: Backtrace, +} + +impl IntoDiagnosticArg for DiagnosticLocation { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::from(self.to_string())) + } +} + +impl IntoDiagnosticArg for Backtrace { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::from(self.to_string())) + } +} + +#[derive(Subdiagnostic)] +#[note(errors_invalid_flushed_delayed_diagnostic_level)] +pub struct InvalidFlushedDelayedDiagnosticLevel { + #[primary_span] + pub span: Span, + pub level: rustc_errors::Level, +} +impl IntoDiagnosticArg for rustc_errors::Level { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::from(self.to_string())) + } +} + +#[derive(Subdiagnostic)] +#[suggestion(errors_indicate_anonymous_lifetime, code = "{suggestion}", style = "verbose")] +pub struct IndicateAnonymousLifetime { + #[primary_span] + pub span: Span, + pub count: usize, + pub suggestion: String, +} diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 9d4d159fd..0cae06881 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -7,8 +7,6 @@ //! //! The output types are defined in `rustc_session::config::ErrorOutputType`. -use Destination::*; - use rustc_span::source_map::SourceMap; use rustc_span::{FileLines, SourceFile, Span}; @@ -24,6 +22,7 @@ use crate::{ }; use rustc_lint_defs::pluralize; +use derive_setters::Setters; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_error_messages::{FluentArgs, SpanLabel}; @@ -35,8 +34,8 @@ use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; use std::path::Path; -use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}; -use termcolor::{Buffer, Color, WriteColor}; +use termcolor::{Ansi, Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream}; +use termcolor::{Color, WriteColor}; /// Default column width, used in tests and when terminal dimensions cannot be determined. const DEFAULT_COLUMN_WIDTH: usize = 140; @@ -60,31 +59,15 @@ impl HumanReadableErrorType { } pub fn new_emitter( self, - dst: Box<dyn Write + Send>, - source_map: Option<Lrc<SourceMap>>, - bundle: Option<Lrc<FluentBundle>>, + mut dst: Box<dyn WriteColor + Send>, fallback_bundle: LazyFallbackBundle, - teach: bool, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, ) -> EmitterWriter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); - EmitterWriter::new( - dst, - source_map, - bundle, - fallback_bundle, - short, - teach, - color, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - ) + if !dst.supports_color() && color { + dst = Box::new(Ansi::new(dst)); + } + EmitterWriter::new(dst, fallback_bundle).short_message(short) } } @@ -279,12 +262,12 @@ pub trait Emitter: Translate { 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: {}", &msg) + format!("help: {msg}") } else { // Show the default suggestion text with the substitution format!( "help: {}{}: `{}`", - &msg, + msg, if self.source_map().is_some_and(|sm| is_case_difference( sm, substitution, @@ -639,10 +622,13 @@ impl ColorConfig { } /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` +#[derive(Setters)] pub struct EmitterWriter { + #[setters(skip)] dst: Destination, sm: Option<Lrc<SourceMap>>, fluent_bundle: Option<Lrc<FluentBundle>>, + #[setters(skip)] fallback_bundle: LazyFallbackBundle, short_message: bool, teach: bool, @@ -662,65 +648,32 @@ pub struct FileWithAnnotatedLines { } impl EmitterWriter { - pub fn stderr( - color_config: ColorConfig, - source_map: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, - fallback_bundle: LazyFallbackBundle, - short_message: bool, - teach: bool, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, - ) -> EmitterWriter { - let dst = Destination::from_stderr(color_config); + pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { + let dst = from_stderr(color_config); + Self::create(dst, fallback_bundle) + } + + fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { EmitterWriter { dst, - sm: source_map, - fluent_bundle, + sm: None, + fluent_bundle: None, fallback_bundle, - short_message, - teach, + short_message: false, + teach: false, ui_testing: false, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, + diagnostic_width: None, + macro_backtrace: false, + track_diagnostics: false, + terminal_url: TerminalUrl::No, } } pub fn new( - dst: Box<dyn Write + Send>, - source_map: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, + dst: Box<dyn WriteColor + Send>, fallback_bundle: LazyFallbackBundle, - short_message: bool, - teach: bool, - colored: bool, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, ) -> EmitterWriter { - EmitterWriter { - dst: Raw(dst, colored), - sm: source_map, - fluent_bundle, - fallback_bundle, - short_message, - teach, - ui_testing: false, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - } - } - - pub fn ui_testing(mut self, ui_testing: bool) -> Self { - self.ui_testing = ui_testing; - self + Self::create(dst, fallback_bundle) } fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { @@ -1982,7 +1935,7 @@ impl EmitterWriter { // We special case `#[derive(_)]\n` and other attribute suggestions, because those // are the ones where context is most useful. let file_lines = sm - .span_to_lines(span.primary_span().unwrap().shrink_to_hi()) + .span_to_lines(parts[0].span.shrink_to_hi()) .expect("span_to_lines failed when emitting suggestion"); let line_num = sm.lookup_char_pos(parts[0].span.lo()).line; if let Some(line) = file_lines.file.get_line(line_num - 1) { @@ -2145,7 +2098,7 @@ impl EmitterWriter { &mut self.dst, self.short_message, ) { - panic!("failed to emit error: {}", e) + panic!("failed to emit error: {e}") } } if !self.short_message { @@ -2161,7 +2114,7 @@ impl EmitterWriter { true, None, ) { - panic!("failed to emit error: {}", err); + panic!("failed to emit error: {err}"); } } for sugg in suggestions { @@ -2180,7 +2133,7 @@ impl EmitterWriter { true, None, ) { - panic!("failed to emit error: {}", e); + panic!("failed to emit error: {e}"); } } SuggestionStyle::HideCodeInline @@ -2193,22 +2146,21 @@ impl EmitterWriter { &Level::Help, max_line_num_len, ) { - panic!("failed to emit error: {}", e); + panic!("failed to emit error: {e}"); } } } } } } - Err(e) => panic!("failed to emit error: {}", e), + Err(e) => panic!("failed to emit error: {e}"), } - let mut dst = self.dst.writable(); - match writeln!(dst) { - Err(e) => panic!("failed to emit error: {}", e), + match writeln!(self.dst) { + Err(e) => panic!("failed to emit error: {e}"), _ => { - if let Err(e) = dst.flush() { - panic!("failed to emit error: {}", e) + if let Err(e) = self.dst.flush() { + panic!("failed to emit error: {e}") } } } @@ -2618,8 +2570,6 @@ fn emit_to_destination( ) -> io::Result<()> { use crate::lock; - let mut dst = dst.writable(); - // In order to prevent error message interleaving, where multiple error lines get intermixed // when multiple compiler processes error simultaneously, we emit errors with additional // steps. @@ -2635,7 +2585,8 @@ fn emit_to_destination( let _buffer_lock = lock::acquire_global_lock("rustc_errors"); for (pos, line) in rendered_buffer.iter().enumerate() { for part in line { - dst.apply_style(*lvl, part.style)?; + let style = part.style.color_spec(*lvl); + dst.set_color(&style)?; write!(dst, "{}", part.text)?; dst.reset()?; } @@ -2647,61 +2598,69 @@ fn emit_to_destination( Ok(()) } -pub enum Destination { - Terminal(StandardStream), - Buffered(BufferWriter), - // The bool denotes whether we should be emitting ansi color codes or not - Raw(Box<(dyn Write + Send)>, bool), -} +pub type Destination = Box<(dyn WriteColor + Send)>; -pub enum WritableDst<'a> { - Terminal(&'a mut StandardStream), - Buffered(&'a mut BufferWriter, Buffer), - Raw(&'a mut (dyn Write + Send)), - ColoredRaw(Ansi<&'a mut (dyn Write + Send)>), +struct Buffy { + buffer_writer: BufferWriter, + buffer: Buffer, } -impl Destination { - fn from_stderr(color: ColorConfig) -> Destination { - let choice = color.to_color_choice(); - // On Windows we'll be performing global synchronization on the entire - // system for emitting rustc errors, so there's no need to buffer - // anything. - // - // On non-Windows we rely on the atomicity of `write` to ensure errors - // don't get all jumbled up. - if cfg!(windows) { - Terminal(StandardStream::stderr(choice)) - } else { - Buffered(BufferWriter::stderr(choice)) - } +impl Write for Buffy { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.buffer.write(buf) } - fn writable(&mut self) -> WritableDst<'_> { - match *self { - Destination::Terminal(ref mut t) => WritableDst::Terminal(t), - Destination::Buffered(ref mut t) => { - let buf = t.buffer(); - WritableDst::Buffered(t, buf) - } - Destination::Raw(ref mut t, false) => WritableDst::Raw(t), - Destination::Raw(ref mut t, true) => WritableDst::ColoredRaw(Ansi::new(t)), + fn flush(&mut self) -> io::Result<()> { + self.buffer_writer.print(&self.buffer)?; + self.buffer.clear(); + Ok(()) + } +} + +impl Drop for Buffy { + fn drop(&mut self) { + if !self.buffer.is_empty() { + self.flush().unwrap(); + panic!("buffers need to be flushed in order to print their contents"); } } +} +impl WriteColor for Buffy { fn supports_color(&self) -> bool { - match *self { - Self::Terminal(ref stream) => stream.supports_color(), - Self::Buffered(ref buffer) => buffer.buffer().supports_color(), - Self::Raw(_, supports_color) => supports_color, - } + self.buffer.supports_color() + } + + fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { + self.buffer.set_color(spec) + } + + fn reset(&mut self) -> io::Result<()> { + self.buffer.reset() + } +} + +fn from_stderr(color: ColorConfig) -> Destination { + let choice = color.to_color_choice(); + // On Windows we'll be performing global synchronization on the entire + // system for emitting rustc errors, so there's no need to buffer + // anything. + // + // On non-Windows we rely on the atomicity of `write` to ensure errors + // don't get all jumbled up. + if cfg!(windows) { + Box::new(StandardStream::stderr(choice)) + } else { + let buffer_writer = BufferWriter::stderr(choice); + let buffer = buffer_writer.buffer(); + Box::new(Buffy { buffer_writer, buffer }) } } -impl<'a> WritableDst<'a> { - fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> { +impl Style { + fn color_spec(&self, lvl: Level) -> ColorSpec { let mut spec = ColorSpec::new(); - match style { + match self { Style::Addition => { spec.set_fg(Some(Color::Green)).set_intense(true); } @@ -2746,53 +2705,7 @@ impl<'a> WritableDst<'a> { spec.set_bold(true); } } - self.set_color(&spec) - } - - fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { - match *self { - WritableDst::Terminal(ref mut t) => t.set_color(color), - WritableDst::Buffered(_, ref mut t) => t.set_color(color), - WritableDst::ColoredRaw(ref mut t) => t.set_color(color), - WritableDst::Raw(_) => Ok(()), - } - } - - fn reset(&mut self) -> io::Result<()> { - match *self { - WritableDst::Terminal(ref mut t) => t.reset(), - WritableDst::Buffered(_, ref mut t) => t.reset(), - WritableDst::ColoredRaw(ref mut t) => t.reset(), - WritableDst::Raw(_) => Ok(()), - } - } -} - -impl<'a> Write for WritableDst<'a> { - fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { - match *self { - WritableDst::Terminal(ref mut t) => t.write(bytes), - WritableDst::Buffered(_, ref mut buf) => buf.write(bytes), - WritableDst::Raw(ref mut w) => w.write(bytes), - WritableDst::ColoredRaw(ref mut t) => t.write(bytes), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - WritableDst::Terminal(ref mut t) => t.flush(), - WritableDst::Buffered(_, ref mut buf) => buf.flush(), - WritableDst::Raw(ref mut w) => w.flush(), - WritableDst::ColoredRaw(ref mut w) => w.flush(), - } - } -} - -impl<'a> Drop for WritableDst<'a> { - fn drop(&mut self) { - if let WritableDst::Buffered(ref mut dst, ref mut buf) = self { - drop(dst.print(buf)); - } + spec } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index f32d6b96b..b8f58e305 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -10,6 +10,7 @@ // FIXME: spec the JSON output properly. use rustc_span::source_map::{FilePathMapping, SourceMap}; +use termcolor::{ColorSpec, WriteColor}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; @@ -159,7 +160,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print diagnostics: {:?}", e); + panic!("failed to print diagnostics: {e:?}"); } } @@ -172,7 +173,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print notification: {:?}", e); + panic!("failed to print notification: {e:?}"); } } @@ -194,7 +195,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print future breakage report: {:?}", e); + panic!("failed to print future breakage report: {e:?}"); } } @@ -208,7 +209,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print unused externs: {:?}", e); + panic!("failed to print unused externs: {e:?}"); } } @@ -356,20 +357,29 @@ impl Diagnostic { self.0.lock().unwrap().flush() } } + impl WriteColor for BufWriter { + fn supports_color(&self) -> bool { + false + } + + fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> { + Ok(()) + } + + fn reset(&mut self) -> io::Result<()> { + Ok(()) + } + } let buf = BufWriter::default(); let output = buf.clone(); je.json_rendered - .new_emitter( - Box::new(buf), - Some(je.sm.clone()), - je.fluent_bundle.clone(), - je.fallback_bundle.clone(), - false, - je.diagnostic_width, - je.macro_backtrace, - je.track_diagnostics, - je.terminal_url, - ) + .new_emitter(Box::new(buf), je.fallback_bundle.clone()) + .sm(Some(je.sm.clone())) + .fluent_bundle(je.fluent_bundle.clone()) + .diagnostic_width(je.diagnostic_width) + .macro_backtrace(je.macro_backtrace) + .track_diagnostics(je.track_diagnostics) + .terminal_url(je.terminal_url) .ui_testing(je.ui_testing) .emit_diagnostic(diag); let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 671dc449e..1f9a2981e 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); - let handler = Handler::with_emitter(true, None, Box::new(je)); + let handler = Handler::with_emitter(Box::new(je)); handler.span_err(span, "foo"); let bytes = output.lock().unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index b9db25103..34518b537 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -15,6 +15,7 @@ #![feature(box_patterns)] #![feature(error_reporter)] #![allow(incomplete_features)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_macros; @@ -22,6 +23,8 @@ extern crate rustc_macros; #[macro_use] extern crate tracing; +extern crate self as rustc_errors; + pub use emitter::ColorConfig; use rustc_lint_defs::LintExpectationId; @@ -47,9 +50,10 @@ use std::borrow::Cow; use std::error::Report; use std::fmt; use std::hash::Hash; +use std::io::Write; use std::num::NonZeroUsize; use std::panic; -use std::path::Path; +use std::path::{Path, PathBuf}; use termcolor::{Color, ColorSpec}; @@ -376,13 +380,16 @@ pub struct ExplicitBug; /// rather than a failed assertion, etc. pub struct DelayedBugPanic; +use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline}; pub use diagnostic::{ AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; pub use diagnostic_impls::{ - DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans, + DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, + IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind, + SingleLabelManySpans, }; use std::backtrace::{Backtrace, BacktraceStatus}; @@ -390,7 +397,6 @@ use std::backtrace::{Backtrace, BacktraceStatus}; /// Certain errors (fatal, bug, unimpl) may cause immediate exit, /// others log errors for later reporting. pub struct Handler { - flags: HandlerFlags, inner: Lock<HandlerInner>, } @@ -446,11 +452,11 @@ struct HandlerInner { /// have been converted. check_unstable_expect_diagnostics: bool, - /// Expected [`Diagnostic`][diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of + /// Expected [`Diagnostic`][struct@diagnostic::Diagnostic]s store a [`LintExpectationId`] as part of /// the lint level. [`LintExpectationId`]s created early during the compilation /// (before `HirId`s have been defined) are not stable and can therefore not be /// stored on disk. This buffer stores these diagnostics until the ID has been - /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][diagnostic::Diagnostic]s are the + /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`][struct@diagnostic::Diagnostic]s are the /// submitted for storage and added to the list of fulfilled expectations. unstable_expect_diagnostics: Vec<Diagnostic>, @@ -461,6 +467,10 @@ struct HandlerInner { /// /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html fulfilled_expectations: FxHashSet<LintExpectationId>, + + /// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be + /// stored along side the main panic backtrace. + ice_file: Option<PathBuf>, } /// A key denoting where from a diagnostic was stashed. @@ -544,63 +554,36 @@ impl Drop for HandlerInner { impl Handler { pub fn with_tty_emitter( - color_config: ColorConfig, - can_emit_warnings: bool, - treat_err_as_bug: Option<NonZeroUsize>, sm: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, ) -> Self { - Self::with_tty_emitter_and_flags( - color_config, - sm, - fluent_bundle, - fallback_bundle, - HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, - ) + let emitter = Box::new(EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm)); + Self::with_emitter(emitter) + } + pub fn disable_warnings(mut self) -> Self { + self.inner.get_mut().flags.can_emit_warnings = false; + self } - pub fn with_tty_emitter_and_flags( - color_config: ColorConfig, - sm: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, - fallback_bundle: LazyFallbackBundle, - flags: HandlerFlags, - ) -> Self { - let emitter = Box::new(EmitterWriter::stderr( - color_config, - sm, - fluent_bundle, - fallback_bundle, - false, - false, - None, - flags.macro_backtrace, - flags.track_diagnostics, - TerminalUrl::No, - )); - Self::with_emitter_and_flags(emitter, flags) - } - - pub fn with_emitter( - can_emit_warnings: bool, - treat_err_as_bug: Option<NonZeroUsize>, - emitter: Box<dyn Emitter + sync::Send>, - ) -> Self { - Handler::with_emitter_and_flags( - emitter, - HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, - ) + pub fn treat_err_as_bug(mut self, treat_err_as_bug: NonZeroUsize) -> Self { + self.inner.get_mut().flags.treat_err_as_bug = Some(treat_err_as_bug); + self } - pub fn with_emitter_and_flags( - emitter: Box<dyn Emitter + sync::Send>, - flags: HandlerFlags, - ) -> Self { + pub fn with_flags(mut self, flags: HandlerFlags) -> Self { + self.inner.get_mut().flags = flags; + self + } + + pub fn with_ice_file(mut self, ice_file: PathBuf) -> Self { + self.inner.get_mut().ice_file = Some(ice_file); + self + } + + pub fn with_emitter(emitter: Box<dyn Emitter + sync::Send>) -> Self { Self { - flags, inner: Lock::new(HandlerInner { - flags, + flags: HandlerFlags { can_emit_warnings: true, ..Default::default() }, lint_err_count: 0, err_count: 0, warn_count: 0, @@ -618,6 +601,7 @@ impl Handler { check_unstable_expect_diagnostics: false, unstable_expect_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), + ice_file: None, }), } } @@ -645,7 +629,7 @@ impl Handler { // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { - self.flags.can_emit_warnings + self.inner.lock().flags.can_emit_warnings } /// Resets the diagnostic error count as well as the cached emitted diagnostics. @@ -991,7 +975,7 @@ impl Handler { self.emit_diag_at_span(Diagnostic::new_with_code(Warning(None), Some(code), msg), span); } - pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<String>) -> ! { self.inner.borrow_mut().span_bug(span, msg) } @@ -1000,7 +984,7 @@ impl Handler { pub fn delay_span_bug( &self, span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, + msg: impl Into<String>, ) -> ErrorGuaranteed { self.inner.borrow_mut().delay_span_bug(span, msg) } @@ -1473,7 +1457,7 @@ impl HandlerInner { let _ = self.fatal(errors); } (_, _) => { - let _ = self.fatal(format!("{}; {}", &errors, &warnings)); + let _ = self.fatal(format!("{errors}; {warnings}")); } } @@ -1584,8 +1568,8 @@ impl HandlerInner { } #[track_caller] - fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { - self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); + fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<String>) -> ! { + self.emit_diag_at_span(Diagnostic::new(Bug, msg.into()), sp); panic::panic_any(ExplicitBug); } @@ -1598,7 +1582,7 @@ impl HandlerInner { fn delay_span_bug( &mut self, sp: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, + msg: impl Into<String>, ) -> 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. @@ -1607,9 +1591,9 @@ impl HandlerInner { 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); + self.span_bug(sp, msg.into()); } - let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); + let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg.into()); diagnostic.set_span(sp.into()); self.emit_diagnostic(&mut diagnostic).unwrap() } @@ -1657,8 +1641,21 @@ impl HandlerInner { explanation: impl Into<DiagnosticMessage> + Copy, ) { let mut no_bugs = true; + // If backtraces are enabled, also print the query stack + let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); for bug in bugs { - let mut bug = bug.decorate(); + if let Some(file) = self.ice_file.as_ref() + && let Ok(mut out) = std::fs::File::options().create(true).append(true).open(file) + { + let _ = write!( + &mut out, + "delayed span bug: {}\n{}\n", + bug.inner.styled_message().iter().filter_map(|(msg, _)| msg.as_str()).collect::<String>(), + &bug.note + ); + } + let mut bug = + if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; if no_bugs { // Put the overall explanation before the `DelayedBug`s, to @@ -1671,11 +1668,10 @@ impl HandlerInner { if bug.level != Level::DelayedBug { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. - bug.note(format!( - "`flushed_delayed` got diagnostic with level {:?}, \ - instead of the expected `DelayedBug`", - bug.level, - )); + bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel { + span: bug.span.primary_span().unwrap(), + level: bug.level, + }); } bug.level = Level::Bug; @@ -1714,13 +1710,11 @@ impl HandlerInner { (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, + "aborting after {count} errors and {delayed_count} delayed bugs due to `-Z treat-err-as-bug={as_bug}`", ) } else { panic!( - "aborting after {} errors due to `-Z treat-err-as-bug={}`", - count, as_bug, + "aborting after {count} errors due to `-Z treat-err-as-bug={as_bug}`", ) } } @@ -1742,12 +1736,22 @@ impl DelayedDiagnostic { fn decorate(mut self) -> Diagnostic { match self.note.status() { BacktraceStatus::Captured => { - self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); + let inner = &self.inner; + self.inner.subdiagnostic(DelayedAtWithNewline { + span: inner.span.primary_span().unwrap(), + emitted_at: inner.emitted_at.clone(), + note: self.note, + }); } // Avoid the needless newline when no backtrace has been captured, // the display impl should just be a single line. _ => { - self.inner.note(format!("delayed at {} - {}", self.inner.emitted_at, self.note)); + let inner = &self.inner; + self.inner.subdiagnostic(DelayedAtWithoutNewline { + span: inner.span.primary_span().unwrap(), + emitted_at: inner.emitted_at.clone(), + note: self.note, + }); } } @@ -1839,20 +1843,36 @@ pub fn add_elided_lifetime_in_path_suggestion( incl_angl_brckt: bool, insertion_span: Span, ) { - diag.span_label(path_span, format!("expected lifetime parameter{}", pluralize!(n))); + diag.subdiagnostic(ExpectedLifetimeParameter { span: path_span, count: n }); if !source_map.is_span_accessible(insertion_span) { // Do not try to suggest anything if generated by a proc-macro. return; } let anon_lts = vec!["'_"; n].join(", "); let suggestion = - if incl_angl_brckt { format!("<{}>", anon_lts) } else { format!("{}, ", anon_lts) }; - diag.span_suggestion_verbose( - insertion_span.shrink_to_hi(), - format!("indicate the anonymous lifetime{}", pluralize!(n)), + if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") }; + + diag.subdiagnostic(IndicateAnonymousLifetime { + span: insertion_span.shrink_to_hi(), + count: n, suggestion, - Applicability::MachineApplicable, - ); + }); +} + +pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( + db: &mut DiagnosticBuilder<'a, G>, + ambiguity: rustc_lint_defs::AmbiguityErrorDiag, +) { + db.span_label(ambiguity.label_span, ambiguity.label_msg); + db.note(ambiguity.note_msg); + db.span_note(ambiguity.b1_span, ambiguity.b1_note_msg); + for help_msg in ambiguity.b1_help_msgs { + db.help(help_msg); + } + db.span_note(ambiguity.b2_span, ambiguity.b2_note_msg); + for help_msg in ambiguity.b2_help_msgs { + db.help(help_msg); + } } #[derive(Clone, Copy, PartialEq, Hash, Debug)] diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index 362a451fd..d3a08da62 100644 --- a/compiler/rustc_errors/src/markdown/parse.rs +++ b/compiler/rustc_errors/src/markdown/parse.rs @@ -272,7 +272,7 @@ fn parse_ordered_li(buf: &[u8]) -> Parsed<'_> { fn get_indented_section(buf: &[u8]) -> (&[u8], &[u8]) { let mut end = buf.len(); for (idx, window) in buf.windows(2).enumerate() { - let &[ch, next_ch] = window else {unreachable!("always 2 elements")}; + let &[ch, next_ch] = window else { unreachable!("always 2 elements") }; if idx >= buf.len().saturating_sub(2) && next_ch == b'\n' { // End of stream end = buf.len().saturating_sub(1); diff --git a/compiler/rustc_errors/src/markdown/term.rs b/compiler/rustc_errors/src/markdown/term.rs index e45ba6d2c..88c3c8b9f 100644 --- a/compiler/rustc_errors/src/markdown/term.rs +++ b/compiler/rustc_errors/src/markdown/term.rs @@ -149,7 +149,7 @@ fn write_wrapping<B: io::Write>( let Some((end_idx, _ch)) = iter.nth(ch_count) else { // Write entire line buf.write_all(to_write.as_bytes())?; - cur.set(cur.get()+to_write.chars().count()); + cur.set(cur.get() + to_write.chars().count()); break; }; diff --git a/compiler/rustc_errors/src/markdown/tests/term.rs b/compiler/rustc_errors/src/markdown/tests/term.rs index 3b31c6d62..6f68fb25a 100644 --- a/compiler/rustc_errors/src/markdown/tests/term.rs +++ b/compiler/rustc_errors/src/markdown/tests/term.rs @@ -63,7 +63,7 @@ fn test_wrapping_write() { #[test] fn test_output() { // Capture `--bless` when run via ./x - let bless = std::env::var("RUSTC_BLESS").unwrap_or_default() == "1"; + let bless = std::env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); let ast = MdStream::parse_str(INPUT); let bufwtr = BufferWriter::stderr(ColorChoice::Always); let mut buffer = bufwtr.buffer(); |