summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_errors/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_errors/src')
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs86
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs54
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs38
-rw-r--r--compiler/rustc_errors/src/emitter.rs191
-rw-r--r--compiler/rustc_errors/src/json.rs11
-rw-r--r--compiler/rustc_errors/src/json/tests.rs5
-rw-r--r--compiler/rustc_errors/src/lib.rs112
-rw-r--r--compiler/rustc_errors/src/registry.rs12
-rw-r--r--compiler/rustc_errors/src/styled_buffer.rs2
-rw-r--r--compiler/rustc_errors/src/translation.rs30
10 files changed, 355 insertions, 186 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 51b2ff6a0..9ed8ab674 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -7,7 +7,6 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
use rustc_error_messages::FluentValue;
use rustc_lint_defs::{Applicability, LintExpectationId};
-use rustc_span::edition::LATEST_STABLE_EDITION;
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use std::borrow::Cow;
@@ -555,18 +554,6 @@ impl Diagnostic {
self
}
- /// Help the user upgrade to the latest edition.
- /// This is factored out to make sure it does the right thing with `Cargo.toml`.
- pub fn help_use_latest_edition(&mut self) -> &mut Self {
- if std::env::var_os("CARGO").is_some() {
- self.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION));
- } else {
- self.help(&format!("pass `--edition {}` to `rustc`", LATEST_STABLE_EDITION));
- }
- self.note("for more on editions, read https://doc.rust-lang.org/edition-guide");
- self
- }
-
/// Disallow attaching suggestions this diagnostic.
/// Any suggestions attached e.g. with the `span_suggestion_*` methods
/// (before and after the call to `disable_suggestions`) will be ignored.
@@ -629,19 +616,27 @@ impl Diagnostic {
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
- assert!(!suggestion.is_empty());
- debug_assert!(
- !(suggestion.iter().any(|(sp, text)| sp.is_empty() && text.is_empty())),
- "Span must not be empty and have no suggestion"
+ let mut parts = suggestion
+ .into_iter()
+ .map(|(span, snippet)| SubstitutionPart { snippet, span })
+ .collect::<Vec<_>>();
+
+ parts.sort_unstable_by_key(|part| part.span);
+
+ assert!(!parts.is_empty());
+ debug_assert_eq!(
+ parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
+ None,
+ "Span must not be empty and have no suggestion",
+ );
+ debug_assert_eq!(
+ parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
+ None,
+ "suggestion must not have overlapping parts",
);
self.push_suggestion(CodeSuggestion {
- substitutions: vec![Substitution {
- parts: suggestion
- .into_iter()
- .map(|(span, snippet)| SubstitutionPart { snippet, span })
- .collect(),
- }],
+ substitutions: vec![Substitution { parts }],
msg: self.subdiagnostic_message_to_diagnostic_message(msg),
style,
applicability,
@@ -802,25 +797,34 @@ impl Diagnostic {
suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
) -> &mut Self {
- let suggestions: Vec<_> = suggestions.into_iter().collect();
- debug_assert!(
- !(suggestions
- .iter()
- .flatten()
- .any(|(sp, suggestion)| sp.is_empty() && suggestion.is_empty())),
- "Span must not be empty and have no suggestion"
- );
+ let substitutions = suggestions
+ .into_iter()
+ .map(|sugg| {
+ let mut parts = sugg
+ .into_iter()
+ .map(|(span, snippet)| SubstitutionPart { snippet, span })
+ .collect::<Vec<_>>();
+
+ parts.sort_unstable_by_key(|part| part.span);
+
+ assert!(!parts.is_empty());
+ debug_assert_eq!(
+ parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
+ None,
+ "Span must not be empty and have no suggestion",
+ );
+ debug_assert_eq!(
+ parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
+ None,
+ "suggestion must not have overlapping parts",
+ );
+
+ Substitution { parts }
+ })
+ .collect();
self.push_suggestion(CodeSuggestion {
- substitutions: suggestions
- .into_iter()
- .map(|sugg| Substitution {
- parts: sugg
- .into_iter()
- .map(|(span, snippet)| SubstitutionPart { snippet, span })
- .collect(),
- })
- .collect(),
+ substitutions,
msg: self.subdiagnostic_message_to_diagnostic_message(msg),
style: SuggestionStyle::ShowCode,
applicability,
@@ -1034,6 +1038,7 @@ impl Diagnostic {
) -> (
&Level,
&[(DiagnosticMessage, Style)],
+ Vec<(&Cow<'static, str>, &DiagnosticArgValue<'static>)>,
&Option<DiagnosticId>,
&MultiSpan,
&Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
@@ -1042,6 +1047,7 @@ impl Diagnostic {
(
&self.level,
&self.message,
+ self.args().collect(),
&self.code,
&self.span,
&self.suggestions,
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index cbfee582d..3064d2bed 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -408,6 +408,59 @@ impl EmissionGuarantee for ! {
}
}
+impl<'a> DiagnosticBuilder<'a, rustc_span::fatal_error::FatalError> {
+ /// Convenience function for internal use, clients should use one of the
+ /// `struct_*` methods on [`Handler`].
+ #[track_caller]
+ pub(crate) fn new_almost_fatal(
+ handler: &'a Handler,
+ message: impl Into<DiagnosticMessage>,
+ ) -> Self {
+ let diagnostic = Diagnostic::new_with_code(Level::Fatal, None, message);
+ Self::new_diagnostic_almost_fatal(handler, diagnostic)
+ }
+
+ /// Creates a new `DiagnosticBuilder` with an already constructed
+ /// diagnostic.
+ pub(crate) fn new_diagnostic_almost_fatal(
+ handler: &'a Handler,
+ diagnostic: Diagnostic,
+ ) -> Self {
+ debug!("Created new diagnostic");
+ Self {
+ inner: DiagnosticBuilderInner {
+ state: DiagnosticBuilderState::Emittable(handler),
+ diagnostic: Box::new(diagnostic),
+ },
+ _marker: PhantomData,
+ }
+ }
+}
+
+impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
+ fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+ match db.inner.state {
+ // First `.emit()` call, the `&Handler` is still available.
+ DiagnosticBuilderState::Emittable(handler) => {
+ db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+ handler.emit_diagnostic(&mut db.inner.diagnostic);
+ }
+ // `.emit()` was previously called, disallowed from repeating it.
+ DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+ }
+ // Then fatally error..
+ rustc_span::fatal_error::FatalError
+ }
+
+ fn make_diagnostic_builder(
+ handler: &Handler,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, Self> {
+ DiagnosticBuilder::new_almost_fatal(handler, msg)
+ }
+}
+
/// In general, the `DiagnosticBuilder` uses deref to allow access to
/// the fields and methods of the embedded `diagnostic` in a
/// transparent way. *However,* many of the methods are intended to
@@ -616,7 +669,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
sp: impl Into<MultiSpan>,
msg: impl Into<SubdiagnosticMessage>,
) -> &mut Self);
- forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self);
forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
forward!(pub fn disable_suggestions(&mut self,) -> &mut Self);
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index dad5e98aa..e82bad67b 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,6 +1,5 @@
-use crate::{
- fluent, DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg,
-};
+use crate::fluent_generated as fluent;
+use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg};
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_hir as hir;
@@ -36,6 +35,12 @@ impl<'a, T: fmt::Display> From<&'a T> for DiagnosticArgFromDisplay<'a> {
}
}
+impl<'a, T: Clone + IntoDiagnosticArg> IntoDiagnosticArg for &'a T {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ self.clone().into_diagnostic_arg()
+ }
+}
+
macro_rules! into_diagnostic_arg_using_display {
($( $ty:ty ),+ $(,)?) => {
$(
@@ -49,6 +54,7 @@ macro_rules! into_diagnostic_arg_using_display {
}
into_diagnostic_arg_using_display!(
+ ast::ParamKindOrd,
i8,
u8,
i16,
@@ -153,12 +159,6 @@ impl IntoDiagnosticArg for ast::Path {
}
}
-impl IntoDiagnosticArg for &ast::Path {
- fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
- DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(self)))
- }
-}
-
impl IntoDiagnosticArg for ast::token::Token {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(pprust::token_to_string(&self))
@@ -177,6 +177,26 @@ impl IntoDiagnosticArg for type_ir::FloatTy {
}
}
+impl IntoDiagnosticArg for std::ffi::CString {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
+ }
+}
+
+impl IntoDiagnosticArg for rustc_data_structures::small_c_str::SmallCStr {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
+ }
+}
+
+impl IntoDiagnosticArg for ast::Visibility {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ let s = pprust::vis_to_string(&self);
+ let s = s.trim_end().to_string();
+ DiagnosticArgValue::Str(Cow::Owned(s))
+ }
+}
+
impl IntoDiagnosticArg for Level {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Borrowed(self.to_cmd_flag()))
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 628e19999..211bbf4f5 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -18,7 +18,7 @@ use crate::translation::{to_fluent_args, Translate};
use crate::{
diagnostic::DiagnosticLocation, CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage,
FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic,
- SubstitutionHighlight, SuggestionStyle,
+ SubstitutionHighlight, SuggestionStyle, TerminalUrl,
};
use rustc_lint_defs::pluralize;
@@ -66,6 +66,7 @@ impl HumanReadableErrorType {
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();
@@ -80,6 +81,7 @@ impl HumanReadableErrorType {
diagnostic_width,
macro_backtrace,
track_diagnostics,
+ terminal_url,
)
}
}
@@ -652,6 +654,7 @@ pub struct EmitterWriter {
macro_backtrace: bool,
track_diagnostics: bool,
+ terminal_url: TerminalUrl,
}
#[derive(Debug)]
@@ -672,6 +675,7 @@ impl EmitterWriter {
diagnostic_width: Option<usize>,
macro_backtrace: bool,
track_diagnostics: bool,
+ terminal_url: TerminalUrl,
) -> EmitterWriter {
let dst = Destination::from_stderr(color_config);
EmitterWriter {
@@ -685,6 +689,7 @@ impl EmitterWriter {
diagnostic_width,
macro_backtrace,
track_diagnostics,
+ terminal_url,
}
}
@@ -699,6 +704,7 @@ impl EmitterWriter {
diagnostic_width: Option<usize>,
macro_backtrace: bool,
track_diagnostics: bool,
+ terminal_url: TerminalUrl,
) -> EmitterWriter {
EmitterWriter {
dst: Raw(dst, colored),
@@ -711,6 +717,7 @@ impl EmitterWriter {
diagnostic_width,
macro_backtrace,
track_diagnostics,
+ terminal_url,
}
}
@@ -1378,7 +1385,13 @@ impl EmitterWriter {
// only render error codes, not lint codes
if let Some(DiagnosticId::Error(ref code)) = *code {
buffer.append(0, "[", Style::Level(*level));
- buffer.append(0, code, Style::Level(*level));
+ let code = if let TerminalUrl::Yes = self.terminal_url {
+ let path = "https://doc.rust-lang.org/error_codes";
+ format!("\x1b]8;;{path}/{code}.html\x07{code}\x1b]8;;\x07")
+ } else {
+ code.clone()
+ };
+ buffer.append(0, &code, Style::Level(*level));
buffer.append(0, "]", Style::Level(*level));
label_width += 2 + code.len();
}
@@ -1755,7 +1768,7 @@ impl EmitterWriter {
// Render the replacements for each suggestion
let suggestions = suggestion.splice_lines(sm);
- debug!("emit_suggestion_default: suggestions={:?}", suggestions);
+ debug!(?suggestions);
if suggestions.is_empty() {
// Suggestions coming from macros can have malformed spans. This is a heavy handed
@@ -1784,6 +1797,7 @@ impl EmitterWriter {
for (complete, parts, highlights, only_capitalization) in
suggestions.iter().take(MAX_SUGGESTIONS)
{
+ debug!(?complete, ?parts, ?highlights);
notice_capitalization |= only_capitalization;
let has_deletion = parts.iter().any(|p| p.is_deletion(sm));
@@ -1796,17 +1810,17 @@ impl EmitterWriter {
// telling users to make a change but not clarifying *where*.
let loc = sm.lookup_char_pos(parts[0].span.lo());
if loc.file.name != sm.span_to_filename(span) && loc.file.name.is_real() {
- buffer.puts(row_num - 1, 0, "--> ", Style::LineNumber);
- buffer.append(
- row_num - 1,
- &format!(
- "{}:{}:{}",
- sm.filename_for_diagnostics(&loc.file.name),
- sm.doctest_offset_line(&loc.file.name, loc.line),
- loc.col.0 + 1,
- ),
- Style::LineAndColumn,
- );
+ let arrow = "--> ";
+ buffer.puts(row_num - 1, 0, arrow, Style::LineNumber);
+ let filename = sm.filename_for_diagnostics(&loc.file.name);
+ let offset = sm.doctest_offset_line(&loc.file.name, loc.line);
+ let message = format!("{}:{}:{}", filename, offset, loc.col.0 + 1);
+ if row_num == 2 {
+ let col = usize::max(max_line_num_len + 1, arrow.len());
+ buffer.puts(1, col, &message, Style::LineAndColumn);
+ } else {
+ buffer.append(row_num - 1, &message, Style::LineAndColumn);
+ }
for _ in 0..max_line_num_len {
buffer.prepend(row_num - 1, " ", Style::NoStyle);
}
@@ -1882,9 +1896,8 @@ impl EmitterWriter {
&mut buffer,
&mut row_num,
&Vec::new(),
- p,
+ p + line_start,
l,
- line_start,
show_code_change,
max_line_num_len,
&file_lines,
@@ -1907,9 +1920,8 @@ impl EmitterWriter {
&mut buffer,
&mut row_num,
&Vec::new(),
- p,
+ p + line_start,
l,
- line_start,
show_code_change,
max_line_num_len,
&file_lines,
@@ -1925,9 +1937,8 @@ impl EmitterWriter {
&mut buffer,
&mut row_num,
&Vec::new(),
- p,
+ p + line_start,
l,
- line_start,
show_code_change,
max_line_num_len,
&file_lines,
@@ -1941,9 +1952,8 @@ impl EmitterWriter {
&mut buffer,
&mut row_num,
highlight_parts,
- line_pos,
+ line_pos + line_start,
line,
- line_start,
show_code_change,
max_line_num_len,
&file_lines,
@@ -2113,30 +2123,38 @@ impl EmitterWriter {
}
}
for sugg in suggestions {
- if sugg.style == SuggestionStyle::CompletelyHidden {
- // do not display this suggestion, it is meant only for tools
- } else if sugg.style == SuggestionStyle::HideCodeAlways {
- if let Err(e) = self.emit_message_default(
- &MultiSpan::new(),
- &[(sugg.msg.to_owned(), Style::HeaderMsg)],
- args,
- &None,
- &Level::Help,
- max_line_num_len,
- true,
- None,
- ) {
- panic!("failed to emit error: {}", e);
+ match sugg.style {
+ SuggestionStyle::CompletelyHidden => {
+ // do not display this suggestion, it is meant only for tools
}
- } else if let Err(e) = self.emit_suggestion_default(
- span,
- sugg,
- args,
- &Level::Help,
- max_line_num_len,
- ) {
- panic!("failed to emit error: {}", e);
- };
+ SuggestionStyle::HideCodeAlways => {
+ if let Err(e) = self.emit_message_default(
+ &MultiSpan::new(),
+ &[(sugg.msg.to_owned(), Style::HeaderMsg)],
+ args,
+ &None,
+ &Level::Help,
+ max_line_num_len,
+ true,
+ None,
+ ) {
+ panic!("failed to emit error: {}", e);
+ }
+ }
+ SuggestionStyle::HideCodeInline
+ | SuggestionStyle::ShowCode
+ | SuggestionStyle::ShowAlways => {
+ if let Err(e) = self.emit_suggestion_default(
+ span,
+ sugg,
+ args,
+ &Level::Help,
+ max_line_num_len,
+ ) {
+ panic!("failed to emit error: {}", e);
+ }
+ }
+ }
}
}
}
@@ -2159,40 +2177,63 @@ impl EmitterWriter {
buffer: &mut StyledBuffer,
row_num: &mut usize,
highlight_parts: &Vec<SubstitutionHighlight>,
- line_pos: usize,
- line: &str,
- line_start: usize,
+ line_num: usize,
+ line_to_add: &str,
show_code_change: DisplaySuggestion,
max_line_num_len: usize,
file_lines: &FileLines,
is_multiline: bool,
) {
- // Print the span column to avoid confusion
- buffer.puts(*row_num, 0, &self.maybe_anonymized(line_start + line_pos), Style::LineNumber);
if let DisplaySuggestion::Diff = show_code_change {
- // Add the line number for both addition and removal to drive the point home.
- //
- // N - fn foo<A: T>(bar: A) {
- // N + fn foo(bar: impl T) {
- buffer.puts(
- *row_num - 1,
- 0,
- &self.maybe_anonymized(line_start + line_pos),
- Style::LineNumber,
- );
- buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal);
- buffer.puts(
- *row_num - 1,
- max_line_num_len + 3,
- &normalize_whitespace(
- &file_lines.file.get_line(file_lines.lines[line_pos].line_index).unwrap(),
- ),
- Style::NoStyle,
- );
- buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
+ // We need to print more than one line if the span we need to remove is multiline.
+ // For more info: https://github.com/rust-lang/rust/issues/92741
+ let lines_to_remove = file_lines.lines.iter().take(file_lines.lines.len() - 1);
+ for (index, line_to_remove) in lines_to_remove.enumerate() {
+ buffer.puts(
+ *row_num - 1,
+ 0,
+ &self.maybe_anonymized(line_num + index),
+ Style::LineNumber,
+ );
+ buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal);
+ let line = normalize_whitespace(
+ &file_lines.file.get_line(line_to_remove.line_index).unwrap(),
+ );
+ buffer.puts(*row_num - 1, max_line_num_len + 3, &line, Style::NoStyle);
+ *row_num += 1;
+ }
+ // If the last line is exactly equal to the line we need to add, we can skip both of them.
+ // This allows us to avoid output like the following:
+ // 2 - &
+ // 2 + if true { true } else { false }
+ // 3 - if true { true } else { false }
+ // If those lines aren't equal, we print their diff
+ let last_line_index = file_lines.lines[file_lines.lines.len() - 1].line_index;
+ let last_line = &file_lines.file.get_line(last_line_index).unwrap();
+ if last_line != line_to_add {
+ buffer.puts(
+ *row_num - 1,
+ 0,
+ &self.maybe_anonymized(line_num + file_lines.lines.len() - 1),
+ Style::LineNumber,
+ );
+ buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal);
+ buffer.puts(
+ *row_num - 1,
+ max_line_num_len + 3,
+ &normalize_whitespace(last_line),
+ Style::NoStyle,
+ );
+ buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
+ buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
+ buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
+ } else {
+ *row_num -= 2;
+ }
} else if is_multiline {
+ buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
match &highlight_parts[..] {
- [SubstitutionHighlight { start: 0, end }] if *end == line.len() => {
+ [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => {
buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
}
[] => {
@@ -2202,17 +2243,17 @@ impl EmitterWriter {
buffer.puts(*row_num, max_line_num_len + 1, "~ ", Style::Addition);
}
}
+ buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
} else {
+ buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
draw_col_separator(buffer, *row_num, max_line_num_len + 1);
+ buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
}
- // print the suggestion
- buffer.append(*row_num, &normalize_whitespace(line), Style::NoStyle);
-
// Colorize addition/replacements with green.
for &SubstitutionHighlight { start, end } in highlight_parts {
// Account for tabs when highlighting (#87972).
- let tabs: usize = line
+ let tabs: usize = line_to_add
.chars()
.take(start)
.map(|ch| match ch {
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index dc38b8725..f32d6b96b 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -17,6 +17,7 @@ use crate::translation::{to_fluent_args, Translate};
use crate::DiagnosticId;
use crate::{
CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic,
+ TerminalUrl,
};
use rustc_lint_defs::Applicability;
@@ -47,6 +48,7 @@ pub struct JsonEmitter {
diagnostic_width: Option<usize>,
macro_backtrace: bool,
track_diagnostics: bool,
+ terminal_url: TerminalUrl,
}
impl JsonEmitter {
@@ -60,6 +62,7 @@ impl JsonEmitter {
diagnostic_width: Option<usize>,
macro_backtrace: bool,
track_diagnostics: bool,
+ terminal_url: TerminalUrl,
) -> JsonEmitter {
JsonEmitter {
dst: Box::new(io::BufWriter::new(io::stderr())),
@@ -73,6 +76,7 @@ impl JsonEmitter {
diagnostic_width,
macro_backtrace,
track_diagnostics,
+ terminal_url,
}
}
@@ -84,6 +88,7 @@ impl JsonEmitter {
diagnostic_width: Option<usize>,
macro_backtrace: bool,
track_diagnostics: bool,
+ terminal_url: TerminalUrl,
) -> JsonEmitter {
let file_path_mapping = FilePathMapping::empty();
JsonEmitter::stderr(
@@ -96,6 +101,7 @@ impl JsonEmitter {
diagnostic_width,
macro_backtrace,
track_diagnostics,
+ terminal_url,
)
}
@@ -110,6 +116,7 @@ impl JsonEmitter {
diagnostic_width: Option<usize>,
macro_backtrace: bool,
track_diagnostics: bool,
+ terminal_url: TerminalUrl,
) -> JsonEmitter {
JsonEmitter {
dst,
@@ -123,6 +130,7 @@ impl JsonEmitter {
diagnostic_width,
macro_backtrace,
track_diagnostics,
+ terminal_url,
}
}
@@ -360,6 +368,7 @@ impl Diagnostic {
je.diagnostic_width,
je.macro_backtrace,
je.track_diagnostics,
+ je.terminal_url,
)
.ui_testing(je.ui_testing)
.emit_diagnostic(diag);
@@ -571,7 +580,7 @@ impl DiagnosticCode {
let je_result =
je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap();
- DiagnosticCode { code: s, explanation: je_result.unwrap_or(None) }
+ DiagnosticCode { code: s, explanation: je_result.ok() }
})
}
}
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index f13146897..671dc449e 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -4,7 +4,7 @@ use crate::json::JsonEmitter;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use crate::emitter::{ColorConfig, HumanReadableErrorType};
-use crate::Handler;
+use crate::{Handler, TerminalUrl};
use rustc_span::{BytePos, Span};
use std::str;
@@ -46,7 +46,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
let fallback_bundle =
- crate::fallback_fluent_bundle(rustc_error_messages::DEFAULT_LOCALE_RESOURCES, false);
+ crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);
let output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new(
@@ -60,6 +60,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
None,
false,
false,
+ TerminalUrl::No,
);
let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 535812fb0..09bf28ed4 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -3,6 +3,7 @@
//! This module contains the code for creating and emitting diagnostics.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(array_windows)]
#![feature(drain_filter)]
#![feature(if_let_guard)]
#![feature(is_terminal)]
@@ -35,13 +36,13 @@ use rustc_data_structures::stable_hasher::StableHasher;
use rustc_data_structures::sync::{self, Lock, Lrc};
use rustc_data_structures::AtomicRef;
pub use rustc_error_messages::{
- fallback_fluent_bundle, fluent, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
+ fallback_fluent_bundle, fluent_bundle, DelayDm, DiagnosticMessage, FluentBundle,
LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagnosticMessage,
- DEFAULT_LOCALE_RESOURCES,
};
pub use rustc_lint_defs::{pluralize, Applicability};
+use rustc_macros::fluent_messages;
use rustc_span::source_map::SourceMap;
-use rustc_span::HashStableContext;
+pub use rustc_span::ErrorGuaranteed;
use rustc_span::{Loc, Span};
use std::borrow::Cow;
@@ -75,6 +76,8 @@ pub use snippet::Style;
pub type PErr<'a> = DiagnosticBuilder<'a, ErrorGuaranteed>;
pub type PResult<'a, T> = Result<T, PErr<'a>>;
+fluent_messages! { "../locales/en-US.ftl" }
+
// `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
// (See also the comment on `DiagnosticBuilderInner`'s `diagnostic` field.)
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
@@ -572,6 +575,7 @@ impl Handler {
None,
flags.macro_backtrace,
flags.track_diagnostics,
+ TerminalUrl::No,
));
Self::with_emitter_and_flags(emitter, flags)
}
@@ -616,22 +620,24 @@ impl Handler {
}
}
- /// Translate `message` eagerly with `args`.
+ /// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`.
pub fn eagerly_translate<'a>(
&self,
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a, 'static>>,
) -> SubdiagnosticMessage {
+ SubdiagnosticMessage::Eager(self.eagerly_translate_to_string(message, args))
+ }
+
+ /// Translate `message` eagerly with `args` to `String`.
+ pub fn eagerly_translate_to_string<'a>(
+ &self,
+ message: DiagnosticMessage,
+ args: impl Iterator<Item = DiagnosticArg<'a, 'static>>,
+ ) -> String {
let inner = self.inner.borrow();
let args = crate::translation::to_fluent_args(args);
- SubdiagnosticMessage::Eager(
- inner
- .emitter
- .translate_message(&message, &args)
- .map_err(Report::new)
- .unwrap()
- .to_string(),
- )
+ inner.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
}
// This is here to not allow mutation of flags;
@@ -1009,6 +1015,7 @@ impl Handler {
}
#[track_caller]
+ #[rustc_lint_diagnostics]
pub fn span_note_without_error(
&self,
span: impl Into<MultiSpan>,
@@ -1018,6 +1025,7 @@ impl Handler {
}
#[track_caller]
+ #[rustc_lint_diagnostics]
pub fn span_note_diag(
&self,
span: Span,
@@ -1029,19 +1037,23 @@ impl Handler {
}
// NOTE: intentionally doesn't raise an error so rustc_codegen_ssa only reports fatal errors in the main thread
+ #[rustc_lint_diagnostics]
pub fn fatal(&self, msg: impl Into<DiagnosticMessage>) -> FatalError {
self.inner.borrow_mut().fatal(msg)
}
+ #[rustc_lint_diagnostics]
pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
self.inner.borrow_mut().err(msg)
}
+ #[rustc_lint_diagnostics]
pub fn warn(&self, msg: impl Into<DiagnosticMessage>) {
let mut db = DiagnosticBuilder::new(self, Warning(None), msg);
db.emit();
}
+ #[rustc_lint_diagnostics]
pub fn note_without_error(&self, msg: impl Into<DiagnosticMessage>) {
DiagnosticBuilder::new(self, Note, msg).emit();
}
@@ -1056,28 +1068,26 @@ impl Handler {
}
pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
- if self.inner.borrow().has_errors() { Some(ErrorGuaranteed(())) } else { None }
+ self.inner.borrow().has_errors().then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
}
+
pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
- if self.inner.borrow().has_errors_or_lint_errors() {
- Some(ErrorGuaranteed::unchecked_claim_error_was_emitted())
- } else {
- None
- }
+ self.inner
+ .borrow()
+ .has_errors_or_lint_errors()
+ .then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
}
pub fn has_errors_or_delayed_span_bugs(&self) -> Option<ErrorGuaranteed> {
- if self.inner.borrow().has_errors_or_delayed_span_bugs() {
- Some(ErrorGuaranteed::unchecked_claim_error_was_emitted())
- } else {
- None
- }
+ self.inner
+ .borrow()
+ .has_errors_or_delayed_span_bugs()
+ .then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
}
pub fn is_compilation_going_to_fail(&self) -> Option<ErrorGuaranteed> {
- if self.inner.borrow().is_compilation_going_to_fail() {
- Some(ErrorGuaranteed::unchecked_claim_error_was_emitted())
- } else {
- None
- }
+ self.inner
+ .borrow()
+ .is_compilation_going_to_fail()
+ .then(ErrorGuaranteed::unchecked_claim_error_was_emitted)
}
pub fn print_error_count(&self, registry: &Registry) {
@@ -1131,6 +1141,20 @@ impl Handler {
self.create_warning(warning).emit()
}
+ pub fn create_almost_fatal<'a>(
+ &'a self,
+ fatal: impl IntoDiagnostic<'a, FatalError>,
+ ) -> DiagnosticBuilder<'a, FatalError> {
+ fatal.into_diagnostic(self)
+ }
+
+ pub fn emit_almost_fatal<'a>(
+ &'a self,
+ fatal: impl IntoDiagnostic<'a, FatalError>,
+ ) -> FatalError {
+ self.create_almost_fatal(fatal).emit()
+ }
+
pub fn create_fatal<'a>(
&'a self,
fatal: impl IntoDiagnostic<'a, !>,
@@ -1156,6 +1180,17 @@ impl Handler {
self.create_bug(bug).emit()
}
+ pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted {
+ self.create_note(note).emit()
+ }
+
+ pub fn create_note<'a>(
+ &'a self,
+ note: impl IntoDiagnostic<'a, Noted>,
+ ) -> DiagnosticBuilder<'a, Noted> {
+ note.into_diagnostic(self)
+ }
+
fn emit_diag_at_span(
&self,
mut diag: Diagnostic,
@@ -1440,9 +1475,7 @@ impl HandlerInner {
.emitted_diagnostic_codes
.iter()
.filter_map(|x| match &x {
- DiagnosticId::Error(s)
- if registry.try_find_description(s).map_or(false, |o| o.is_some()) =>
- {
+ DiagnosticId::Error(s) if registry.try_find_description(s).is_ok() => {
Some(s.clone())
}
_ => None,
@@ -1803,16 +1836,9 @@ pub fn add_elided_lifetime_in_path_suggestion(
);
}
-/// Useful type to use with `Result<>` indicate that an error has already
-/// been reported to the user, so no need to continue checking.
-#[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)]
-#[derive(HashStable_Generic)]
-pub struct ErrorGuaranteed(());
-
-impl ErrorGuaranteed {
- /// To be used only if you really know what you are doing... ideally, we would find a way to
- /// eliminate all calls to this method.
- pub fn unchecked_claim_error_was_emitted() -> Self {
- ErrorGuaranteed(())
- }
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum TerminalUrl {
+ No,
+ Yes,
+ Auto,
}
diff --git a/compiler/rustc_errors/src/registry.rs b/compiler/rustc_errors/src/registry.rs
index da764d993..f26d8e7eb 100644
--- a/compiler/rustc_errors/src/registry.rs
+++ b/compiler/rustc_errors/src/registry.rs
@@ -5,21 +5,17 @@ pub struct InvalidErrorCode;
#[derive(Clone)]
pub struct Registry {
- long_descriptions: FxHashMap<&'static str, Option<&'static str>>,
+ long_descriptions: FxHashMap<&'static str, &'static str>,
}
impl Registry {
- pub fn new(long_descriptions: &[(&'static str, Option<&'static str>)]) -> Registry {
+ pub fn new(long_descriptions: &[(&'static str, &'static str)]) -> Registry {
Registry { long_descriptions: long_descriptions.iter().copied().collect() }
}
/// Returns `InvalidErrorCode` if the code requested does not exist in the
- /// registry. Otherwise, returns an `Option` where `None` means the error
- /// code is valid but has no extended information.
- pub fn try_find_description(
- &self,
- code: &str,
- ) -> Result<Option<&'static str>, InvalidErrorCode> {
+ /// registry.
+ pub fn try_find_description(&self, code: &str) -> Result<&'static str, InvalidErrorCode> {
self.long_descriptions.get(code).copied().ok_or(InvalidErrorCode)
}
}
diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs
index 9abdb5fc9..9aa14e1f2 100644
--- a/compiler/rustc_errors/src/styled_buffer.rs
+++ b/compiler/rustc_errors/src/styled_buffer.rs
@@ -142,7 +142,7 @@ impl StyledBuffer {
pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
if let Some(ref mut line) = self.lines.get_mut(line) {
if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
- if overwrite || *s == Style::NoStyle || *s == Style::Quotation {
+ if overwrite || matches!(s, Style::NoStyle | Style::Quotation) {
*s = style;
}
}
diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs
index addfc9726..ed35eb1b6 100644
--- a/compiler/rustc_errors/src/translation.rs
+++ b/compiler/rustc_errors/src/translation.rs
@@ -1,9 +1,10 @@
-use crate::error::TranslateError;
+use crate::error::{TranslateError, TranslateErrorKind};
use crate::snippet::Style;
use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle};
use rustc_data_structures::sync::Lrc;
use rustc_error_messages::FluentArgs;
use std::borrow::Cow;
+use std::env;
use std::error::Report;
/// Convert diagnostic arguments (a rustc internal type that exists to implement
@@ -94,12 +95,29 @@ pub trait Translate {
// The primary bundle was present and translation succeeded
Some(Ok(t)) => t,
- // Always yeet out for errors on debug
- Some(Err(primary)) if cfg!(debug_assertions) => do yeet primary,
-
// If `translate_with_bundle` returns `Err` with the primary bundle, this is likely
- // just that the primary bundle doesn't contain the message being translated or
- // something else went wrong) so proceed to the fallback bundle.
+ // just that the primary bundle doesn't contain the message being translated, so
+ // proceed to the fallback bundle.
+ Some(Err(
+ primary @ TranslateError::One {
+ kind: TranslateErrorKind::MessageMissing, ..
+ },
+ )) => translate_with_bundle(self.fallback_fluent_bundle())
+ .map_err(|fallback| primary.and(fallback))?,
+
+ // Always yeet out for errors on debug (unless
+ // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows
+ // local runs of the test suites, of builds with debug assertions, to test the
+ // behaviour in a normal build).
+ Some(Err(primary))
+ if cfg!(debug_assertions)
+ && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() =>
+ {
+ do yeet primary
+ }
+
+ // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so
+ // just hide it and try with the fallback bundle.
Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle())
.map_err(|fallback| primary.and(fallback))?,