diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index a5b8c0ebe25eb..665c516c3e8e8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -13,6 +13,7 @@ use thin_vec::ThinVec; use super::prelude::{ALL_TARGETS, AllowedTargets}; use super::{AcceptMapping, AttributeParser}; use crate::context::{AcceptContext, FinalizeContext, Stage}; +use crate::errors::{DocAliasDuplicated, DocAutoCfgExpectsHideOrShow, IllFormedAttributeInput}; use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser}; use crate::session_diagnostics::{ DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel, @@ -255,9 +256,9 @@ impl DocParser { } if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() { - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - AttributeLintKind::DuplicateDocAlias { first_definition }, + move |dcx, level| DocAliasDuplicated { first_definition }.into_diag(dcx, level), span, ); } @@ -343,9 +344,9 @@ impl DocParser { ArgParser::List(list) => { for meta in list.mixed() { let MetaItemOrLitParser::MetaItemParser(item) = meta else { - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::DocAutoCfgExpectsHideOrShow, + |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level), meta.span(), ); continue; @@ -354,9 +355,9 @@ impl DocParser { Some(sym::hide) => (HideOrShow::Hide, sym::hide), Some(sym::show) => (HideOrShow::Show, sym::show), _ => { - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::DocAutoCfgExpectsHideOrShow, + |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level), item.span(), ); continue; @@ -666,12 +667,10 @@ impl DocParser { ArgParser::NoArgs => { let suggestions = cx.adcx().suggestions(); let span = cx.attr_span; - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::IllFormedAttributeInput { - suggestions, - docs: None, - help: None, + move |dcx, level| { + IllFormedAttributeInput::new(&suggestions, None, None).into_diag(dcx, level) }, span, ); diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index aa9284e54d369..becdaee0f3d3a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -897,11 +897,18 @@ where } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { - let attr_path = self.attr_path.clone().to_string(); + let attr_path = self.attr_path.to_string(); let valid_without_list = self.template.word; - self.emit_lint( + self.emit_dyn_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list }, + move |dcx, level| { + crate::errors::EmptyAttributeList { + attr_span: span, + attr_path: &attr_path, + valid_without_list, + } + .into_diag(dcx, level) + }, span, ); } @@ -916,9 +923,12 @@ where ) { let suggestions = self.suggestions(); let span = self.attr_span; - self.emit_lint( + self.emit_dyn_lint( lint, - AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None, help }, + move |dcx, level| { + crate::errors::IllFormedAttributeInput::new(&suggestions, None, help.as_deref()) + .into_diag(dcx, level) + }, span, ); } diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs index 7049ffae89ab1..8148a859958b5 100644 --- a/compiler/rustc_attr_parsing/src/errors.rs +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::MultiSpan; +use rustc_errors::{DiagArgValue, MultiSpan}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -66,3 +66,114 @@ pub(crate) struct UnsafeAttrOutsideUnsafeLint { #[subdiagnostic] pub suggestion: Option, } + +#[derive(Diagnostic)] +#[diag( + "{$num_suggestions -> + [1] attribute must be of the form {$suggestions} + *[other] valid forms for the attribute are {$suggestions} + }" +)] +pub(crate) struct IllFormedAttributeInput { + pub num_suggestions: usize, + pub suggestions: DiagArgValue, + #[note("for more information, visit <{$docs}>")] + pub has_docs: bool, + pub docs: &'static str, + #[subdiagnostic] + help: Option, +} + +impl IllFormedAttributeInput { + pub(crate) fn new( + suggestions: &[String], + docs: Option<&'static str>, + help: Option<&str>, + ) -> Self { + Self { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + has_docs: docs.is_some(), + docs: docs.unwrap_or(""), + help: help.map(|h| IllFormedAttributeInputHelp { lint: h.to_string() }), + } + } +} + +#[derive(Subdiagnostic)] +#[help( + "if you meant to silence a warning, consider using #![allow({$lint})] or #![expect({$lint})]" +)] +struct IllFormedAttributeInputHelp { + pub lint: String, +} + +#[derive(Diagnostic)] +#[diag("unused attribute")] +#[note( + "{$valid_without_list -> + [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all + *[other] using `{$attr_path}` with an empty list has no effect + }" +)] +pub(crate) struct EmptyAttributeList<'a> { + #[suggestion( + "{$valid_without_list -> + [true] remove these parentheses + *[other] remove this attribute + }", + code = "", + applicability = "machine-applicable" + )] + pub attr_span: Span, + pub attr_path: &'a str, + pub valid_without_list: bool, +} + +#[derive(Diagnostic)] +#[diag("`#[{$name}]` attribute cannot be used on {$target}")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +#[help("`#[{$name}]` can {$only}be applied to {$applied}")] +pub(crate) struct InvalidTargetLint { + pub name: String, + pub target: &'static str, + pub applied: DiagArgValue, + pub only: &'static str, + #[suggestion( + "remove the attribute", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] + pub attr_span: Span, +} + +#[derive(Diagnostic)] +#[diag( + "{$is_used_as_inner -> + [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` + *[other] the `#![{$name}]` attribute can only be used at the crate root + }" +)] +pub(crate) struct InvalidAttrStyle<'a> { + pub name: &'a str, + pub is_used_as_inner: bool, + #[note("this attribute does not have an `!`, which means it is applied to this {$target}")] + pub target_span: Option, + pub target: &'static str, +} + +#[derive(Diagnostic)] +#[diag("doc alias is duplicated")] +pub(crate) struct DocAliasDuplicated { + #[label("first defined here")] + pub first_definition: Span, +} + +#[derive(Diagnostic)] +#[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")] +pub(crate) struct DocAutoCfgExpectsHideOrShow; diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 253a089e49f1a..65e716921f5cb 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -1,17 +1,17 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; -use rustc_errors::{DiagArgValue, MultiSpan, StashKey}; +use rustc_errors::{DiagArgValue, Diagnostic, MultiSpan, StashKey}; use rustc_feature::Features; use rustc_hir::attrs::AttributeKind; -use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrItem, Attribute, MethodKind, Target}; use rustc_span::{BytePos, Span, Symbol, sym}; use crate::AttributeParser; use crate::context::{AcceptContext, Stage}; use crate::errors::{ - InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere, + InvalidAttrAtCrateLevel, InvalidTargetLint, ItemFollowingInnerAttr, + UnsupportedAttributesInWhere, }; use crate::session_diagnostics::InvalidTarget; use crate::target_checking::Policy::Allow; @@ -142,14 +142,19 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { }; let attr_span = cx.attr_span; - cx.emit_lint( + cx.emit_dyn_lint( lint, - AttributeLintKind::InvalidTarget { - name: name.to_string(), - target: target.plural_name(), - only: if only { "only " } else { "" }, - applied, - attr_span, + move |dcx, level| { + InvalidTargetLint { + name: name.to_string(), + target: target.plural_name(), + only: if only { "only " } else { "" }, + applied: DiagArgValue::StrListSepByAnd( + applied.iter().map(|i| Cow::Owned(i.to_string())).collect(), + ), + attr_span, + } + .into_diag(dcx, level) }, attr_span, ); @@ -176,15 +181,24 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } - let kind = AttributeLintKind::InvalidStyle { - name: cx.attr_path.to_string(), - is_used_as_inner: cx.attr_style == AttrStyle::Inner, - target: target.name(), - target_span: cx.target_span, - }; + let name = cx.attr_path.to_string(); + let is_used_as_inner = cx.attr_style == AttrStyle::Inner; + let target_span = cx.target_span; let attr_span = cx.attr_span; - cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span); + cx.emit_dyn_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, + move |dcx, level| { + crate::errors::InvalidAttrStyle { + name: &name, + is_used_as_inner, + target_span: (!is_used_as_inner).then_some(target_span), + target: target.name(), + } + .into_diag(dcx, level) + }, + attr_span, + ); } // FIXME: Fix "Cannot determine resolution" error and remove built-in macros diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 06ff674f25389..d8c4aaa2e11ef 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -8,10 +8,9 @@ use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, }; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, Diagnostic, PResult}; use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, template}; use rustc_hir::AttrPath; -use rustc_hir::lints::AttributeLintKind; use rustc_parse::parse_in; use rustc_session::errors::report_lit_error; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; @@ -210,14 +209,14 @@ pub fn emit_malformed_attribute( suggestions.clear(); } if should_warn(name) { - psess.buffer_lint( + let suggestions = suggestions.clone(); + psess.dyn_buffer_lint( ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, - AttributeLintKind::IllFormedAttributeInput { - suggestions: suggestions.clone(), - docs: template.docs, - help: None, + move |dcx, level| { + crate::errors::IllFormedAttributeInput::new(&suggestions, template.docs, None) + .into_diag(dcx, level) }, ); } else { diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7340ba0b2f391..fb59cd35ad463 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -1,8 +1,7 @@ use std::any::Any; -use std::borrow::Cow; use rustc_data_structures::sync::DynSend; -use rustc_errors::{Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -35,50 +34,6 @@ pub struct DecorateAttrLint<'a, 'sess, 'tcx> { impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { match self.diagnostic { - AttributeLintKind::IllFormedAttributeInput { suggestions, docs, help } => { - lints::IllFormedAttributeInput { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - has_docs: docs.is_some(), - docs: docs.unwrap_or(""), - help: help.clone().map(|h| lints::IllFormedAttributeInputHelp { lint: h }), - } - .into_diag(dcx, level) - } - AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { - lints::EmptyAttributeList { - attr_span: *first_span, - attr_path: attr_path.clone(), - valid_without_list: *valid_without_list, - } - .into_diag(dcx, level) - } - AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => { - lints::InvalidTargetLint { - name: name.clone(), - target, - applied: DiagArgValue::StrListSepByAnd( - applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), - ), - only, - attr_span: *attr_span, - } - .into_diag(dcx, level) - } - &AttributeLintKind::InvalidStyle { - ref name, - is_used_as_inner, - target, - target_span, - } => lints::InvalidAttrStyle { - name: name.clone(), - is_used_as_inner, - target_span: (!is_used_as_inner).then_some(target_span), - target, - } - .into_diag(dcx, level), &AttributeLintKind::UnexpectedCfgName(name, value) => { check_cfg::unexpected_cfg_name(self.sess, self.tcx, name, value) .into_diag(dcx, level) @@ -87,13 +42,6 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { check_cfg::unexpected_cfg_value(self.sess, self.tcx, name, value) .into_diag(dcx, level) } - &AttributeLintKind::DuplicateDocAlias { first_definition } => { - lints::DocAliasDuplicated { first_defn: first_definition }.into_diag(dcx, level) - } - - &AttributeLintKind::DocAutoCfgExpectsHideOrShow => { - lints::DocAutoCfgExpectsHideOrShow.into_diag(dcx, level) - } &AttributeLintKind::AmbiguousDeriveHelpers => { lints::AmbiguousDeriveHelpers.into_diag(dcx, level) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 20d88505f042d..b92efc408ae81 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::codes::*; use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, + Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, SuggestionStyle, msg, }; use rustc_hir as hir; @@ -3025,32 +3025,6 @@ pub(crate) mod unexpected_cfg_value { } } -// FIXME(jdonszelmann): duplicated in rustc_attr_parsing, should be moved there completely. -#[derive(Diagnostic)] -#[diag( - "{$num_suggestions -> - [1] attribute must be of the form {$suggestions} - *[other] valid forms for the attribute are {$suggestions} - }" -)] -pub(crate) struct IllFormedAttributeInput { - pub num_suggestions: usize, - pub suggestions: DiagArgValue, - #[note("for more information, visit <{$docs}>")] - pub has_docs: bool, - pub docs: &'static str, - #[subdiagnostic] - pub help: Option, -} - -#[derive(Subdiagnostic)] -#[help( - "if you meant to silence a warning, consider using #![allow({$lint})] or #![expect({$lint})]" -)] -pub(crate) struct IllFormedAttributeInputHelp { - pub lint: String, -} - #[derive(Diagnostic)] #[diag("creating a {$shared_label}reference to mutable static")] pub(crate) struct RefOfMutStatic<'a> { @@ -3308,63 +3282,6 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { } } -#[derive(Diagnostic)] -#[diag("unused attribute")] -#[note( - "{$valid_without_list -> - [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all - *[other] using `{$attr_path}` with an empty list has no effect - }" -)] -pub(crate) struct EmptyAttributeList { - #[suggestion( - "{$valid_without_list -> - [true] remove these parentheses - *[other] remove this attribute - }", - code = "", - applicability = "machine-applicable" - )] - pub attr_span: Span, - pub attr_path: String, - pub valid_without_list: bool, -} - -#[derive(Diagnostic)] -#[diag("`#[{$name}]` attribute cannot be used on {$target}")] -#[warning( - "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" -)] -#[help("`#[{$name}]` can {$only}be applied to {$applied}")] -pub(crate) struct InvalidTargetLint { - pub name: String, - pub target: &'static str, - pub applied: DiagArgValue, - pub only: &'static str, - #[suggestion( - "remove the attribute", - code = "", - applicability = "machine-applicable", - style = "tool-only" - )] - pub attr_span: Span, -} - -#[derive(Diagnostic)] -#[diag( - "{$is_used_as_inner -> - [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` - *[other] the `#![{$name}]` attribute can only be used at the crate root - }" -)] -pub(crate) struct InvalidAttrStyle { - pub name: String, - pub is_used_as_inner: bool, - #[note("this attribute does not have an `!`, which means it is applied to this {$target}")] - pub target_span: Option, - pub target: &'static str, -} - #[derive(Diagnostic)] #[diag("malformed `doc` attribute input")] #[warning( @@ -3386,17 +3303,6 @@ pub(crate) struct ExpectedNoArgs; )] pub(crate) struct ExpectedNameValue; -#[derive(Diagnostic)] -#[diag("doc alias is duplicated")] -pub(crate) struct DocAliasDuplicated { - #[label("first defined here")] - pub first_defn: Span, -} - -#[derive(Diagnostic)] -#[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")] -pub(crate) struct DocAutoCfgExpectsHideOrShow; - #[derive(Diagnostic)] #[diag("there exists a built-in attribute with the same name")] pub(crate) struct AmbiguousDeriveHelpers; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ea5006c7f03f3..29da46770d52b 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -654,98 +654,32 @@ pub enum DeprecatedSinceKind { #[derive(Debug)] pub enum AttributeLintKind { - IllFormedAttributeInput { - suggestions: Vec, - docs: Option<&'static str>, - help: Option, - }, - EmptyAttribute { - first_span: Span, - attr_path: String, - valid_without_list: bool, - }, - InvalidTarget { - name: String, - target: &'static str, - applied: Vec, - only: &'static str, - attr_span: Span, - }, - InvalidStyle { - name: String, - is_used_as_inner: bool, - target: &'static str, - target_span: Span, - }, UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), - DuplicateDocAlias { - first_definition: Span, - }, - DocAutoCfgExpectsHideOrShow, - DocAutoCfgHideShowUnexpectedItem { - attr_name: Symbol, - }, - DocAutoCfgHideShowExpectsList { - attr_name: Symbol, - }, + DocAutoCfgHideShowUnexpectedItem { attr_name: Symbol }, + DocAutoCfgHideShowExpectsList { attr_name: Symbol }, DocInvalid, AmbiguousDeriveHelpers, - DocUnknownInclude { - span: Span, - inner: &'static str, - value: Symbol, - }, - DocUnknownSpotlight { - span: Span, - }, - DocUnknownPasses { - name: Symbol, - span: Span, - }, - DocUnknownPlugins { - span: Span, - }, - DocUnknownAny { - name: Symbol, - }, + DocUnknownInclude { span: Span, inner: &'static str, value: Symbol }, + DocUnknownSpotlight { span: Span }, + DocUnknownPasses { name: Symbol, span: Span }, + DocUnknownPlugins { span: Span }, + DocUnknownAny { name: Symbol }, DocAutoCfgWrongLiteral, DocTestTakesList, - DocTestUnknown { - name: Symbol, - }, + DocTestUnknown { name: Symbol }, DocTestLiteral, AttrCrateLevelOnly, DoNotRecommendDoesNotExpectArgs, - CrateTypeUnknown { - span: Span, - suggested: Option, - }, + CrateTypeUnknown { span: Span, suggested: Option }, MalformedDoc, ExpectedNoArgs, ExpectedNameValue, - MalFormedDiagnosticAttribute { - attribute: &'static str, - options: &'static str, - span: Span, - }, - MalformedDiagnosticFormat { - warning: FormatWarning, - }, - DiagnosticWrappedParserError { - description: String, - label: String, - span: Span, - }, - IgnoredDiagnosticOption { - option_name: Symbol, - first_span: Span, - later_span: Span, - }, - MissingOptionsForDiagnosticAttribute { - attribute: &'static str, - options: &'static str, - }, + MalFormedDiagnosticAttribute { attribute: &'static str, options: &'static str, span: Span }, + MalformedDiagnosticFormat { warning: FormatWarning }, + DiagnosticWrappedParserError { description: String, label: String, span: Span }, + IgnoredDiagnosticOption { option_name: Symbol, first_span: Span, later_span: Span }, + MissingOptionsForDiagnosticAttribute { attribute: &'static str, options: &'static str }, NonMetaItemDiagnosticAttribute, }