mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 02:03:53 +00:00
Auto merge of #114452 - weiznich:feature/diagnostic_on_unimplemented, r=compiler-errors
`#[diagnostic::on_unimplemented]` without filters 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 The relevant behavior is specified in [RFC-3366](https://rust-lang.github.io/rfcs/3366-diagnostic-attribute-namespace.html)
This commit is contained in:
commit
327e6cf55c
@ -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 {
|
pub fn is_word(&self) -> bool {
|
||||||
if let AttrKind::Normal(normal) = &self.kind {
|
if let AttrKind::Normal(normal) = &self.kind {
|
||||||
matches!(normal.item.args, AttrArgs::Empty)
|
matches!(normal.item.args, AttrArgs::Empty)
|
||||||
|
@ -416,7 +416,7 @@ declare_features! (
|
|||||||
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
|
/// Allows having using `suggestion` in the `#[deprecated]` attribute.
|
||||||
(active, deprecated_suggestion, "1.61.0", Some(94785), None),
|
(active, deprecated_suggestion, "1.61.0", Some(94785), None),
|
||||||
/// Allows using the `#[diagnostic]` attribute tool namespace
|
/// 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.
|
/// Controls errors in trait implementations.
|
||||||
(active, do_not_recommend, "1.67.0", Some(51992), None),
|
(active, do_not_recommend, "1.67.0", Some(51992), None),
|
||||||
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
|
/// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
|
||||||
|
@ -3405,8 +3405,8 @@ declare_lint_pass! {
|
|||||||
UNFULFILLED_LINT_EXPECTATIONS,
|
UNFULFILLED_LINT_EXPECTATIONS,
|
||||||
UNINHABITED_STATIC,
|
UNINHABITED_STATIC,
|
||||||
UNKNOWN_CRATE_TYPES,
|
UNKNOWN_CRATE_TYPES,
|
||||||
UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
|
|
||||||
UNKNOWN_LINTS,
|
UNKNOWN_LINTS,
|
||||||
|
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
UNNAMEABLE_TEST_ITEMS,
|
UNNAMEABLE_TEST_ITEMS,
|
||||||
UNNAMEABLE_TYPES,
|
UNNAMEABLE_TYPES,
|
||||||
UNREACHABLE_CODE,
|
UNREACHABLE_CODE,
|
||||||
@ -4420,7 +4420,8 @@ declare_lint! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
/// ### Example
|
||||||
///
|
///
|
||||||
@ -4432,15 +4433,17 @@ declare_lint! {
|
|||||||
///
|
///
|
||||||
/// {{produces}}
|
/// {{produces}}
|
||||||
///
|
///
|
||||||
|
///
|
||||||
/// ### Explanation
|
/// ### Explanation
|
||||||
///
|
///
|
||||||
/// It is usually a mistake to specify a diagnostic attribute that does not exist. Check
|
/// 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
|
/// 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
|
/// consider if you are using an old version of the compiler, and the attribute
|
||||||
/// is only available in a newer version.
|
/// is only available in a newer version.
|
||||||
pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
|
pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
Warn,
|
Warn,
|
||||||
"unrecognized diagnostic attribute"
|
"unrecognized or malformed diagnostic attribute",
|
||||||
|
@feature_gate = sym::diagnostic_namespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
|
@ -2408,6 +2408,22 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_attrs_by_path<'attr>(
|
||||||
|
self,
|
||||||
|
did: DefId,
|
||||||
|
attr: &'attr [Symbol],
|
||||||
|
) -> impl Iterator<Item = &'tcx ast::Attribute> + '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<DefId>, attr: Symbol) -> Option<&'tcx ast::Attribute> {
|
pub fn get_attr(self, did: impl Into<DefId>, attr: Symbol) -> Option<&'tcx ast::Attribute> {
|
||||||
if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) {
|
if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) {
|
||||||
let did: DefId = did.into();
|
let did: DefId = did.into();
|
||||||
|
@ -153,6 +153,9 @@ passes_deprecated_annotation_has_no_effect =
|
|||||||
passes_deprecated_attribute =
|
passes_deprecated_attribute =
|
||||||
deprecated attribute must be paired with either stable or unstable 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 =
|
passes_diagnostic_item_first_defined =
|
||||||
the diagnostic item is first defined here
|
the diagnostic item is first defined here
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ use rustc_hir::{
|
|||||||
self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
|
self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
|
||||||
};
|
};
|
||||||
use rustc_hir::{MethodKind, Target, Unsafety};
|
use rustc_hir::{MethodKind, Target, Unsafety};
|
||||||
|
use rustc_macros::LintDiagnostic;
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
|
use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
|
||||||
use rustc_middle::query::Providers;
|
use rustc_middle::query::Providers;
|
||||||
@ -24,7 +25,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
|||||||
use rustc_middle::ty::{self, TyCtxt};
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
use rustc_session::lint::builtin::{
|
use rustc_session::lint::builtin::{
|
||||||
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
|
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_session::parse::feature_err;
|
||||||
use rustc_span::symbol::{kw, sym, Symbol};
|
use rustc_span::symbol::{kw, sym, Symbol};
|
||||||
@ -36,6 +37,10 @@ use rustc_trait_selection::traits::ObligationCtxt;
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::hash_map::Entry;
|
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>(
|
pub(crate) fn target_from_impl_item<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
impl_item: &hir::ImplItem<'_>,
|
impl_item: &hir::ImplItem<'_>,
|
||||||
@ -104,6 +109,9 @@ impl CheckAttrVisitor<'_> {
|
|||||||
let mut seen = FxHashMap::default();
|
let mut seen = FxHashMap::default();
|
||||||
let attrs = self.tcx.hir().attrs(hir_id);
|
let attrs = self.tcx.hir().attrs(hir_id);
|
||||||
for attr in attrs {
|
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() {
|
match attr.name_or_empty() {
|
||||||
sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
|
sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
|
||||||
sym::inline => self.check_inline(hir_id, attr, span, target),
|
sym::inline => self.check_inline(hir_id, attr, span, target),
|
||||||
@ -287,6 +295,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.
|
/// 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 {
|
fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
|
||||||
match target {
|
match target {
|
||||||
|
@ -26,7 +26,7 @@ use rustc_middle::middle::stability;
|
|||||||
use rustc_middle::ty::RegisteredTools;
|
use rustc_middle::ty::RegisteredTools;
|
||||||
use rustc_middle::ty::{TyCtxt, Visibility};
|
use rustc_middle::ty::{TyCtxt, Visibility};
|
||||||
use rustc_session::lint::builtin::{
|
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::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
|
||||||
use rustc_session::lint::BuiltinLintDiagnostics;
|
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||||
@ -610,9 +610,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||||||
if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
|
if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
|
||||||
&& path.segments.len() >= 2
|
&& path.segments.len() >= 2
|
||||||
&& path.segments[0].ident.name == sym::diagnostic
|
&& path.segments[0].ident.name == sym::diagnostic
|
||||||
|
&& path.segments[1].ident.name != sym::on_unimplemented
|
||||||
{
|
{
|
||||||
self.tcx.sess.parse_sess.buffer_lint(
|
self.tcx.sess.parse_sess.buffer_lint(
|
||||||
UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
|
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
path.segments[1].span(),
|
path.segments[1].span(),
|
||||||
node_id,
|
node_id,
|
||||||
"unknown diagnostic attribute",
|
"unknown diagnostic attribute",
|
||||||
|
@ -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]`
|
trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]`
|
||||||
.label = invalid on-clause here
|
.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 ->
|
trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc ->
|
||||||
[none] {""}
|
[none] {""}
|
||||||
*[default] {" "}for type `{$self_desc}`
|
*[default] {" "}for type `{$self_desc}`
|
||||||
|
@ -9,6 +9,7 @@ use rustc_hir::def_id::DefId;
|
|||||||
use rustc_middle::ty::GenericArgsRef;
|
use rustc_middle::ty::GenericArgsRef;
|
||||||
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
|
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
|
||||||
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
|
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::symbol::{kw, sym, Symbol};
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
use rustc_span::{Span, DUMMY_SP};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@ -336,6 +337,10 @@ pub enum AppendConstMessage {
|
|||||||
Custom(Symbol),
|
Custom(Symbol),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(trait_selection_malformed_on_unimplemented_attr)]
|
||||||
|
pub struct NoValueInOnUnimplementedLint;
|
||||||
|
|
||||||
impl<'tcx> OnUnimplementedDirective {
|
impl<'tcx> OnUnimplementedDirective {
|
||||||
fn parse(
|
fn parse(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
@ -343,7 +348,8 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
items: &[NestedMetaItem],
|
items: &[NestedMetaItem],
|
||||||
span: Span,
|
span: Span,
|
||||||
is_root: bool,
|
is_root: bool,
|
||||||
) -> Result<Self, ErrorGuaranteed> {
|
is_diagnostic_namespace_variant: bool,
|
||||||
|
) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||||
let mut errored = None;
|
let mut errored = None;
|
||||||
let mut item_iter = items.iter();
|
let mut item_iter = items.iter();
|
||||||
|
|
||||||
@ -391,7 +397,10 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
note = parse_value(note_)?;
|
note = parse_value(note_)?;
|
||||||
continue;
|
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() {
|
if let Some(parent_label_) = item.value_str() {
|
||||||
parent_label = parse_value(parent_label_)?;
|
parent_label = parse_value(parent_label_)?;
|
||||||
continue;
|
continue;
|
||||||
@ -401,15 +410,30 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
&& message.is_none()
|
&& message.is_none()
|
||||||
&& label.is_none()
|
&& label.is_none()
|
||||||
&& note.is_none()
|
&& note.is_none()
|
||||||
|
&& !is_diagnostic_namespace_variant
|
||||||
|
// FIXME(diagnostic_namespace): disallow filters for now
|
||||||
{
|
{
|
||||||
if let Some(items) = item.meta_item_list() {
|
if let Some(items) = item.meta_item_list() {
|
||||||
match Self::parse(tcx, item_def_id, &items, item.span(), false) {
|
match Self::parse(
|
||||||
Ok(subcommand) => subcommands.push(subcommand),
|
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),
|
Err(reported) => errored = Some(reported),
|
||||||
};
|
};
|
||||||
continue;
|
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() {
|
if let Some(msg) = item.value_str() {
|
||||||
append_const_msg = Some(AppendConstMessage::Custom(msg));
|
append_const_msg = Some(AppendConstMessage::Custom(msg));
|
||||||
continue;
|
continue;
|
||||||
@ -419,14 +443,23 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nothing found
|
if is_diagnostic_namespace_variant {
|
||||||
tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
|
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 {
|
if let Some(reported) = errored {
|
||||||
Err(reported)
|
if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) }
|
||||||
} else {
|
} else {
|
||||||
Ok(OnUnimplementedDirective {
|
Ok(Some(OnUnimplementedDirective {
|
||||||
condition,
|
condition,
|
||||||
subcommands,
|
subcommands,
|
||||||
message,
|
message,
|
||||||
@ -434,32 +467,58 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
note,
|
note,
|
||||||
parent_label,
|
parent_label,
|
||||||
append_const_msg,
|
append_const_msg,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
|
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, 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);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = if let Some(items) = attr.meta_item_list() {
|
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() {
|
} else if let Some(value) = attr.value_str() {
|
||||||
Ok(Some(OnUnimplementedDirective {
|
if !is_diagnostic_namespace_variant {
|
||||||
condition: None,
|
Ok(Some(OnUnimplementedDirective {
|
||||||
message: None,
|
condition: None,
|
||||||
subcommands: vec![],
|
message: None,
|
||||||
label: Some(OnUnimplementedFormatString::try_parse(
|
subcommands: vec![],
|
||||||
tcx,
|
label: Some(OnUnimplementedFormatString::try_parse(
|
||||||
item_def_id,
|
tcx,
|
||||||
value,
|
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,
|
attr.span,
|
||||||
)?),
|
NoValueInOnUnimplementedLint,
|
||||||
note: None,
|
);
|
||||||
parent_label: None,
|
Ok(None)
|
||||||
append_const_msg: 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 {
|
} else {
|
||||||
let reported =
|
let reported =
|
||||||
tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
|
tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#[diagnostic::non_existing_attribute]
|
#[diagnostic::non_existing_attribute]
|
||||||
//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
|
//~^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 {
|
pub trait Bar {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[diagnostic::non_existing_attribute(with_option = "foo")]
|
#[diagnostic::non_existing_attribute(with_option = "foo")]
|
||||||
//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
|
//~^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;
|
struct Foo;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -4,7 +4,7 @@ error[E0658]: `#[diagnostic]` attribute name space is experimental
|
|||||||
LL | #[diagnostic::non_existing_attribute]
|
LL | #[diagnostic::non_existing_attribute]
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: see issue #94785 <https://github.com/rust-lang/rust/issues/94785> for more information
|
= note: see issue #111996 <https://github.com/rust-lang/rust/issues/111996> for more information
|
||||||
= help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
|
= help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0658]: `#[diagnostic]` attribute name space is experimental
|
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")]
|
LL | #[diagnostic::non_existing_attribute(with_option = "foo")]
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: see issue #94785 <https://github.com/rust-lang/rust/issues/94785> for more information
|
= note: see issue #111996 <https://github.com/rust-lang/rust/issues/111996> for more information
|
||||||
= help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
|
= help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
|
||||||
|
|
||||||
warning: unknown diagnostic attribute
|
warning: unknown diagnostic attribute
|
||||||
@ -22,7 +22,7 @@ warning: unknown diagnostic attribute
|
|||||||
LL | #[diagnostic::non_existing_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
|
warning: unknown diagnostic attribute
|
||||||
--> $DIR/feature-gate-diagnostic_namespace.rs:7:15
|
--> $DIR/feature-gate-diagnostic_namespace.rs:7:15
|
||||||
|
@ -4,7 +4,7 @@ warning: unknown diagnostic attribute
|
|||||||
LL | #[diagnostic::non_existing_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
|
warning: unknown diagnostic attribute
|
||||||
--> $DIR/non_existing_attributes_accepted.rs:8:15
|
--> $DIR/non_existing_attributes_accepted.rs:8:15
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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`.
|
@ -0,0 +1,7 @@
|
|||||||
|
#[diagnostic::on_unimplemented(message = "Foo")]
|
||||||
|
//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
|
||||||
|
pub trait Bar {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
@ -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 <https://github.com/rust-lang/rust/issues/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`.
|
@ -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
|
||||||
|
}
|
@ -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`.
|
Loading…
Reference in New Issue
Block a user