From 5b8a7a09178f73a581db2b21cf516fea7be9b49f Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 2 Jun 2023 13:55:46 +0200 Subject: [PATCH] `#[diagnostic::on_unimplemented]` without filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for a `#[diagnostic::on_unimplemented]` attribute with the following options: * `message` to customize the primary error message * `note` to add a customized note message to an error message * `label` to customize the label part of the error message Co-authored-by: León Orell Valerian Liehr Co-authored-by: Michael Goulet --- compiler/rustc_ast/src/attr/mod.rs | 16 +++ compiler/rustc_feature/src/active.rs | 2 +- compiler/rustc_lint_defs/src/builtin.rs | 11 +- compiler/rustc_middle/src/ty/mod.rs | 16 +++ compiler/rustc_passes/messages.ftl | 3 + compiler/rustc_passes/src/check_attr.rs | 22 +++- compiler/rustc_resolve/src/macros.rs | 5 +- compiler/rustc_trait_selection/messages.ftl | 2 + .../error_reporting/on_unimplemented.rs | 109 +++++++++++++---- .../feature-gate-diagnostic_namespace.rs | 4 +- .../feature-gate-diagnostic_namespace.stderr | 6 +- .../non_existing_attributes_accepted.stderr | 2 +- ...o_not_fail_parsing_on_invalid_options_1.rs | 37 ++++++ ...t_fail_parsing_on_invalid_options_1.stderr | 110 ++++++++++++++++++ ...eature-gate-diagnostic_on_unimplemented.rs | 7 ++ ...re-gate-diagnostic_on_unimplemented.stderr | 12 ++ .../on_unimplemented_simple.rs | 11 ++ .../on_unimplemented_simple.stderr | 24 ++++ 18 files changed, 360 insertions(+), 39 deletions(-) create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs create mode 100644 tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 19a2b3017bc53..db008ea139d58 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -99,6 +99,22 @@ impl Attribute { } } + pub fn path_matches(&self, name: &[Symbol]) -> bool { + match &self.kind { + AttrKind::Normal(normal) => { + normal.item.path.segments.len() == name.len() + && normal + .item + .path + .segments + .iter() + .zip(name) + .all(|(s, n)| s.args.is_none() && s.ident.name == *n) + } + AttrKind::DocComment(..) => false, + } + } + pub fn is_word(&self) -> bool { if let AttrKind::Normal(normal) = &self.kind { matches!(normal.item.args, AttrArgs::Empty) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1941390dc4a1c..e651fdc4ad213 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -411,7 +411,7 @@ declare_features! ( /// Allows having using `suggestion` in the `#[deprecated]` attribute. (active, deprecated_suggestion, "1.61.0", Some(94785), None), /// Allows using the `#[diagnostic]` attribute tool namespace - (active, diagnostic_namespace, "1.73.0", Some(94785), None), + (active, diagnostic_namespace, "1.73.0", Some(111996), None), /// Controls errors in trait implementations. (active, do_not_recommend, "1.67.0", Some(51992), None), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2567e273e1131..63e147ec82fee 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3404,8 +3404,8 @@ declare_lint_pass! { UNFULFILLED_LINT_EXPECTATIONS, UNINHABITED_STATIC, UNKNOWN_CRATE_TYPES, - UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNKNOWN_LINTS, + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, UNREACHABLE_CODE, @@ -4419,7 +4419,8 @@ declare_lint! { } declare_lint! { - /// The `unknown_diagnostic_attributes` lint detects unrecognized diagnostic attributes. + /// The `unknown_or_malformed_diagnostic_attributes` lint detects unrecognized or otherwise malformed + /// diagnostic attributes. /// /// ### Example /// @@ -4431,15 +4432,17 @@ declare_lint! { /// /// {{produces}} /// + /// /// ### Explanation /// /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check /// the spelling, and check the diagnostic attribute listing for the correct name. Also /// consider if you are using an old version of the compiler, and the attribute /// is only available in a newer version. - pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, Warn, - "unrecognized diagnostic attribute" + "unrecognized or malformed diagnostic attribute", + @feature_gate = sym::diagnostic_namespace; } declare_lint! { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 99b697d0ea507..ff420cf902aed 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2408,6 +2408,22 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn get_attrs_by_path<'attr>( + self, + did: DefId, + attr: &'attr [Symbol], + ) -> impl Iterator + 'attr + where + 'tcx: 'attr, + { + let filter_fn = move |a: &&ast::Attribute| a.path_matches(&attr); + if let Some(did) = did.as_local() { + self.hir().attrs(self.hir().local_def_id_to_hir_id(did)).iter().filter(filter_fn) + } else { + self.item_attrs(did).iter().filter(filter_fn) + } + } + pub fn get_attr(self, did: impl Into, attr: Symbol) -> Option<&'tcx ast::Attribute> { if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) { let did: DefId = did.into(); diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index eb1e1269f7f08..a4abded67c5ca 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -140,6 +140,9 @@ passes_deprecated_annotation_has_no_effect = passes_deprecated_attribute = deprecated attribute must be paired with either stable or unstable attribute +passes_diagnostic_diagnostic_on_unimplemented_only_for_traits = + `#[diagnostic::on_unimplemented]` can only be applied to trait definitions + passes_diagnostic_item_first_defined = the diagnostic item is first defined here diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 50a087c78477b..ba9e7ddaf5105 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -16,6 +16,7 @@ use rustc_hir::{ self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID, }; use rustc_hir::{MethodKind, Target, Unsafety}; +use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::query::Providers; @@ -24,7 +25,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, - UNUSED_ATTRIBUTES, + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; @@ -36,6 +37,10 @@ use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; use std::collections::hash_map::Entry; +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)] +pub struct DiagnosticOnUnimplementedOnlyForTraits; + pub(crate) fn target_from_impl_item<'tcx>( tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>, @@ -104,6 +109,9 @@ impl CheckAttrVisitor<'_> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { + if attr.path_matches(&[sym::diagnostic, sym::on_unimplemented]) { + self.check_diagnostic_on_unimplemented(attr.span, hir_id, target); + } match attr.name_or_empty() { sym::do_not_recommend => self.check_do_not_recommend(attr.span, target), sym::inline => self.check_inline(hir_id, attr, span, target), @@ -284,6 +292,18 @@ impl CheckAttrVisitor<'_> { } } + /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition + fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { + if !matches!(target, Target::Trait) { + self.tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnUnimplementedOnlyForTraits, + ); + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { match target { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 8206071657577..90ae08ce37c1f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -26,7 +26,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; use rustc_middle::ty::{TyCtxt, Visibility}; use rustc_session::lint::builtin::{ - LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, }; use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES}; use rustc_session::lint::BuiltinLintDiagnostics; @@ -610,9 +610,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && path.segments.len() >= 2 && path.segments[0].ident.name == sym::diagnostic + && path.segments[1].ident.name != sym::on_unimplemented { self.tcx.sess.parse_sess.buffer_lint( - UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, path.segments[1].span(), node_id, "unknown diagnostic attribute", diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 2db24c43734f8..20253b32add8c 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -27,6 +27,8 @@ trait_selection_inherent_projection_normalization_overflow = overflow evaluating trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` .label = invalid on-clause here +trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute + trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc -> [none] {""} *[default] {" "}for type `{$self_desc}` 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 0e73bad19185b..e01713e3d887a 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 @@ -9,6 +9,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::iter; @@ -336,6 +337,10 @@ pub enum AppendConstMessage { Custom(Symbol), } +#[derive(LintDiagnostic)] +#[diag(trait_selection_malformed_on_unimplemented_attr)] +pub struct NoValueInOnUnimplementedLint; + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -343,7 +348,8 @@ impl<'tcx> OnUnimplementedDirective { items: &[NestedMetaItem], span: Span, is_root: bool, - ) -> Result { + is_diagnostic_namespace_variant: bool, + ) -> Result, ErrorGuaranteed> { let mut errored = None; let mut item_iter = items.iter(); @@ -391,7 +397,10 @@ impl<'tcx> OnUnimplementedDirective { note = parse_value(note_)?; continue; } - } else if item.has_name(sym::parent_label) && parent_label.is_none() { + } else if item.has_name(sym::parent_label) + && parent_label.is_none() + && !is_diagnostic_namespace_variant + { if let Some(parent_label_) = item.value_str() { parent_label = parse_value(parent_label_)?; continue; @@ -401,15 +410,30 @@ impl<'tcx> OnUnimplementedDirective { && message.is_none() && label.is_none() && note.is_none() + && !is_diagnostic_namespace_variant + // FIXME(diagnostic_namespace): disallow filters for now { if let Some(items) = item.meta_item_list() { - match Self::parse(tcx, item_def_id, &items, item.span(), false) { - Ok(subcommand) => subcommands.push(subcommand), + match Self::parse( + tcx, + item_def_id, + &items, + item.span(), + false, + is_diagnostic_namespace_variant, + ) { + Ok(Some(subcommand)) => subcommands.push(subcommand), + Ok(None) => bug!( + "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false" + ), Err(reported) => errored = Some(reported), }; continue; } - } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() { + } else if item.has_name(sym::append_const_msg) + && append_const_msg.is_none() + && !is_diagnostic_namespace_variant + { if let Some(msg) = item.value_str() { append_const_msg = Some(AppendConstMessage::Custom(msg)); continue; @@ -419,14 +443,23 @@ impl<'tcx> OnUnimplementedDirective { } } - // nothing found - tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() }); + if is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + vec![item.span()], + NoValueInOnUnimplementedLint, + ); + } else { + // nothing found + tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() }); + } } if let Some(reported) = errored { - Err(reported) + if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) } } else { - Ok(OnUnimplementedDirective { + Ok(Some(OnUnimplementedDirective { condition, subcommands, message, @@ -434,32 +467,58 @@ impl<'tcx> OnUnimplementedDirective { note, parent_label, append_const_msg, - }) + })) } } pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { - let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else { + let mut is_diagnostic_namespace_variant = false; + let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| { + if tcx.features().diagnostic_namespace { + is_diagnostic_namespace_variant = true; + tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next() + } else { + None + } + }) else { return Ok(None); }; let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some) + Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant) } else if let Some(value) = attr.value_str() { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value, + if !is_diagnostic_namespace_variant { + Ok(Some(OnUnimplementedDirective { + condition: None, + message: None, + subcommands: vec![], + label: Some(OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value, + attr.span, + )?), + note: None, + parent_label: None, + append_const_msg: None, + })) + } else { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), attr.span, - )?), - note: None, - parent_label: None, - append_const_msg: None, - })) + NoValueInOnUnimplementedLint, + ); + Ok(None) + } + } else if is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + NoValueInOnUnimplementedLint, + ); + Ok(None) } else { let reported = tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs index a686ed9c84ef8..b08e291621fdc 100644 --- a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs +++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs @@ -1,12 +1,12 @@ #[diagnostic::non_existing_attribute] //~^ERROR `#[diagnostic]` attribute name space is experimental [E0658] -//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes] +//~|WARNING unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes] pub trait Bar { } #[diagnostic::non_existing_attribute(with_option = "foo")] //~^ERROR `#[diagnostic]` attribute name space is experimental [E0658] -//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes] +//~|WARNING unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes] struct Foo; fn main() { diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr index 45c95cbb3c7e1..017d00e2c8e51 100644 --- a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr +++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr @@ -4,7 +4,7 @@ error[E0658]: `#[diagnostic]` attribute name space is experimental LL | #[diagnostic::non_existing_attribute] | ^^^^^^^^^^ | - = note: see issue #94785 for more information + = note: see issue #111996 for more information = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable error[E0658]: `#[diagnostic]` attribute name space is experimental @@ -13,7 +13,7 @@ error[E0658]: `#[diagnostic]` attribute name space is experimental LL | #[diagnostic::non_existing_attribute(with_option = "foo")] | ^^^^^^^^^^ | - = note: see issue #94785 for more information + = note: see issue #111996 for more information = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable warning: unknown diagnostic attribute @@ -22,7 +22,7 @@ warning: unknown diagnostic attribute LL | #[diagnostic::non_existing_attribute] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[warn(unknown_diagnostic_attributes)]` on by default + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default warning: unknown diagnostic attribute --> $DIR/feature-gate-diagnostic_namespace.rs:7:15 diff --git a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr index 4f9b7ba2bcf33..753077b365e8f 100644 --- a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr +++ b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr @@ -4,7 +4,7 @@ warning: unknown diagnostic attribute LL | #[diagnostic::non_existing_attribute] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[warn(unknown_diagnostic_attributes)]` on by default + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default warning: unknown diagnostic attribute --> $DIR/non_existing_attributes_accepted.rs:8:15 diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs new file mode 100644 index 0000000000000..00fb59d14d76a --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs @@ -0,0 +1,37 @@ +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented(unsupported = "foo")] +//~^WARN malformed `on_unimplemented` attribute +//~|WARN malformed `on_unimplemented` attribute +trait Foo {} + +#[diagnostic::on_unimplemented(message = "Baz")] +//~^WARN `#[diagnostic::on_unimplemented]` can only be applied to trait definitions +struct Bar {} + +#[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] +//~^WARN malformed `on_unimplemented` attribute +//~|WARN malformed `on_unimplemented` attribute +trait Baz {} + +#[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] +//~^WARN malformed `on_unimplemented` attribute +//~|WARN malformed `on_unimplemented` attribute +trait Boom {} + +#[diagnostic::on_unimplemented = "boom"] +//~^WARN malformed `on_unimplemented` attribute +trait Doom {} + +fn take_foo(_: impl Foo) {} +fn take_baz(_: impl Baz) {} +fn take_boom(_: impl Boom) {} + +fn main() { + take_foo(1_i32); + //~^ERROR the trait bound `i32: Foo` is not satisfied + take_baz(1_i32); + //~^ERROR Boom + take_boom(1_i32); + //~^ERROR Boom +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr new file mode 100644 index 0000000000000..bb1b29ef248f0 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -0,0 +1,110 @@ +warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:8:1 + | +LL | #[diagnostic::on_unimplemented(message = "Baz")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 + | +LL | #[diagnostic::on_unimplemented(unsupported = "foo")] + | ^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] + | ^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:1 + | +LL | #[diagnostic::on_unimplemented = "boom"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 + | +LL | #[diagnostic::on_unimplemented(unsupported = "foo")] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:14 + | +LL | take_foo(1_i32); + | -------- ^^^^^ the trait `Foo` is not implemented for `i32` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:6:1 + | +LL | trait Foo {} + | ^^^^^^^^^ +note: required by a bound in `take_foo` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:21 + | +LL | fn take_foo(_: impl Foo) {} + | ^^^ required by this bound in `take_foo` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: Boom + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:14 + | +LL | take_baz(1_i32); + | -------- ^^^^^ the trait `Baz` is not implemented for `i32` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:1 + | +LL | trait Baz {} + | ^^^^^^^^^ +note: required by a bound in `take_baz` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:27:21 + | +LL | fn take_baz(_: impl Baz) {} + | ^^^ required by this bound in `take_baz` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 + | +LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: Boom + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:15 + | +LL | take_boom(1_i32); + | --------- ^^^^^ the trait `Boom` is not implemented for `i32` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:20:1 + | +LL | trait Boom {} + | ^^^^^^^^^^ +note: required by a bound in `take_boom` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:28:22 + | +LL | fn take_boom(_: impl Boom) {} + | ^^^^ required by this bound in `take_boom` + +error: aborting due to 3 previous errors; 8 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs new file mode 100644 index 0000000000000..609a840c118c6 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs @@ -0,0 +1,7 @@ +#[diagnostic::on_unimplemented(message = "Foo")] +//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658] +pub trait Bar { +} + +fn main() { +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr new file mode 100644 index 0000000000000..21f02e3a73ba6 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr @@ -0,0 +1,12 @@ +error[E0658]: `#[diagnostic]` attribute name space is experimental + --> $DIR/feature-gate-diagnostic_on_unimplemented.rs:1:3 + | +LL | #[diagnostic::on_unimplemented(message = "Foo")] + | ^^^^^^^^^^ + | + = note: see issue #111996 for more information + = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs new file mode 100644 index 0000000000000..797edbc9ec564 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs @@ -0,0 +1,11 @@ +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented(message = "Foo", label = "Bar", note = "Baz")] +trait Foo {} + +fn takes_foo(_: impl Foo) {} + +fn main() { + takes_foo(()); + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr new file mode 100644 index 0000000000000..549c7caa72058 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr @@ -0,0 +1,24 @@ +error[E0277]: Foo + --> $DIR/on_unimplemented_simple.rs:9:15 + | +LL | takes_foo(()); + | --------- ^^ Bar + | | + | required by a bound introduced by this call + | + = help: the trait `Foo` is not implemented for `()` + = note: Baz +help: this trait has no implementations, consider adding one + --> $DIR/on_unimplemented_simple.rs:4:1 + | +LL | trait Foo {} + | ^^^^^^^^^ +note: required by a bound in `takes_foo` + --> $DIR/on_unimplemented_simple.rs:6:22 + | +LL | fn takes_foo(_: impl Foo) {} + | ^^^ required by this bound in `takes_foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.