Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce precedence of expressions that have an outer attr #134661

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Member

@fmease fmease Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ExprPrecedence::Prefix isn't low enough. Consider the following cases involving binops:

#![feature(stmt_expr_attributes)]

macro_rules! group { ($e:expr) => { $e } }
macro_rules! extra { ($e:expr) => { #[allow()] $e } }

fn main() {                             // `-Zunpretty=expanded` on master & prefixattr:

    let _ = #[allow()] 1 + 1;           // let _ = #[allow()] 1 + 1;
    let _ = group!(#[allow()] 1) + 1;   // let _ = #[allow()] 1 + 1;
    let _ = 1 + group!(#[allow()] 1);   // let _ = 1 + #[allow()] 1;

    let _ = extra!({ 0 }) + 1;          // let _ = #[allow()] { 0 } + 1;
    let _ = extra!({ 0 } + 1);          // let _ = #[allow()] { 0 } + 1;
}

I haven't given it that much thought yet, so I don't know which specific precedence level(s) would make sense instead.

And indeed, this example is heavily inspired by the ones found in #15701 / #127436. So maybe answering this pretty-printing question is partially blocked by the open design question (meaning we can ignore it for now).

}
}
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),
}
}

2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
@@ -696,7 +696,7 @@ fn walk_local<T: MutVisitor>(vis: &mut T, local: &mut P<Local>) {
vis.visit_span(span);
}

fn walk_attribute<T: MutVisitor>(vis: &mut T, attr: &mut Attribute) {
pub fn walk_attribute<T: MutVisitor>(vis: &mut T, attr: &mut Attribute) {
let Attribute { kind, id: _, style: _, span } = attr;
match kind {
AttrKind::Normal(normal) => {
21 changes: 16 additions & 5 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}

52 changes: 34 additions & 18 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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();
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/callee.rs
Original file line number Diff line number Diff line change
@@ -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)),
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/cast.rs
Original file line number Diff line number Diff line change
@@ -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));
27 changes: 26 additions & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
@@ -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.
8 changes: 4 additions & 4 deletions compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
@@ -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 {
15 changes: 15 additions & 0 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
@@ -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.
///
10 changes: 5 additions & 5 deletions src/tools/clippy/clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
@@ -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 {
Original file line number Diff line number Diff line change
@@ -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();
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/neg_multiply.rs
Original file line number Diff line number Diff line change
@@ -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}")
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/redundant_slicing.rs
Original file line number Diff line number Diff line change
@@ -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 {
Original file line number Diff line number Diff line change
@@ -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})");
}
11 changes: 8 additions & 3 deletions tests/ui-fulldeps/auxiliary/parser.rs
Original file line number Diff line number Diff line change
@@ -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<P<Expr>> {
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);
}
}
5 changes: 5 additions & 0 deletions tests/ui-fulldeps/pprust-parenthesis-insertion.rs
Original file line number Diff line number Diff line change
@@ -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",
Loading