From 160b1793b2e29cfb10d7c7a5932ccb7f991f02bd Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 26 Oct 2023 08:20:23 +0200 Subject: [PATCH] Allows `#[diagnostic::on_unimplemented]` attributes to have multiple notes This commit extends the `#[diagnostic::on_unimplemented]` (and `#[rustc_on_unimplemented]`) attributes to allow multiple `note` options. This enables emitting multiple notes for custom error messages. For now I've opted to not change any of the existing usages of `#[rustc_on_unimplemented]` and just updated the relevant compile tests. --- .../error_reporting/on_unimplemented.rs | 32 +++++++------ .../error_reporting/type_err_ctxt_ext.rs | 14 +++--- library/core/src/marker.rs | 15 +++++- ...ptions_and_continue_to_use_fallback.stderr | 1 + .../on_unimplemented/multiple_notes.rs | 18 +++++++ .../on_unimplemented/multiple_notes.stderr | 47 +++++++++++++++++++ .../fn-traits.stderr | 3 ++ tests/ui/suggestions/path-display.stderr | 2 + tests/ui/traits/new-solver/fn-trait.stderr | 2 + 9 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.rs create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index c96e41b88bd..a5508c74134 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -319,7 +319,7 @@ pub struct OnUnimplementedDirective { pub subcommands: Vec, pub message: Option, pub label: Option, - pub note: Option, + pub notes: Vec, pub parent_label: Option, pub append_const_msg: Option, } @@ -329,7 +329,7 @@ pub struct OnUnimplementedDirective { pub struct OnUnimplementedNote { pub message: Option, pub label: Option, - pub note: Option, + pub notes: Vec, pub parent_label: Option, // If none, should fall back to a generic message pub append_const_msg: Option, @@ -399,7 +399,7 @@ fn parse( let mut message = None; let mut label = None; - let mut note = None; + let mut notes = Vec::new(); let mut parent_label = None; let mut subcommands = vec![]; let mut append_const_msg = None; @@ -415,10 +415,12 @@ fn parse( label = parse_value(label_)?; continue; } - } else if item.has_name(sym::note) && note.is_none() { + } else if item.has_name(sym::note) { if let Some(note_) = item.value_str() { - note = parse_value(note_)?; - continue; + if let Some(note) = parse_value(note_)? { + notes.push(note); + continue; + } } } else if item.has_name(sym::parent_label) && parent_label.is_none() @@ -432,7 +434,7 @@ fn parse( && is_root && message.is_none() && label.is_none() - && note.is_none() + && notes.is_empty() && !is_diagnostic_namespace_variant // FIXME(diagnostic_namespace): disallow filters for now { @@ -487,7 +489,7 @@ fn parse( subcommands, message, label, - note, + notes, parent_label, append_const_msg, })) @@ -505,12 +507,14 @@ pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, Er if let Some(aggr) = aggr { let mut subcommands = aggr.subcommands; subcommands.extend(directive.subcommands); + let mut notes = aggr.notes; + notes.extend(directive.notes); Ok(Some(Self { condition: aggr.condition.or(directive.condition), subcommands, message: aggr.message.or(directive.message), label: aggr.label.or(directive.label), - note: aggr.note.or(directive.note), + notes, parent_label: aggr.parent_label.or(directive.parent_label), append_const_msg: aggr.append_const_msg.or(directive.append_const_msg), })) @@ -543,7 +547,7 @@ fn parse_attribute( value, attr.span, )?), - note: None, + notes: Vec::new(), parent_label: None, append_const_msg: None, })) @@ -600,7 +604,7 @@ pub fn evaluate( ) -> OnUnimplementedNote { let mut message = None; let mut label = None; - let mut note = None; + let mut notes = Vec::new(); let mut parent_label = None; let mut append_const_msg = None; info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); @@ -637,9 +641,7 @@ pub fn evaluate( label = Some(label_.clone()); } - if let Some(ref note_) = command.note { - note = Some(note_.clone()); - } + notes.extend(command.notes.clone()); if let Some(ref parent_label_) = command.parent_label { parent_label = Some(parent_label_.clone()); @@ -651,7 +653,7 @@ pub fn evaluate( OnUnimplementedNote { label: label.map(|l| l.format(tcx, trait_ref, &options_map)), message: message.map(|m| m.format(tcx, trait_ref, &options_map)), - note: note.map(|n| n.format(tcx, trait_ref, &options_map)), + notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, &options_map)).collect(), parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), append_const_msg, } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 353a69ef6d3..ce7510cccb4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -428,7 +428,7 @@ fn report_selection_error( let OnUnimplementedNote { message, label, - note, + notes, parent_label, append_const_msg, } = self.on_unimplemented_note(trait_ref, &obligation); @@ -436,21 +436,21 @@ fn report_selection_error( let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id()); let is_unsize = Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait(); - let (message, note, append_const_msg) = if is_try_conversion { + let (message, notes, append_const_msg) = if is_try_conversion { ( Some(format!( "`?` couldn't convert the error to `{}`", trait_ref.skip_binder().self_ty(), )), - Some( + vec![ "the question mark operation (`?`) implicitly performs a \ conversion on the error value using the `From` trait" .to_owned(), - ), + ], Some(AppendConstMessage::Default), ) } else { - (message, note, append_const_msg) + (message, notes, append_const_msg) }; let err_msg = self.get_standard_error_message( @@ -569,9 +569,9 @@ fn report_selection_error( if let Some((msg, span)) = type_def { err.span_label(span, msg); } - if let Some(s) = note { + for note in notes { // If it has a custom `#[rustc_on_unimplemented]` note, let's display it - err.note(s); + err.note(note); } if let Some(s) = parent_label { let body = obligation.cause.body_id; diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index f1594501d40..d396a707b55 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -621,7 +621,20 @@ impl Copy for &T {} note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead", ), on( - _Self = "core::cell::Cell", + all( + _Self = "core::cell::Cell", + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell"), + not(_Self = "core::cell::Cell") + ), note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`", ), on( diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr index 7860e540589..906472beb49 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr @@ -26,6 +26,7 @@ LL | takes_foo(()); | = help: the trait `Foo` is not implemented for `()` = note: custom note + = note: fallback note help: this trait has no implementations, consider adding one --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:13:1 | diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.rs b/tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.rs new file mode 100644 index 00000000000..34cdb99c754 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.rs @@ -0,0 +1,18 @@ +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented(message = "Foo", label = "Bar", note = "Baz", note = "Boom")] +trait Foo {} + +#[diagnostic::on_unimplemented(message = "Bar", label = "Foo", note = "Baz")] +#[diagnostic::on_unimplemented(note = "Baz2")] +trait Bar {} + +fn takes_foo(_: impl Foo) {} +fn takes_bar(_: impl Bar) {} + +fn main() { + takes_foo(()); + //~^ERROR Foo + takes_bar(()); + //~^ERROR Bar +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.stderr new file mode 100644 index 00000000000..c72321d4617 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/multiple_notes.stderr @@ -0,0 +1,47 @@ +error[E0277]: Foo + --> $DIR/multiple_notes.rs:14:15 + | +LL | takes_foo(()); + | --------- ^^ Bar + | | + | required by a bound introduced by this call + | + = help: the trait `Foo` is not implemented for `()` + = note: Baz + = note: Boom +help: this trait has no implementations, consider adding one + --> $DIR/multiple_notes.rs:4:1 + | +LL | trait Foo {} + | ^^^^^^^^^ +note: required by a bound in `takes_foo` + --> $DIR/multiple_notes.rs:10:22 + | +LL | fn takes_foo(_: impl Foo) {} + | ^^^ required by this bound in `takes_foo` + +error[E0277]: Bar + --> $DIR/multiple_notes.rs:16:15 + | +LL | takes_bar(()); + | --------- ^^ Foo + | | + | required by a bound introduced by this call + | + = help: the trait `Bar` is not implemented for `()` + = note: Baz + = note: Baz2 +help: this trait has no implementations, consider adding one + --> $DIR/multiple_notes.rs:8:1 + | +LL | trait Bar {} + | ^^^^^^^^^ +note: required by a bound in `takes_bar` + --> $DIR/multiple_notes.rs:11:22 + | +LL | fn takes_bar(_: impl Bar) {} + | ^^^ required by this bound in `takes_bar` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index fc7bf22775d..4fb0d43d1b7 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -58,6 +58,7 @@ LL | call(foo_unsafe); | required by a bound introduced by this call | = help: the trait `Fn<()>` is not implemented for fn item `unsafe fn() {foo_unsafe}` + = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits note: required by a bound in `call` @@ -75,6 +76,7 @@ LL | call_mut(foo_unsafe); | required by a bound introduced by this call | = help: the trait `FnMut<()>` is not implemented for fn item `unsafe fn() {foo_unsafe}` + = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits note: required by a bound in `call_mut` @@ -92,6 +94,7 @@ LL | call_once(foo_unsafe); | required by a bound introduced by this call | = help: the trait `FnOnce<()>` is not implemented for fn item `unsafe fn() {foo_unsafe}` + = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits note: required by a bound in `call_once` diff --git a/tests/ui/suggestions/path-display.stderr b/tests/ui/suggestions/path-display.stderr index 8359b36588e..46d0b35825b 100644 --- a/tests/ui/suggestions/path-display.stderr +++ b/tests/ui/suggestions/path-display.stderr @@ -5,6 +5,7 @@ LL | println!("{}", path); | ^^^^ `Path` cannot be formatted with the default formatter; call `.display()` on it | = help: the trait `std::fmt::Display` is not implemented for `Path` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -15,6 +16,7 @@ LL | println!("{}", path); | ^^^^ `PathBuf` cannot be formatted with the default formatter; call `.display()` on it | = help: the trait `std::fmt::Display` is not implemented for `PathBuf` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/traits/new-solver/fn-trait.stderr b/tests/ui/traits/new-solver/fn-trait.stderr index d52bcaf25b8..ff6903c5dbf 100644 --- a/tests/ui/traits/new-solver/fn-trait.stderr +++ b/tests/ui/traits/new-solver/fn-trait.stderr @@ -7,6 +7,7 @@ LL | require_fn(f as unsafe fn() -> i32); | required by a bound introduced by this call | = help: the trait `Fn<()>` is not implemented for `unsafe fn() -> i32` + = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() -> i32` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `require_fn` --> $DIR/fn-trait.rs:3:23 @@ -97,6 +98,7 @@ LL | require_fn(h); | required by a bound introduced by this call | = help: the trait `Fn<()>` is not implemented for fn item `unsafe fn() -> i32 {h}` + = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() -> i32 {h}` in a closure with no arguments: `|| { /* code */ }` note: required by a bound in `require_fn` --> $DIR/fn-trait.rs:3:23