Move attribute lints to rustc_lint

This commit is contained in:
Jonathan Brouwer 2025-12-04 20:59:53 +01:00
parent 864339abf9
commit 8f59eb0177
No known key found for this signature in database
GPG key ID: 13619B051B673C52
27 changed files with 284 additions and 344 deletions

View file

@ -3945,6 +3945,7 @@ dependencies = [
"rustc_hashes",
"rustc_hir_id",
"rustc_index",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
"rustc_span",
@ -3962,7 +3963,6 @@ dependencies = [
"rustc_abi",
"rustc_arena",
"rustc_ast",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
@ -3970,6 +3970,7 @@ dependencies = [
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_lint",
"rustc_lint_defs",
"rustc_macros",
"rustc_middle",

View file

@ -14,18 +14,6 @@ attr_parsing_deprecated_item_suggestion =
.help = add `#![feature(deprecated_suggestion)]` to the crate root
.note = see #94785 for more details
attr_parsing_empty_attribute =
unused attribute
.suggestion = {$valid_without_list ->
[true] remove these parentheses
*[other] remove this 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
}
attr_parsing_empty_confusables =
expected at least one confusable name
attr_parsing_empty_link_name =
@ -119,19 +107,9 @@ attr_parsing_invalid_repr_hint_no_value =
attr_parsing_invalid_since =
'since' must be a Rust version number, such as "1.31.0"
attr_parsing_invalid_style = {$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
}
.note = This attribute does not have an `!`, which means it is applied to this {$target}
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_invalid_target_lint = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-attr_parsing_previously_accepted}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_limit_invalid =
`limit` must be a non-negative integer
@ -250,19 +228,10 @@ attr_parsing_unsupported_literal_generic =
attr_parsing_unsupported_literal_suggestion =
consider removing the prefix
attr_parsing_unused_duplicate =
unused attribute
.suggestion = remove this attribute
.note = attribute also specified here
.warn = {-attr_parsing_previously_accepted}
attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute
.note = attribute also specified here
-attr_parsing_previously_accepted =
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
attr_parsing_whole_archive_needs_static =
linking modifier `whole-archive` is only compatible with `static` linking kind

View file

@ -3,6 +3,7 @@
// SingleAttributeParser which is what we have two of here.
use rustc_hir::attrs::{AttributeKind, InlineAttr};
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use super::prelude::*;
@ -56,9 +57,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
}
}
ArgParser::NameValue(_) => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
}
}

View file

@ -2,6 +2,7 @@ use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
use rustc_hir::attrs::*;
use rustc_session::Session;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use rustc_session::parse::feature_err;
use rustc_span::kw;
use rustc_target::spec::{Arch, BinaryFormat};
@ -71,9 +72,7 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
// Specifically `#[link = "dl"]` is accepted with a FCW
// For more information, see https://github.com/rust-lang/rust/pull/143193
ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
}
_ => {

View file

@ -1,5 +1,6 @@
use rustc_errors::DiagArgValue;
use rustc_hir::attrs::MacroUseArgs;
use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS;
use super::prelude::*;
use crate::session_diagnostics::IllFormedAttributeInputLint;
@ -152,23 +153,13 @@ impl<S: Stage> SingleAttributeParser<S> for MacroExportParser {
ArgParser::NoArgs => false,
ArgParser::List(list) => {
let Some(l) = list.single() else {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
return None;
};
match l.meta_item().and_then(|i| i.path().word_sym()) {
Some(sym::local_inner_macros) => true,
_ => {
let span = cx.attr_span;
let suggestions = cx.suggestions();
cx.emit_lint(
AttributeLintKind::InvalidMacroExportArguments { suggestions },
span,
);
cx.warn_ill_formed_attribute_input(INVALID_MACRO_EXPORT_ARGUMENTS);
return None;
}
}

View file

@ -4,8 +4,6 @@ pub(super) use rustc_feature::{AttributeTemplate, template};
#[doc(hidden)]
pub(super) use rustc_hir::attrs::AttributeKind;
#[doc(hidden)]
pub(super) use rustc_hir::lints::AttributeLintKind;
#[doc(hidden)]
pub(super) use rustc_hir::{MethodKind, Target};
#[doc(hidden)]
pub(super) use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};

View file

@ -1,3 +1,5 @@
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use super::prelude::*;
pub(crate) struct IgnoreParser;
@ -20,20 +22,13 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
ArgParser::NoArgs => None,
ArgParser::NameValue(name_value) => {
let Some(str_value) = name_value.value_as_str() else {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(
AttributeLintKind::IllFormedAttributeInput { suggestions },
span,
);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
};
Some(str_value)
}
ArgParser::List(_) => {
let suggestions = cx.suggestions();
let span = cx.attr_span;
cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
cx.warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
return None;
}
},

View file

@ -11,6 +11,7 @@ use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId};
use rustc_session::Session;
use rustc_session::lint::{Lint, LintId};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use crate::AttributeParser;
@ -381,7 +382,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
/// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
/// must be delayed until after HIR is built. This method will take care of the details of
/// that.
pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) {
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
@ -389,11 +390,12 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
return;
}
let id = self.target_id;
(self.emit_lint)(AttributeLint { id, span, kind: lint });
(self.emit_lint)(AttributeLint { lint_id: LintId::of(lint), id, span, kind });
}
pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) {
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
@ -409,6 +411,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
unused_span: Span,
) {
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::UnusedDuplicate {
this: unused_span,
other: used_span,
@ -632,14 +635,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
}
pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
let attr_path = self.attr_path.clone();
let attr_path = self.attr_path.clone().to_string();
let valid_without_list = self.template.word;
self.emit_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list },
span,
);
}
pub(crate) fn warn_ill_formed_attribute_input(&mut self, lint: &'static Lint) {
let suggestions = self.suggestions();
let span = self.attr_span;
self.emit_lint(
lint,
AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None },
span,
);
}
pub(crate) fn suggestions(&self) -> Vec<String> {
let style = match self.parsed_description {
// If the outer and inner spans are equal, we are parsing an embedded attribute

View file

@ -8,6 +8,7 @@ use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLint;
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
@ -115,7 +116,12 @@ impl<'sess> AttributeParser<'sess, Early> {
OmitDoc::Skip,
std::convert::identity,
|lint| {
crate::lints::emit_attribute_lint(&lint, sess);
sess.psess.buffer_lint(
lint.lint_id.lint,
lint.span,
lint.id,
BuiltinLintDiag::AttributeLint(lint.kind),
)
},
)
}
@ -183,8 +189,13 @@ impl<'sess> AttributeParser<'sess, Early> {
sess,
stage: Early { emit_errors },
};
let mut emit_lint = |lint| {
crate::lints::emit_attribute_lint(&lint, sess);
let mut emit_lint = |lint: AttributeLint<NodeId>| {
sess.psess.buffer_lint(
lint.lint_id.lint,
lint.span,
lint.id,
BuiltinLintDiag::AttributeLint(lint.kind),
)
};
if let Some(safety) = attr_safety {
parser.check_attribute_safety(

View file

@ -97,7 +97,6 @@ mod interface;
/// like lists or name-value pairs.
pub mod parser;
mod lints;
mod safety;
mod session_diagnostics;
mod target_checking;
@ -111,7 +110,6 @@ pub use attributes::cfg_select::*;
pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};
pub use interface::AttributeParser;
pub use lints::emit_attribute_lint;
pub use session_diagnostics::ParsedDescription;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

View file

@ -1,114 +0,0 @@
use std::borrow::Cow;
use rustc_errors::{DiagArgValue, LintEmitter};
use rustc_hir::Target;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_span::sym;
use crate::session_diagnostics;
pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emitter: L) {
let AttributeLint { id, span, kind } = lint;
match kind {
&AttributeLintKind::UnusedDuplicate { this, other, warning } => lint_emitter
.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*span,
session_diagnostics::UnusedDuplicate { this, other, warning },
),
AttributeLintKind::IllFormedAttributeInput { suggestions } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT,
*id,
*span,
session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
},
);
}
AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter
.emit_node_span_lint(
rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS,
*id,
*span,
session_diagnostics::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
},
),
AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*first_span,
session_diagnostics::EmptyAttributeList {
attr_span: *first_span,
attr_path: attr_path.clone(),
valid_without_list: *valid_without_list,
},
)
}
AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter
.emit_node_span_lint(
// This check is here because `deprecated` had its own lint group and removing this would be a breaking change
if name.segments[0].name == sym::deprecated
&& ![
Target::Closure,
Target::Expression,
Target::Statement,
Target::Arm,
Target::MacroCall,
]
.contains(target)
{
rustc_session::lint::builtin::USELESS_DEPRECATED
} else {
rustc_session::lint::builtin::UNUSED_ATTRIBUTES
},
*id,
*span,
session_diagnostics::InvalidTargetLint {
name: name.clone(),
target: target.plural_name(),
applied: DiagArgValue::StrListSepByAnd(
applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
),
only,
attr_span: *span,
},
),
&AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => {
lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
*id,
*span,
session_diagnostics::InvalidAttrStyle {
name: name.clone(),
is_used_as_inner,
target_span: (!is_used_as_inner).then_some(target_span),
target,
},
)
}
&AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span,
sugg_spans: (left, right),
} => lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE,
*id,
*span,
session_diagnostics::UnsafeAttrOutsideUnsafeLint {
span: attribute_name_span,
suggestion: session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right },
},
),
}
}

View file

@ -2,6 +2,8 @@ use rustc_ast::Safety;
use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir::AttrPath;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_session::lint::LintId;
use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
use rustc_span::{Span, sym};
use crate::context::Stage;
@ -74,6 +76,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
);
} else {
emit_lint(AttributeLint {
lint_id: LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
id: target_id,
span: path_span,
kind: AttributeLintKind::UnsafeAttrOutsideUnsafe {

View file

@ -6,8 +6,8 @@ use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
};
use rustc_feature::AttributeTemplate;
use rustc_hir::{AttrPath, Target};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_hir::AttrPath;
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
use crate::fluent_generated as fluent;
@ -417,25 +417,6 @@ pub(crate) struct UnusedMultiple {
pub name: Symbol,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_unused_duplicate)]
pub(crate) struct UnusedDuplicate {
#[suggestion(code = "", applicability = "machine-applicable")]
pub this: Span,
#[note]
pub other: Span,
#[warning]
pub warning: bool,
}
// FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely.
#[derive(LintDiagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInput {
pub num_suggestions: usize,
pub suggestions: DiagArgValue,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInputLint {
@ -501,29 +482,6 @@ pub(crate) struct EmptyConfusables {
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_empty_attribute)]
#[note]
pub(crate) struct EmptyAttributeList {
#[suggestion(code = "", applicability = "machine-applicable")]
pub attr_span: Span,
pub attr_path: AttrPath,
pub valid_without_list: bool,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_invalid_target_lint)]
#[warning]
#[help]
pub(crate) struct InvalidTargetLint {
pub name: AttrPath,
pub target: &'static str,
pub applied: DiagArgValue,
pub only: &'static str,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub attr_span: Span,
}
#[derive(Diagnostic)]
#[help]
#[diag(attr_parsing_invalid_target)]
@ -803,15 +761,6 @@ pub(crate) struct UnsafeAttrOutsideUnsafe {
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_unsafe_attr_outside_unsafe)]
pub(crate) struct UnsafeAttrOutsideUnsafeLint {
#[label]
pub span: Span,
#[subdiagnostic]
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
attr_parsing_unsafe_attr_outside_unsafe_suggestion,
@ -881,16 +830,6 @@ pub(crate) struct SuffixedLiteralInAttribute {
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(attr_parsing_invalid_style)]
pub(crate) struct InvalidAttrStyle {
pub name: AttrPath,
pub is_used_as_inner: bool,
#[note]
pub target_span: Option<Span>,
pub target: Target,
}
#[derive(Diagnostic)]
#[diag(attr_parsing_empty_link_name, code = E0454)]
pub(crate) struct EmptyLinkName {

View file

@ -5,6 +5,7 @@ use rustc_errors::DiagArgValue;
use rustc_feature::Features;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{MethodKind, Target};
use rustc_span::sym;
use crate::AttributeParser;
use crate::context::{AcceptContext, Stage};
@ -102,13 +103,31 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let allowed_targets = allowed_targets.allowed_targets();
let (applied, only) = allowed_targets_applied(allowed_targets, target, cx.features);
let name = cx.attr_path.clone();
let lint = if name.segments[0].name == sym::deprecated
&& ![
Target::Closure,
Target::Expression,
Target::Statement,
Target::Arm,
Target::MacroCall,
]
.contains(&target)
{
rustc_session::lint::builtin::USELESS_DEPRECATED
} else {
rustc_session::lint::builtin::UNUSED_ATTRIBUTES
};
let attr_span = cx.attr_span;
cx.emit_lint(
lint,
AttributeLintKind::InvalidTarget {
name,
target,
name: name.to_string(),
target: target.plural_name(),
only: if only { "only " } else { "" },
applied,
attr_span,
},
attr_span,
);
@ -145,15 +164,15 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
return;
}
let lint = AttributeLintKind::InvalidStyle {
name: cx.attr_path.clone(),
let kind = AttributeLintKind::InvalidStyle {
name: cx.attr_path.to_string(),
is_used_as_inner: cx.attr_style == AttrStyle::Inner,
target,
target: target.name(),
target_span: cx.target_span,
};
let attr_span = cx.attr_span;
cx.emit_lint(lint, attr_span);
cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span);
}
}

View file

@ -11,6 +11,7 @@ use rustc_ast::{
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
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::BuiltinLintDiag;
@ -202,10 +203,10 @@ fn emit_malformed_attribute(
ILL_FORMED_ATTRIBUTE_INPUT,
span,
ast::CRATE_NODE_ID,
BuiltinLintDiag::IllFormedAttributeInput {
BuiltinLintDiag::AttributeLint(AttributeLintKind::IllFormedAttributeInput {
suggestions: suggestions.clone(),
docs: template.docs,
},
}),
);
} else {
suggestions.sort();

View file

@ -64,8 +64,8 @@ pub use rustc_error_messages::{
fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display,
};
use rustc_hashes::Hash128;
use rustc_lint_defs::LintExpectationId;
pub use rustc_lint_defs::{Applicability, listify, pluralize};
use rustc_lint_defs::{Lint, LintExpectationId};
use rustc_macros::{Decodable, Encodable};
pub use rustc_span::ErrorGuaranteed;
pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
@ -106,20 +106,6 @@ rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24);
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);
/// Used to avoid depending on `rustc_middle` in `rustc_attr_parsing`.
/// Always the `TyCtxt`.
pub trait LintEmitter: Copy {
type Id: Copy;
#[track_caller]
fn emit_node_span_lint(
self,
lint: &'static Lint,
hir_id: Self::Id,
span: impl Into<MultiSpan>,
decorator: impl for<'a> LintDiagnostic<'a, ()> + DynSend + 'static,
);
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
pub enum SuggestionStyle {
/// Hide the suggested code when displaying this suggestion inline.

View file

@ -16,6 +16,7 @@ rustc_error_messages = { path = "../rustc_error_messages" }
rustc_hashes = { path = "../rustc_hashes" }
rustc_hir_id = { path = "../rustc_hir_id" }
rustc_index = { path = "../rustc_index" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" }

View file

@ -1,8 +1,10 @@
use rustc_data_structures::fingerprint::Fingerprint;
pub use rustc_lint_defs::AttributeLintKind;
use rustc_lint_defs::LintId;
use rustc_macros::HashStable_Generic;
use rustc_span::Span;
use crate::{AttrPath, HirId, Target};
use crate::HirId;
#[derive(Debug)]
pub struct DelayedLints {
@ -24,46 +26,8 @@ pub enum DelayedLint {
#[derive(Debug, HashStable_Generic)]
pub struct AttributeLint<Id> {
pub lint_id: LintId,
pub id: Id,
pub span: Span,
pub kind: AttributeLintKind,
}
#[derive(Debug, HashStable_Generic)]
pub enum AttributeLintKind {
/// Copy of `IllFormedAttributeInput`
/// specifically for the `invalid_macro_export_arguments` lint until that is removed,
/// see <https://github.com/rust-lang/rust/pull/143857#issuecomment-3079175663>
InvalidMacroExportArguments {
suggestions: Vec<String>,
},
UnusedDuplicate {
this: Span,
other: Span,
warning: bool,
},
IllFormedAttributeInput {
suggestions: Vec<String>,
},
EmptyAttribute {
first_span: Span,
attr_path: AttrPath,
valid_without_list: bool,
},
InvalidTarget {
name: AttrPath,
target: Target,
applied: Vec<String>,
only: &'static str,
},
InvalidStyle {
name: AttrPath,
is_used_as_inner: bool,
target: Target,
target_span: Span,
},
UnsafeAttrOutsideUnsafe {
attribute_name_span: Span,
sugg_spans: (Span, Span),
},
}

View file

@ -13,7 +13,6 @@ itertools = "0.12"
rustc_abi = { path = "../rustc_abi" }
rustc_arena = { path = "../rustc_arena" }
rustc_ast = { path = "../rustc_ast" }
rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
@ -21,6 +20,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_lint = { path = "../rustc_lint" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }

View file

@ -158,7 +158,19 @@ pub fn provide(providers: &mut Providers) {
fn emit_delayed_lint(lint: &DelayedLint, tcx: TyCtxt<'_>) {
match lint {
DelayedLint::AttributeParsing(attribute_lint) => {
rustc_attr_parsing::emit_attribute_lint(attribute_lint, tcx)
tcx.node_span_lint(
attribute_lint.lint_id.lint,
attribute_lint.id,
attribute_lint.span,
|diag| {
rustc_lint::decorate_attribute_lint(
tcx.sess,
Some(tcx),
&attribute_lint.kind,
diag,
);
},
);
}
}
}

View file

@ -245,6 +245,19 @@ lint_dropping_copy_types = calls to `std::mem::drop` with a value that implement
lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing
.label = argument has type `{$arg_ty}`
lint_empty_attribute =
unused attribute
.suggestion = {$valid_without_list ->
[true] remove these parentheses
*[other] remove this 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
}
-lint_previously_accepted =
this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
lint_enum_intrinsics_mem_discriminant =
the return value of `mem::discriminant` is unspecified when called with a non-enum type
.note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum
@ -458,6 +471,17 @@ lint_invalid_reference_casting_note_book = for more information, visit <https://
lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`
lint_invalid_style = {$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
}
.note = This attribute does not have an `!`, which means it is applied to this {$target}
lint_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-lint_previously_accepted}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
lint_lintpass_by_hand = implementing `LintPass` by hand
.help = try using `declare_lint_pass!` or `impl_lint_pass!` instead
@ -890,6 +914,10 @@ lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not
lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`
lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´
lint_untranslatable_diag = diagnostics should be created using translatable messages
@ -922,6 +950,12 @@ lint_unused_def = unused {$pre}`{$def}`{$post} that must be used
lint_unused_delim = unnecessary {$delim} around {$item}
.suggestion = remove these {$delim}
lint_unused_duplicate =
unused attribute
.suggestion = remove this attribute
.note = attribute also specified here
.warn = {-lint_previously_accepted}
lint_unused_import_braces = braces around {$node} is unnecessary
lint_unused_imports = {$num_snippets ->

View file

@ -4,6 +4,7 @@ use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
use rustc_errors::{
Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion,
};
use rustc_hir::lints::AttributeLintKind;
use rustc_middle::middle::stability;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@ -301,7 +302,21 @@ pub fn decorate_builtin_lint(
BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
}
BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => {
BuiltinLintDiag::AttributeLint(kind) => decorate_attribute_lint(sess, tcx, &kind, diag),
}
}
pub fn decorate_attribute_lint(
_sess: &Session,
_tcx: Option<TyCtxt<'_>>,
kind: &AttributeLintKind,
diag: &mut Diag<'_, ()>,
) {
match kind {
&AttributeLintKind::UnusedDuplicate { this, other, warning } => {
lints::UnusedDuplicate { this, other, warning }.decorate_lint(diag)
}
AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => {
lints::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
@ -312,5 +327,42 @@ pub fn decorate_builtin_lint(
}
.decorate_lint(diag)
}
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,
}
.decorate_lint(diag)
}
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,
}
.decorate_lint(diag)
}
&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,
}
.decorate_lint(diag)
}
&AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span,
sugg_spans: (left, right),
} => lints::UnsafeAttrOutsideUnsafeLint {
span: attribute_name_span,
suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right },
}
.decorate_lint(diag),
}
}

View file

@ -129,7 +129,7 @@ use unused::*;
#[rustfmt::skip]
pub use builtin::{MissingDoc, SoftLints};
pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore};
pub use early::diagnostics::decorate_builtin_lint;
pub use early::diagnostics::{decorate_attribute_lint, decorate_builtin_lint};
pub use early::{EarlyCheckNode, check_ast_node};
pub use late::{check_crate, late_lint_mod, unerased_lint_store};
pub use levels::LintLevelsBuilder;

View file

@ -3126,3 +3126,68 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
}
}
}
#[derive(LintDiagnostic)]
#[diag(lint_empty_attribute)]
#[note]
pub(crate) struct EmptyAttributeList {
#[suggestion(code = "", applicability = "machine-applicable")]
pub attr_span: Span,
pub attr_path: String,
pub valid_without_list: bool,
}
#[derive(LintDiagnostic)]
#[diag(lint_invalid_target)]
#[warning]
#[help]
pub(crate) struct InvalidTargetLint {
pub name: String,
pub target: &'static str,
pub applied: DiagArgValue,
pub only: &'static str,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub attr_span: Span,
}
#[derive(LintDiagnostic)]
#[diag(lint_invalid_style)]
pub(crate) struct InvalidAttrStyle {
pub name: String,
pub is_used_as_inner: bool,
#[note]
pub target_span: Option<Span>,
pub target: &'static str,
}
#[derive(LintDiagnostic)]
#[diag(lint_unused_duplicate)]
pub(crate) struct UnusedDuplicate {
#[suggestion(code = "", applicability = "machine-applicable")]
pub this: Span,
#[note]
pub other: Span,
#[warning]
pub warning: bool,
}
#[derive(LintDiagnostic)]
#[diag(lint_unsafe_attr_outside_unsafe)]
pub(crate) struct UnsafeAttrOutsideUnsafeLint {
#[label]
pub span: Span,
#[subdiagnostic]
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
lint_unsafe_attr_outside_unsafe_suggestion,
applicability = "machine-applicable"
)]
pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
#[suggestion_part(code = "unsafe(")]
pub left: Span,
#[suggestion_part(code = ")")]
pub right: Span,
}

View file

@ -696,10 +696,42 @@ pub enum BuiltinLintDiag {
extern_crate: Symbol,
local_crate: Symbol,
},
AttributeLint(AttributeLintKind),
}
#[derive(Debug, HashStable_Generic)]
pub enum AttributeLintKind {
UnusedDuplicate {
this: Span,
other: Span,
warning: bool,
},
IllFormedAttributeInput {
suggestions: Vec<String>,
docs: Option<&'static str>,
},
EmptyAttribute {
first_span: Span,
attr_path: String,
valid_without_list: bool,
},
InvalidTarget {
name: String,
target: &'static str,
applied: Vec<String>,
only: &'static str,
attr_span: Span,
},
InvalidStyle {
name: String,
is_used_as_inner: bool,
target: &'static str,
target_span: Span,
},
UnsafeAttrOutsideUnsafe {
attribute_name_span: Span,
sugg_spans: (Span, Span),
},
}
pub type RegisteredTools = FxIndexSet<Ident>;

View file

@ -30,7 +30,7 @@ use rustc_data_structures::sync::{
self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal,
};
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, LintEmitter, MultiSpan,
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan,
};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
@ -1534,20 +1534,6 @@ pub struct TyCtxt<'tcx> {
gcx: &'tcx GlobalCtxt<'tcx>,
}
impl<'tcx> LintEmitter for TyCtxt<'tcx> {
type Id = HirId;
fn emit_node_span_lint(
self,
lint: &'static Lint,
hir_id: HirId,
span: impl Into<MultiSpan>,
decorator: impl for<'a> LintDiagnostic<'a, ()>,
) {
self.emit_node_span_lint(lint, hir_id, span, decorator);
}
}
// Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. Its
// field are asserted to implement these traits below, so this is trivially safe, and it greatly
// speeds-up compilation of this crate and its dependents.

View file

@ -6,7 +6,6 @@ use std::sync::atomic::AtomicBool;
use std::{env, io};
use rand::{RngCore, rng};
use rustc_ast::NodeId;
use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN};
use rustc_data_structures::flock;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
@ -22,7 +21,7 @@ use rustc_errors::timings::TimingSectionHandler;
use rustc_errors::translation::Translator;
use rustc_errors::{
Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
LintEmitter, TerminalUrl, fallback_fluent_bundle,
TerminalUrl, fallback_fluent_bundle,
};
use rustc_hir::limit::Limit;
use rustc_macros::HashStable_Generic;
@ -160,20 +159,6 @@ pub struct Session {
pub invocation_temp: Option<String>,
}
impl LintEmitter for &'_ Session {
type Id = NodeId;
fn emit_node_span_lint(
self,
lint: &'static rustc_lint_defs::Lint,
node_id: Self::Id,
span: impl Into<rustc_errors::MultiSpan>,
decorator: impl for<'a> rustc_errors::LintDiagnostic<'a, ()> + DynSend + 'static,
) {
self.psess.buffer_lint(lint, span, node_id, decorator);
}
}
#[derive(Clone, Copy)]
pub enum CodegenUnits {
/// Specified by the user. In this case we try fairly hard to produce the