diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:41 +0000 |
commit | 4f9fe856a25ab29345b90e7725509e9ee38a37be (patch) | |
tree | e4ffd8a9374cae7b21f7cbfb352927e0e074aff6 /compiler/rustc_errors/src | |
parent | Adding upstream version 1.68.2+dfsg1. (diff) | |
download | rustc-upstream/1.69.0+dfsg1.tar.xz rustc-upstream/1.69.0+dfsg1.zip |
Adding upstream version 1.69.0+dfsg1.upstream/1.69.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_errors/src')
-rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 86 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 54 | ||||
-rw-r--r-- | compiler/rustc_errors/src/diagnostic_impls.rs | 38 | ||||
-rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 191 | ||||
-rw-r--r-- | compiler/rustc_errors/src/json.rs | 11 | ||||
-rw-r--r-- | compiler/rustc_errors/src/json/tests.rs | 5 | ||||
-rw-r--r-- | compiler/rustc_errors/src/lib.rs | 112 | ||||
-rw-r--r-- | compiler/rustc_errors/src/registry.rs | 12 | ||||
-rw-r--r-- | compiler/rustc_errors/src/styled_buffer.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_errors/src/translation.rs | 30 |
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))?, |