Rollup merge of #153582 - mehdiakiki:simplify-find-attr-hir-id, r=JonathanBrouwer

Simplify find_attr! for HirId usage

Add a `HasAttrs<'tcx, Tcx>` trait to `rustc_hir` that allows `find_attr!` to accept `DefId`, `LocalDefId`, `OwnerId`, and `HirId` directly, instead of requiring callers to manually fetch the attribute slice first.

Before:
  `find_attr!(tcx.hir_attrs(hir_id), SomeAttr)`

After:
  `find_attr!(tcx, hir_id, SomeAttr)`

The trait is defined in `rustc_hir` with a generic `Tcx` parameter to avoid a dependency cycle (`rustc_hir` cannot depend on `rustc_middle`). The four concrete impls for `TyCtxt` are in `rustc_middle`.

Fixes https://github.com/rust-lang/rust/issues/153103
This commit is contained in:
Jonathan Brouwer
2026-03-23 12:14:56 +01:00
committed by GitHub
13 changed files with 67 additions and 25 deletions
@@ -37,7 +37,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness {
}
}
Node::TraitItem(ti @ TraitItem { kind: TraitItemKind::Fn(..), .. }) => {
if find_attr!(tcx.hir_attrs(ti.hir_id()), RustcNonConstTraitMethod) {
if find_attr!(tcx, ti.hir_id(), RustcNonConstTraitMethod) {
Constness::NotConst
} else {
tcx.trait_def(tcx.local_parent(def_id)).constness
+19 -7
View File
@@ -13,6 +13,15 @@
mod encode_cross_crate;
mod pretty_printing;
/// A trait for types that can provide a list of attributes given a `TyCtxt`.
///
/// It allows `find_attr!` to accept either a `DefId`, `LocalDefId`, `OwnerId`, or `HirId`.
/// It is defined here with a generic `Tcx` because `rustc_hir` can't depend on `rustc_middle`.
/// The concrete implementations are in `rustc_middle`.
pub trait HasAttrs<'tcx, Tcx> {
fn get_attrs(self, tcx: &Tcx) -> &'tcx [crate::Attribute];
}
/// Finds attributes in sequences of attributes by pattern matching.
///
/// A little like `matches` but for attributes.
@@ -34,10 +43,12 @@
///
/// As a convenience, this macro can do that for you!
///
/// Instead of providing an attribute list, provide the `tcx` and a `DefId`.
/// Instead of providing an attribute list, provide the `tcx` and an id
/// (a `DefId`, `LocalDefId`, `OwnerId` or `HirId`).
///
/// ```rust,ignore (illustrative)
/// find_attr!(tcx, def_id, <pattern>)
/// find_attr!(tcx, hir_id, <pattern>)
/// ```
///
/// Another common case is finding attributes applied to the root of the current crate.
@@ -55,13 +66,14 @@ macro_rules! find_attr {
$crate::find_attr!($tcx.hir_krate_attrs(), $pattern $(if $guard)? => $e)
};
($tcx: expr, $def_id: expr, $pattern: pat $(if $guard: expr)?) => {
$crate::find_attr!($tcx, $def_id, $pattern $(if $guard)? => ()).is_some()
($tcx: expr, $id: expr, $pattern: pat $(if $guard: expr)?) => {
$crate::find_attr!($tcx, $id, $pattern $(if $guard)? => ()).is_some()
};
($tcx: expr, $def_id: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
#[allow(deprecated)] {
$crate::find_attr!($tcx.get_all_attrs($def_id), $pattern $(if $guard)? => $e)
}
($tcx: expr, $id: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
$crate::find_attr!(
$crate::attrs::HasAttrs::get_attrs($id, &$tcx),
$pattern $(if $guard)? => $e
)
}};
+2 -2
View File
@@ -207,7 +207,7 @@ fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
};
// A `#[const_continue]` must break to a block in a `#[loop_match]`.
if find_attr!(self.tcx.hir_attrs(e.hir_id), ConstContinue(_)) {
if find_attr!(self.tcx, e.hir_id, ConstContinue(_)) {
let Some(label) = break_destination.label else {
let span = e.span;
self.tcx.dcx().emit_fatal(ConstContinueBadLabel { span });
@@ -420,7 +420,7 @@ fn is_loop_match(
e: &'hir hir::Expr<'hir>,
body: &'hir hir::Block<'hir>,
) -> Option<Destination> {
if !find_attr!(self.tcx.hir_attrs(e.hir_id), LoopMatch(_)) {
if !find_attr!(self.tcx, e.hir_id, LoopMatch(_)) {
return None;
}
@@ -7,7 +7,7 @@ fn proc_macro_decls_static(tcx: TyCtxt<'_>, (): ()) -> Option<LocalDefId> {
let mut decls = None;
for id in tcx.hir_free_items() {
if find_attr!(tcx.hir_attrs(id.hir_id()), RustcProcMacroDecls) {
if find_attr!(tcx, id.hir_id(), RustcProcMacroDecls) {
decls = Some(id.owner_id.def_id);
}
}
+30
View File
@@ -2172,6 +2172,36 @@ pub fn fn_abi_of_instance(
}
}
// `HasAttrs` impls: allow `find_attr!(tcx, id, ...)` to work with both DefId-like types and HirId.
impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for DefId {
fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] {
if let Some(did) = self.as_local() {
tcx.hir_attrs(tcx.local_def_id_to_hir_id(did))
} else {
tcx.attrs_for_def(self)
}
}
}
impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for LocalDefId {
fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] {
tcx.hir_attrs(tcx.local_def_id_to_hir_id(self))
}
}
impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for hir::OwnerId {
fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] {
hir::attrs::HasAttrs::get_attrs(self.def_id, tcx)
}
}
impl<'tcx> hir::attrs::HasAttrs<'tcx, TyCtxt<'tcx>> for hir::HirId {
fn get_attrs(self, tcx: &TyCtxt<'tcx>) -> &'tcx [hir::Attribute] {
tcx.hir_attrs(self)
}
}
pub fn provide(providers: &mut Providers) {
closure::provide(providers);
context::provide(providers);
+1 -1
View File
@@ -491,7 +491,7 @@ fn construct_fn<'tcx>(
};
if let Some((dialect, phase)) =
find_attr!(tcx.hir_attrs(fn_id), CustomMir(dialect, phase, _) => (dialect, phase))
find_attr!(tcx, fn_id, CustomMir(dialect, phase, _) => (dialect, phase))
{
return custom::build_custom_mir(
tcx,
+2 -2
View File
@@ -917,7 +917,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
hir::ExprKind::Ret(v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
hir::ExprKind::Break(dest, ref value) => {
if find_attr!(self.tcx.hir_attrs(expr.hir_id), ConstContinue(_)) {
if find_attr!(self.tcx, expr.hir_id, ConstContinue(_)) {
match dest.target_id {
Ok(target_id) => {
let (Some(value), Some(_)) = (value, dest.label) else {
@@ -982,7 +982,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
match_source,
},
hir::ExprKind::Loop(body, ..) => {
if find_attr!(self.tcx.hir_attrs(expr.hir_id), LoopMatch(_)) {
if find_attr!(self.tcx, expr.hir_id, LoopMatch(_)) {
let dcx = self.tcx.dcx();
// Accept either `state = expr` or `state = expr;`.
+1 -1
View File
@@ -104,7 +104,7 @@ fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self {
typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
typeck_results,
body_owner: def.to_def_id(),
apply_adjustments: !find_attr!(tcx.hir_attrs(hir_id), CustomMir(..) => ()).is_some(),
apply_adjustments: !find_attr!(tcx, hir_id, CustomMir(..)),
}
}
@@ -24,7 +24,7 @@
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir_attrs(item.hir_id());
let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), MustUse { span, reason } => (span, reason));
let attr = find_attr!(cx.tcx, item.hir_id(), MustUse { span, reason } => (span, reason));
if let hir::ItemKind::Fn {
ref sig,
body: ref body_id,
@@ -65,7 +65,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir_attrs(item.hir_id());
let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), MustUse { span, reason } => (span, reason));
let attr = find_attr!(cx.tcx, item.hir_id(), MustUse { span, reason } => (span, reason));
if let Some((attr_span, reason)) = attr {
check_needless_must_use(
cx,
@@ -98,7 +98,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
let attrs = cx.tcx.hir_attrs(item.hir_id());
let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), MustUse { span, reason } => (span, reason));
let attr = find_attr!(cx.tcx, item.hir_id(), MustUse { span, reason } => (span, reason));
if let Some((attr_span, reason)) = attr {
check_needless_must_use(
cx,
@@ -269,5 +269,5 @@ fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &'tcx hir::Ty<'tcx, Ambig
fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool {
cx.tcx
.hir_parent_id_iter(hir_id)
.any(|id| find_attr!(cx.tcx.hir_attrs(id), CfgTrace(..) | CfgAttrTrace))
.any(|id| find_attr!(cx.tcx, id, CfgTrace(..) | CfgAttrTrace))
}
@@ -92,7 +92,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
.then_some((v.def_id, v.span))
});
if let Ok((id, span)) = iter.exactly_one()
&& !find_attr!(cx.tcx.hir_attrs(item.hir_id()), NonExhaustive(..))
&& !find_attr!(cx.tcx, item.hir_id(), NonExhaustive(..))
{
self.potential_enums.push((item.owner_id.def_id, id, item.span, span));
}
@@ -113,7 +113,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
if let Some(non_exhaustive_span) =
find_attr!(cx.tcx.hir_attrs(item.hir_id()), NonExhaustive(span) => *span)
find_attr!(cx.tcx, item.hir_id(), NonExhaustive(span) => *span)
{
diag.span_note(non_exhaustive_span, "the struct is already non-exhaustive");
} else {
@@ -39,7 +39,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| find_attr!(cx.tcx.hir_attrs(id), CfgTrace(..)))
.any(|id| find_attr!(cx.tcx, id, CfgTrace(..)))
}
/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization
+3 -3
View File
@@ -1710,7 +1710,7 @@ pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
}
pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
find_attr!(cx.tcx.hir_attrs(hir_id), Repr { .. })
find_attr!(cx.tcx, hir_id, Repr { .. })
}
pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
@@ -2410,7 +2410,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(&
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
// We could also check for the type name `test::TestDescAndFn`
&& let Res::Def(DefKind::Struct, _) = path.res
&& find_attr!(tcx.hir_attrs(item.hir_id()), RustcTestMarker(..))
&& find_attr!(tcx, item.hir_id(), RustcTestMarker(..))
{
names.push(ident.name);
}
@@ -2468,7 +2468,7 @@ 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 {
if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), CfgTrace(cfgs) => cfgs)
if let Some(cfgs) = find_attr!(tcx, id, CfgTrace(cfgs) => cfgs)
&& cfgs
.iter()
.any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))