about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2025-06-22 08:49:05 +0200
committerGitHub <noreply@github.com>2025-06-22 08:49:05 +0200
commitbf63acfd3553fbd21e85e3eac3dd795d35fd0804 (patch)
treec752c7744aa84547803687c0deb9489bc641846e
parentb5b106ab91ef614c0e6629df8e5f42a283746346 (diff)
parent6729b667ce4b013a5ec6f50b096bde3edabc28e3 (diff)
downloadrust-bf63acfd3553fbd21e85e3eac3dd795d35fd0804.tar.gz
rust-bf63acfd3553fbd21e85e3eac3dd795d35fd0804.zip
Rollup merge of #142776 - dtolnay:hirattrstyle2, r=jdonszelmann
All HIR attributes are outer

Fixes https://github.com/rust-lang/rust/issues/142649. Closes https://github.com/rust-lang/rust/pull/142759.

All HIR attributes, including parsed and not yet parsed, will now be rendered as outer attributes by `rustc_hir_pretty`. The original style of the corresponding AST attribute(s) is not relevant for pretty printing, only for diagnostics.

r? ````@jdonszelmann````
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs29
-rw-r--r--compiler/rustc_hir/src/hir.rs31
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs94
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs9
-rw-r--r--compiler/rustc_lint/src/context.rs7
-rw-r--r--compiler/rustc_passes/src/check_attr.rs66
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs13
-rw-r--r--tests/ui/deprecation/deprecated-expr-precedence.rs8
-rw-r--r--tests/ui/deprecation/deprecated-expr-precedence.stderr11
-rw-r--r--tests/ui/unpretty/deprecated-attr.rs5
-rw-r--r--tests/ui/unpretty/deprecated-attr.stdout9
-rw-r--r--tests/ui/unpretty/diagnostic-attr.stdout4
-rw-r--r--tests/ui/unpretty/exhaustive-asm.hir.stdout2
-rw-r--r--tests/ui/unpretty/exhaustive.hir.stdout69
15 files changed, 197 insertions, 167 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 621e3042b62..44865c493b3 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -206,12 +206,24 @@ impl AttributeExt for Attribute {
         }
     }
 
-    fn style(&self) -> AttrStyle {
-        self.style
+    fn doc_resolution_scope(&self) -> Option<AttrStyle> {
+        match &self.kind {
+            AttrKind::DocComment(..) => Some(self.style),
+            AttrKind::Normal(normal)
+                if normal.item.path == sym::doc && normal.item.value_str().is_some() =>
+            {
+                Some(self.style)
+            }
+            _ => None,
+        }
     }
 }
 
 impl Attribute {
+    pub fn style(&self) -> AttrStyle {
+        self.style
+    }
+
     pub fn may_have_doc_links(&self) -> bool {
         self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
     }
@@ -806,7 +818,14 @@ pub trait AttributeExt: Debug {
     /// * `#[doc(...)]` returns `None`.
     fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>;
 
-    fn style(&self) -> AttrStyle;
+    /// Returns outer or inner if this is a doc attribute or a sugared doc
+    /// comment, otherwise None.
+    ///
+    /// This is used in the case of doc comments on modules, to decide whether
+    /// to resolve intra-doc links against the symbols in scope within the
+    /// commented module (for inner doc) vs within its parent module (for outer
+    /// doc).
+    fn doc_resolution_scope(&self) -> Option<AttrStyle>;
 }
 
 // FIXME(fn_delegation): use function delegation instead of manually forwarding
@@ -881,8 +900,4 @@ impl Attribute {
     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
         AttributeExt::doc_str_and_comment_kind(self)
     }
-
-    pub fn style(&self) -> AttrStyle {
-        AttributeExt::style(self)
-    }
 }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 1a526d5bce0..679904c7cfe 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1346,12 +1346,13 @@ impl AttributeExt for Attribute {
         }
     }
 
-    #[inline]
-    fn style(&self) -> AttrStyle {
-        match &self {
-            Attribute::Unparsed(u) => u.style,
-            Attribute::Parsed(AttributeKind::DocComment { style, .. }) => *style,
-            _ => panic!(),
+    fn doc_resolution_scope(&self) -> Option<AttrStyle> {
+        match self {
+            Attribute::Parsed(AttributeKind::DocComment { style, .. }) => Some(*style),
+            Attribute::Unparsed(attr) if self.has_name(sym::doc) && self.value_str().is_some() => {
+                Some(attr.style)
+            }
+            _ => None,
         }
     }
 }
@@ -1442,11 +1443,6 @@ impl Attribute {
     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
         AttributeExt::doc_str_and_comment_kind(self)
     }
-
-    #[inline]
-    pub fn style(&self) -> AttrStyle {
-        AttributeExt::style(self)
-    }
 }
 
 /// Attributes owned by a HIR owner.
@@ -2286,16 +2282,9 @@ pub struct Expr<'hir> {
 }
 
 impl Expr<'_> {
-    pub fn precedence(
-        &self,
-        for_each_attr: &dyn Fn(HirId, &mut dyn FnMut(&Attribute)),
-    ) -> ExprPrecedence {
+    pub fn precedence(&self, has_attr: &dyn Fn(HirId) -> bool) -> ExprPrecedence {
         let prefix_attrs_precedence = || -> ExprPrecedence {
-            let mut has_outer_attr = false;
-            for_each_attr(self.hir_id, &mut |attr: &Attribute| {
-                has_outer_attr |= matches!(attr.style(), AttrStyle::Outer)
-            });
-            if has_outer_attr { ExprPrecedence::Prefix } else { ExprPrecedence::Unambiguous }
+            if has_attr(self.hir_id) { ExprPrecedence::Prefix } else { ExprPrecedence::Unambiguous }
         };
 
         match &self.kind {
@@ -2351,7 +2340,7 @@ impl Expr<'_> {
             | ExprKind::Use(..)
             | ExprKind::Err(_) => prefix_attrs_precedence(),
 
-            ExprKind::DropTemps(expr, ..) => expr.precedence(for_each_attr),
+            ExprKind::DropTemps(expr, ..) => expr.precedence(has_attr),
         }
     }
 
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 233bb5cd5b8..d3289e4cc6d 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -10,7 +10,7 @@ use std::vec;
 
 use rustc_abi::ExternAbi;
 use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
-use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs};
+use rustc_ast::{DUMMY_NODE_ID, DelimArgs};
 use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
 use rustc_ast_pretty::pp::{self, BoxMarker, Breaks};
 use rustc_ast_pretty::pprust::state::MacHeader;
@@ -81,32 +81,24 @@ impl<'a> State<'a> {
     }
 
     fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
-        let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&hir::Attribute)| {
-            self.attrs(id).iter().for_each(callback);
-        };
-        expr.precedence(&for_each_attr)
-    }
-
-    fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Inner)
-    }
-
-    fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) {
-        self.print_either_attributes(attrs, ast::AttrStyle::Outer)
+        let has_attr = |id: HirId| !self.attrs(id).is_empty();
+        expr.precedence(&has_attr)
     }
 
-    fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) {
+    fn print_attrs(&mut self, attrs: &[hir::Attribute]) {
         if attrs.is_empty() {
             return;
         }
 
         for attr in attrs {
-            self.print_attribute_inline(attr, style);
+            self.print_attribute_as_style(attr, ast::AttrStyle::Outer);
         }
         self.hardbreak_if_not_bol();
     }
 
-    fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) {
+    /// Print a single attribute as if it has style `style`, disregarding the
+    /// actual style of the attribute.
+    fn print_attribute_as_style(&mut self, attr: &hir::Attribute, style: ast::AttrStyle) {
         match &attr {
             hir::Attribute::Unparsed(unparsed) => {
                 self.maybe_print_comment(unparsed.span.lo());
@@ -118,14 +110,17 @@ impl<'a> State<'a> {
                 self.word("]");
                 self.hardbreak()
             }
-            hir::Attribute::Parsed(AttributeKind::DocComment { style, kind, comment, .. }) => {
+            hir::Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
                 self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string(
-                    *kind, *style, *comment,
+                    *kind, style, *comment,
                 ));
                 self.hardbreak()
             }
             hir::Attribute::Parsed(pa) => {
-                self.word("#[attr = ");
+                match style {
+                    ast::AttrStyle::Inner => self.word("#![attr = "),
+                    ast::AttrStyle::Outer => self.word("#[attr = "),
+                }
                 pa.print_attribute(self);
                 self.word("]");
                 self.hardbreak()
@@ -281,10 +276,17 @@ pub fn print_crate<'a>(
         ann,
     };
 
+    // Print all attributes, regardless of actual style, as inner attributes
+    // since this is the crate root with nothing above it to print outer
+    // attributes.
+    for attr in s.attrs(hir::CRATE_HIR_ID) {
+        s.print_attribute_as_style(attr, ast::AttrStyle::Inner);
+    }
+
     // When printing the AST, we sometimes need to inject `#[no_std]` here.
     // Since you can't compile the HIR, it's not necessary.
 
-    s.print_mod(krate, (*attrs)(hir::CRATE_HIR_ID));
+    s.print_mod(krate);
     s.print_remaining_comments();
     s.s.eof()
 }
@@ -299,7 +301,7 @@ where
 }
 
 pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String {
-    to_string(ann, |s| s.print_attribute_inline(attr, AttrStyle::Outer))
+    to_string(ann, |s| s.print_attribute_as_style(attr, ast::AttrStyle::Outer))
 }
 
 pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String {
@@ -361,8 +363,7 @@ impl<'a> State<'a> {
         self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span);
     }
 
-    fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) {
-        self.print_attrs_as_inner(attrs);
+    fn print_mod(&mut self, _mod: &hir::Mod<'_>) {
         for &item_id in _mod.item_ids {
             self.ann.nested(self, Nested::Item(item_id));
         }
@@ -479,7 +480,7 @@ impl<'a> State<'a> {
     fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) {
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(item.span.lo());
-        self.print_attrs_as_outer(self.attrs(item.hir_id()));
+        self.print_attrs(self.attrs(item.hir_id()));
         match item.kind {
             hir::ForeignItemKind::Fn(sig, arg_idents, generics) => {
                 let (cb, ib) = self.head("");
@@ -565,7 +566,7 @@ impl<'a> State<'a> {
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(item.span.lo());
         let attrs = self.attrs(item.hir_id());
-        self.print_attrs_as_outer(attrs);
+        self.print_attrs(attrs);
         self.ann.pre(self, AnnNode::Item(item));
         match item.kind {
             hir::ItemKind::ExternCrate(orig_name, ident) => {
@@ -647,14 +648,13 @@ impl<'a> State<'a> {
                 self.print_ident(ident);
                 self.nbsp();
                 self.bopen(ib);
-                self.print_mod(mod_, attrs);
+                self.print_mod(mod_);
                 self.bclose(item.span, cb);
             }
             hir::ItemKind::ForeignMod { abi, items } => {
                 let (cb, ib) = self.head("extern");
                 self.word_nbsp(abi.to_string());
                 self.bopen(ib);
-                self.print_attrs_as_inner(self.attrs(item.hir_id()));
                 for item in items {
                     self.ann.nested(self, Nested::ForeignItem(item.id));
                 }
@@ -731,7 +731,6 @@ impl<'a> State<'a> {
 
                 self.space();
                 self.bopen(ib);
-                self.print_attrs_as_inner(attrs);
                 for impl_item in items {
                     self.ann.nested(self, Nested::ImplItem(impl_item.id));
                 }
@@ -822,7 +821,7 @@ impl<'a> State<'a> {
         for v in variants {
             self.space_if_not_bol();
             self.maybe_print_comment(v.span.lo());
-            self.print_attrs_as_outer(self.attrs(v.hir_id));
+            self.print_attrs(self.attrs(v.hir_id));
             let ib = self.ibox(INDENT_UNIT);
             self.print_variant(v);
             self.word(",");
@@ -857,7 +856,7 @@ impl<'a> State<'a> {
                     self.popen();
                     self.commasep(Inconsistent, struct_def.fields(), |s, field| {
                         s.maybe_print_comment(field.span.lo());
-                        s.print_attrs_as_outer(s.attrs(field.hir_id));
+                        s.print_attrs(s.attrs(field.hir_id));
                         s.print_type(field.ty);
                     });
                     self.pclose();
@@ -878,7 +877,7 @@ impl<'a> State<'a> {
                 for field in struct_def.fields() {
                     self.hardbreak_if_not_bol();
                     self.maybe_print_comment(field.span.lo());
-                    self.print_attrs_as_outer(self.attrs(field.hir_id));
+                    self.print_attrs(self.attrs(field.hir_id));
                     self.print_ident(field.ident);
                     self.word_nbsp(":");
                     self.print_type(field.ty);
@@ -916,7 +915,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::SubItem(ti.hir_id()));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(ti.span.lo());
-        self.print_attrs_as_outer(self.attrs(ti.hir_id()));
+        self.print_attrs(self.attrs(ti.hir_id()));
         match ti.kind {
             hir::TraitItemKind::Const(ty, default) => {
                 self.print_associated_const(ti.ident, ti.generics, ty, default);
@@ -944,7 +943,7 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::SubItem(ii.hir_id()));
         self.hardbreak_if_not_bol();
         self.maybe_print_comment(ii.span.lo());
-        self.print_attrs_as_outer(self.attrs(ii.hir_id()));
+        self.print_attrs(self.attrs(ii.hir_id()));
 
         match ii.kind {
             hir::ImplItemKind::Const(ty, expr) => {
@@ -1028,27 +1027,16 @@ impl<'a> State<'a> {
     }
 
     fn print_block(&mut self, blk: &hir::Block<'_>, cb: BoxMarker, ib: BoxMarker) {
-        self.print_block_with_attrs(blk, &[], cb, ib)
+        self.print_block_maybe_unclosed(blk, Some(cb), ib)
     }
 
     fn print_block_unclosed(&mut self, blk: &hir::Block<'_>, ib: BoxMarker) {
-        self.print_block_maybe_unclosed(blk, &[], None, ib)
-    }
-
-    fn print_block_with_attrs(
-        &mut self,
-        blk: &hir::Block<'_>,
-        attrs: &[hir::Attribute],
-        cb: BoxMarker,
-        ib: BoxMarker,
-    ) {
-        self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib)
+        self.print_block_maybe_unclosed(blk, None, ib)
     }
 
     fn print_block_maybe_unclosed(
         &mut self,
         blk: &hir::Block<'_>,
-        attrs: &[hir::Attribute],
         cb: Option<BoxMarker>,
         ib: BoxMarker,
     ) {
@@ -1060,8 +1048,6 @@ impl<'a> State<'a> {
         self.ann.pre(self, AnnNode::Block(blk));
         self.bopen(ib);
 
-        self.print_attrs_as_inner(attrs);
-
         for st in blk.stmts {
             self.print_stmt(st);
         }
@@ -1251,7 +1237,7 @@ impl<'a> State<'a> {
 
     fn print_expr_field(&mut self, field: &hir::ExprField<'_>) {
         let cb = self.cbox(INDENT_UNIT);
-        self.print_attrs_as_outer(self.attrs(field.hir_id));
+        self.print_attrs(self.attrs(field.hir_id));
         if !field.is_shorthand {
             self.print_ident(field.ident);
             self.word_space(":");
@@ -1451,7 +1437,7 @@ impl<'a> State<'a> {
 
     fn print_expr(&mut self, expr: &hir::Expr<'_>) {
         self.maybe_print_comment(expr.span.lo());
-        self.print_attrs_as_outer(self.attrs(expr.hir_id));
+        self.print_attrs(self.attrs(expr.hir_id));
         let ib = self.ibox(INDENT_UNIT);
         self.ann.pre(self, AnnNode::Expr(expr));
         match expr.kind {
@@ -2076,7 +2062,7 @@ impl<'a> State<'a> {
             self.space();
         }
         let cb = self.cbox(INDENT_UNIT);
-        self.print_attrs_as_outer(self.attrs(field.hir_id));
+        self.print_attrs(self.attrs(field.hir_id));
         if !field.is_shorthand {
             self.print_ident(field.ident);
             self.word_nbsp(":");
@@ -2086,7 +2072,7 @@ impl<'a> State<'a> {
     }
 
     fn print_param(&mut self, arg: &hir::Param<'_>) {
-        self.print_attrs_as_outer(self.attrs(arg.hir_id));
+        self.print_attrs(self.attrs(arg.hir_id));
         self.print_pat(arg.pat);
     }
 
@@ -2121,7 +2107,7 @@ impl<'a> State<'a> {
         let cb = self.cbox(INDENT_UNIT);
         self.ann.pre(self, AnnNode::Arm(arm));
         let ib = self.ibox(0);
-        self.print_attrs_as_outer(self.attrs(arm.hir_id));
+        self.print_attrs(self.attrs(arm.hir_id));
         self.print_pat(arm.pat);
         self.space();
         if let Some(ref g) = arm.guard {
@@ -2409,7 +2395,7 @@ impl<'a> State<'a> {
     }
 
     fn print_where_predicate(&mut self, predicate: &hir::WherePredicate<'_>) {
-        self.print_attrs_as_outer(self.attrs(predicate.hir_id));
+        self.print_attrs(self.attrs(predicate.hir_id));
         match *predicate.kind {
             hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
                 bound_generic_params,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 223cadc056f..bd3ca8317eb 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -18,7 +18,7 @@ use rustc_errors::{
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{Attribute, ExprKind, HirId, QPath};
+use rustc_hir::{ExprKind, HirId, QPath};
 use rustc_hir_analysis::NoVariantNamed;
 use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _};
 use rustc_infer::infer;
@@ -55,7 +55,7 @@ use crate::{
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(crate) fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
-        let for_each_attr = |id: HirId, callback: &mut dyn FnMut(&Attribute)| {
+        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?`
@@ -71,11 +71,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 //     let y: u32 = (x?).try_into().unwrap();
                 //                  +  +++++++++++++++++++++
                 if attr.span().desugaring_kind().is_none() {
-                    callback(attr);
+                    return true;
                 }
             }
+            false
         };
-        expr.precedence(&for_each_attr)
+        expr.precedence(&has_attr)
     }
 
     /// Check an expr with an expectation type, and also demand that the expr's
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b6bf45dfbcf..414f2a1846b 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -855,14 +855,15 @@ impl<'tcx> LateContext<'tcx> {
     /// rendering diagnostic. This is not the same as the precedence that would
     /// be used for pretty-printing HIR by rustc_hir_pretty.
     pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
-        let for_each_attr = |id: hir::HirId, callback: &mut dyn FnMut(&hir::Attribute)| {
+        let has_attr = |id: hir::HirId| -> bool {
             for attr in self.tcx.hir_attrs(id) {
                 if attr.span().desugaring_kind().is_none() {
-                    callback(attr);
+                    return true;
                 }
             }
+            false
         };
-        expr.precedence(&for_each_attr)
+        expr.precedence(&has_attr)
     }
 
     /// If the given expression is a local binding, find the initializer expression.
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 57af9d62999..d802bf4df19 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -116,6 +116,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         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::Confusables { first_span, .. }) => {
                     self.check_confusables(*first_span, target);
@@ -167,10 +168,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => {
                     self.check_applied_to_fn_or_method(hir_id, *attr_span, span, target)
                 }
-                &Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
-                    self.check_may_dangle(hir_id, attr_span)
+                Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => {
+                    self.check_may_dangle(hir_id, *attr_span)
                 }
-                Attribute::Unparsed(_) => {
+                Attribute::Unparsed(attr_item) => {
+                    style = Some(attr_item.style);
                     match attr.path().as_slice() {
                         [sym::diagnostic, sym::do_not_recommend, ..] => {
                             self.check_do_not_recommend(attr.span(), hir_id, target, attr, item)
@@ -193,6 +195,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         }
                         [sym::doc, ..] => self.check_doc_attrs(
                             attr,
+                            attr_item.style,
                             hir_id,
                             target,
                             &mut specified_inline,
@@ -353,14 +356,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
                     attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
                 {
-                    match attr.style() {
-                        ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
+                    match style {
+                        Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint(
                             UNUSED_ATTRIBUTES,
                             hir_id,
                             attr.span(),
                             errors::OuterCrateLevelAttr,
                         ),
-                        ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
+                        Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
                             UNUSED_ATTRIBUTES,
                             hir_id,
                             attr.span(),
@@ -374,7 +377,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen);
             }
 
-            self.check_unused_attribute(hir_id, attr)
+            self.check_unused_attribute(hir_id, attr, style)
         }
 
         self.check_repr(attrs, span, target, item, hir_id);
@@ -1197,7 +1200,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     /// the first `inline`/`no_inline` attribute.
     fn check_doc_inline(
         &self,
-        attr: &Attribute,
+        style: AttrStyle,
         meta: &MetaItemInner,
         hir_id: HirId,
         target: Target,
@@ -1227,8 +1230,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     meta.span(),
                     errors::DocInlineOnlyUse {
                         attr_span: meta.span(),
-                        item_span: (attr.style() == AttrStyle::Outer)
-                            .then(|| self.tcx.hir_span(hir_id)),
+                        item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
                     },
                 );
             }
@@ -1237,7 +1239,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
 
     fn check_doc_masked(
         &self,
-        attr: &Attribute,
+        style: AttrStyle,
         meta: &MetaItemInner,
         hir_id: HirId,
         target: Target,
@@ -1249,8 +1251,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 meta.span(),
                 errors::DocMaskedOnlyExternCrate {
                     attr_span: meta.span(),
-                    item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir_span(hir_id)),
+                    item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
                 },
             );
             return;
@@ -1263,8 +1264,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 meta.span(),
                 errors::DocMaskedNotExternCrateSelf {
                     attr_span: meta.span(),
-                    item_span: (attr.style() == AttrStyle::Outer)
-                        .then(|| self.tcx.hir_span(hir_id)),
+                    item_span: (style == AttrStyle::Outer).then(|| self.tcx.hir_span(hir_id)),
                 },
             );
         }
@@ -1288,13 +1288,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     fn check_attr_crate_level(
         &self,
         attr: &Attribute,
+        style: AttrStyle,
         meta: &MetaItemInner,
         hir_id: HirId,
     ) -> bool {
         if hir_id != CRATE_HIR_ID {
             // insert a bang between `#` and `[...`
             let bang_span = attr.span().lo() + BytePos(1);
-            let sugg = (attr.style() == AttrStyle::Outer
+            let sugg = (style == AttrStyle::Outer
                 && self.tcx.hir_get_parent_item(hir_id) == CRATE_OWNER_ID)
                 .then_some(errors::AttrCrateLevelOnlySugg {
                     attr: attr.span().with_lo(bang_span).with_hi(bang_span),
@@ -1311,7 +1312,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks that `doc(test(...))` attribute contains only valid attributes and are at the right place.
-    fn check_test_attr(&self, attr: &Attribute, meta: &MetaItemInner, hir_id: HirId) {
+    fn check_test_attr(
+        &self,
+        attr: &Attribute,
+        style: AttrStyle,
+        meta: &MetaItemInner,
+        hir_id: HirId,
+    ) {
         if let Some(metas) = meta.meta_item_list() {
             for i_meta in metas {
                 match (i_meta.name(), i_meta.meta_item()) {
@@ -1319,7 +1326,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         // Allowed everywhere like `#[doc]`
                     }
                     (Some(sym::no_crate_inject), _) => {
-                        self.check_attr_crate_level(attr, meta, hir_id);
+                        self.check_attr_crate_level(attr, style, meta, hir_id);
                     }
                     (_, Some(m)) => {
                         self.tcx.emit_node_span_lint(
@@ -1373,6 +1380,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     fn check_doc_attrs(
         &self,
         attr: &Attribute,
+        style: AttrStyle,
         hir_id: HirId,
         target: Target,
         specified_inline: &mut Option<(bool, Span)>,
@@ -1407,7 +1415,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         }
 
                         Some(sym::test) => {
-                            self.check_test_attr(attr, meta, hir_id);
+                            self.check_test_attr(attr, style, meta, hir_id);
                         }
 
                         Some(
@@ -1418,25 +1426,25 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             | sym::html_root_url
                             | sym::html_no_source,
                         ) => {
-                            self.check_attr_crate_level(attr, meta, hir_id);
+                            self.check_attr_crate_level(attr, style, meta, hir_id);
                         }
 
                         Some(sym::cfg_hide) => {
-                            if self.check_attr_crate_level(attr, meta, hir_id) {
+                            if self.check_attr_crate_level(attr, style, meta, hir_id) {
                                 self.check_doc_cfg_hide(meta, hir_id);
                             }
                         }
 
                         Some(sym::inline | sym::no_inline) => {
-                            self.check_doc_inline(attr, meta, hir_id, target, specified_inline)
+                            self.check_doc_inline(style, meta, hir_id, target, specified_inline)
                         }
 
-                        Some(sym::masked) => self.check_doc_masked(attr, meta, hir_id, target),
+                        Some(sym::masked) => self.check_doc_masked(style, meta, hir_id, target),
 
                         Some(sym::cfg | sym::hidden | sym::notable_trait) => {}
 
                         Some(sym::rust_logo) => {
-                            if self.check_attr_crate_level(attr, meta, hir_id)
+                            if self.check_attr_crate_level(attr, style, meta, hir_id)
                                 && !self.tcx.features().rustdoc_internals()
                             {
                                 feature_err(
@@ -1475,7 +1483,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                                     errors::DocTestUnknownInclude {
                                         path,
                                         value: value.to_string(),
-                                        inner: match attr.style() {
+                                        inner: match style {
                                             AttrStyle::Inner => "!",
                                             AttrStyle::Outer => "",
                                         },
@@ -2429,7 +2437,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
+    fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
         // FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very
         // ugly now but can 100% be removed later.
         if let Attribute::Parsed(p) = attr {
@@ -2482,14 +2490,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             })
         {
             if hir_id != CRATE_HIR_ID {
-                match attr.style() {
-                    ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
+                match style {
+                    Some(ast::AttrStyle::Outer) => self.tcx.emit_node_span_lint(
                         UNUSED_ATTRIBUTES,
                         hir_id,
                         attr.span(),
                         errors::OuterCrateLevelAttr,
                     ),
-                    ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
+                    Some(ast::AttrStyle::Inner) | None => self.tcx.emit_node_span_lint(
                         UNUSED_ATTRIBUTES,
                         hir_id,
                         attr.span(),
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index fa839d2748d..931c6241bf2 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -356,7 +356,12 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen
 /// If there are no doc-comments, return true.
 /// FIXME(#78591): Support both inner and outer attributes on the same item.
 pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool {
-    attrs.iter().find(|a| a.doc_str().is_some()).is_none_or(|a| a.style() == ast::AttrStyle::Inner)
+    for attr in attrs {
+        if let Some(attr_style) = attr.doc_resolution_scope() {
+            return attr_style == ast::AttrStyle::Inner;
+        }
+    }
+    true
 }
 
 /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`.
diff --git a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
index 289b6b915d4..d3c39686976 100644
--- a/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::attr::AttributeExt as _;
 use rustc_ast::token::CommentKind;
 use rustc_errors::Applicability;
 use rustc_hir::{AttrStyle, Attribute};
@@ -43,13 +44,19 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &F
                 "looks like a footnote ref, but has no matching footnote",
                 |diag| {
                     if this_fragment.kind == DocFragmentKind::SugaredDoc {
-                        let (doc_attr, (_, doc_attr_comment_kind)) = attrs
+                        let (doc_attr, (_, doc_attr_comment_kind), attr_style) = attrs
                             .iter()
                             .filter(|attr| attr.span().overlaps(this_fragment.span))
                             .rev()
-                            .find_map(|attr| Some((attr, attr.doc_str_and_comment_kind()?)))
+                            .find_map(|attr| {
+                                Some((
+                                    attr,
+                                    attr.doc_str_and_comment_kind()?,
+                                    attr.doc_resolution_scope()?,
+                                ))
+                            })
                             .unwrap();
-                        let (to_add, terminator) = match (doc_attr_comment_kind, doc_attr.style()) {
+                        let (to_add, terminator) = match (doc_attr_comment_kind, attr_style) {
                             (CommentKind::Line, AttrStyle::Outer) => ("\n///\n/// ", ""),
                             (CommentKind::Line, AttrStyle::Inner) => ("\n//!\n//! ", ""),
                             (CommentKind::Block, AttrStyle::Outer) => ("\n/** ", " */"),
diff --git a/tests/ui/deprecation/deprecated-expr-precedence.rs b/tests/ui/deprecation/deprecated-expr-precedence.rs
new file mode 100644
index 00000000000..9636b46df20
--- /dev/null
+++ b/tests/ui/deprecation/deprecated-expr-precedence.rs
@@ -0,0 +1,8 @@
+//@ check-fail
+//@ compile-flags: --crate-type=lib
+
+// Regression test for issue 142649
+pub fn public() {
+    #[deprecated] 0
+    //~^ ERROR mismatched types
+}
diff --git a/tests/ui/deprecation/deprecated-expr-precedence.stderr b/tests/ui/deprecation/deprecated-expr-precedence.stderr
new file mode 100644
index 00000000000..3275f2e790a
--- /dev/null
+++ b/tests/ui/deprecation/deprecated-expr-precedence.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+  --> $DIR/deprecated-expr-precedence.rs:6:19
+   |
+LL | pub fn public() {
+   |                - help: try adding a return type: `-> i32`
+LL |     #[deprecated] 0
+   |                   ^ expected `()`, found integer
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/unpretty/deprecated-attr.rs b/tests/ui/unpretty/deprecated-attr.rs
index 0c80203e965..e2ab5efb5d8 100644
--- a/tests/ui/unpretty/deprecated-attr.rs
+++ b/tests/ui/unpretty/deprecated-attr.rs
@@ -16,3 +16,8 @@ pub struct SinceAndNote;
 
 #[deprecated(note = "here's why this is deprecated", since = "1.2.3")]
 pub struct FlippedOrder;
+
+pub fn f() {
+    // Attribute is ignored here (with a warning), but still preserved in HIR
+    #[deprecated] 0
+}
diff --git a/tests/ui/unpretty/deprecated-attr.stdout b/tests/ui/unpretty/deprecated-attr.stdout
index 97d863b2e94..a2b645d00d0 100644
--- a/tests/ui/unpretty/deprecated-attr.stdout
+++ b/tests/ui/unpretty/deprecated-attr.stdout
@@ -24,3 +24,12 @@ struct SinceAndNote;
 #[attr = Deprecation {deprecation: Deprecation {since: NonStandard("1.2.3"),
 note: "here's why this is deprecated"}}]
 struct FlippedOrder;
+
+fn f() {
+
+    // Attribute is ignored here (with a warning), but still preserved in HIR
+    #[attr = Deprecation {deprecation:
+    Deprecation {since:
+    Unspecified}}]
+    0
+}
diff --git a/tests/ui/unpretty/diagnostic-attr.stdout b/tests/ui/unpretty/diagnostic-attr.stdout
index 81d71b91d81..3b15a845d68 100644
--- a/tests/ui/unpretty/diagnostic-attr.stdout
+++ b/tests/ui/unpretty/diagnostic-attr.stdout
@@ -12,6 +12,4 @@ extern crate std;
 trait ImportantTrait<A> { }
 
 #[diagnostic::do_not_recommend]
-impl <T> ImportantTrait<T> for T where T: Clone
-    {#![diagnostic::do_not_recommend]
-}
+impl <T> ImportantTrait<T> for T where T: Clone { }
diff --git a/tests/ui/unpretty/exhaustive-asm.hir.stdout b/tests/ui/unpretty/exhaustive-asm.hir.stdout
index 810db69bff1..ec9bda57331 100644
--- a/tests/ui/unpretty/exhaustive-asm.hir.stdout
+++ b/tests/ui/unpretty/exhaustive-asm.hir.stdout
@@ -26,7 +26,7 @@ mod expressions {
 
 mod items {
     /// ItemKind::GlobalAsm
-    mod item_global_asm {/// ItemKind::GlobalAsm
+    mod item_global_asm {
         global_asm! (".globl my_asm_func");
     }
 }
diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout
index 5d6e3907d75..b15c02003a6 100644
--- a/tests/ui/unpretty/exhaustive.hir.stdout
+++ b/tests/ui/unpretty/exhaustive.hir.stdout
@@ -50,20 +50,14 @@ mod prelude {
     }
 }
 
-//! inner single-line doc comment
-/*!
+/// inner single-line doc comment
+/**
      * inner multi-line doc comment
      */
 #[doc = "inner doc attribute"]
 #[allow(dead_code, unused_variables)]
 #[no_std]
-mod attributes {//! inner single-line doc comment
-    /*!
-     * inner multi-line doc comment
-     */
-    #![doc = "inner doc attribute"]
-    #![allow(dead_code, unused_variables)]
-    #![no_std]
+mod attributes {
 
     /// outer single-line doc comment
     /**
@@ -413,25 +407,25 @@ mod expressions {
 }
 mod items {
     /// ItemKind::ExternCrate
-    mod item_extern_crate {/// ItemKind::ExternCrate
+    mod item_extern_crate {
         extern crate core;
         extern crate self as unpretty;
         extern crate core as _;
     }
     /// ItemKind::Use
-    mod item_use {/// ItemKind::Use
+    mod item_use {
         use ::{};
         use crate::expressions;
         use crate::items::item_use;
         use core::*;
     }
     /// ItemKind::Static
-    mod item_static {/// ItemKind::Static
+    mod item_static {
         static A: () = { };
         static mut B: () = { };
     }
     /// ItemKind::Const
-    mod item_const {/// ItemKind::Const
+    mod item_const {
         const A: () = { };
         trait TraitItems {
             const
@@ -445,7 +439,7 @@ mod items {
         }
     }
     /// ItemKind::Fn
-    mod item_fn {/// ItemKind::Fn
+    mod item_fn {
         const unsafe extern "C" fn f() { }
         async unsafe extern "C" fn g()
             ->
@@ -460,21 +454,19 @@ mod items {
         }
     }
     /// ItemKind::Mod
-    mod item_mod {/// ItemKind::Mod
-    }
+    mod item_mod { }
     /// ItemKind::ForeignMod
-    mod item_foreign_mod {/// ItemKind::ForeignMod
+    mod item_foreign_mod {
         extern "Rust" { }
         extern "C" { }
     }
     /// ItemKind::GlobalAsm: see exhaustive-asm.rs
     /// ItemKind::TyAlias
-    mod item_ty_alias {/// ItemKind::GlobalAsm: see exhaustive-asm.rs
-        /// ItemKind::TyAlias
+    mod item_ty_alias {
         type Type<'a> where T: 'a = T;
     }
     /// ItemKind::Enum
-    mod item_enum {/// ItemKind::Enum
+    mod item_enum {
         enum Void { }
         enum Empty {
             Unit,
@@ -490,7 +482,7 @@ mod items {
         }
     }
     /// ItemKind::Struct
-    mod item_struct {/// ItemKind::Struct
+    mod item_struct {
         struct Unit;
         struct Tuple();
         struct Newtype(Unit);
@@ -501,45 +493,40 @@ mod items {
         }
     }
     /// ItemKind::Union
-    mod item_union {/// ItemKind::Union
+    mod item_union {
         union Generic<'a, T> where T: 'a {
             t: T,
         }
     }
     /// ItemKind::Trait
-    mod item_trait {/// ItemKind::Trait
+    mod item_trait {
         auto unsafe trait Send { }
         trait Trait<'a>: Sized where Self: 'a { }
     }
     /// ItemKind::TraitAlias
-    mod item_trait_alias {/// ItemKind::TraitAlias
+    mod item_trait_alias {
         trait Trait<T> = Sized where for<'a> T: 'a;
     }
     /// ItemKind::Impl
-    mod item_impl {/// ItemKind::Impl
+    mod item_impl {
         impl () { }
         impl <T> () { }
         impl Default for () { }
         impl const <T> Default for () { }
     }
     /// ItemKind::MacCall
-    mod item_mac_call {/// ItemKind::MacCall
-    }
+    mod item_mac_call { }
     /// ItemKind::MacroDef
-    mod item_macro_def {/// ItemKind::MacroDef
+    mod item_macro_def {
         macro_rules! mac { () => {...}; }
         macro stringify { () => {} }
     }
     /// ItemKind::Delegation
-    /*! FIXME: todo */
-    mod item_delegation {/// ItemKind::Delegation
-        /*! FIXME: todo */
-    }
+    /** FIXME: todo */
+    mod item_delegation { }
     /// ItemKind::DelegationMac
-    /*! FIXME: todo */
-    mod item_delegation_mac {/// ItemKind::DelegationMac
-        /*! FIXME: todo */
-    }
+    /** FIXME: todo */
+    mod item_delegation_mac { }
 }
 mod patterns {
     /// PatKind::Missing
@@ -690,29 +677,29 @@ mod types {
     /// TyKind::Paren
     fn ty_paren() { let _: T; }
     /// TyKind::Typeof
-    /*! unused for now */
+    /** unused for now */
     fn ty_typeof() { }
     /// TyKind::Infer
     fn ty_infer() { let _: _; }
     /// TyKind::ImplicitSelf
-    /*! there is no syntax for this */
+    /** there is no syntax for this */
     fn ty_implicit_self() { }
     /// TyKind::MacCall
     #[expect(deprecated)]
     fn ty_mac_call() { let _: T; let _: T; let _: T; }
     /// TyKind::CVarArgs
-    /*! FIXME: todo */
+    /** FIXME: todo */
     fn ty_c_var_args() { }
     /// TyKind::Pat
     fn ty_pat() { let _: u32 is 1..=RangeMax; }
 }
 mod visibilities {
     /// VisibilityKind::Public
-    mod visibility_public {/// VisibilityKind::Public
+    mod visibility_public {
         struct Pub;
     }
     /// VisibilityKind::Restricted
-    mod visibility_restricted {/// VisibilityKind::Restricted
+    mod visibility_restricted {
         struct PubCrate;
         struct PubSelf;
         struct PubSuper;