mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rollup merge of #134600 - dtolnay:chainedcomparison, r=oli-obk
Fix parenthesization of chained comparisons by pretty-printer
Example:
```rust
macro_rules! repro {
() => {
1 < 2
};
}
fn main() {
let _ = repro!() == false;
}
```
Previously `-Zunpretty=expanded` would pretty-print this syntactically invalid output: `fn main() { let _ = 1 < 2 == false; }`
```console
error: comparison operators cannot be chained
--> <anon>:8:23
|
8 | fn main() { let _ = 1 < 2 == false; }
| ^ ^^
|
help: parenthesize the comparison
|
8 | fn main() { let _ = (1 < 2) == false; }
| + +
```
With the fix, it will print `fn main() { let _ = (1 < 2) == false; }`.
Making `-Zunpretty=expanded` consistently produce syntactically valid Rust output is important because that is what makes it possible for `cargo expand` to format and perform filtering on the expanded code.
## Review notes
According to `rg '\.fixity\(\)' compiler/` the `fixity` function is called only 3 places:
- https://github.com/rust-lang/rust/blob/13170cd787cb733ed24842ee825bcbd98dc01476/compiler/rustc_ast_pretty/src/pprust/state/expr.rs#L283-L287
- https://github.com/rust-lang/rust/blob/13170cd787cb733ed24842ee825bcbd98dc01476/compiler/rustc_hir_pretty/src/lib.rs#L1295-L1299
- https://github.com/rust-lang/rust/blob/13170cd787cb733ed24842ee825bcbd98dc01476/compiler/rustc_parse/src/parser/expr.rs#L282-L289
The 2 pretty printers definitely want to treat comparisons using `Fixity::None`. That's the whole bug being fixed. Meanwhile, the parser's `Fixity::None` codepath is previously unreachable as indicated by the comment, so as long as `Fixity::None` here behaves exactly the way that `Fixity::Left` used to behave, you can tell that this PR definitely does not constitute any behavior change for the parser.
My guess for why comparison operators were set to `Fixity::Left` instead of `Fixity::None` is that it's a very old workaround for giving a good chained comparisons diagnostic (like what I pasted above). Nowadays that is handled by a different dedicated codepath.
This commit is contained in:
@@ -153,9 +153,10 @@ pub fn fixity(&self) -> Fixity {
|
||||
match *self {
|
||||
Assign | AssignOp(_) => Fixity::Right,
|
||||
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
|
||||
| BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual
|
||||
| LAnd | LOr => Fixity::Left,
|
||||
DotDot | DotDotEq => Fixity::None,
|
||||
| BitXor | BitOr | LAnd | LOr => Fixity::Left,
|
||||
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
|
||||
Fixity::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -279,13 +279,9 @@ pub(super) fn parse_expr_assoc_rest_with(
|
||||
break;
|
||||
}
|
||||
|
||||
let fixity = op.fixity();
|
||||
let min_prec = match fixity {
|
||||
let min_prec = match op.fixity() {
|
||||
Fixity::Right => Bound::Included(prec),
|
||||
Fixity::Left => Bound::Excluded(prec),
|
||||
// We currently have no non-associative operators that are not handled above by
|
||||
// the special cases. The code is here only for future convenience.
|
||||
Fixity::None => Bound::Excluded(prec),
|
||||
Fixity::Left | Fixity::None => Bound::Excluded(prec),
|
||||
};
|
||||
let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
|
||||
let attrs = this.parse_outer_attributes()?;
|
||||
@@ -337,10 +333,6 @@ pub(super) fn parse_expr_assoc_rest_with(
|
||||
self.dcx().span_bug(span, "AssocOp should have been handled by special case")
|
||||
}
|
||||
};
|
||||
|
||||
if let Fixity::None = fixity {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((lhs, parsed_something))
|
||||
|
||||
@@ -61,6 +61,9 @@
|
||||
"(2 + 2) * 2",
|
||||
"2 * (2 + 2)",
|
||||
"2 + 2 + 2",
|
||||
// Right-associative operator.
|
||||
"2 += 2 += 2",
|
||||
"(2 += 2) += 2",
|
||||
// Return has lower precedence than a binary operator.
|
||||
"(return 2) + 2",
|
||||
"2 + (return 2)", // FIXME: no parenthesis needed.
|
||||
@@ -89,6 +92,13 @@
|
||||
// allowed, except if the break is also labeled.
|
||||
"break 'outer 'inner: loop {} + 2",
|
||||
"break ('inner: loop {} + 2)",
|
||||
// Grammar restriction: ranges cannot be the endpoint of another range.
|
||||
"(2..2)..2",
|
||||
"2..(2..2)",
|
||||
"(2..2)..",
|
||||
"..(2..2)",
|
||||
// Grammar restriction: comparison operators cannot be chained (1 < 2 == false).
|
||||
"((1 < 2) == false) as usize",
|
||||
// Grammar restriction: the value in let-else is not allowed to end in a
|
||||
// curly brace.
|
||||
"{ let _ = 1 + 1 else {}; }",
|
||||
@@ -113,10 +123,6 @@
|
||||
"if let _ = () && (Struct {}).x {}",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer produces invalid syntax. `(1 < 2 == false) as usize`
|
||||
"((1 < 2) == false) as usize",
|
||||
*/
|
||||
/*
|
||||
// FIXME: pretty-printer produces invalid syntax. `for _ in 1..{ 2 } {}`
|
||||
"for _ in (1..{ 2 }) {}",
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user