integrate parsed lint attrs into clippy

This commit is contained in:
Edvin Bryntesson
2026-02-22 19:50:23 +01:00
committed by Jonathan Brouwer
parent 345a3eb08b
commit d884f92b04
7 changed files with 50 additions and 39 deletions
+2
View File
@@ -578,6 +578,8 @@ fn opt_span_lint<S: Into<MultiSpan>>(
}
}
/// Only appropriate for use inside of the compiler
/// since the compiler doesn't track levels of tool lints
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
}
@@ -583,7 +583,7 @@ fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
if matches!(name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
allow_attributes_without_reason::check(cx, name, items, attr);
}
if is_lint_level(name, attr.id) {
if is_lint_level(name) {
blanket_clippy_restriction_lints::check(cx, name, items);
}
if items.is_empty() || !attr.has_name(sym::deprecated) {
@@ -1,10 +1,12 @@
use crate::attrs::is_lint_level;
use super::{Attribute, UNNECESSARY_CLIPPY_CFG};
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::SpanRangeExt;
use itertools::Itertools;
use rustc_ast::AttrStyle;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, Level};
use rustc_lint::{EarlyContext};
use rustc_span::sym;
pub(super) fn check(
@@ -13,9 +15,10 @@ pub(super) fn check(
behind_cfg_attr: &rustc_ast::MetaItem,
attr: &Attribute,
) {
// FIXME use proper attr parsing here
if cfg_attr.has_name(sym::clippy)
&& let Some(ident) = behind_cfg_attr.ident()
&& Level::from_symbol(ident.name, || Some(attr.id)).is_some()
&& is_lint_level(ident.name)
&& let Some(items) = behind_cfg_attr.meta_item_list()
{
let nb_items = items.len();
@@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
return;
}
if let Some(lint_list) = &attr.meta_item_list()
&& attr.name().is_some_and(|name| is_lint_level(name, attr.id))
&& attr.name().is_some_and(is_lint_level)
{
for lint in lint_list {
match item.kind {
@@ -1,5 +1,5 @@
use clippy_utils::macros::{is_panic, macro_backtrace};
use rustc_ast::{AttrId, MetaItemInner};
use rustc_ast::{MetaItemInner};
use rustc_hir::{
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
};
@@ -16,8 +16,8 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
}
}
pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool {
Level::from_symbol(symbol, || Some(attr_id)).is_some()
pub(super) fn is_lint_level(symbol: Symbol) -> bool {
Level::from_symbol(symbol).is_some()
}
pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
@@ -3,11 +3,12 @@
use clippy_utils::msrvs::Msrv;
use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability};
use clippy_utils::{can_use_if_let_chains, span_contains_non_whitespace, sym, tokenize_with_text};
use rustc_ast::{BinOpKind, MetaItemInner};
use rustc_ast::BinOpKind;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, StmtKind};
use rustc_hir::attrs::{AttributeKind, LintAttributeKind};
use rustc_hir::{Attribute, Block, Expr, ExprKind, StmtKind};
use rustc_lexer::TokenKind;
use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::{BytePos, Span, Symbol};
@@ -237,19 +238,26 @@ fn check_significant_tokens_and_expect_attrs(
!span_contains_non_whitespace(cx, span, self.lint_commented_code)
},
[attr]
if matches!(Level::from_attr(attr), Some((Level::Expect, _)))
&& let Some(metas) = attr.meta_item_list()
&& let Some(MetaItemInner::MetaItem(meta_item)) = metas.first()
&& let [tool, lint_name] = meta_item.path.segments.as_slice()
&& tool.ident.name == sym::clippy
&& [expected_lint_name, sym::style, sym::all].contains(&lint_name.ident.name) =>
{
// There is an `expect` attribute -- check that there is no _other_ significant text
let span_before_attr = inner_if.span.split_at(1).1.until(attr.span());
let span_after_attr = attr.span().between(inner_if_expr.span);
!span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code)
&& !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code)
[
Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs)),
] => {
sub_attrs
.into_iter()
.filter(|attr|attr.kind == LintAttributeKind::Expect)
.flat_map(|attr| attr.lint_instances.iter().map(|group| (attr.attr_span, group)))
.filter(|(_, lint_id)| {
lint_id.tool_is_named(sym::clippy)
&& (expected_lint_name == lint_id.lint_name()
|| [expected_lint_name, sym::style, sym::all]
.contains(&lint_id.original_name_without_tool()))
})
.any(|(attr_span, _)| {
// There is an `expect` attribute -- check that there is no _other_ significant text
let span_before_attr = inner_if.span.split_at(1).1.until(attr_span);
let span_after_attr = attr_span.between(inner_if_expr.span);
!span_contains_non_whitespace(cx, span_before_attr, self.lint_commented_code)
&& !span_contains_non_whitespace(cx, span_after_attr, self.lint_commented_code)
})
},
// There are other attributes, which are significant tokens -- check failed
@@ -4,11 +4,11 @@
binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime,
span_contains_cfg, span_find_starting_semi, sym,
};
use rustc_ast::MetaItemInner;
use rustc_errors::Applicability;
use rustc_hir::attrs::{AttributeKind, LintAttributeKind};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, StmtKind};
use rustc_lint::{LateContext, Level, LintContext};
use rustc_hir::{Attribute, Body, Expr, ExprKind, HirId, LangItem, MatchSource, StmtKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::{self, Ty};
use rustc_span::{BytePos, Pos, Span};
use std::borrow::Cow;
@@ -180,20 +180,18 @@ fn check_final_expr<'tcx>(
// actually fulfill the expectation (clippy::#12998)
match cx.tcx.hir_attrs(expr.hir_id) {
[] => {},
[attr] => {
if matches!(Level::from_attr(attr), Some((Level::Expect, _)))
&& let metas = attr.meta_item_list()
&& let Some(lst) = metas
&& let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()
&& let [tool, lint_name] = meta_item.path.segments.as_slice()
&& tool.ident.name == sym::clippy
&& matches!(
lint_name.ident.name,
sym::needless_return | sym::style | sym::all | sym::warnings
)
[Attribute::Parsed(AttributeKind::LintAttributes(sub_attrs))] => {
if !sub_attrs
.into_iter()
.filter(|attr| attr.kind == LintAttributeKind::Expect)
.flat_map(|attr| &attr.lint_instances)
.any(|lint| {
matches!(
lint.original_name_without_tool(),
sym::needless_return | sym::style | sym::all | sym::warnings
)
})
{
// This is an expectation of the `needless_return` lint
} else {
return;
}
},