about summary refs log tree commit diff
path: root/tests/ui/macros/stringify.rs
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-12-11 20:46:49 +0100
committerGitHub <noreply@github.com>2023-12-11 20:46:49 +0100
commit3a0562b93c29b79aa1a9e775c2fdca068d23fd1e (patch)
treed0867c678e0300f31727a7bee636991163252778 /tests/ui/macros/stringify.rs
parentea82b110fe4078f541383acf414c1c92425539bc (diff)
parent8997215a24f5a2e79e0c329730d525769c5e61c1 (diff)
downloadrust-3a0562b93c29b79aa1a9e775c2fdca068d23fd1e.tar.gz
rust-3a0562b93c29b79aa1a9e775c2fdca068d23fd1e.zip
Rollup merge of #118726 - dtolnay:matchguardlet, r=compiler-errors
Do not parenthesize exterior struct lit inside match guards

Before this PR, the AST pretty-printer injects parentheses around expressions any time parens _could_ be needed depending on what else is in the code that surrounds that expression. But the pretty-printer did not pass around enough context to understand whether parentheses really _are_ needed on any particular expression. As a consequence, there are false positives where unneeded parentheses are being inserted.

Example:

```rust
#![feature(if_let_guard)]

macro_rules! pp {
    ($e:expr) => {
        stringify!($e)
    };
}

fn main() {
    println!("{}", pp!(match () { () if let _ = Struct {} => {} }));
}
```

**Before:**

```console
match () { () if let _ = (Struct {}) => {} }
```

**After:**

```console
match () { () if let _ = Struct {} => {} }
```

This PR introduces a bit of state that is passed across various expression printing methods to help understand accurately whether particular situations require parentheses injected by the pretty printer, and it fixes one such false positive involving match guards as shown above.

There are other parenthesization false positive cases not fixed by this PR. I intend to address these in follow-up PRs. For example here is one: the expression `{ let _ = match x {} + 1; }` is pretty-printed as `{ let _ = (match x {}) + 1; }` despite there being no reason for parentheses to appear there.
Diffstat (limited to 'tests/ui/macros/stringify.rs')
-rw-r--r--tests/ui/macros/stringify.rs21
1 files changed, 20 insertions, 1 deletions
diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs
index d65f7a8ea8c..6fc12509aad 100644
--- a/tests/ui/macros/stringify.rs
+++ b/tests/ui/macros/stringify.rs
@@ -10,6 +10,8 @@
 #![feature(coroutines)]
 #![feature(decl_macro)]
 #![feature(explicit_tail_calls)]
+#![feature(if_let_guard)]
+#![feature(let_chains)]
 #![feature(more_qualified_paths)]
 #![feature(never_patterns)]
 #![feature(raw_ref_op)]
@@ -47,7 +49,7 @@ macro_rules! c1 {
 // easy to find the cases where the two pretty-printing approaches give
 // different results.
 macro_rules! c2 {
-    ($frag:ident, [$($tt:tt)*], $s1:literal, $s2:literal) => {
+    ($frag:ident, [$($tt:tt)*], $s1:literal, $s2:literal $(,)?) => {
         assert_ne!($s1, $s2, "should use `c1!` instead");
         assert_eq!($frag!($($tt)*), $s1);
         assert_eq!(stringify!($($tt)*), $s2);
@@ -127,6 +129,23 @@ fn test_expr() {
 
     // ExprKind::Let
     c1!(expr, [ if let Some(a) = b { c } else { d } ], "if let Some(a) = b { c } else { d }");
+    c1!(expr, [ if let _ = true && false {} ], "if let _ = true && false {}");
+    c1!(expr, [ if let _ = (true && false) {} ], "if let _ = (true && false) {}");
+    macro_rules! c2_if_let {
+        ($expr:expr, $expr_expected:expr, $tokens_expected:expr $(,)?) => {
+            c2!(expr, [ if let _ = $expr {} ], $expr_expected, $tokens_expected);
+        };
+    }
+    c2_if_let!(
+        true && false,
+        "if let _ = (true && false) {}",
+        "if let _ = true && false {}",
+    );
+    c2!(expr,
+        [ match () { _ if let _ = Struct {} => {} } ],
+        "match () { _ if let _ = Struct {} => {} }",
+        "match() { _ if let _ = Struct {} => {} }",
+    );
 
     // ExprKind::If
     c1!(expr, [ if true {} ], "if true {}");