-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Reduce precedence of expressions that have an outer attr #134661
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1450,11 +1450,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), | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now with #134847, you also need to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. Great catch. |
||
|
@@ -1463,7 +1472,7 @@ impl Expr { | |
| ExprKind::Yield(YieldKind::Prefix(value)) | ||
| ExprKind::Yeet(value) => match value { | ||
Some(_) => ExprPrecedence::Jump, | ||
None => ExprPrecedence::Unambiguous, | ||
None => prefix_attrs_precedence(&self.attrs), | ||
}, | ||
|
||
ExprKind::Become(_) => ExprPrecedence::Jump, | ||
|
@@ -1490,7 +1499,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::Use(..) | ||
|
@@ -1525,7 +1534,7 @@ impl Expr { | |
| ExprKind::While(..) | ||
| ExprKind::Yield(YieldKind::Postfix(..)) | ||
| ExprKind::Err(_) | ||
| ExprKind::Dummy => ExprPrecedence::Unambiguous, | ||
| ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2285,12 +2285,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)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could instead be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The double indirection is quite something but I guess it's no worse than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The If the - 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);
+ fn precedence(&self, mut expr: &hir::Expr<'_>) -> ExprPrecedence {
+ while let hir::ExprKind::DropTemps(inner, ..) = expr.kind {
+ expr = inner;
+ }
+ let for_each_attr = |callback: &mut dyn FnMut(&hir::Attribute)| {
+ self.attrs(expr.hir_id).iter().for_each(callback);
};
expr.precedence(&for_each_attr)
} which is fine but I think less straightforward than the current approach. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I missed that! Thanks for clearing it up, I agree the current approach is fine then. |
||
) -> 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(), | ||
} | ||
} | ||
|
||
|
@@ -2315,7 +2326,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(..) | ||
|
@@ -2337,9 +2348,9 @@ impl Expr<'_> { | |
| ExprKind::Type(..) | ||
| ExprKind::UnsafeBinderCast(..) | ||
| ExprKind::Use(..) | ||
| ExprKind::Err(_) => ExprPrecedence::Unambiguous, | ||
| ExprKind::Err(_) => prefix_attrs_precedence(), | ||
|
||
ExprKind::DropTemps(expr, ..) => expr.precedence(), | ||
ExprKind::DropTemps(expr, ..) => expr.precedence(for_each_attr), | ||
} | ||
} | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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: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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that bug is orthogonal to this one.
The bug fixed by this PR is about when an outer expression A contains a subexpression B where B contains an attribute, and in the prettyprinter-produced code the attribute ended up applying to parts of A instead of only to B. This is fixed by having A observe a different effective precedence for its subexpression B, making A generate parentheses around the subexpression containing the attribute, i.e. the attribute will end up inside the parentheses.
In contrast, the bug you have identified is about an outer expression A that has an attribute, and a subexpression B that has no attribute. In this case when parentheses are inserted, the attribute will end up outside the parentheses.
I will fix that as well but I expect it will involve unrelated code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix: #142476