diff --git a/clippy_lints/src/attrs/allow_attributes.rs b/clippy_lints/src/attrs/allow_attributes.rs index 53d9725703c3..0235b130b6c0 100644 --- a/clippy_lints/src/attrs/allow_attributes.rs +++ b/clippy_lints/src/attrs/allow_attributes.rs @@ -1,6 +1,7 @@ use super::ALLOW_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_ast::attr::AttributeExt; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, LintContext}; @@ -9,17 +10,12 @@ use rustc_lint::{EarlyContext, LintContext}; pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) { if !attr.span.in_external_macro(cx.sess().source_map()) && let AttrStyle::Outer = attr.style - && let Some(ident) = attr.ident() + && let Some(path_span) = attr.path_span() && !is_from_proc_macro(cx, attr) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then(cx, ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", |diag| { - diag.span_suggestion( - ident.span, - "replace it with", - "expect", - Applicability::MachineApplicable, - ); + span_lint_and_then(cx, ALLOW_ATTRIBUTES, path_span, "#[allow] attribute found", |diag| { + diag.span_suggestion(path_span, "replace it with", "expect", Applicability::MachineApplicable); }); } } diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 91c2dc7f3dc6..42c321df61c1 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -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, AttrItemKind, AttrKind, Attribute, MetaItemInner, MetaItemKind}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -574,16 +574,16 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { if let Some(items) = &attr.meta_item_list() - && let Some(ident) = attr.ident() + && let Some(name) = attr.name() { - if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { + if matches!(name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { allow_attributes::check(cx, attr); } - if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { - allow_attributes_without_reason::check(cx, ident.name, items, attr); + 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(ident.name, attr.id) { - blanket_clippy_restriction_lints::check(cx, ident.name, items); + if is_lint_level(name, attr.id) { + blanket_clippy_restriction_lints::check(cx, name, items); } if items.is_empty() || !attr.has_name(sym::deprecated) { return; @@ -604,7 +604,9 @@ 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, } { diff --git a/clippy_lints/src/attrs/should_panic_without_expect.rs b/clippy_lints/src/attrs/should_panic_without_expect.rs index fd27e30a67f3..1a9abd88a46a 100644 --- a/clippy_lints/src/attrs/should_panic_without_expect.rs +++ b/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -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, AttrItemKind, AttrKind}; 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 { diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 1cebc18edc90..aa9a6654bee3 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -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.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) + && attr.name().is_some_and(|name| is_lint_level(name, attr.id)) { for lint in lint_list { match item.kind { diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs index 7590fe96fd21..88d07be0d4d4 100644 --- a/clippy_lints/src/cfg_not_test.rs +++ b/clippy_lints/src/cfg_not_test.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::MetaItemInner; +use rustc_ast::attr::data_structures::CfgEntry; +use rustc_ast::{AttrItemKind, EarlyParsedAttribute}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -32,29 +34,32 @@ 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, + } } diff --git a/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/clippy_lints/src/doc/include_in_doc_without_cfg.rs index bca1cd03bb7e..6c800f47b68a 100644 --- a/clippy_lints/src/doc/include_in_doc_without_cfg.rs +++ b/clippy_lints/src/doc/include_in_doc_without_cfg.rs @@ -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, AttrItemKind, AttrKind, AttrStyle, Attribute}; 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. diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index c3bc9048c23a..8d538ff1acba 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -3,7 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test, sym}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::impl_lint_pass; @@ -268,11 +269,9 @@ 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.ident().map(|ident| ident.name), - Some(sym::cfg_trace | sym::cfg_attr_trace) - ) - }) + find_attr!( + cx.tcx.hir_attrs(id), + AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace + ) }) } diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 48ce1afc6e69..54599499515e 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_opt; -use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind}; +use rustc_ast::{AttrArgs, AttrItemKind, AttrKind, Attribute, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -92,7 +92,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. diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index add01b6a0837..8135c67d0d78 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -3,9 +3,9 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{is_assert_macro, root_macro_call}; 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_hir::attrs::AttributeKind; +use rustc_hir::{Expr, HirId, find_attr}; use rustc_lint::{LateContext, LintContext}; -use rustc_span::sym; use super::CONST_IS_EMPTY; @@ -40,7 +40,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 diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index ac221743cfd6..375b275cd113 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -287,8 +287,8 @@ fn is_doc_attr(attr: &Attribute) -> bool { match attr { Attribute::Parsed(AttributeKind::DocComment { .. }) => true, Attribute::Unparsed(attr) - if let [ident] = &*attr.path.segments - && ident.name == sym::doc => + if let [name] = &*attr.path.segments + && *name == sym::doc => { matches!(attr.args, AttrArgs::Eq { .. }) }, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index af78c093db6f..e151c06c6c20 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -4,7 +4,8 @@ use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicabi use clippy_utils::sugg::DiagExt; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::HirIdSet; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Attribute, HirIdSet}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocKind; use rustc_session::impl_lint_pass; @@ -122,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]` @@ -132,10 +133,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // reduce the applicability app = Applicability::MaybeIncorrect; continue; - } + }; - sugg.push_str(&snippet_with_applicability(cx.sess(), attr.span(), "_", &mut app)); - sugg.push('\n'); + for (_, attr_span) in attrs { + sugg.push_str(&snippet_with_applicability(cx.sess(), *attr_span, "_", &mut app)); + sugg.push('\n'); + } } sugg }; diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 685b0a9c0d60..f515f9987a80 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -320,6 +320,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.body(field!(anon_const.body)); }, ConstArgKind::Struct(..) => chain!(self, "let ConstArgKind::Struct(..) = {const_arg}.kind"), + ConstArgKind::TupleCall(..) => chain!(self, "let ConstArgKind::TupleCall(..) = {const_arg}.kind"), ConstArgKind::Infer(..) => chain!(self, "let ConstArgKind::Infer(..) = {const_arg}.kind"), ConstArgKind::Error(..) => chain!(self, "let ConstArgKind::Error(..) = {const_arg}.kind"), } @@ -724,7 +725,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Lit {{ ref {lit}, {negated} }}"); self.lit(lit); }, - PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), PatExprKind::Path(_) => self.maybe_path(pat), } } diff --git a/clippy_lints_internal/src/lint_without_lint_pass.rs b/clippy_lints_internal/src/lint_without_lint_pass.rs index fda65bc84eda..f56b6f31550b 100644 --- a/clippy_lints_internal/src/lint_without_lint_pass.rs +++ b/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -250,8 +250,8 @@ pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item< if let hir::Attribute::Unparsed(attr_kind) = &attr // Identify attribute && let [tool_name, attr_name] = &attr_kind.path.segments[..] - && tool_name.name == sym::clippy - && attr_name.name == sym::version + && tool_name == &sym::clippy + && attr_name == &sym::version && let Some(version) = attr.value_str() { Some(version) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 69bb6a2d6669..ecd36b157571 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-12-25 +nightly-2026-01-08 ``` diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 432d7a251a21..cf8716398efb 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -976,11 +976,21 @@ 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) { diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8820801853e0..32f6cb4fd5e9 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -20,10 +20,13 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>( name: Symbol, ) -> impl Iterator { attrs.iter().filter(move |attr| { - if let Some([clippy, segment2]) = attr.ident_path().as_deref() - && clippy.name == sym::clippy + if let [clippy, segment2] = &*attr.path() + && *clippy == sym::clippy { - let new_name = match segment2.name { + let path_span = attr + .path_span() + .expect("Clippy attributes are unparsed and have a span"); + let new_name = match *segment2 { sym::cyclomatic_complexity => Some("cognitive_complexity"), sym::author | sym::version @@ -35,7 +38,7 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>( | sym::has_significant_drop | sym::format_args => None, _ => { - sess.dcx().span_err(segment2.span, "usage of unknown attribute"); + sess.dcx().span_err(path_span, "usage of unknown attribute"); return false; }, }; @@ -43,17 +46,17 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>( match new_name { Some(new_name) => { sess.dcx() - .struct_span_err(segment2.span, "usage of deprecated attribute") + .struct_span_err(path_span, "usage of deprecated attribute") .with_span_suggestion( - segment2.span, + path_span, "consider using", - new_name, + format!("clippy::{new_name}"), Applicability::MachineApplicable, ) .emit(); false }, - None => segment2.name == name, + None => *segment2 == name, } } else { false diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index d9254fca9453..7fb8616072a5 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -348,9 +348,9 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { match attr.kind { AttrKind::Normal(..) => { - if let Some(ident) = attr.ident() { + if let Some(name) = attr.name() { // NOTE: This will likely have false positives, like `allow = 1` - let ident_string = ident.to_string(); + let ident_string = name.to_string(); if attr.style == AttrStyle::Outer { ( Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]), diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9574e6fa40b0..334cc6bb5d55 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -578,7 +578,6 @@ impl<'tcx> ConstEvalCtxt<'tcx> { Some(val) } }, - PatExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir_body(*body).value), PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id), } } @@ -1141,9 +1140,11 @@ pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value), ConstItemRhs::TypeConst(const_arg) => match const_arg.kind { ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value), - ConstArgKind::Struct(..) | ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => { - None - }, + ConstArgKind::Struct(..) + | ConstArgKind::TupleCall(..) + | ConstArgKind::Path(_) + | ConstArgKind::Error(..) + | ConstArgKind::Infer(..) => None, }, } } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 055f6d03eff0..bdc1550d69b7 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -672,10 +672,18 @@ impl HirEqInterExpr<'_, '_, '_> { .zip(*inits_b) .all(|(init_a, init_b)| self.eq_const_arg(init_a.expr, init_b.expr)) }, + (ConstArgKind::TupleCall(path_a, args_a), ConstArgKind::TupleCall(path_b, args_b)) => { + self.eq_qpath(path_a, path_b) + && args_a + .iter() + .zip(*args_b) + .all(|(arg_a, arg_b)| self.eq_const_arg(arg_a, arg_b)) + }, // Use explicit match for now since ConstArg is undergoing flux. ( ConstArgKind::Path(..) | ConstArgKind::Anon(..) + | ConstArgKind::TupleCall(..) | ConstArgKind::Infer(..) | ConstArgKind::Struct(..) | ConstArgKind::Error(..), @@ -705,9 +713,8 @@ impl HirEqInterExpr<'_, '_, '_> { negated: right_neg, }, ) => left_neg == right_neg && left.node == right.node, - (PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body), (PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right), - (PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false, + (PatExprKind::Lit { .. } | PatExprKind::Path(..), _) => false, } } @@ -1312,7 +1319,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { lit.node.hash(&mut self.s); negated.hash(&mut self.s); }, - PatExprKind::ConstBlock(c) => self.hash_body(c.body), PatExprKind::Path(qpath) => self.hash_qpath(qpath), } } @@ -1548,6 +1554,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_const_arg(init.expr); } }, + ConstArgKind::TupleCall(path, args) => { + self.hash_qpath(path); + for arg in *args { + self.hash_const_arg(arg); + } + }, ConstArgKind::Infer(..) | ConstArgKind::Error(..) => {}, } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2d079deb0ce4..2a620e917228 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -96,7 +96,7 @@ use rustc_data_structures::indexmap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::{AttributeKind, CfgEntry}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; @@ -2412,17 +2412,15 @@ 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 @@ -2437,11 +2435,12 @@ 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 - .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)) + 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)), + AttributeKind::CfgTrace(..) + ) } /// Walks up the HIR tree from the given expression in an attempt to find where the value is diff --git a/clippy_utils/src/res.rs b/clippy_utils/src/res.rs index a3efece7d224..e890a73620a5 100644 --- a/clippy_utils/src/res.rs +++ b/clippy_utils/src/res.rs @@ -301,7 +301,7 @@ impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> { fn opt_qpath(self) -> Option> { match &self.kind { PatExprKind::Path(qpath) => Some((qpath, self.hir_id)), - _ => None, + PatExprKind::Lit { .. } => None, } } } @@ -419,7 +419,7 @@ impl<'a> MaybeResPath<'a> for &PatExpr<'a> { fn opt_res_path(self) -> OptResPath<'a> { match &self.kind { PatExprKind::Path(qpath) => qpath.opt_res_path(), - _ => (None, None), + PatExprKind::Lit { .. } => (None, None), } } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index dbec79e111fb..0755e1d29c69 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-12-25" +channel = "nightly-2026-01-08" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/tests/ui/renamed_builtin_attr.stderr b/tests/ui/renamed_builtin_attr.stderr index fb51313dab69..0ebc43739d6b 100644 --- a/tests/ui/renamed_builtin_attr.stderr +++ b/tests/ui/renamed_builtin_attr.stderr @@ -1,8 +1,8 @@ error: usage of deprecated attribute - --> tests/ui/renamed_builtin_attr.rs:3:11 + --> tests/ui/renamed_builtin_attr.rs:3:3 | LL | #[clippy::cyclomatic_complexity = "1"] - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `clippy::cognitive_complexity` error: aborting due to 1 previous error diff --git a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed index f2bd306a99e4..f8be3331317c 100644 --- a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed +++ b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed @@ -1,6 +1,6 @@ #![deny(clippy::trait_duplication_in_bounds)] #![expect(incomplete_features)] -#![feature(associated_const_equality, min_generic_const_args)] +#![feature(min_generic_const_args)] trait AssocConstTrait { #[type_const] diff --git a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs index 8e7d843a44c0..a0d7a653993f 100644 --- a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs +++ b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs @@ -1,6 +1,6 @@ #![deny(clippy::trait_duplication_in_bounds)] #![expect(incomplete_features)] -#![feature(associated_const_equality, min_generic_const_args)] +#![feature(min_generic_const_args)] trait AssocConstTrait { #[type_const] diff --git a/tests/ui/unknown_attribute.stderr b/tests/ui/unknown_attribute.stderr index b306abe0a9d1..1d4d50ffc02a 100644 --- a/tests/ui/unknown_attribute.stderr +++ b/tests/ui/unknown_attribute.stderr @@ -1,8 +1,8 @@ error: usage of unknown attribute - --> tests/ui/unknown_attribute.rs:3:11 + --> tests/ui/unknown_attribute.rs:3:3 | LL | #[clippy::unknown] - | ^^^^^^^ + | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error