mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
Parenthesize or-patterns in prefix pattern positions in pretty printer
The AST pretty printer was dropping parentheses around or-patterns
when they appeared inside `@` bindings, `&` references, or `box`
patterns. For example:
- `v @ (1 | 2 | 3)` was printed as `v @ 1 | 2 | 3`
- `&(1 | 2 | 3)` was printed as `&1 | 2 | 3`
- `box (1 | 2 | 3)` was printed as `box 1 | 2 | 3`
Since `|` has the lowest precedence among pattern operators, all of
these are parsed incorrectly without parentheses — e.g. `v @ 1 | 2 | 3`
becomes `(v @ 1) | 2 | 3`, binding `v` only to the first alternative.
This caused E0408 ("variable not bound in all patterns") when the
expanded output was fed back to the compiler, affecting crates like
html5ever and wgpu-core that use macros expanding to or-patterns
after `@`.
The fix adds a `print_pat_paren_if_or` helper that wraps `PatKind::Or`
subpatterns in parentheses, and uses it in the `@`, `&`, and `box`
printing arms. This is similar in spirit to the existing `FixupContext`
parenthesization approach used for expression printing.
Signed-off-by: Andrew V. Teylu <andrew.teylu@vector.com>
This commit is contained in:
@@ -1749,6 +1749,23 @@ fn print_qpath(&mut self, path: &ast::Path, qself: &ast::QSelf, colons_before_pa
|
||||
}
|
||||
}
|
||||
|
||||
/// Print a pattern, parenthesizing it if it is an or-pattern (`A | B`).
|
||||
///
|
||||
/// Or-patterns have the lowest precedence among patterns, so they need
|
||||
/// parentheses when nested inside `@` bindings, `&` references, or `box`
|
||||
/// patterns — otherwise `x @ A | B` parses as `(x @ A) | B`, `&A | B`
|
||||
/// parses as `(&A) | B`, etc.
|
||||
fn print_pat_paren_if_or(&mut self, pat: &ast::Pat) {
|
||||
let needs_paren = matches!(pat.kind, PatKind::Or(..));
|
||||
if needs_paren {
|
||||
self.popen();
|
||||
}
|
||||
self.print_pat(pat);
|
||||
if needs_paren {
|
||||
self.pclose();
|
||||
}
|
||||
}
|
||||
|
||||
fn print_pat(&mut self, pat: &ast::Pat) {
|
||||
self.maybe_print_comment(pat.span.lo());
|
||||
self.ann.pre(self, AnnNode::Pat(pat));
|
||||
@@ -1776,7 +1793,7 @@ fn print_pat(&mut self, pat: &ast::Pat) {
|
||||
if let Some(p) = sub {
|
||||
self.space();
|
||||
self.word_space("@");
|
||||
self.print_pat(p);
|
||||
self.print_pat_paren_if_or(p);
|
||||
}
|
||||
}
|
||||
PatKind::TupleStruct(qself, path, elts) => {
|
||||
@@ -1848,7 +1865,7 @@ fn print_pat(&mut self, pat: &ast::Pat) {
|
||||
}
|
||||
PatKind::Box(inner) => {
|
||||
self.word("box ");
|
||||
self.print_pat(inner);
|
||||
self.print_pat_paren_if_or(inner);
|
||||
}
|
||||
PatKind::Deref(inner) => {
|
||||
self.word("deref!");
|
||||
@@ -1872,7 +1889,7 @@ fn print_pat(&mut self, pat: &ast::Pat) {
|
||||
self.print_pat(inner);
|
||||
self.pclose();
|
||||
} else {
|
||||
self.print_pat(inner);
|
||||
self.print_pat_paren_if_or(inner);
|
||||
}
|
||||
}
|
||||
PatKind::Expr(e) => self.print_expr(e, FixupContext::default()),
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#![feature(prelude_import)]
|
||||
#![no_std]
|
||||
#![feature(box_patterns)]
|
||||
extern crate std;
|
||||
#[prelude_import]
|
||||
use ::std::prelude::rust_2015::*;
|
||||
|
||||
//@ pretty-compare-only
|
||||
//@ pretty-mode:expanded
|
||||
//@ pp-exact:or-pattern-paren.pp
|
||||
|
||||
macro_rules! or_pat { ($($name:pat),+) => { $($name)|+ } }
|
||||
|
||||
fn check_at(x: Option<i32>) {
|
||||
match x {
|
||||
Some(v @ (1 | 2 | 3)) =>
|
||||
|
||||
|
||||
{
|
||||
::std::io::_print(format_args!("{0}\n", v));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn check_ref(x: &i32) { match x { &(1 | 2 | 3) => {} _ => {} } }
|
||||
fn check_box(x: Box<i32>) { match x { box (1 | 2 | 3) => {} _ => {} } }
|
||||
fn main() { check_at(Some(2)); check_ref(&1); check_box(Box::new(1)); }
|
||||
@@ -0,0 +1,36 @@
|
||||
#![feature(box_patterns)]
|
||||
|
||||
//@ pretty-compare-only
|
||||
//@ pretty-mode:expanded
|
||||
//@ pp-exact:or-pattern-paren.pp
|
||||
|
||||
macro_rules! or_pat {
|
||||
($($name:pat),+) => { $($name)|+ }
|
||||
}
|
||||
|
||||
fn check_at(x: Option<i32>) {
|
||||
match x {
|
||||
Some(v @ or_pat!(1, 2, 3)) => println!("{v}"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ref(x: &i32) {
|
||||
match x {
|
||||
&or_pat!(1, 2, 3) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_box(x: Box<i32>) {
|
||||
match x {
|
||||
box or_pat!(1, 2, 3) => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
check_at(Some(2));
|
||||
check_ref(&1);
|
||||
check_box(Box::new(1));
|
||||
}
|
||||
Reference in New Issue
Block a user