diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index c3c9f26c31571..a14364f9253bc 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -47,6 +47,7 @@ pub struct AnnotateSnippetEmitter { track_diagnostics: bool, terminal_url: TerminalUrl, theme: OutputTheme, + show_suggestions_with_unavailable_source: bool, } impl Debug for AnnotateSnippetEmitter { @@ -63,6 +64,10 @@ impl Debug for AnnotateSnippetEmitter { .field("track_diagnostics", &self.track_diagnostics) .field("terminal_url", &self.terminal_url) .field("theme", &self.theme) + .field( + "show_suggestions_with_unavailable_source", + &self.show_suggestions_with_unavailable_source, + ) .finish() } } @@ -136,6 +141,7 @@ impl AnnotateSnippetEmitter { track_diagnostics: false, terminal_url: TerminalUrl::No, theme: OutputTheme::Ascii, + show_suggestions_with_unavailable_source: false, } } @@ -307,6 +313,12 @@ impl AnnotateSnippetEmitter { SuggestionStyle::HideCodeInline | SuggestionStyle::ShowCode | SuggestionStyle::ShowAlways => { + // Get the original unavailable spans before `suggestion` is consumed. + let unavailable_source_span = if self.show_suggestions_with_unavailable_source { + self.suggestion_span_with_unavailable_source(sm, &suggestion) + } else { + None + }; let substitutions = suggestion .substitutions .into_iter() @@ -356,6 +368,30 @@ impl AnnotateSnippetEmitter { .collect::>(); if substitutions.is_empty() { + if let Some(span) = unavailable_source_span { + let msg = format_diag_message(&suggestion.msg, args).to_string(); + report.push(std::mem::replace( + &mut group, + Group::with_title( + annotate_snippets::Level::HELP.secondary_title(msg), + ), + )); + + let file_ann = collect_annotations(args, &span, sm); + let level = annotate_snippets::Level::HELP; + for (file_idx, (file, annotations)) in file_ann.into_iter().enumerate() + { + group = self.unannotated_messages( + annotations, + &file.name, + sm, + file_idx, + &mut report, + group, + &level, + ); + } + } continue; } let mut msg = format_diag_message(&suggestion.msg, args).to_string(); @@ -645,6 +681,34 @@ impl AnnotateSnippetEmitter { } group } + + fn suggestion_span_with_unavailable_source( + &self, + sm: &Arc, + suggestion: &CodeSuggestion, + ) -> Option { + // These spans cannot be rendered as source patches because their source + // files are unavailable, but can still be shown as locations. + let spans = suggestion + .substitutions + .iter() + .flat_map(|subst| &subst.parts) + .filter_map(|part| { + if sm.is_valid_span(part.span).is_err() { + debug!("suggestion contains an invalid span: {:?}", part); + return None; + } + let lines = sm.span_to_lines(part.span).ok()?; + if sm.ensure_source_file_source_present(&lines.file) { + None + } else { + Some(part.span) + } + }) + .collect::>(); + + if spans.is_empty() { None } else { Some(MultiSpan::from_spans(spans)) } + } } fn emit_to_destination( diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 04ac140f33261..f367c309cbac4 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -54,6 +54,7 @@ pub struct JsonEmitter { macro_backtrace: bool, track_diagnostics: bool, terminal_url: TerminalUrl, + show_suggestions_with_unavailable_source: bool, } impl JsonEmitter { @@ -76,6 +77,7 @@ impl JsonEmitter { macro_backtrace: false, track_diagnostics: false, terminal_url: TerminalUrl::No, + show_suggestions_with_unavailable_source: false, } } @@ -373,6 +375,7 @@ impl Diagnostic { .terminal_url(je.terminal_url) .ui_testing(je.ui_testing) .ignored_directories_in_source_blocks(je.ignored_directories_in_source_blocks.clone()) + .show_suggestions_with_unavailable_source(je.show_suggestions_with_unavailable_source) .theme(if je.json_rendered.unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) .emit_diagnostic(diag); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aa9331ee8f659..00b0d701a58be 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2629,6 +2629,8 @@ written to standard error output)"), "make the current crate share its generic instantiations"), shell_argfiles: bool = (false, parse_bool, [UNTRACKED], "allow argument files to be specified with POSIX \"shell-style\" argument quoting"), + show_suggestions_with_unavailable_source: bool = (false, parse_bool, [UNTRACKED], + "show span-only suggestions when the source code is unavailable"), simulate_remapped_rust_src_base: Option = (None, parse_opt_pathbuf, [TRACKED], "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \ to rust's source base directory. only meant for testing purposes"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 003164e8f9054..fb85fca0f806a 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -947,6 +947,8 @@ impl Session { fn default_emitter(sopts: &config::Options, source_map: Arc) -> Box { let macro_backtrace = sopts.unstable_opts.macro_backtrace; let track_diagnostics = sopts.unstable_opts.track_diagnostics; + let show_suggestions_with_unavailable_source = + sopts.unstable_opts.show_suggestions_with_unavailable_source; let terminal_url = match sopts.unstable_opts.terminal_urls { TerminalUrl::Auto => { match (std::env::var("COLORTERM").as_deref(), std::env::var("TERM").as_deref()) { @@ -974,6 +976,9 @@ fn default_emitter(sopts: &config::Options, source_map: Arc) -> Box) -> Box TestCx<'test> { compiler.arg("-Ccodegen-units=1"); // Hide line numbers to reduce churn compiler.arg("-Zui-testing"); + compiler.arg("-Zshow-suggestions-with-unavailable-source"); compiler.arg("-Zdeduplicate-diagnostics=no"); compiler.arg("-Zwrite-long-types-to-disk=no"); // FIXME: use this for other modes too, for perf? diff --git a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr index 38440af675feb..8e642de858563 100644 --- a/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr +++ b/tests/ui/consts/const_in_pattern/suggest_equality_comparison_instead_of_pattern_matching.stderr @@ -147,6 +147,8 @@ LL | if let V = vec![] {} = note: `Vec<()>` is not usable in patterns | = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details +help: check for equality instead of pattern matching + --> $SRC_DIR/alloc/src/macros.rs:LL:COL error: constant of non-structural type `Vec<()>` in a pattern --> $DIR/suggest_equality_comparison_instead_of_pattern_matching.rs:28:13 diff --git a/tests/ui/pin-ergonomics/pinned-drop-check.stderr b/tests/ui/pin-ergonomics/pinned-drop-check.stderr index 3a848c66578b4..0b5e2e5d6a969 100644 --- a/tests/ui/pin-ergonomics/pinned-drop-check.stderr +++ b/tests/ui/pin-ergonomics/pinned-drop-check.stderr @@ -283,36 +283,54 @@ error[E0040]: explicit use of destructor method | LL | Drop::pin_drop(todo!()); | ^^^^^^^^^^^^^^ explicit destructor calls not allowed + | +help: consider using `drop` function + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL error[E0040]: explicit use of destructor method --> $DIR/pinned-drop-check.rs:156:13 | LL | Drop::pin_drop(todo!()); | ^^^^^^^^^^^^^^ explicit destructor calls not allowed + | +help: consider using `drop` function + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL error[E0040]: explicit use of destructor method --> $DIR/pinned-drop-check.rs:168:13 | LL | Drop::drop(todo!()); | ^^^^^^^^^^ explicit destructor calls not allowed + | +help: consider using `drop` function + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL error[E0040]: explicit use of destructor method --> $DIR/pinned-drop-check.rs:173:13 | LL | Drop::drop(todo!()); | ^^^^^^^^^^ explicit destructor calls not allowed + | +help: consider using `drop` function + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL error[E0040]: explicit use of destructor method --> $DIR/pinned-drop-check.rs:183:13 | LL | Drop::drop(todo!()); | ^^^^^^^^^^ explicit destructor calls not allowed + | +help: consider using `drop` function + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL error[E0040]: explicit use of destructor method --> $DIR/pinned-drop-check.rs:184:13 | LL | Drop::pin_drop(todo!()); | ^^^^^^^^^^^^^^ explicit destructor calls not allowed + | +help: consider using `drop` function + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL error: aborting due to 25 previous errors diff --git a/tests/ui/structs/ice-line-bounds-issue-148684.stderr b/tests/ui/structs/ice-line-bounds-issue-148684.stderr index 71ed393bf0563..8e9a2ce9877f3 100644 --- a/tests/ui/structs/ice-line-bounds-issue-148684.stderr +++ b/tests/ui/structs/ice-line-bounds-issue-148684.stderr @@ -9,6 +9,9 @@ LL | | } ... LL | A(2, vec![]) | ^^^^^^^^^^^^ + | +help: use struct literal syntax instead of calling + --> $SRC_DIR/alloc/src/macros.rs:LL:COL error: aborting due to 1 previous error