summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_errors/src/emitter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_errors/src/emitter.rs')
-rw-r--r--compiler/rustc_errors/src/emitter.rs271
1 files changed, 92 insertions, 179 deletions
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
}
}