From 7199df231264b69a830442386de86dbfb32ba01c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Dec 2024 09:44:41 -0800 Subject: [PATCH] Reduce precedence of expressions that have an outer attr --- compiler/rustc_ast/src/ast.rs | 15 ++++-- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_hir/src/hir.rs | 21 ++++++-- compiler/rustc_hir_pretty/src/lib.rs | 52 ++++++++++++------- compiler/rustc_hir_typeck/src/callee.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 27 +++++++++- .../src/fn_ctxt/suggestions.rs | 8 +-- compiler/rustc_lint/src/context.rs | 15 ++++++ .../clippy/clippy_lints/src/dereference.rs | 10 ++-- .../src/loops/single_element_loop.rs | 2 +- .../clippy_lints/src/matches/manual_utils.rs | 2 +- .../clippy/clippy_lints/src/neg_multiply.rs | 2 +- .../clippy_lints/src/redundant_slicing.rs | 2 +- .../transmutes_expressible_as_ptr_casts.rs | 2 +- tests/ui-fulldeps/auxiliary/parser.rs | 11 ++-- .../pprust-parenthesis-insertion.rs | 5 ++ 17 files changed, 133 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 31e6750a6788c..6fb4af221e52d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1309,11 +1309,20 @@ impl Expr { } pub fn precedence(&self) -> ExprPrecedence { + fn prefix_attrs_precedence(attrs: &AttrVec) -> ExprPrecedence { + for attr in attrs { + if let AttrStyle::Outer = attr.style { + return ExprPrecedence::Prefix; + } + } + ExprPrecedence::Unambiguous + } + match &self.kind { ExprKind::Closure(closure) => { match closure.fn_decl.output { FnRetTy::Default(_) => ExprPrecedence::Jump, - FnRetTy::Ty(_) => ExprPrecedence::Unambiguous, + FnRetTy::Ty(_) => prefix_attrs_precedence(&self.attrs), } } @@ -1345,7 +1354,7 @@ impl Expr { | ExprKind::Let(..) | ExprKind::Unary(..) => ExprPrecedence::Prefix, - // Never need parens + // Need parens if and only if there are prefix attributes. ExprKind::Array(_) | ExprKind::Await(..) | ExprKind::Block(..) @@ -1378,7 +1387,7 @@ impl Expr { | ExprKind::UnsafeBinderCast(..) | ExprKind::While(..) | ExprKind::Err(_) - | ExprKind::Dummy => ExprPrecedence::Unambiguous, + | ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 995924c2a2949..1cc4078746d09 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -696,7 +696,7 @@ fn walk_local(vis: &mut T, local: &mut P) { vis.visit_span(span); } -fn walk_attribute(vis: &mut T, attr: &mut Attribute) { +pub fn walk_attribute(vis: &mut T, attr: &mut Attribute) { let Attribute { kind, id: _, style: _, span } = attr; match kind { AttrKind::Normal(normal) => { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 8cea269f29823..1206d4f85d839 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1942,12 +1942,23 @@ pub struct Expr<'hir> { } impl Expr<'_> { - pub fn precedence(&self) -> ExprPrecedence { + pub fn precedence( + &self, + for_each_attr: &dyn Fn(HirId, &mut dyn FnMut(&Attribute)), + ) -> 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 } + }; + match &self.kind { ExprKind::Closure(closure) => { match closure.fn_decl.output { FnRetTy::DefaultReturn(_) => ExprPrecedence::Jump, - FnRetTy::Return(_) => ExprPrecedence::Unambiguous, + FnRetTy::Return(_) => prefix_attrs_precedence(), } } @@ -1972,7 +1983,7 @@ impl Expr<'_> { | ExprKind::Let(..) | ExprKind::Unary(..) => ExprPrecedence::Prefix, - // Never need parens + // Need parens if and only if there are prefix attributes. ExprKind::Array(_) | ExprKind::Block(..) | ExprKind::Call(..) @@ -1993,9 +2004,9 @@ impl Expr<'_> { | ExprKind::Tup(_) | ExprKind::Type(..) | ExprKind::UnsafeBinderCast(..) - | ExprKind::Err(_) => ExprPrecedence::Unambiguous, + | ExprKind::Err(_) => prefix_attrs_precedence(), - ExprKind::DropTemps(expr, ..) => expr.precedence(), + ExprKind::DropTemps(expr, ..) => expr.precedence(for_each_attr), } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5c1c58921907e..13fc998424e9f 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -78,6 +78,13 @@ impl<'a> State<'a> { (self.attrs)(id) } + 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_inner_attributes(&mut self, attrs: &[hir::Attribute]) -> bool { self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true) } @@ -1155,7 +1162,7 @@ impl<'a> State<'a> { } self.space(); self.word_space("="); - let npals = || parser::needs_par_as_let_scrutinee(init.precedence()); + let npals = || parser::needs_par_as_let_scrutinee(self.precedence(init)); self.print_expr_cond_paren(init, Self::cond_needs_par(init) || npals()) } @@ -1262,7 +1269,7 @@ impl<'a> State<'a> { fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let needs_paren = match func.kind { hir::ExprKind::Field(..) => true, - _ => func.precedence() < ExprPrecedence::Unambiguous, + _ => self.precedence(func) < ExprPrecedence::Unambiguous, }; self.print_expr_cond_paren(func, needs_paren); @@ -1276,7 +1283,10 @@ impl<'a> State<'a> { args: &[hir::Expr<'_>], ) { let base_args = args; - self.print_expr_cond_paren(receiver, receiver.precedence() < ExprPrecedence::Unambiguous); + self.print_expr_cond_paren( + receiver, + self.precedence(receiver) < ExprPrecedence::Unambiguous, + ); self.word("."); self.print_ident(segment.ident); @@ -1291,8 +1301,8 @@ impl<'a> State<'a> { fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { let assoc_op = AssocOp::from_ast_binop(op.node); let binop_prec = assoc_op.precedence(); - let left_prec = lhs.precedence(); - let right_prec = rhs.precedence(); + let left_prec = self.precedence(lhs); + let right_prec = self.precedence(rhs); let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), @@ -1321,7 +1331,7 @@ impl<'a> State<'a> { fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { self.word(op.as_str()); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Prefix); } fn print_expr_addr_of( @@ -1338,7 +1348,7 @@ impl<'a> State<'a> { self.print_mutability(mutability, true); } } - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Prefix); } fn print_literal(&mut self, lit: &hir::Lit) { @@ -1476,7 +1486,7 @@ impl<'a> State<'a> { self.print_literal(lit); } hir::ExprKind::Cast(expr, ty) => { - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Cast); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Cast); self.space(); self.word_space("as"); self.print_type(ty); @@ -1577,25 +1587,31 @@ impl<'a> State<'a> { self.print_block(blk); } hir::ExprKind::Assign(lhs, rhs, _) => { - self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); + self.print_expr_cond_paren(lhs, self.precedence(lhs) <= ExprPrecedence::Assign); self.space(); self.word_space("="); - self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); + self.print_expr_cond_paren(rhs, self.precedence(rhs) < ExprPrecedence::Assign); } hir::ExprKind::AssignOp(op, lhs, rhs) => { - self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); + self.print_expr_cond_paren(lhs, self.precedence(lhs) <= ExprPrecedence::Assign); self.space(); self.word(op.node.as_str()); self.word_space("="); - self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); + self.print_expr_cond_paren(rhs, self.precedence(rhs) < ExprPrecedence::Assign); } hir::ExprKind::Field(expr, ident) => { - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); + self.print_expr_cond_paren( + expr, + self.precedence(expr) < ExprPrecedence::Unambiguous, + ); self.word("."); self.print_ident(ident); } hir::ExprKind::Index(expr, index, _) => { - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); + self.print_expr_cond_paren( + expr, + self.precedence(expr) < ExprPrecedence::Unambiguous, + ); self.word("["); self.print_expr(index); self.word("]"); @@ -1609,7 +1625,7 @@ impl<'a> State<'a> { } if let Some(expr) = opt_expr { self.space(); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Jump); } } hir::ExprKind::Continue(destination) => { @@ -1623,13 +1639,13 @@ impl<'a> State<'a> { self.word("return"); if let Some(expr) = result { self.word(" "); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Jump); } } hir::ExprKind::Become(result) => { self.word("become"); self.word(" "); - self.print_expr_cond_paren(result, result.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(result, self.precedence(result) < ExprPrecedence::Jump); } hir::ExprKind::InlineAsm(asm) => { self.word("asm!"); @@ -1667,7 +1683,7 @@ impl<'a> State<'a> { } hir::ExprKind::Yield(expr, _) => { self.word_space("yield"); - self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); + self.print_expr_cond_paren(expr, self.precedence(expr) < ExprPrecedence::Jump); } hir::ExprKind::Err(_) => { self.popen(); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 9d364775445d6..483f960affd6d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -605,7 +605,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Ok(rest_snippet) = rest_snippet { - let sugg = if callee_expr.precedence() >= ExprPrecedence::Unambiguous { + let sugg = if self.precedence(callee_expr) >= ExprPrecedence::Unambiguous { vec![ (up_to_rcvr_span, "".to_string()), (rest_span, format!(".{}({rest_snippet}", segment.ident)), diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7b07e0ee939c5..ca47bb81ba2e8 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -1107,7 +1107,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - let expr_prec = self.expr.precedence(); + let expr_prec = fcx.precedence(self.expr); let needs_parens = expr_prec < ExprPrecedence::Unambiguous; let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a7d12cae7b5f3..91e38712daf7e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -6,6 +6,7 @@ //! See [`rustc_hir_analysis::check`] for more context on type checking in general. use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordMap; @@ -18,7 +19,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, HirId, QPath}; +use rustc_hir::{Attribute, ExprKind, HirId, QPath}; use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _}; use rustc_infer::infer; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; @@ -55,6 +56,30 @@ 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)| { + 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?` + // desugars to `#[allow(unreachable_code)] match ...`. Failing to + // ignore the prefix attribute in the desugaring would cause this + // suggestion: + // + // let y: u32 = x?.try_into().unwrap(); + // ++++++++++++++++++++ + // + // to be rendered as: + // + // let y: u32 = (x?).try_into().unwrap(); + // + +++++++++++++++++++++ + if attr.span.desugaring_kind().is_none() { + callback(attr); + } + } + }; + expr.precedence(&for_each_attr) + } + /// Check an expr with an expectation type, and also demand that the expr's /// evaluated type is a subtype of the expectation at the end. This is a /// *hard* requirement. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 964ef5b2106b1..d935f4c4ef6a7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -396,7 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we remove the user's `clone` call. { vec![(receiver_method.ident.span, conversion_method.name.to_string())] - } else if expr.precedence() < ExprPrecedence::Unambiguous { + } else if self.precedence(expr) < ExprPrecedence::Unambiguous { vec![ (expr.span.shrink_to_lo(), "(".to_string()), (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), @@ -1374,7 +1374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let span = expr.span.find_oldest_ancestor_in_same_ctxt(); - let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous { + let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous { vec![(span.shrink_to_hi(), ".into()".to_owned())] } else { vec![ @@ -2988,7 +2988,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`", ); - let close_paren = if expr.precedence() < ExprPrecedence::Unambiguous { + let close_paren = if self.precedence(expr) < ExprPrecedence::Unambiguous { sugg.push((expr.span.shrink_to_lo(), "(".to_string())); ")" } else { @@ -3013,7 +3013,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = src.trim_end_matches(&checked_ty.to_string()).len(); expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) }, - if expr.precedence() < ExprPrecedence::Unambiguous { + if self.precedence(expr) < ExprPrecedence::Unambiguous { // Readd `)` format!("{expected_ty})") } else { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 50bb1fb942e98..564120eda28cb 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -6,6 +6,7 @@ use std::cell::Cell; use std::{iter, slice}; +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; @@ -881,6 +882,20 @@ impl<'tcx> LateContext<'tcx> { }) } + /// Returns the effective precedence of an expression for the purpose of + /// 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)| { + for attr in self.tcx.hir().attrs(id) { + if attr.span.desugaring_kind().is_none() { + callback(attr); + } + } + }; + expr.precedence(&for_each_attr) + } + /// If the given expression is a local binding, find the initializer expression. /// If that initializer expression is another local binding, find its initializer again. /// diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 653726872c645..d574c3bfa6450 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -965,7 +965,7 @@ fn report<'tcx>( // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix { + expr_str = if !expr_is_macro_call && is_final_ufcs && cx.precedence(expr) < ExprPrecedence::Prefix { Cow::Owned(format!("({expr_str})")) } else { expr_str @@ -1005,10 +1005,10 @@ fn report<'tcx>( Node::Expr(e) => match e.kind { ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false, ExprKind::Call(..) => { - expr.precedence() < ExprPrecedence::Unambiguous + cx.precedence(expr) < ExprPrecedence::Unambiguous || matches!(expr.kind, ExprKind::Field(..)) }, - _ => expr.precedence() < e.precedence(), + _ => cx.precedence(expr) < cx.precedence(e), }, _ => false, }; @@ -1056,7 +1056,7 @@ fn report<'tcx>( Mutability::Not => "&", Mutability::Mut => "&mut ", }; - (prefix, expr.precedence() < ExprPrecedence::Prefix) + (prefix, cx.precedence(expr) < ExprPrecedence::Prefix) }, None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false), _ => ("", false), @@ -1158,7 +1158,7 @@ impl<'tcx> Dereferencing<'tcx> { }, Some(parent) if !parent.span.from_expansion() => { // Double reference might be needed at this point. - if parent.precedence() == ExprPrecedence::Unambiguous { + if cx.precedence(parent) == ExprPrecedence::Unambiguous { // Parentheses would be needed here, don't lint. *outer_pat = None; } else { diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index 12719c4f94bfd..d66771a8b5bec 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( if !prefix.is_empty() && ( // Precedence of internal expression is less than or equal to precedence of `&expr`. - arg_expression.precedence() <= ExprPrecedence::Prefix || is_range_literal(arg_expression) + cx.precedence(arg_expression) <= ExprPrecedence::Prefix || is_range_literal(arg_expression) ) { arg_snip = format!("({arg_snip})").into(); diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index bac5cf88cfbf8..d9c85e7bb8873 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -117,7 +117,7 @@ where // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < ExprPrecedence::Unambiguous { + let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && cx.precedence(scrutinee) < ExprPrecedence::Unambiguous { format!("({scrutinee_str})") } else { scrutinee_str.into() diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index 429afff9b6642..a64fd6f1dae6b 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { { let mut applicability = Applicability::MachineApplicable; let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && exp.precedence() < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { + let suggestion = if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 7038b19d27596..90bb1feb3f071 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); let needs_parens_for_prefix = - parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix); + parent_expr.is_some_and(|parent| cx.precedence(parent) > ExprPrecedence::Prefix); if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 0d5cf45a5e653..18897fbb5b8f0 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( }; if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id) - && parent.precedence() > ExprPrecedence::Cast + && cx.precedence(parent) > ExprPrecedence::Cast { sugg = format!("({sugg})"); } diff --git a/tests/ui-fulldeps/auxiliary/parser.rs b/tests/ui-fulldeps/auxiliary/parser.rs index 4ea0d814b1faa..e86e6516355d3 100644 --- a/tests/ui-fulldeps/auxiliary/parser.rs +++ b/tests/ui-fulldeps/auxiliary/parser.rs @@ -7,15 +7,15 @@ extern crate rustc_parse; extern crate rustc_session; extern crate rustc_span; -use rustc_ast::ast::{DUMMY_NODE_ID, Expr}; -use rustc_ast::mut_visit::MutVisitor; +use rustc_ast::ast::{DUMMY_NODE_ID, Attribute, Expr}; +use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::node_id::NodeId; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_errors::Diag; use rustc_parse::parser::Recovery; use rustc_session::parse::ParseSess; -use rustc_span::{DUMMY_SP, FileName, Span}; +use rustc_span::{DUMMY_SP, AttrId, FileName, Span}; pub fn parse_expr(psess: &ParseSess, source_code: &str) -> Option> { let parser = rustc_parse::unwrap_or_emit_fatal(rustc_parse::new_parser_from_source_str( @@ -48,4 +48,9 @@ impl MutVisitor for Normalize { fn visit_span(&mut self, span: &mut Span) { *span = DUMMY_SP; } + + fn visit_attribute(&mut self, attr: &mut Attribute) { + attr.id = AttrId::from_u32(0); + mut_visit::walk_attribute(self, attr); + } } diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs index 94c7964392d1d..346de6a4e5500 100644 --- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -85,6 +85,11 @@ static EXPRS: &[&str] = &[ // expressions. "match 2 { _ => 1 - 1 }", "match 2 { _ => ({ 1 }) - 1 }", + // Expressions with an outer attr have lower precedence than expressions + // with an inner attr. + "#[attr] loop {}.field", + "(#[attr] loop {}).field", + "loop { #![attr] }.field", // Grammar restriction: break value starting with a labeled loop is not // allowed, except if the break is also labeled. "break 'outer 'inner: loop {} + 2",