Convert clippy to use the new parsed representation

This commit is contained in:
Jonathan Brouwer 2025-12-26 20:57:35 +01:00
parent 4429814412
commit e9fdf11c66
No known key found for this signature in database
GPG key ID: F13E55D38C971DEF
10 changed files with 72 additions and 57 deletions

View file

@ -16,7 +16,7 @@ mod utils;
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::msrvs::{self, Msrv, MsrvStack};
use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind};
use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind, AttrItemKind};
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -604,7 +604,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
if attr.has_name(sym::ignore)
&& match &attr.kind {
AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }),
AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })),
AttrKind::DocComment(..) => true,
}
{

View file

@ -2,19 +2,19 @@ use super::{Attribute, SHOULD_PANIC_WITHOUT_EXPECT};
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::token::{Token, TokenKind};
use rustc_ast::tokenstream::TokenTree;
use rustc_ast::{AttrArgs, AttrKind};
use rustc_ast::{AttrArgs, AttrKind, AttrItemKind};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::sym;
pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
if let AttrKind::Normal(normal_attr) = &attr.kind {
if let AttrArgs::Eq { .. } = &normal_attr.item.args {
if let AttrItemKind::Unparsed(AttrArgs::Eq { .. }) = &normal_attr.item.args {
// `#[should_panic = ".."]` found, good
return;
}
if let AttrArgs::Delimited(args) = &normal_attr.item.args
if let AttrItemKind::Unparsed(AttrArgs::Delimited(args)) = &normal_attr.item.args
&& let mut tt_iter = args.tokens.iter()
&& let Some(TokenTree::Token(
Token {

View file

@ -1,7 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::MetaItemInner;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;
use rustc_ast::AttrItemKind;
use rustc_ast::EarlyParsedAttribute;
use rustc_span::sym;
use rustc_ast::attr::data_structures::CfgEntry;
declare_clippy_lint! {
/// ### What it does
@ -32,29 +35,34 @@ declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]);
impl EarlyLintPass for CfgNotTest {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) {
if attr.has_name(rustc_span::sym::cfg_trace) && contains_not_test(attr.meta_item_list().as_deref(), false) {
span_lint_and_then(
cx,
CFG_NOT_TEST,
attr.span,
"code is excluded from test builds",
|diag| {
diag.help("consider not excluding any code from test builds");
diag.note_once("this could increase code coverage despite not actually being tested");
},
);
if attr.has_name(sym::cfg_trace) {
let AttrItemKind::Parsed(EarlyParsedAttribute::CfgTrace(cfg)) = &attr.get_normal_item().args else {
unreachable!()
};
if contains_not_test(&cfg, false) {
span_lint_and_then(
cx,
CFG_NOT_TEST,
attr.span,
"code is excluded from test builds",
|diag| {
diag.help("consider not excluding any code from test builds");
diag.note_once("this could increase code coverage despite not actually being tested");
},
);
}
}
}
}
fn contains_not_test(list: Option<&[MetaItemInner]>, not: bool) -> bool {
list.is_some_and(|list| {
list.iter().any(|item| {
item.ident().is_some_and(|ident| match ident.name {
rustc_span::sym::not => contains_not_test(item.meta_item_list(), !not),
rustc_span::sym::test => not,
_ => contains_not_test(item.meta_item_list(), not),
})
})
})
fn contains_not_test(cfg: &CfgEntry, not: bool) -> bool {
match cfg {
CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| {
contains_not_test(item, not)
}),
CfgEntry::Not(sub, _) => contains_not_test(sub, !not),
CfgEntry::NameValue { name: sym::test, .. } => not,
_ => false
}
}

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute};
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute, AttrItemKind};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
@ -11,7 +11,7 @@ pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
if !attr.span.from_expansion()
&& let AttrKind::Normal(ref item) = attr.kind
&& attr.doc_str().is_some()
&& let AttrArgs::Eq { expr: meta, .. } = &item.item.args
&& let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.item.args
&& !attr.span.contains(meta.span)
// Since the `include_str` is already expanded at this point, we can only take the
// whole attribute snippet and then modify for our suggestion.

View file

@ -9,6 +9,8 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::{ExpnKind, Span};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
declare_clippy_lint! {
/// ### What it does
@ -268,11 +270,6 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
/// attribute.
fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool {
cx.tcx.hir_parent_id_iter(hir_id).any(|id| {
cx.tcx.hir_attrs(id).iter().any(|attr| {
matches!(
attr.name(),
Some(sym::cfg_trace | sym::cfg_attr_trace)
)
})
find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace)
})
}

View file

@ -1,3 +1,4 @@
use rustc_ast::AttrItemKind;
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call_first_node;
@ -92,7 +93,7 @@ impl EarlyLintPass for LargeIncludeFile {
&& let AttrKind::Normal(ref item) = attr.kind
&& let Some(doc) = attr.doc_str()
&& doc.as_str().len() as u64 > self.max_file_size
&& let AttrArgs::Eq { expr: meta, .. } = &item.item.args
&& let AttrItemKind::Unparsed(AttrArgs::Eq { expr: meta, .. }) = &item.item.args
&& !attr.span.contains(meta.span)
// Since the `include_str` is already expanded at this point, we can only take the
// whole attribute snippet and then modify for our suggestion.

View file

@ -5,7 +5,8 @@ use clippy_utils::res::MaybeResPath;
use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context};
use rustc_hir::{Expr, HirId};
use rustc_lint::{LateContext, LintContext};
use rustc_span::sym;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
use super::CONST_IS_EMPTY;
@ -40,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool {
cx.tcx
.hir_parent_id_iter(id)
.any(|id| cx.tcx.hir_attrs(id).iter().any(|attr| attr.has_name(sym::cfg_trace)))
.any(|id| find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..)))
}
/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization

View file

@ -9,6 +9,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::AssocKind;
use rustc_session::impl_lint_pass;
use rustc_span::sym;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
declare_clippy_lint! {
/// ### What it does
@ -121,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
let attrs_sugg = {
let mut sugg = String::new();
for attr in cx.tcx.hir_attrs(assoc_item_hir_id) {
if !attr.has_name(sym::cfg_trace) {
let Attribute::Parsed(AttributeKind::CfgTrace(attrs)) = attr else {
// This might be some other attribute that the `impl Default` ought to inherit.
// But it could also be one of the many attributes that:
// - can't be put on an impl block -- like `#[inline]`
@ -131,10 +133,13 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
// reduce the applicability
app = Applicability::MaybeIncorrect;
continue;
};
for (_, attr_span) in attrs {
sugg.push_str(&snippet_with_applicability(cx.sess(), *attr_span, "_", &mut app));
sugg.push('\n');
}
sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app));
sugg.push('\n');
}
sugg
};

View file

@ -976,11 +976,19 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
l.style == r.style
&& match (&l.kind, &r.kind) {
(DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
(Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args),
(Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args),
_ => false,
}
}
pub fn eq_attr_item_kind(l: &AttrItemKind, r: &AttrItemKind) -> bool {
match (l, r) {
(AttrItemKind::Unparsed(l), AttrItemKind::Unparsed(r)) => eq_attr_args(l, r),
(AttrItemKind::Parsed(_l), AttrItemKind::Parsed(_r)) => todo!(),
_ => false,
}
}
pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool {
use AttrArgs::*;
match (l, r) {

View file

@ -121,6 +121,7 @@ use rustc_middle::ty::{
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
};
use rustc_hir::attrs::CfgEntry;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{Ident, Symbol, kw};
@ -2401,17 +2402,12 @@ pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
/// This only checks directly applied attributes, to see if a node is inside a `#[cfg(test)]` parent
/// use [`is_in_cfg_test`]
pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
tcx.hir_attrs(id).iter().any(|attr| {
if attr.has_name(sym::cfg_trace)
&& let Some(items) = attr.meta_item_list()
&& let [item] = &*items
&& item.has_name(sym::test)
{
true
} else {
false
}
})
if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), AttributeKind::CfgTrace(cfgs) => cfgs)
&& cfgs.iter().any(|(cfg, _)| { matches!(cfg, CfgEntry::NameValue { name: sym::test, ..})}) {
true
} else {
false
}
}
/// Checks if any parent node of `HirId` has `#[cfg(test)]` attribute applied
@ -2426,11 +2422,10 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied.
pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
tcx.has_attr(def_id, sym::cfg_trace)
|| tcx
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::CfgTrace(..))
|| find_attr!(tcx
.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id))
.any(|attr| attr.has_name(sym::cfg_trace))
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)), AttributeKind::CfgTrace(..))
}
/// Walks up the HIR tree from the given expression in an attempt to find where the value is