mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Port #[allow], #[deny], #[expect], #[forbid], #[warn] to attr parser
also changes method `parse_limited_all` to take Iterator as an input, to avoid needing to do expensive allocation
This commit is contained in:
committed by
Jonathan Brouwer
parent
cd88c395c8
commit
345a3eb08b
@@ -0,0 +1,369 @@
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::HashIgnoredAttrId;
|
||||
use rustc_hir::attrs::{LintAttribute, LintAttributeKind, LintInstance};
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_hir::target::GenericParamKind;
|
||||
use rustc_session::DynLintStore;
|
||||
use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
|
||||
use rustc_session::lint::{CheckLintNameResult, LintId};
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::attributes::AcceptFn;
|
||||
use crate::session_diagnostics::UnknownToolInScopedLint;
|
||||
|
||||
pub(crate) trait Lint {
|
||||
const KIND: LintAttributeKind;
|
||||
const ATTR_SYMBOL: Symbol = Self::KIND.symbol();
|
||||
}
|
||||
|
||||
pub(crate) struct Allow;
|
||||
|
||||
impl Lint for Allow {
|
||||
const KIND: LintAttributeKind = LintAttributeKind::Allow;
|
||||
}
|
||||
pub(crate) struct Deny;
|
||||
|
||||
impl Lint for Deny {
|
||||
const KIND: LintAttributeKind = LintAttributeKind::Deny;
|
||||
}
|
||||
pub(crate) struct Expect;
|
||||
|
||||
impl Lint for Expect {
|
||||
const KIND: LintAttributeKind = LintAttributeKind::Expect;
|
||||
}
|
||||
pub(crate) struct Forbid;
|
||||
|
||||
impl Lint for Forbid {
|
||||
const KIND: LintAttributeKind = LintAttributeKind::Forbid;
|
||||
}
|
||||
pub(crate) struct Warn;
|
||||
|
||||
impl Lint for Warn {
|
||||
const KIND: LintAttributeKind = LintAttributeKind::Warn;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct LintParser {
|
||||
lint_attrs: ThinVec<LintAttribute>,
|
||||
}
|
||||
|
||||
trait Mapping<S: Stage> {
|
||||
const MAPPING: (&'static [Symbol], AttributeTemplate, AcceptFn<LintParser, S>);
|
||||
}
|
||||
impl<S: Stage, T: Lint> Mapping<S> for T {
|
||||
const MAPPING: (&'static [Symbol], AttributeTemplate, AcceptFn<LintParser, S>) = (
|
||||
&[T::ATTR_SYMBOL],
|
||||
template!(
|
||||
List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
|
||||
"https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
|
||||
),
|
||||
|this, cx, args| {
|
||||
if let Some(lint_attr) = validate_lint_attr::<T, S>(cx, args) {
|
||||
this.lint_attrs.push(lint_attr);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for LintParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> =
|
||||
&[Allow::MAPPING, Deny::MAPPING, Expect::MAPPING, Forbid::MAPPING, Warn::MAPPING];
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets = {
|
||||
use super::prelude::{Allow, Warn};
|
||||
AllowedTargets::AllowList(&[
|
||||
Allow(Target::ExternCrate),
|
||||
Allow(Target::Use),
|
||||
Allow(Target::Static),
|
||||
Allow(Target::Const),
|
||||
Allow(Target::Fn),
|
||||
Allow(Target::Closure),
|
||||
Allow(Target::Mod),
|
||||
Allow(Target::ForeignMod),
|
||||
Allow(Target::GlobalAsm),
|
||||
Allow(Target::TyAlias),
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Variant),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Field),
|
||||
Allow(Target::Union),
|
||||
Allow(Target::Trait),
|
||||
Allow(Target::TraitAlias),
|
||||
Allow(Target::Impl { of_trait: false }),
|
||||
Allow(Target::Impl { of_trait: true }),
|
||||
Allow(Target::Expression),
|
||||
Allow(Target::Statement),
|
||||
Allow(Target::Arm),
|
||||
Allow(Target::AssocConst),
|
||||
Allow(Target::Method(MethodKind::Inherent)),
|
||||
Allow(Target::Method(MethodKind::Trait { body: false })),
|
||||
Allow(Target::Method(MethodKind::Trait { body: true })),
|
||||
Allow(Target::Method(MethodKind::TraitImpl)),
|
||||
Allow(Target::AssocTy),
|
||||
Allow(Target::ForeignFn),
|
||||
Allow(Target::ForeignStatic),
|
||||
Allow(Target::ForeignTy),
|
||||
Allow(Target::MacroDef),
|
||||
Allow(Target::Param),
|
||||
Allow(Target::PatField),
|
||||
Allow(Target::ExprField),
|
||||
Allow(Target::Crate),
|
||||
Allow(Target::Delegation { mac: false }),
|
||||
Allow(Target::Delegation { mac: true }),
|
||||
Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: false }),
|
||||
Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: false }),
|
||||
Allow(Target::GenericParam { kind: GenericParamKind::Const, has_default: false }),
|
||||
Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }),
|
||||
Allow(Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: true }),
|
||||
Allow(Target::GenericParam { kind: GenericParamKind::Const, has_default: true }),
|
||||
Warn(Target::MacroCall),
|
||||
])
|
||||
};
|
||||
|
||||
fn finalize(mut self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if !self.lint_attrs.is_empty() {
|
||||
// Sort to ensure correct order operations later
|
||||
self.lint_attrs.sort_by(|a, b| a.attr_span.cmp(&b.attr_span));
|
||||
Some(AttributeKind::LintAttributes(self.lint_attrs))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn validate_lint_attr<T: Lint, S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
args: &ArgParser,
|
||||
) -> Option<LintAttribute> {
|
||||
let Some(lint_store) = cx.sess.lint_store.as_ref().map(|store| store.to_owned()) else {
|
||||
unreachable!("lint_store required while parsing attributes");
|
||||
};
|
||||
let lint_store = lint_store.as_ref();
|
||||
let Some(list) = args.list() else {
|
||||
cx.expected_list(cx.inner_span, args);
|
||||
return None;
|
||||
};
|
||||
let mut list = list.mixed().peekable();
|
||||
|
||||
let mut skip_unused_check = false;
|
||||
let mut errored = false;
|
||||
let mut reason = None;
|
||||
let mut lint_instances = ThinVec::new();
|
||||
let mut lint_index = 0;
|
||||
let targeting_crate = matches!(cx.target, Target::Crate);
|
||||
while let Some(item) = list.next() {
|
||||
let Some(meta_item) = item.meta_item() else {
|
||||
cx.expected_identifier(item.span());
|
||||
errored = true;
|
||||
continue;
|
||||
};
|
||||
|
||||
match meta_item.args() {
|
||||
ArgParser::NameValue(nv_parser) if meta_item.path().word_is(sym::reason) => {
|
||||
//FIXME replace this with duplicate check?
|
||||
if list.peek().is_some() {
|
||||
cx.expected_nv_as_last_argument(meta_item.span(), sym::reason);
|
||||
errored = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let val_lit = nv_parser.value_as_lit();
|
||||
let LitKind::Str(reason_sym, _) = val_lit.kind else {
|
||||
cx.expected_string_literal(nv_parser.value_span, Some(val_lit));
|
||||
errored = true;
|
||||
continue;
|
||||
};
|
||||
reason = Some(reason_sym);
|
||||
}
|
||||
ArgParser::NameValue(_) => {
|
||||
cx.expected_specific_argument(meta_item.span(), &[sym::reason]);
|
||||
errored = true;
|
||||
}
|
||||
ArgParser::List(list) => {
|
||||
cx.expected_no_args(list.span);
|
||||
errored = true;
|
||||
}
|
||||
ArgParser::NoArgs => {
|
||||
skip_unused_check = true;
|
||||
let mut segments = meta_item.path().segments();
|
||||
|
||||
let Some(tool_or_name) = segments.next() else {
|
||||
unreachable!("first segment should always exist");
|
||||
};
|
||||
|
||||
let rest = segments.collect::<Vec<_>>();
|
||||
let (tool_name, tool_span, name): (Option<Symbol>, Option<Span>, _) =
|
||||
if rest.is_empty() {
|
||||
let name = tool_or_name.name;
|
||||
(None, None, name.to_string())
|
||||
} else {
|
||||
let tool = tool_or_name;
|
||||
let name = rest
|
||||
.into_iter()
|
||||
.map(|ident| ident.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("::");
|
||||
(Some(tool.name), Some(tool.span), name)
|
||||
};
|
||||
|
||||
let meta_item_span = meta_item.span();
|
||||
let original_name = Symbol::intern(&name);
|
||||
let mut full_name = tool_name
|
||||
.map(|tool| Symbol::intern(&format!("{tool}::{}", original_name)))
|
||||
.unwrap_or(original_name);
|
||||
|
||||
if let Some(ids) = check_lint(
|
||||
cx,
|
||||
lint_store,
|
||||
original_name,
|
||||
&mut full_name,
|
||||
tool_name,
|
||||
tool_span,
|
||||
meta_item_span,
|
||||
) {
|
||||
if !targeting_crate && ids.iter().any(|lint_id| lint_id.lint.crate_level_only) {
|
||||
cx.emit_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
AttributeLintKind::IgnoredUnlessCrateSpecified {
|
||||
level: T::ATTR_SYMBOL,
|
||||
name: original_name,
|
||||
},
|
||||
meta_item_span,
|
||||
);
|
||||
}
|
||||
lint_instances.extend(ids.into_iter().map(|id| {
|
||||
LintInstance::new(full_name, id.to_string(), meta_item_span, lint_index)
|
||||
}));
|
||||
}
|
||||
lint_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !skip_unused_check && !errored && lint_instances.is_empty() {
|
||||
cx.warn_empty_attribute(cx.attr_span);
|
||||
}
|
||||
|
||||
(!errored).then_some(LintAttribute {
|
||||
reason,
|
||||
lint_instances,
|
||||
attr_span: cx.attr_span,
|
||||
attr_style: cx.attr_style,
|
||||
attr_id: HashIgnoredAttrId { attr_id: cx.attr_id },
|
||||
kind: T::KIND,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_lint<'a, S: Stage>(
|
||||
cx: &mut AcceptContext<'_, '_, S>,
|
||||
lint_store: &'a dyn DynLintStore,
|
||||
original_name: Symbol,
|
||||
full_name: &mut Symbol,
|
||||
tool_name: Option<Symbol>,
|
||||
tool_span: Option<Span>,
|
||||
span: Span,
|
||||
) -> Option<&'a [LintId]> {
|
||||
let Some(tools) = cx.tools else {
|
||||
unreachable!("tools required while parsing attributes");
|
||||
};
|
||||
if tools.is_empty() {
|
||||
unreachable!("tools should never be empty")
|
||||
}
|
||||
|
||||
match lint_store.check_lint_name(original_name.as_str(), tool_name, tools) {
|
||||
CheckLintNameResult::Ok(ids) => Some(ids),
|
||||
CheckLintNameResult::Tool(ids, new_lint_name) => {
|
||||
let _name = match new_lint_name {
|
||||
None => original_name,
|
||||
Some(new_lint_name) => {
|
||||
let new_lint_name = Symbol::intern(&new_lint_name);
|
||||
cx.emit_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
AttributeLintKind::DeprecatedLintName {
|
||||
name: *full_name,
|
||||
suggestion: span,
|
||||
replace: new_lint_name,
|
||||
},
|
||||
span,
|
||||
);
|
||||
new_lint_name
|
||||
}
|
||||
};
|
||||
Some(ids)
|
||||
}
|
||||
|
||||
CheckLintNameResult::MissingTool => {
|
||||
// If `MissingTool` is returned, then either the lint does not
|
||||
// exist in the tool or the code was not compiled with the tool and
|
||||
// therefore the lint was never added to the `LintStore`. To detect
|
||||
// this is the responsibility of the lint tool.
|
||||
None
|
||||
}
|
||||
|
||||
CheckLintNameResult::NoTool => {
|
||||
cx.emit_err(UnknownToolInScopedLint {
|
||||
span: tool_span,
|
||||
tool_name: tool_name.unwrap(),
|
||||
full_lint_name: *full_name,
|
||||
is_nightly_build: cx.sess.is_nightly_build(),
|
||||
});
|
||||
None
|
||||
}
|
||||
|
||||
CheckLintNameResult::Renamed(replace) => {
|
||||
cx.emit_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
AttributeLintKind::RenamedLint { name: *full_name, replace, suggestion: span },
|
||||
span,
|
||||
);
|
||||
|
||||
// Since it was renamed, and we have emitted the warning
|
||||
// we replace the "full_name", to ensure we don't get notes with:
|
||||
// `#[allow(NEW_NAME)]` implied by `#[allow(OLD_NAME)]`
|
||||
// Other lints still have access to the original name as the user wrote it,
|
||||
// through `original_name`
|
||||
*full_name = replace;
|
||||
|
||||
// If this lint was renamed, apply the new lint instead of ignoring the
|
||||
// attribute. Ignore any errors or warnings that happen because the new
|
||||
// name is inaccurate.
|
||||
// NOTE: `new_name` already includes the tool name, so we don't
|
||||
// have to add it again.
|
||||
match lint_store.check_lint_name(replace.as_str(), None, tools) {
|
||||
CheckLintNameResult::Ok(ids) => Some(ids),
|
||||
_ => panic!("renamed lint does not exist: {replace}"),
|
||||
}
|
||||
}
|
||||
|
||||
CheckLintNameResult::RenamedToolLint(new_name) => {
|
||||
cx.emit_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
AttributeLintKind::RenamedLint {
|
||||
name: *full_name,
|
||||
replace: new_name,
|
||||
suggestion: span,
|
||||
},
|
||||
span,
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
CheckLintNameResult::Removed(reason) => {
|
||||
cx.emit_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
AttributeLintKind::RemovedLint { name: *full_name, reason },
|
||||
span,
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
CheckLintNameResult::NoLint(suggestion) => {
|
||||
cx.emit_lint(
|
||||
UNKNOWN_LINTS,
|
||||
AttributeLintKind::UnknownLint { name: *full_name, suggestion, span },
|
||||
span,
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@
|
||||
pub(crate) mod inline;
|
||||
pub(crate) mod instruction_set;
|
||||
pub(crate) mod link_attrs;
|
||||
pub(crate) mod lint;
|
||||
pub(crate) mod lint_helpers;
|
||||
pub(crate) mod loop_match;
|
||||
pub(crate) mod macro_attrs;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
use crate::attributes::inline::*;
|
||||
use crate::attributes::instruction_set::*;
|
||||
use crate::attributes::link_attrs::*;
|
||||
use crate::attributes::lint::*;
|
||||
use crate::attributes::lint_helpers::*;
|
||||
use crate::attributes::loop_match::*;
|
||||
use crate::attributes::macro_attrs::*;
|
||||
@@ -149,6 +150,7 @@ mod late {
|
||||
ConfusablesParser,
|
||||
ConstStabilityParser,
|
||||
DocParser,
|
||||
LintParser,
|
||||
MacroUseParser,
|
||||
NakedParser,
|
||||
OnConstParser,
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::token::DocFragmentKind;
|
||||
use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_feature::{AttributeTemplate, Features};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::lints::AttributeLintKind;
|
||||
use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
|
||||
use rustc_middle::ty::RegisteredTools;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::lint::{BuiltinLintDiag, LintId};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
|
||||
|
||||
use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage};
|
||||
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
|
||||
@@ -22,7 +22,7 @@
|
||||
/// Context created once, for example as part of the ast lowering
|
||||
/// context, through which all attributes can be lowered.
|
||||
pub struct AttributeParser<'sess, S: Stage = Late> {
|
||||
pub(crate) tools: Option<&'sess RegisteredTools>,
|
||||
pub(crate) tools: Option<&'sess FxIndexSet<Ident>>,
|
||||
pub(crate) features: Option<&'sess Features>,
|
||||
pub(crate) sess: &'sess Session,
|
||||
pub(crate) stage: S,
|
||||
@@ -49,7 +49,7 @@ impl<'sess> AttributeParser<'sess, Early> {
|
||||
/// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
|
||||
/// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
|
||||
///
|
||||
/// Due to this function not taking in RegisteredTools, *do not* use this for parsing any lint attributes
|
||||
/// Due to this function not taking in RegisteredTools (`FxIndexSet<Ident>`), *do not* use this for parsing any lint attributes
|
||||
pub fn parse_limited(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
@@ -72,7 +72,7 @@ pub fn parse_limited(
|
||||
/// This does the same as `parse_limited`, except it has a `should_emit` parameter which allows it to emit errors.
|
||||
/// Usually you want `parse_limited`, which emits no errors.
|
||||
///
|
||||
/// Due to this function not taking in RegisteredTools, *do not* use this for parsing any lint attributes
|
||||
/// Due to this function not taking in RegisteredTools (`FxIndexSet<Ident>`), *do not* use this for parsing any lint attributes
|
||||
pub fn parse_limited_should_emit(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
@@ -104,16 +104,16 @@ pub fn parse_limited_should_emit(
|
||||
/// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
|
||||
/// crash if you tried to do so through [`parse_limited_all`](Self::parse_limited_all).
|
||||
/// Therefore, if `parse_only` is None, then features *must* be provided.
|
||||
pub fn parse_limited_all(
|
||||
pub fn parse_limited_all<'a>(
|
||||
sess: &'sess Session,
|
||||
attrs: &[ast::Attribute],
|
||||
attrs: impl IntoIterator<Item = &'a ast::Attribute>,
|
||||
parse_only: Option<Symbol>,
|
||||
target: Target,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: ShouldEmit,
|
||||
tools: Option<&'sess RegisteredTools>,
|
||||
tools: Option<&'sess FxIndexSet<Ident>>,
|
||||
) -> Vec<Attribute> {
|
||||
let mut p = Self { features, tools, parse_only, sess, stage: Early { emit_errors } };
|
||||
p.parse_attribute_list(
|
||||
@@ -133,6 +133,32 @@ pub fn parse_limited_all(
|
||||
)
|
||||
}
|
||||
|
||||
/// This method provides the same functionality as [`parse_limited_all`](Self::parse_limited_all) except filtered,
|
||||
/// making sure that only allow-listed symbols are parsed
|
||||
pub fn parse_limited_all_filtered<'a>(
|
||||
sess: &'sess Session,
|
||||
attrs: impl IntoIterator<Item = &'a ast::Attribute>,
|
||||
filter: &[Symbol],
|
||||
target: Target,
|
||||
target_span: Span,
|
||||
target_node_id: NodeId,
|
||||
features: Option<&'sess Features>,
|
||||
emit_errors: ShouldEmit,
|
||||
tools: &'sess FxIndexSet<Ident>,
|
||||
) -> Vec<Attribute> {
|
||||
Self::parse_limited_all(
|
||||
sess,
|
||||
attrs.into_iter().filter(|attr| attr.has_any_name(filter)),
|
||||
None,
|
||||
target,
|
||||
target_span,
|
||||
target_node_id,
|
||||
features,
|
||||
emit_errors,
|
||||
Some(tools),
|
||||
)
|
||||
}
|
||||
|
||||
/// This method parses a single attribute, using `parse_fn`.
|
||||
/// This is useful if you already know what exact attribute this is, and want to parse it.
|
||||
pub fn parse_single<T>(
|
||||
@@ -236,7 +262,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
pub fn new(
|
||||
sess: &'sess Session,
|
||||
features: &'sess Features,
|
||||
tools: &'sess RegisteredTools,
|
||||
tools: &'sess FxIndexSet<Ident>,
|
||||
stage: S,
|
||||
) -> Self {
|
||||
Self { features: Some(features), tools: Some(tools), parse_only: None, sess, stage }
|
||||
@@ -262,9 +288,9 @@ pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
|
||||
///
|
||||
/// `target_span` is the span of the thing this list of attributes is applied to,
|
||||
/// and when `omit_doc` is set, doc attributes are filtered out.
|
||||
pub fn parse_attribute_list(
|
||||
pub fn parse_attribute_list<'a>(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
attrs: impl IntoIterator<Item = &'a ast::Attribute>,
|
||||
target_span: Span,
|
||||
target: Target,
|
||||
omit_doc: OmitDoc,
|
||||
@@ -280,9 +306,9 @@ pub fn parse_attribute_list(
|
||||
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
|
||||
let mut early_parsed_state = EarlyParsedState::default();
|
||||
|
||||
let mut finalizers: Vec<&FinalizeFn<S>> = Vec::with_capacity(attrs.len());
|
||||
let mut finalizers: Vec<&FinalizeFn<S>> = Vec::new();
|
||||
|
||||
for attr in attrs {
|
||||
for attr in attrs.into_iter() {
|
||||
// If we're only looking for a single attribute, skip all the ones we don't care about.
|
||||
if let Some(expected) = self.parse_only {
|
||||
if !attr.has_name(expected) {
|
||||
@@ -419,7 +445,9 @@ pub fn parse_attribute_list(
|
||||
|
||||
let attr = Attribute::Unparsed(Box::new(attr));
|
||||
|
||||
if self.tools.is_some_and(|tools|tools.iter().any(|tool|tool.name == parts[0]))
|
||||
if self
|
||||
.tools
|
||||
.is_some_and(|tools| tools.iter().any(|tool| tool.name == parts[0]))
|
||||
{
|
||||
attributes.push(attr);
|
||||
} else {
|
||||
|
||||
@@ -1138,3 +1138,14 @@ pub(crate) struct UnstableAttrForAlreadyStableFeature {
|
||||
#[label("the stability attribute annotates this item")]
|
||||
pub item_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$full_lint_name}`", code = E0710)]
|
||||
pub(crate) struct UnknownToolInScopedLint {
|
||||
#[primary_span]
|
||||
pub span: Option<Span>,
|
||||
pub tool_name: Symbol,
|
||||
pub full_lint_name: Symbol,
|
||||
#[help("add `#![register_tool({$tool_name})]` to the crate root")]
|
||||
pub is_nightly_build: bool,
|
||||
}
|
||||
|
||||
@@ -15,14 +15,16 @@
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::hygiene::Transparency;
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
|
||||
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, sym};
|
||||
pub use rustc_target::spec::SanitizerSet;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::attrs::diagnostic::*;
|
||||
use crate::attrs::pretty_printing::PrintAttribute;
|
||||
use crate::limit::Limit;
|
||||
use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
|
||||
use crate::{
|
||||
DefaultBodyStability, HashIgnoredAttrId, PartialConstStability, RustcVersion, Stability,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub enum EiiImplResolution {
|
||||
@@ -894,6 +896,143 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
|
||||
pub struct LintAttribute {
|
||||
/// See RFC #2383
|
||||
pub reason: Option<Symbol>,
|
||||
pub kind: LintAttributeKind,
|
||||
pub attr_style: AttrStyle,
|
||||
pub attr_span: Span,
|
||||
/// Needed by `LintExpectationId` to track fulfilled expectations
|
||||
pub attr_id: HashIgnoredAttrId,
|
||||
pub lint_instances: ThinVec<LintInstance>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
|
||||
pub struct LintInstance {
|
||||
/// The span of the `MetaItem` that produced this `LintInstance`
|
||||
span: Span,
|
||||
/// The fully resolved name of the lint
|
||||
/// for renamed lints, this gets updated to match the new name
|
||||
lint_name: Symbol,
|
||||
/// The raw identifier for resolving this lint
|
||||
/// if this is none, lint_name never diffed from the original
|
||||
/// name after parsing, original_name.unwrap_or(self.lint_name)
|
||||
original_name: Option<Symbol>,
|
||||
/// Index of this lint, used to keep track of lint groups
|
||||
lint_index: usize,
|
||||
kind: LintAttrTool,
|
||||
}
|
||||
|
||||
impl fmt::Display for LintInstance {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.full_lint().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl LintInstance {
|
||||
pub fn new(
|
||||
original_name: Symbol,
|
||||
long_lint_name: String,
|
||||
span: Span,
|
||||
lint_index: usize,
|
||||
) -> Self {
|
||||
let original_name = (original_name.as_str() != long_lint_name).then_some(original_name);
|
||||
let mut tool_name = None;
|
||||
|
||||
let lint_name = match long_lint_name.split_once("::") {
|
||||
Some((new_tool_name, lint_name)) => {
|
||||
tool_name = Some(Symbol::intern(new_tool_name));
|
||||
Symbol::intern(lint_name)
|
||||
}
|
||||
None => Symbol::intern(&long_lint_name),
|
||||
};
|
||||
let kind = match tool_name {
|
||||
Some(tool_name) => {
|
||||
let full_lint = Symbol::intern(&format!("{tool_name}::{lint_name}",));
|
||||
LintAttrTool::Present { tool_name, full_lint }
|
||||
}
|
||||
None => LintAttrTool::NoTool,
|
||||
};
|
||||
|
||||
Self { original_name, span, lint_index, lint_name, kind }
|
||||
}
|
||||
|
||||
pub fn full_lint(&self) -> Symbol {
|
||||
match self.kind {
|
||||
LintAttrTool::Present { full_lint, .. } => full_lint,
|
||||
LintAttrTool::NoTool => self.lint_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
pub fn lint_index(&self) -> usize {
|
||||
self.lint_index
|
||||
}
|
||||
|
||||
pub fn lint_name(&self) -> Symbol {
|
||||
self.lint_name
|
||||
}
|
||||
|
||||
pub fn original_name_without_tool(&self) -> Symbol {
|
||||
let full_original_lint_name = self.original_lint_name();
|
||||
match self.kind {
|
||||
LintAttrTool::Present { tool_name, .. } => Symbol::intern(
|
||||
full_original_lint_name
|
||||
.as_str()
|
||||
.trim_start_matches(tool_name.as_str())
|
||||
.trim_start_matches("::"),
|
||||
),
|
||||
LintAttrTool::NoTool => full_original_lint_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tool_name(&self) -> Option<Symbol> {
|
||||
if let LintAttrTool::Present { tool_name, .. } = self.kind { Some(tool_name) } else { None }
|
||||
}
|
||||
|
||||
pub fn tool_is_named(&self, other: Symbol) -> bool {
|
||||
self.tool_name().is_some_and(|tool_name| tool_name == other)
|
||||
}
|
||||
|
||||
pub fn original_lint_name(&self) -> Symbol {
|
||||
match self.original_name {
|
||||
Some(name) => name,
|
||||
None => self.full_lint(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PrintAttribute, Encodable, Decodable, HashStable_Generic)]
|
||||
enum LintAttrTool {
|
||||
Present { tool_name: Symbol, full_lint: Symbol },
|
||||
NoTool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq)]
|
||||
pub enum LintAttributeKind {
|
||||
Allow,
|
||||
Deny,
|
||||
Expect,
|
||||
Forbid,
|
||||
Warn,
|
||||
}
|
||||
|
||||
impl LintAttributeKind {
|
||||
pub const fn symbol(&self) -> Symbol {
|
||||
match self {
|
||||
Self::Allow => sym::allow,
|
||||
Self::Deny => sym::deny,
|
||||
Self::Expect => sym::expect,
|
||||
Self::Forbid => sym::forbid,
|
||||
Self::Warn => sym::warn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents parsed *built-in* inert attributes.
|
||||
///
|
||||
/// ## Overview
|
||||
@@ -1097,6 +1236,9 @@ pub enum AttributeKind {
|
||||
/// Represents `#[linkage]`.
|
||||
Linkage(Linkage, Span),
|
||||
|
||||
/// Represents `#[allow]`, `#[expect]`, `#[warn]`, `#[deny]`, `#[forbid]`
|
||||
LintAttributes(ThinVec<LintAttribute>),
|
||||
|
||||
/// Represents `#[loop_match]`.
|
||||
LoopMatch(Span),
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
|
||||
LinkOrdinal { .. } => No,
|
||||
LinkSection { .. } => Yes, // Needed for rustdoc
|
||||
Linkage(..) => No,
|
||||
LintAttributes { .. } => No,
|
||||
LoopMatch(..) => No,
|
||||
MacroEscape(..) => No,
|
||||
MacroExport { .. } => Yes,
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
use rustc_target::spec::SanitizerSet;
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use crate::HashIgnoredAttrId;
|
||||
use crate::attrs::LintInstance;
|
||||
use crate::limit::Limit;
|
||||
|
||||
/// This trait is used to print attributes in `rustc_hir_pretty`.
|
||||
@@ -191,8 +193,8 @@ fn print_attribute(&self, p: &mut Printer) {
|
||||
}
|
||||
|
||||
print_tup!(A B C D E F G H);
|
||||
print_skip!(Span, (), ErrorGuaranteed, AttrId);
|
||||
print_disp!(u8, u16, u32, u128, usize, bool, NonZero<u32>, Limit);
|
||||
print_skip!(Span, (), ErrorGuaranteed, AttrId, HashIgnoredAttrId);
|
||||
print_disp!(u8, u16, u32, u128, usize, bool, NonZero<u32>, Limit, LintInstance);
|
||||
print_debug!(
|
||||
Symbol,
|
||||
Ident,
|
||||
|
||||
@@ -1304,6 +1304,19 @@ pub fn is_parsed_attr(&self) -> bool {
|
||||
Attribute::Unparsed(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_span_without_desugaring_kind(&self) -> bool {
|
||||
let span = match self {
|
||||
Attribute::Unparsed(attr) => attr.span,
|
||||
Attribute::Parsed(AttributeKind::Deprecated { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs)) => {
|
||||
return sub_attrs.iter().any(|attr| attr.attr_span.desugaring_kind().is_none());
|
||||
}
|
||||
Attribute::Parsed(attr) => panic!("can't get span of parsed attr: {:?}", attr),
|
||||
};
|
||||
|
||||
span.desugaring_kind().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributeExt for Attribute {
|
||||
@@ -1378,6 +1391,7 @@ fn span(&self) -> Span {
|
||||
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::Deprecated { span, .. }) => *span,
|
||||
Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) => cfgs[0].1,
|
||||
Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs)) => sub_attrs[0].attr_span,
|
||||
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, listify, pluralize,
|
||||
struct_span_code_err,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal};
|
||||
use rustc_hir::{self as hir, Attribute, ExprKind, HirId, QPath, find_attr, is_range_literal};
|
||||
use rustc_hir_analysis::NoVariantNamed;
|
||||
use rustc_hir_analysis::errors::NoFieldOnType;
|
||||
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _;
|
||||
@@ -56,26 +55,21 @@
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(crate) fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
|
||||
// For the purpose of rendering suggestions, disregard attributes
|
||||
// that originate from desugaring of any kind. For example, `x?`
|
||||
// desugars to `#[allow(unreachable_code)] match ...`. Failing to
|
||||
// ignore the prefix attribute in the desugaring would cause this
|
||||
// suggestion:
|
||||
//
|
||||
// let y: u32 = x?.try_into().unwrap();
|
||||
// ++++++++++++++++++++
|
||||
//
|
||||
// to be rendered as:
|
||||
//
|
||||
// let y: u32 = (x?).try_into().unwrap();
|
||||
// + +++++++++++++++++++++
|
||||
let has_attr = |id: HirId| -> bool {
|
||||
for attr in self.tcx.hir_attrs(id) {
|
||||
// For the purpose of rendering suggestions, disregard attributes
|
||||
// that originate from desugaring of any kind. For example, `x?`
|
||||
// desugars to `#[allow(unreachable_code)] match ...`. Failing to
|
||||
// ignore the prefix attribute in the desugaring would cause this
|
||||
// suggestion:
|
||||
//
|
||||
// let y: u32 = x?.try_into().unwrap();
|
||||
// ++++++++++++++++++++
|
||||
//
|
||||
// to be rendered as:
|
||||
//
|
||||
// let y: u32 = (x?).try_into().unwrap();
|
||||
// + +++++++++++++++++++++
|
||||
if attr.span().desugaring_kind().is_none() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
self.tcx.hir_attrs(id).iter().any(Attribute::has_span_without_desugaring_kind)
|
||||
};
|
||||
|
||||
// Special case: range expressions are desugared to struct literals in HIR,
|
||||
|
||||
@@ -369,7 +369,7 @@ pub fn check_lint_name(
|
||||
}
|
||||
}
|
||||
match self.by_name.get(&complete_name) {
|
||||
Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()),
|
||||
Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(Symbol::intern(new_name)),
|
||||
Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()),
|
||||
None => match self.lint_groups.get(&*complete_name) {
|
||||
// If neither the lint, nor the lint group exists check if there is a `clippy::`
|
||||
@@ -818,12 +818,7 @@ pub fn get_associated_type(
|
||||
/// be used for pretty-printing HIR by rustc_hir_pretty.
|
||||
pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
|
||||
let has_attr = |id: hir::HirId| -> bool {
|
||||
for attr in self.tcx.hir_attrs(id) {
|
||||
if attr.span().desugaring_kind().is_none() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
self.tcx.hir_attrs(id).iter().any(hir::Attribute::has_span_without_desugaring_kind)
|
||||
};
|
||||
expr.precedence(&has_attr)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
use rustc_middle::ty::{RegisteredTools, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::lint::LintPass;
|
||||
use rustc_span::{Ident, Span};
|
||||
use rustc_span::{DUMMY_SP, Ident, Span};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::DecorateBuiltinLint;
|
||||
@@ -59,13 +59,17 @@ fn check_id(&mut self, id: ast::NodeId) {
|
||||
/// Merge the lints specified by any lint attributes into the
|
||||
/// current lint context, call the provided function, then reset the
|
||||
/// lints in effect to their previous state.
|
||||
fn with_lint_attrs<F>(&mut self, id: ast::NodeId, attrs: &'_ [ast::Attribute], f: F)
|
||||
where
|
||||
fn with_lint_attrs<F>(
|
||||
&mut self,
|
||||
id: ast::NodeId,
|
||||
attrs: &'_ [ast::Attribute],
|
||||
f: F,
|
||||
target_span: Span,
|
||||
) where
|
||||
F: FnOnce(&mut Self),
|
||||
{
|
||||
let is_crate_node = id == ast::CRATE_NODE_ID;
|
||||
debug!(?id);
|
||||
let push = self.context.builder.push(attrs, is_crate_node, None);
|
||||
let push = self.context.builder.push(attrs, id, target_span);
|
||||
|
||||
debug!("early context: enter_attrs({:?})", attrs);
|
||||
lint_callback!(self, check_attributes, attrs);
|
||||
@@ -84,24 +88,39 @@ fn visit_id(&mut self, id: rustc_ast::NodeId) {
|
||||
}
|
||||
|
||||
fn visit_param(&mut self, param: &'ast ast::Param) {
|
||||
self.with_lint_attrs(param.id, ¶m.attrs, |cx| {
|
||||
lint_callback!(cx, check_param, param);
|
||||
ast_visit::walk_param(cx, param);
|
||||
});
|
||||
self.with_lint_attrs(
|
||||
param.id,
|
||||
¶m.attrs,
|
||||
|cx| {
|
||||
lint_callback!(cx, check_param, param);
|
||||
ast_visit::walk_param(cx, param);
|
||||
},
|
||||
param.span,
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, it: &'ast ast::Item) {
|
||||
self.with_lint_attrs(it.id, &it.attrs, |cx| {
|
||||
lint_callback!(cx, check_item, it);
|
||||
ast_visit::walk_item(cx, it);
|
||||
lint_callback!(cx, check_item_post, it);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
it.id,
|
||||
&it.attrs,
|
||||
|cx| {
|
||||
lint_callback!(cx, check_item, it);
|
||||
ast_visit::walk_item(cx, it);
|
||||
lint_callback!(cx, check_item_post, it);
|
||||
},
|
||||
it.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, it: &'ast ast::ForeignItem) {
|
||||
self.with_lint_attrs(it.id, &it.attrs, |cx| {
|
||||
ast_visit::walk_item(cx, it);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
it.id,
|
||||
&it.attrs,
|
||||
|cx| {
|
||||
ast_visit::walk_item(cx, it);
|
||||
},
|
||||
it.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, p: &'ast ast::Pat) {
|
||||
@@ -111,23 +130,38 @@ fn visit_pat(&mut self, p: &'ast ast::Pat) {
|
||||
}
|
||||
|
||||
fn visit_pat_field(&mut self, field: &'ast ast::PatField) {
|
||||
self.with_lint_attrs(field.id, &field.attrs, |cx| {
|
||||
ast_visit::walk_pat_field(cx, field);
|
||||
});
|
||||
self.with_lint_attrs(
|
||||
field.id,
|
||||
&field.attrs,
|
||||
|cx| {
|
||||
ast_visit::walk_pat_field(cx, field);
|
||||
},
|
||||
field.span,
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &'ast ast::Expr) {
|
||||
self.with_lint_attrs(e.id, &e.attrs, |cx| {
|
||||
lint_callback!(cx, check_expr, e);
|
||||
ast_visit::walk_expr(cx, e);
|
||||
lint_callback!(cx, check_expr_post, e);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
e.id,
|
||||
&e.attrs,
|
||||
|cx| {
|
||||
lint_callback!(cx, check_expr, e);
|
||||
ast_visit::walk_expr(cx, e);
|
||||
lint_callback!(cx, check_expr_post, e);
|
||||
},
|
||||
e.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_expr_field(&mut self, f: &'ast ast::ExprField) {
|
||||
self.with_lint_attrs(f.id, &f.attrs, |cx| {
|
||||
ast_visit::walk_expr_field(cx, f);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
f.id,
|
||||
&f.attrs,
|
||||
|cx| {
|
||||
ast_visit::walk_expr_field(cx, f);
|
||||
},
|
||||
f.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, s: &'ast ast::Stmt) {
|
||||
@@ -139,10 +173,15 @@ fn visit_stmt(&mut self, s: &'ast ast::Stmt) {
|
||||
//
|
||||
// Note that statements get their attributes from
|
||||
// the AST struct that they wrap (e.g. an item)
|
||||
self.with_lint_attrs(s.id, s.attrs(), |cx| {
|
||||
lint_callback!(cx, check_stmt, s);
|
||||
ast_visit::walk_stmt(cx, s);
|
||||
});
|
||||
self.with_lint_attrs(
|
||||
s.id,
|
||||
s.attrs(),
|
||||
|cx| {
|
||||
lint_callback!(cx, check_stmt, s);
|
||||
ast_visit::walk_stmt(cx, s);
|
||||
},
|
||||
s.span,
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, _: &AttrVec, span: Span, id: ast::NodeId) {
|
||||
@@ -151,16 +190,26 @@ fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, _: &AttrVec, span: Span, id:
|
||||
}
|
||||
|
||||
fn visit_field_def(&mut self, s: &'ast ast::FieldDef) {
|
||||
self.with_lint_attrs(s.id, &s.attrs, |cx| {
|
||||
ast_visit::walk_field_def(cx, s);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
s.id,
|
||||
&s.attrs,
|
||||
|cx| {
|
||||
ast_visit::walk_field_def(cx, s);
|
||||
},
|
||||
s.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_variant(&mut self, v: &'ast ast::Variant) {
|
||||
self.with_lint_attrs(v.id, &v.attrs, |cx| {
|
||||
lint_callback!(cx, check_variant, v);
|
||||
ast_visit::walk_variant(cx, v);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
v.id,
|
||||
&v.attrs,
|
||||
|cx| {
|
||||
lint_callback!(cx, check_variant, v);
|
||||
ast_visit::walk_variant(cx, v);
|
||||
},
|
||||
v.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, t: &'ast ast::Ty) {
|
||||
@@ -173,10 +222,15 @@ fn visit_ident(&mut self, ident: &Ident) {
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'ast ast::Local) {
|
||||
self.with_lint_attrs(l.id, &l.attrs, |cx| {
|
||||
lint_callback!(cx, check_local, l);
|
||||
ast_visit::walk_local(cx, l);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
l.id,
|
||||
&l.attrs,
|
||||
|cx| {
|
||||
lint_callback!(cx, check_local, l);
|
||||
ast_visit::walk_local(cx, l);
|
||||
},
|
||||
l.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &'ast ast::Block) {
|
||||
@@ -185,10 +239,15 @@ fn visit_block(&mut self, b: &'ast ast::Block) {
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, a: &'ast ast::Arm) {
|
||||
self.with_lint_attrs(a.id, &a.attrs, |cx| {
|
||||
lint_callback!(cx, check_arm, a);
|
||||
ast_visit::walk_arm(cx, a);
|
||||
})
|
||||
self.with_lint_attrs(
|
||||
a.id,
|
||||
&a.attrs,
|
||||
|cx| {
|
||||
lint_callback!(cx, check_arm, a);
|
||||
ast_visit::walk_arm(cx, a);
|
||||
},
|
||||
a.span,
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_generic_arg(&mut self, arg: &'ast ast::GenericArg) {
|
||||
@@ -197,10 +256,15 @@ fn visit_generic_arg(&mut self, arg: &'ast ast::GenericArg) {
|
||||
}
|
||||
|
||||
fn visit_generic_param(&mut self, param: &'ast ast::GenericParam) {
|
||||
self.with_lint_attrs(param.id, ¶m.attrs, |cx| {
|
||||
lint_callback!(cx, check_generic_param, param);
|
||||
ast_visit::walk_generic_param(cx, param);
|
||||
});
|
||||
self.with_lint_attrs(
|
||||
param.id,
|
||||
¶m.attrs,
|
||||
|cx| {
|
||||
lint_callback!(cx, check_generic_param, param);
|
||||
ast_visit::walk_generic_param(cx, param);
|
||||
},
|
||||
param.span(),
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_generics(&mut self, g: &'ast ast::Generics) {
|
||||
@@ -220,25 +284,30 @@ fn visit_poly_trait_ref(&mut self, t: &'ast ast::PolyTraitRef) {
|
||||
}
|
||||
|
||||
fn visit_assoc_item(&mut self, item: &'ast ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
|
||||
self.with_lint_attrs(item.id, &item.attrs, |cx| {
|
||||
match ctxt {
|
||||
ast_visit::AssocCtxt::Trait => {
|
||||
lint_callback!(cx, check_trait_item, item);
|
||||
self.with_lint_attrs(
|
||||
item.id,
|
||||
&item.attrs,
|
||||
|cx| {
|
||||
match ctxt {
|
||||
ast_visit::AssocCtxt::Trait => {
|
||||
lint_callback!(cx, check_trait_item, item);
|
||||
}
|
||||
ast_visit::AssocCtxt::Impl { .. } => {
|
||||
lint_callback!(cx, check_impl_item, item);
|
||||
}
|
||||
}
|
||||
ast_visit::AssocCtxt::Impl { .. } => {
|
||||
lint_callback!(cx, check_impl_item, item);
|
||||
ast_visit::walk_assoc_item(cx, item, ctxt);
|
||||
match ctxt {
|
||||
ast_visit::AssocCtxt::Trait => {
|
||||
lint_callback!(cx, check_trait_item_post, item);
|
||||
}
|
||||
ast_visit::AssocCtxt::Impl { .. } => {
|
||||
lint_callback!(cx, check_impl_item_post, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_visit::walk_assoc_item(cx, item, ctxt);
|
||||
match ctxt {
|
||||
ast_visit::AssocCtxt::Trait => {
|
||||
lint_callback!(cx, check_trait_item_post, item);
|
||||
}
|
||||
ast_visit::AssocCtxt::Impl { .. } => {
|
||||
lint_callback!(cx, check_impl_item_post, item);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
item.span,
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_attribute(&mut self, attr: &'ast ast::Attribute) {
|
||||
@@ -367,7 +436,7 @@ fn check_ast_node_inner<'a, T: EarlyLintPass>(
|
||||
) {
|
||||
let mut cx = EarlyContextAndPass { context, tcx, pass };
|
||||
|
||||
cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx));
|
||||
cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx), DUMMY_SP);
|
||||
|
||||
// All of the buffered lints should have been emitted at this point.
|
||||
// If not, that means that we somehow buffered a lint for a node id
|
||||
|
||||
@@ -351,6 +351,28 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
|
||||
&AttributeLintKind::MissingOptionsForOnMove => {
|
||||
lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level)
|
||||
}
|
||||
&AttributeLintKind::RenamedLint { name, replace, suggestion } => lints::RenamedLint {
|
||||
name,
|
||||
replace,
|
||||
suggestion: lints::RenamedLintSuggestion::WithSpan { suggestion, replace },
|
||||
}
|
||||
.into_diag(dcx, level),
|
||||
&AttributeLintKind::DeprecatedLintName { name, suggestion, replace } => {
|
||||
lints::DeprecatedLintName { name, suggestion, replace }.into_diag(dcx, level)
|
||||
}
|
||||
&AttributeLintKind::RemovedLint { name, ref reason } => {
|
||||
lints::RemovedLint { name, reason }.into_diag(dcx, level)
|
||||
}
|
||||
&AttributeLintKind::UnknownLint { name, span, suggestion } => lints::UnknownLint {
|
||||
name,
|
||||
suggestion: suggestion.map(|(replace, from_rustc)| {
|
||||
lints::UnknownLintSuggestion::WithSpan { suggestion: span, replace, from_rustc }
|
||||
}),
|
||||
}
|
||||
.into_diag(dcx, level),
|
||||
&AttributeLintKind::IgnoredUnlessCrateSpecified { level: attr_level, name } => {
|
||||
lints::IgnoredUnlessCrateSpecified { level: attr_level, name }.into_diag(dcx, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,36 +44,6 @@ fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("malformed lint attribute input", code = E0452)]
|
||||
pub(crate) struct MalformedAttribute {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sub: MalformedAttributeSub,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum MalformedAttributeSub {
|
||||
#[label("bad attribute argument")]
|
||||
BadAttributeArgument(#[primary_span] Span),
|
||||
#[label("reason must be a string literal")]
|
||||
ReasonMustBeStringLiteral(#[primary_span] Span),
|
||||
#[label("reason in lint attribute must come last")]
|
||||
ReasonMustComeLast(#[primary_span] Span),
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`", code = E0710)]
|
||||
pub(crate) struct UnknownToolInScopedLint {
|
||||
#[primary_span]
|
||||
pub span: Option<Span>,
|
||||
pub tool_name: Symbol,
|
||||
pub lint_name: String,
|
||||
#[help("add `#![register_tool({$tool_name})]` to the crate root")]
|
||||
pub is_nightly_build: bool,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`...` range patterns are deprecated", code = E0783)]
|
||||
pub(crate) struct BuiltinEllipsisInclusiveRangePatterns {
|
||||
|
||||
+139
-304
@@ -1,13 +1,13 @@
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::attr::AttributeExt;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_ast::{DUMMY_NODE_ID, NodeId};
|
||||
use rustc_attr_parsing::AttributeParser;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, MultiSpan, msg};
|
||||
use rustc_feature::{Features, GateIssue};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::attrs::{LintAttribute, LintAttributeKind, LintInstance};
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{self as hir, HirId, Target, find_attr};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
@@ -20,7 +20,7 @@
|
||||
use rustc_session::Session;
|
||||
use rustc_session::lint::builtin::{
|
||||
self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
|
||||
UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
|
||||
UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS,
|
||||
};
|
||||
use rustc_session::lint::{
|
||||
CheckLintNameResult, Level, Lint, LintExpectationId, LintId, TargetLint,
|
||||
@@ -31,17 +31,26 @@
|
||||
use crate::builtin::MISSING_DOCS;
|
||||
use crate::context::LintStore;
|
||||
use crate::errors::{
|
||||
CheckNameUnknownTool, MalformedAttribute, MalformedAttributeSub, OverruledAttribute,
|
||||
OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
|
||||
CheckNameUnknownTool, OverruledAttribute, OverruledAttributeSub, RequestedLevel,
|
||||
UnsupportedGroup,
|
||||
};
|
||||
use crate::late::unerased_lint_store;
|
||||
use crate::lints::{
|
||||
DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
|
||||
OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint,
|
||||
RenamedLintFromCommandLine, RenamedLintSuggestion, UnknownLint, UnknownLintFromCommandLine,
|
||||
DeprecatedLintNameFromCommandLine, OverruledAttributeLint, RemovedLintFromCommandLine,
|
||||
RenamedLintFromCommandLine, RenamedLintSuggestion, UnknownLintFromCommandLine,
|
||||
UnknownLintSuggestion,
|
||||
};
|
||||
|
||||
const ALLOW_LISTED_ATTRS: &[Symbol] = &[
|
||||
sym::allow,
|
||||
sym::deny,
|
||||
sym::expect,
|
||||
sym::forbid,
|
||||
sym::warn,
|
||||
sym::automatically_derived,
|
||||
sym::doc,
|
||||
];
|
||||
|
||||
/// Collection of lint levels for the whole crate.
|
||||
/// This is used by AST-based lints, which do not
|
||||
/// wait until we have built HIR to be emitted.
|
||||
@@ -268,11 +277,7 @@ fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectati
|
||||
impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
|
||||
fn add_id(&mut self, hir_id: HirId) {
|
||||
self.provider.cur = hir_id;
|
||||
self.add(
|
||||
self.provider.attrs.get(hir_id.local_id),
|
||||
hir_id == hir::CRATE_HIR_ID,
|
||||
Some(hir_id),
|
||||
);
|
||||
self.add(self.provider.attrs.get(hir_id.local_id), Some(hir_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,7 +397,19 @@ pub fn crate_root(
|
||||
crate_attrs: &[ast::Attribute],
|
||||
) -> Self {
|
||||
let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools);
|
||||
builder.add(crate_attrs, true, None);
|
||||
let parsed_crate_attrs = AttributeParser::parse_limited_all_filtered(
|
||||
sess,
|
||||
crate_attrs,
|
||||
ALLOW_LISTED_ATTRS,
|
||||
Target::Crate,
|
||||
DUMMY_SP,
|
||||
DUMMY_NODE_ID,
|
||||
Some(features),
|
||||
rustc_attr_parsing::ShouldEmit::Nothing,
|
||||
registered_tools,
|
||||
);
|
||||
|
||||
builder.add(&parsed_crate_attrs, None);
|
||||
builder
|
||||
}
|
||||
|
||||
@@ -422,18 +439,31 @@ fn process_command_line(&mut self) {
|
||||
pub(crate) fn push(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
is_crate_node: bool,
|
||||
source_hir_id: Option<HirId>,
|
||||
node_id: NodeId,
|
||||
target_span: Span,
|
||||
) -> BuilderPush {
|
||||
let prev = self.provider.cur;
|
||||
self.provider.cur =
|
||||
self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev });
|
||||
if !attrs.is_empty() {
|
||||
let attrs = AttributeParser::parse_limited_all_filtered(
|
||||
self.sess,
|
||||
attrs,
|
||||
ALLOW_LISTED_ATTRS,
|
||||
Target::Fn,
|
||||
target_span,
|
||||
node_id,
|
||||
Some(self.features),
|
||||
rustc_attr_parsing::ShouldEmit::Nothing,
|
||||
self.registered_tools,
|
||||
);
|
||||
|
||||
self.add(attrs, is_crate_node, source_hir_id);
|
||||
self.add(&attrs, None);
|
||||
|
||||
if self.provider.current_specs().is_empty() {
|
||||
self.provider.sets.list.pop();
|
||||
self.provider.cur = prev;
|
||||
if self.provider.current_specs().is_empty() {
|
||||
self.provider.sets.list.pop();
|
||||
self.provider.cur = prev;
|
||||
}
|
||||
}
|
||||
|
||||
BuilderPush { prev }
|
||||
@@ -480,7 +510,7 @@ fn add_command_line(&mut self) {
|
||||
.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
|
||||
}
|
||||
match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
|
||||
CheckLintNameResult::Renamed(ref replace) => {
|
||||
CheckLintNameResult::Renamed(replace) => {
|
||||
let name = lint_name.as_str();
|
||||
let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
|
||||
let requested_level = RequestedLevel { level, lint_name };
|
||||
@@ -640,297 +670,102 @@ fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: Le
|
||||
};
|
||||
}
|
||||
|
||||
fn add(
|
||||
fn simple_add(
|
||||
&mut self,
|
||||
attrs: &[impl AttributeExt],
|
||||
is_crate_node: bool,
|
||||
source_hir_id: Option<HirId>,
|
||||
level: Level,
|
||||
lint: &LintInstance,
|
||||
reason: Option<Symbol>,
|
||||
expect_lint_id: Option<LintExpectationId>,
|
||||
) {
|
||||
let sess = self.sess;
|
||||
for (attr_index, attr) in attrs.iter().enumerate() {
|
||||
if attr.is_automatically_derived_attr() {
|
||||
self.insert(
|
||||
LintId::of(SINGLE_USE_LIFETIMES),
|
||||
LevelAndSource {
|
||||
level: Level::Allow,
|
||||
lint_id: None,
|
||||
src: LintLevelSource::Default,
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// If this function returns none, it means the attribute parser has already emitted appropriate errors
|
||||
|
||||
// `#[doc(hidden)]` disables missing_docs check.
|
||||
if attr.is_doc_hidden() {
|
||||
self.insert(
|
||||
LintId::of(MISSING_DOCS),
|
||||
LevelAndSource {
|
||||
level: Level::Allow,
|
||||
lint_id: None,
|
||||
src: LintLevelSource::Default,
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let src =
|
||||
LintLevelSource::Node { name: lint.original_lint_name(), span: lint.span(), reason };
|
||||
|
||||
let (level, lint_id) = match Level::from_attr(attr) {
|
||||
None => continue,
|
||||
// This is the only lint level with a `LintExpectationId` that can be created from
|
||||
// an attribute.
|
||||
Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
|
||||
let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
|
||||
else {
|
||||
bug!("stable id Level::from_attr")
|
||||
};
|
||||
let id = match self.store.get_lint_by_name(lint.full_lint().as_str()) {
|
||||
Some(TargetLint::Id(id)) => id,
|
||||
None | Some(_) => bug!(
|
||||
"guaranteed to find id due to previous parsing, happened while parsing {:?}",
|
||||
lint,
|
||||
),
|
||||
};
|
||||
|
||||
let stable_id = LintExpectationId::Stable {
|
||||
hir_id,
|
||||
attr_index: attr_index.try_into().unwrap(),
|
||||
lint_index: None,
|
||||
};
|
||||
if self.check_gated_lint(*id, lint.span(), false) {
|
||||
self.insert_spec(*id, LevelAndSource { level, lint_id: expect_lint_id, src });
|
||||
}
|
||||
}
|
||||
|
||||
(Level::Expect, Some(stable_id))
|
||||
}
|
||||
Some((lvl, id)) => (lvl, id),
|
||||
};
|
||||
|
||||
let Some(mut metas) = attr.meta_item_list() else { continue };
|
||||
|
||||
// Check whether `metas` is empty, and get its last element.
|
||||
let Some(tail_li) = metas.last() else {
|
||||
// This emits the unused_attributes lint for `#[level()]`
|
||||
continue;
|
||||
};
|
||||
|
||||
// Before processing the lint names, look for a reason (RFC 2383)
|
||||
// at the end.
|
||||
let mut reason = None;
|
||||
if let Some(item) = tail_li.meta_item() {
|
||||
match item.kind {
|
||||
ast::MetaItemKind::Word => {} // actual lint names handled later
|
||||
ast::MetaItemKind::NameValue(ref name_value) => {
|
||||
if item.path == sym::reason {
|
||||
if let ast::LitKind::Str(rationale, _) = name_value.kind {
|
||||
reason = Some(rationale);
|
||||
} else {
|
||||
sess.dcx().emit_err(MalformedAttribute {
|
||||
span: name_value.span,
|
||||
sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
|
||||
name_value.span,
|
||||
),
|
||||
});
|
||||
}
|
||||
// found reason, reslice meta list to exclude it
|
||||
metas.pop().unwrap();
|
||||
} else {
|
||||
sess.dcx().emit_err(MalformedAttribute {
|
||||
span: item.span,
|
||||
sub: MalformedAttributeSub::BadAttributeArgument(item.span),
|
||||
});
|
||||
}
|
||||
}
|
||||
ast::MetaItemKind::List(_) => {
|
||||
sess.dcx().emit_err(MalformedAttribute {
|
||||
span: item.span,
|
||||
sub: MalformedAttributeSub::BadAttributeArgument(item.span),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (lint_index, li) in metas.iter_mut().enumerate() {
|
||||
let mut lint_id = lint_id;
|
||||
if let Some(id) = &mut lint_id {
|
||||
id.set_lint_index(Some(lint_index as u16));
|
||||
}
|
||||
|
||||
let sp = li.span();
|
||||
let meta_item = match li {
|
||||
ast::MetaItemInner::MetaItem(meta_item) if meta_item.is_word() => meta_item,
|
||||
_ => {
|
||||
let sub = if let Some(item) = li.meta_item()
|
||||
&& let ast::MetaItemKind::NameValue(_) = item.kind
|
||||
&& item.path == sym::reason
|
||||
{
|
||||
MalformedAttributeSub::ReasonMustComeLast(sp)
|
||||
} else {
|
||||
MalformedAttributeSub::BadAttributeArgument(sp)
|
||||
};
|
||||
|
||||
sess.dcx().emit_err(MalformedAttribute { span: sp, sub });
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let tool_ident = if meta_item.path.segments.len() > 1 {
|
||||
Some(meta_item.path.segments.remove(0).ident)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let tool_name = tool_ident.map(|ident| ident.name);
|
||||
let name = pprust::path_to_string(&meta_item.path);
|
||||
let lint_result =
|
||||
self.store.check_lint_name(&name, tool_name, self.registered_tools);
|
||||
|
||||
let (ids, name) = match lint_result {
|
||||
CheckLintNameResult::Ok(ids) => {
|
||||
let name =
|
||||
meta_item.path.segments.last().expect("empty lint name").ident.name;
|
||||
(ids, name)
|
||||
}
|
||||
|
||||
CheckLintNameResult::Tool(ids, new_lint_name) => {
|
||||
let name = match new_lint_name {
|
||||
None => {
|
||||
let complete_name =
|
||||
&format!("{}::{}", tool_ident.unwrap().name, name);
|
||||
Symbol::intern(complete_name)
|
||||
}
|
||||
Some(new_lint_name) => {
|
||||
self.emit_span_lint(
|
||||
builtin::RENAMED_AND_REMOVED_LINTS,
|
||||
sp.into(),
|
||||
DeprecatedLintName {
|
||||
name,
|
||||
suggestion: sp,
|
||||
replace: &new_lint_name,
|
||||
},
|
||||
);
|
||||
Symbol::intern(&new_lint_name)
|
||||
}
|
||||
};
|
||||
(ids, name)
|
||||
}
|
||||
|
||||
CheckLintNameResult::MissingTool => {
|
||||
// If `MissingTool` is returned, then either the lint does not
|
||||
// exist in the tool or the code was not compiled with the tool and
|
||||
// therefore the lint was never added to the `LintStore`. To detect
|
||||
// this is the responsibility of the lint tool.
|
||||
continue;
|
||||
}
|
||||
|
||||
CheckLintNameResult::NoTool => {
|
||||
sess.dcx().emit_err(UnknownToolInScopedLint {
|
||||
span: tool_ident.map(|ident| ident.span),
|
||||
tool_name: tool_name.unwrap(),
|
||||
lint_name: pprust::path_to_string(&meta_item.path),
|
||||
is_nightly_build: sess.is_nightly_build(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
CheckLintNameResult::Renamed(ref replace) => {
|
||||
if self.lint_added_lints {
|
||||
let suggestion =
|
||||
RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
|
||||
let name =
|
||||
tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
|
||||
self.emit_span_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
sp.into(),
|
||||
RenamedLint { name: name.as_str(), replace, suggestion },
|
||||
);
|
||||
}
|
||||
|
||||
// If this lint was renamed, apply the new lint instead of ignoring the
|
||||
// attribute. Ignore any errors or warnings that happen because the new
|
||||
// name is inaccurate.
|
||||
// NOTE: `new_name` already includes the tool name, so we don't
|
||||
// have to add it again.
|
||||
let CheckLintNameResult::Ok(ids) =
|
||||
self.store.check_lint_name(replace, None, self.registered_tools)
|
||||
else {
|
||||
panic!("renamed lint does not exist: {replace}");
|
||||
};
|
||||
|
||||
(ids, Symbol::intern(&replace))
|
||||
}
|
||||
|
||||
CheckLintNameResult::Removed(ref reason) => {
|
||||
if self.lint_added_lints {
|
||||
let name =
|
||||
tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
|
||||
self.emit_span_lint(
|
||||
RENAMED_AND_REMOVED_LINTS,
|
||||
sp.into(),
|
||||
RemovedLint { name: name.as_str(), reason },
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
CheckLintNameResult::NoLint(suggestion) => {
|
||||
if self.lint_added_lints {
|
||||
let name =
|
||||
tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
|
||||
let suggestion = suggestion.map(|(replace, from_rustc)| {
|
||||
UnknownLintSuggestion::WithSpan {
|
||||
suggestion: sp,
|
||||
replace,
|
||||
from_rustc,
|
||||
}
|
||||
});
|
||||
self.emit_span_lint(
|
||||
UNKNOWN_LINTS,
|
||||
sp.into(),
|
||||
UnknownLint { name, suggestion },
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let src = LintLevelSource::Node { name, span: sp, reason };
|
||||
for &id in ids {
|
||||
if self.check_gated_lint(id, sp, false) {
|
||||
self.insert_spec(id, LevelAndSource { level, lint_id, src });
|
||||
}
|
||||
}
|
||||
|
||||
// This checks for instances where the user writes
|
||||
// `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid
|
||||
// overriding the lint level but instead add an expectation that can't be
|
||||
// fulfilled. The lint message will include an explanation, that the
|
||||
// `unfulfilled_lint_expectations` lint can't be expected.
|
||||
if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
|
||||
// The `unfulfilled_lint_expectations` lint is not part of any lint
|
||||
// groups. Therefore. we only need to check the slice if it contains a
|
||||
// single lint.
|
||||
let is_unfulfilled_lint_expectations = match ids {
|
||||
[lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
|
||||
_ => false,
|
||||
};
|
||||
self.provider.push_expectation(
|
||||
expect_id,
|
||||
LintExpectation::new(
|
||||
reason,
|
||||
sp,
|
||||
is_unfulfilled_lint_expectations,
|
||||
tool_name,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
fn add(&mut self, attrs: &[hir::Attribute], source_hir_id: Option<HirId>) {
|
||||
if find_attr!(attrs, AutomaticallyDerived(..)) {
|
||||
self.insert(
|
||||
LintId::of(SINGLE_USE_LIFETIMES),
|
||||
LevelAndSource {
|
||||
level: Level::Allow,
|
||||
lint_id: None,
|
||||
src: LintLevelSource::Default,
|
||||
},
|
||||
);
|
||||
}
|
||||
// `#[doc(hidden)]` disables missing_docs check.
|
||||
if find_attr!(attrs, Doc(d) if d.hidden.is_some()) {
|
||||
self.insert(
|
||||
LintId::of(MISSING_DOCS),
|
||||
LevelAndSource {
|
||||
level: Level::Allow,
|
||||
lint_id: None,
|
||||
src: LintLevelSource::Default,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if self.lint_added_lints && !is_crate_node {
|
||||
for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
|
||||
if !id.lint.crate_level_only {
|
||||
let Some(attrs) = find_attr!(attrs, LintAttributes(sub_attrs) => sub_attrs.into_iter())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (attr_index, LintAttribute { reason, lint_instances, attr_id, kind, .. }) in
|
||||
attrs.enumerate()
|
||||
{
|
||||
let attr_id = attr_id.attr_id;
|
||||
let level = match kind {
|
||||
LintAttributeKind::Allow => Level::Allow,
|
||||
LintAttributeKind::Deny => Level::Deny,
|
||||
LintAttributeKind::Forbid => Level::Forbid,
|
||||
LintAttributeKind::Warn => Level::Warn,
|
||||
LintAttributeKind::Expect => {
|
||||
for lint in lint_instances {
|
||||
let lint_index = lint.lint_index().try_into().unwrap();
|
||||
let attr_index = attr_index.try_into().unwrap();
|
||||
let expectation_id = match source_hir_id {
|
||||
None => LintExpectationId::Unstable { attr_id, lint_index },
|
||||
Some(hir_id) => LintExpectationId::Stable {
|
||||
hir_id,
|
||||
attr_id,
|
||||
lint_index,
|
||||
attr_index,
|
||||
},
|
||||
};
|
||||
|
||||
self.simple_add(Level::Expect, lint, *reason, Some(expectation_id));
|
||||
|
||||
let is_unfulfilled_lint_expectations =
|
||||
lint.lint_name().as_str() == UNFULFILLED_LINT_EXPECTATIONS.name_lower();
|
||||
self.provider.push_expectation(
|
||||
expectation_id,
|
||||
LintExpectation::new(
|
||||
*reason,
|
||||
lint.span(),
|
||||
is_unfulfilled_lint_expectations,
|
||||
lint.tool_name(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
self.emit_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
lint_attr_span.into(),
|
||||
IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
|
||||
);
|
||||
// don't set a separate error for every lint in the group
|
||||
break;
|
||||
};
|
||||
for lint in lint_instances {
|
||||
self.simple_add(level, lint, *reason, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1238,11 +1238,11 @@ pub(crate) struct OverruledAttributeLint<'a> {
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("lint name `{$name}` is deprecated and may not have an effect in the future")]
|
||||
pub(crate) struct DeprecatedLintName<'a> {
|
||||
pub name: String,
|
||||
pub(crate) struct DeprecatedLintName {
|
||||
pub name: Symbol,
|
||||
#[suggestion("change it to", code = "{replace}", applicability = "machine-applicable")]
|
||||
pub suggestion: Span,
|
||||
pub replace: &'a str,
|
||||
pub replace: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
@@ -1257,32 +1257,32 @@ pub(crate) struct DeprecatedLintNameFromCommandLine<'a> {
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("lint `{$name}` has been renamed to `{$replace}`")]
|
||||
pub(crate) struct RenamedLint<'a> {
|
||||
pub name: &'a str,
|
||||
pub replace: &'a str,
|
||||
pub(crate) struct RenamedLint {
|
||||
pub name: Symbol,
|
||||
pub replace: Symbol,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: RenamedLintSuggestion<'a>,
|
||||
pub suggestion: RenamedLintSuggestion,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
pub(crate) enum RenamedLintSuggestion<'a> {
|
||||
pub(crate) enum RenamedLintSuggestion {
|
||||
#[suggestion("use the new name", code = "{replace}", applicability = "machine-applicable")]
|
||||
WithSpan {
|
||||
#[primary_span]
|
||||
suggestion: Span,
|
||||
replace: &'a str,
|
||||
replace: Symbol,
|
||||
},
|
||||
#[help("use the new name `{$replace}`")]
|
||||
WithoutSpan { replace: &'a str },
|
||||
WithoutSpan { replace: Symbol },
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("lint `{$name}` has been renamed to `{$replace}`")]
|
||||
pub(crate) struct RenamedLintFromCommandLine<'a> {
|
||||
pub name: &'a str,
|
||||
pub replace: &'a str,
|
||||
pub replace: Symbol,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: RenamedLintSuggestion<'a>,
|
||||
pub suggestion: RenamedLintSuggestion,
|
||||
#[subdiagnostic]
|
||||
pub requested_level: RequestedLevel<'a>,
|
||||
}
|
||||
@@ -1290,7 +1290,7 @@ pub(crate) struct RenamedLintFromCommandLine<'a> {
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("lint `{$name}` has been removed: {$reason}")]
|
||||
pub(crate) struct RemovedLint<'a> {
|
||||
pub name: &'a str,
|
||||
pub name: Symbol,
|
||||
pub reason: &'a str,
|
||||
}
|
||||
|
||||
@@ -1306,7 +1306,7 @@ pub(crate) struct RemovedLintFromCommandLine<'a> {
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("unknown lint: `{$name}`")]
|
||||
pub(crate) struct UnknownLint {
|
||||
pub name: String,
|
||||
pub name: Symbol,
|
||||
#[subdiagnostic]
|
||||
pub suggestion: Option<UnknownLintSuggestion>,
|
||||
}
|
||||
@@ -1348,8 +1348,8 @@ pub(crate) struct UnknownLintFromCommandLine<'a> {
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("{$level}({$name}) is ignored unless specified at crate level")]
|
||||
pub(crate) struct IgnoredUnlessCrateSpecified<'a> {
|
||||
pub level: &'a str,
|
||||
pub(crate) struct IgnoredUnlessCrateSpecified {
|
||||
pub level: Symbol,
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_error_messages = { path = "../rustc_error_messages" }
|
||||
rustc_hir_id = { path = "../rustc_hir_id" }
|
||||
|
||||
@@ -802,6 +802,29 @@ pub enum AttributeLintKind {
|
||||
name: Symbol,
|
||||
},
|
||||
OnMoveMalformedAttrExpectedLiteralOrDelimiter,
|
||||
RenamedLint {
|
||||
name: Symbol,
|
||||
replace: Symbol,
|
||||
suggestion: Span,
|
||||
},
|
||||
DeprecatedLintName {
|
||||
name: Symbol,
|
||||
suggestion: Span,
|
||||
replace: Symbol,
|
||||
},
|
||||
RemovedLint {
|
||||
name: Symbol,
|
||||
reason: String,
|
||||
},
|
||||
UnknownLint {
|
||||
name: Symbol,
|
||||
span: Span,
|
||||
suggestion: Option<(Symbol, bool)>,
|
||||
},
|
||||
IgnoredUnlessCrateSpecified {
|
||||
level: Symbol,
|
||||
name: Symbol,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, HashStable_Generic)]
|
||||
|
||||
@@ -85,7 +85,8 @@
|
||||
|
||||
use interpret::ErrorHandled;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::{Attribute, HirId};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::{self, *};
|
||||
@@ -93,7 +94,6 @@
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, ValTree};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_pattern_analysis::rustc::RustcPatCtxt;
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_span::{DUMMY_SP, Span, Spanned};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
@@ -1298,7 +1298,12 @@ fn maybe_lint_level_root_bounded(&mut self, orig_id: HirId) -> HirId {
|
||||
break;
|
||||
}
|
||||
|
||||
if self.tcx.hir_attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) {
|
||||
if self
|
||||
.tcx
|
||||
.hir_attrs(id)
|
||||
.iter()
|
||||
.any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::LintAttributes { .. })))
|
||||
{
|
||||
// This is a rare case. It's for a node path that doesn't reach the root due to an
|
||||
// intervening lint level attribute. This result doesn't get cached.
|
||||
return id;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
use std::slice;
|
||||
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_ast::{AttrStyle, MetaItemKind, ast};
|
||||
use rustc_ast::ast;
|
||||
use rustc_attr_parsing::{AttributeParser, Late};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::thin_vec::ThinVec;
|
||||
@@ -19,8 +19,8 @@
|
||||
use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
|
||||
use rustc_hir::attrs::diagnostic::Directive;
|
||||
use rustc_hir::attrs::{
|
||||
AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr,
|
||||
ReprAttr, SanitizerSet,
|
||||
AttributeKind, CrateType, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution,
|
||||
InlineAttr, LintAttribute, ReprAttr, SanitizerSet,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalModDefId;
|
||||
@@ -37,7 +37,6 @@
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypingMode};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_session::lint;
|
||||
use rustc_session::lint::builtin::{
|
||||
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
|
||||
@@ -141,7 +140,6 @@ fn check_attributes(
|
||||
let mut seen = FxHashMap::default();
|
||||
let attrs = self.tcx.hir_attrs(hir_id);
|
||||
for attr in attrs {
|
||||
let mut style = None;
|
||||
match attr {
|
||||
Attribute::Parsed(AttributeKind::ProcMacro(_)) => {
|
||||
self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
|
||||
@@ -223,6 +221,7 @@ fn check_attributes(
|
||||
Attribute::Parsed(AttributeKind::OnMove { span, directive }) => {
|
||||
self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref())
|
||||
},
|
||||
Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs)) => self.check_lint_attr(hir_id, sub_attrs),
|
||||
Attribute::Parsed(
|
||||
// tidy-alphabetical-start
|
||||
AttributeKind::RustcAllowIncoherentImpl(..)
|
||||
@@ -380,18 +379,8 @@ fn check_attributes(
|
||||
| AttributeKind::WindowsSubsystem(..)
|
||||
// tidy-alphabetical-end
|
||||
) => { /* do nothing */ }
|
||||
Attribute::Unparsed(attr_item) => {
|
||||
style = Some(attr_item.style);
|
||||
Attribute::Unparsed(_) => {
|
||||
match attr.path().as_slice() {
|
||||
[
|
||||
// ok
|
||||
sym::allow
|
||||
| sym::expect
|
||||
| sym::warn
|
||||
| sym::deny
|
||||
| sym::forbid,
|
||||
..
|
||||
] => {}
|
||||
[name, rest@..] => {
|
||||
match BUILTIN_ATTRIBUTE_MAP.get(name) {
|
||||
Some(_) => {
|
||||
@@ -473,8 +462,7 @@ fn check_attributes(
|
||||
&mut seen,
|
||||
);
|
||||
}
|
||||
|
||||
self.check_unused_attribute(hir_id, attr, style)
|
||||
self.check_unused_attribute(hir_id, attr)
|
||||
}
|
||||
|
||||
self.check_repr(attrs, span, target, item, hir_id);
|
||||
@@ -1583,88 +1571,76 @@ fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
|
||||
// Warn on useless empty attributes.
|
||||
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
|
||||
let note =
|
||||
if attr.has_any_name(&[sym::allow, sym::expect, sym::warn, sym::deny, sym::forbid])
|
||||
&& attr.meta_item_list().is_some_and(|list| list.is_empty())
|
||||
{
|
||||
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
|
||||
} else if attr.has_any_name(&[
|
||||
sym::allow,
|
||||
sym::warn,
|
||||
sym::deny,
|
||||
sym::forbid,
|
||||
sym::expect,
|
||||
]) && let Some(meta) = attr.meta_item_list()
|
||||
&& let [meta] = meta.as_slice()
|
||||
&& let Some(item) = meta.meta_item()
|
||||
&& let MetaItemKind::NameValue(_) = &item.kind
|
||||
&& item.path == sym::reason
|
||||
{
|
||||
errors::UnusedNote::NoLints { name: attr.name().unwrap() }
|
||||
} else if attr.has_any_name(&[
|
||||
sym::allow,
|
||||
sym::warn,
|
||||
sym::deny,
|
||||
sym::forbid,
|
||||
sym::expect,
|
||||
]) && let Some(meta) = attr.meta_item_list()
|
||||
&& meta.iter().any(|meta| {
|
||||
meta.meta_item().map_or(false, |item| {
|
||||
item.path == sym::linker_messages || item.path == sym::linker_info
|
||||
})
|
||||
})
|
||||
{
|
||||
if hir_id != CRATE_HIR_ID {
|
||||
match style {
|
||||
Some(ast::AttrStyle::Outer) => {
|
||||
let attr_span = attr.span();
|
||||
let bang_position = self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_until_char(attr_span, '[')
|
||||
.shrink_to_hi();
|
||||
fn check_lint_attr(&self, hir_id: HirId, sub_attrs: &[LintAttribute]) {
|
||||
for LintAttribute { attr_span, lint_instances, attr_style, .. } in sub_attrs {
|
||||
if !lint_instances.iter().any(|id| {
|
||||
id.lint_name() == sym::linker_messages || id.lint_name() == sym::linker_info
|
||||
}) {
|
||||
continue;
|
||||
};
|
||||
let note = if hir_id != CRATE_HIR_ID {
|
||||
match attr_style {
|
||||
ast::AttrStyle::Outer => {
|
||||
let attr_span = attr_span;
|
||||
let bang_position = self
|
||||
.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.span_until_char(*attr_span, '[')
|
||||
.shrink_to_hi();
|
||||
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr_span,
|
||||
errors::OuterCrateLevelAttr {
|
||||
suggestion: errors::OuterCrateLevelAttrSuggestion {
|
||||
bang_position,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span(),
|
||||
errors::InnerCrateLevelAttr,
|
||||
),
|
||||
};
|
||||
return;
|
||||
} else {
|
||||
let never_needs_link = self
|
||||
.tcx
|
||||
.crate_types()
|
||||
.iter()
|
||||
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib));
|
||||
if never_needs_link {
|
||||
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
|
||||
} else {
|
||||
return;
|
||||
*attr_span,
|
||||
errors::OuterCrateLevelAttr {
|
||||
suggestion: errors::OuterCrateLevelAttrSuggestion { bang_position },
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if attr.has_name(sym::default_method_body_is_const) {
|
||||
errors::UnusedNote::DefaultMethodBodyConst
|
||||
ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
*attr_span,
|
||||
errors::InnerCrateLevelAttr,
|
||||
),
|
||||
};
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
let never_needs_link = self
|
||||
.tcx
|
||||
.crate_types()
|
||||
.iter()
|
||||
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::StaticLib));
|
||||
if never_needs_link {
|
||||
errors::UnusedNote::LinkerMessagesBinaryCrateOnly
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
*attr_span,
|
||||
errors::Unused { attr_span: *attr_span, note },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
|
||||
// Warn on useless empty attributes.
|
||||
// FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
|
||||
let note = if attr.has_any_name(&[sym::feature])
|
||||
&& attr.meta_item_list().is_some_and(|list| list.is_empty())
|
||||
{
|
||||
errors::UnusedNote::EmptyList { name: attr.name().unwrap() }
|
||||
} else if attr.has_name(sym::default_method_body_is_const) {
|
||||
errors::UnusedNote::DefaultMethodBodyConst
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
|
||||
@@ -294,8 +294,6 @@ pub(crate) enum MacroExport {
|
||||
pub(crate) enum UnusedNote {
|
||||
#[note("attribute `{$name}` with an empty list has no effect")]
|
||||
EmptyList { name: Symbol },
|
||||
#[note("attribute `{$name}` without any lints has no effect")]
|
||||
NoLints { name: Symbol },
|
||||
#[note("`default_method_body_is_const` has been replaced with `const` on traits")]
|
||||
DefaultMethodBodyConst,
|
||||
#[note(
|
||||
|
||||
Reference in New Issue
Block a user