about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_lint/src/unused.rs42
-rw-r--r--compiler/rustc_parse/src/errors.rs6
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs24
-rw-r--r--src/test/ui/lint/issue-103435-extra-parentheses.fixed18
-rw-r--r--src/test/ui/lint/issue-103435-extra-parentheses.rs18
-rw-r--r--src/test/ui/lint/issue-103435-extra-parentheses.stderr61
6 files changed, 149 insertions, 20 deletions
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 46706e49844..d4b083de276 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -565,10 +565,24 @@ trait UnusedDelimLint {
             lint.set_arg("delim", Self::DELIM_STR);
             lint.set_arg("item", msg);
             if let Some((lo, hi)) = spans {
-                let replacement = vec![
-                    (lo, if keep_space.0 { " ".into() } else { "".into() }),
-                    (hi, if keep_space.1 { " ".into() } else { "".into() }),
-                ];
+                let sm = cx.sess().source_map();
+                let lo_replace =
+                    if keep_space.0 &&
+                        let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(" ") {
+                        " ".to_string()
+                        } else {
+                            "".to_string()
+                        };
+
+                let hi_replace =
+                    if keep_space.1 &&
+                        let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(" ") {
+                        " ".to_string()
+                        } else {
+                            "".to_string()
+                        };
+
+                let replacement = vec![(lo, lo_replace), (hi, hi_replace)];
                 lint.multipart_suggestion(
                     fluent::suggestion,
                     replacement,
@@ -765,6 +779,7 @@ impl UnusedParens {
         value: &ast::Pat,
         avoid_or: bool,
         avoid_mut: bool,
+        keep_space: (bool, bool),
     ) {
         use ast::{BindingAnnotation, PatKind};
 
@@ -789,7 +804,7 @@ impl UnusedParens {
             } else {
                 None
             };
-            self.emit_unused_delims(cx, value.span, spans, "pattern", (false, false));
+            self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space);
         }
     }
 }
@@ -798,7 +813,7 @@ impl EarlyLintPass for UnusedParens {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         match e.kind {
             ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
-                self.check_unused_parens_pat(cx, pat, false, false);
+                self.check_unused_parens_pat(cx, pat, false, false, (true, true));
             }
             // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
             // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still
@@ -842,6 +857,7 @@ impl EarlyLintPass for UnusedParens {
 
     fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
         use ast::{Mutability, PatKind::*};
+        let keep_space = (false, false);
         match &p.kind {
             // Do not lint on `(..)` as that will result in the other arms being useless.
             Paren(_)
@@ -849,33 +865,33 @@ impl EarlyLintPass for UnusedParens {
             | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {},
             // These are list-like patterns; parens can always be removed.
             TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
-                self.check_unused_parens_pat(cx, p, false, false);
+                self.check_unused_parens_pat(cx, p, false, false, keep_space);
             },
             Struct(_, _, fps, _) => for f in fps {
-                self.check_unused_parens_pat(cx, &f.pat, false, false);
+                self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
             },
             // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
-            Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false),
+            Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
             // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
             // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
-            Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not),
+            Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
         }
     }
 
     fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
         if let StmtKind::Local(ref local) = s.kind {
-            self.check_unused_parens_pat(cx, &local.pat, true, false);
+            self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false));
         }
 
         <Self as UnusedDelimLint>::check_stmt(self, cx, s)
     }
 
     fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
-        self.check_unused_parens_pat(cx, &param.pat, true, false);
+        self.check_unused_parens_pat(cx, &param.pat, true, false, (false, false));
     }
 
     fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
-        self.check_unused_parens_pat(cx, &arm.pat, false, false);
+        self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
     }
 
     fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index e3acc11811f..d59982f7063 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1165,10 +1165,12 @@ pub(crate) struct ParenthesesInForHead {
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(suggestion, applicability = "machine-applicable")]
 pub(crate) struct ParenthesesInForHeadSugg {
-    #[suggestion_part(code = "")]
+    #[suggestion_part(code = "{left_snippet}")]
     pub left: Span,
-    #[suggestion_part(code = "")]
+    pub left_snippet: String,
+    #[suggestion_part(code = "{right_snippet}")]
     pub right: Span,
+    pub right_snippet: String,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index c609aa93da3..0bbe073fe2a 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -1653,15 +1653,29 @@ impl<'a> Parser<'a> {
             (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => {
                 self.bump();
 
+                let sm = self.sess.source_map();
+                let left = begin_par_sp;
+                let right = self.prev_token.span;
+                let left_snippet = if let Ok(snip) = sm.span_to_prev_source(left) &&
+                        !snip.ends_with(" ") {
+                                " ".to_string()
+                            } else {
+                                "".to_string()
+                            };
+
+                let right_snippet = if let Ok(snip) = sm.span_to_next_source(right) &&
+                        !snip.starts_with(" ") {
+                                " ".to_string()
+                            } else {
+                                "".to_string()
+                        };
+
                 self.sess.emit_err(ParenthesesInForHead {
-                    span: vec![begin_par_sp, self.prev_token.span],
+                    span: vec![left, right],
                     // With e.g. `for (x) in y)` this would replace `(x) in y)`
                     // with `x) in y)` which is syntactically invalid.
                     // However, this is prevented before we get here.
-                    sugg: ParenthesesInForHeadSugg {
-                        left: begin_par_sp,
-                        right: self.prev_token.span,
-                    },
+                    sugg: ParenthesesInForHeadSugg { left, right, left_snippet, right_snippet },
                 });
 
                 // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.fixed b/src/test/ui/lint/issue-103435-extra-parentheses.fixed
new file mode 100644
index 00000000000..2b01b414baa
--- /dev/null
+++ b/src/test/ui/lint/issue-103435-extra-parentheses.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+#![deny(unused_parens)]
+
+fn main() {
+    if let Some(_) = Some(1) {}
+    //~^ ERROR unnecessary parentheses around pattern
+
+    for _x in 1..10 {}
+    //~^ ERROR unnecessary parentheses around pattern
+
+    if 2 == 1 {}
+    //~^ ERROR unnecessary parentheses around `if` condition
+
+    // reported by parser
+    for _x in 1..10 {}
+    //~^ ERROR expected one of
+    //~| ERROR unexpected parentheses surrounding
+}
diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.rs b/src/test/ui/lint/issue-103435-extra-parentheses.rs
new file mode 100644
index 00000000000..8261610cf56
--- /dev/null
+++ b/src/test/ui/lint/issue-103435-extra-parentheses.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+#![deny(unused_parens)]
+
+fn main() {
+    if let(Some(_))= Some(1) {}
+    //~^ ERROR unnecessary parentheses around pattern
+
+    for(_x)in 1..10 {}
+    //~^ ERROR unnecessary parentheses around pattern
+
+    if(2 == 1){}
+    //~^ ERROR unnecessary parentheses around `if` condition
+
+    // reported by parser
+    for(_x in 1..10){}
+    //~^ ERROR expected one of
+    //~| ERROR unexpected parentheses surrounding
+}
diff --git a/src/test/ui/lint/issue-103435-extra-parentheses.stderr b/src/test/ui/lint/issue-103435-extra-parentheses.stderr
new file mode 100644
index 00000000000..29c41c91050
--- /dev/null
+++ b/src/test/ui/lint/issue-103435-extra-parentheses.stderr
@@ -0,0 +1,61 @@
+error: expected one of `)`, `,`, `@`, or `|`, found keyword `in`
+  --> $DIR/issue-103435-extra-parentheses.rs:15:12
+   |
+LL |     for(_x in 1..10){}
+   |            ^^ expected one of `)`, `,`, `@`, or `|`
+
+error: unexpected parentheses surrounding `for` loop head
+  --> $DIR/issue-103435-extra-parentheses.rs:15:8
+   |
+LL |     for(_x in 1..10){}
+   |        ^           ^
+   |
+help: remove parentheses in `for` loop
+   |
+LL -     for(_x in 1..10){}
+LL +     for _x in 1..10 {}
+   |
+
+error: unnecessary parentheses around pattern
+  --> $DIR/issue-103435-extra-parentheses.rs:5:11
+   |
+LL |     if let(Some(_))= Some(1) {}
+   |           ^       ^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-103435-extra-parentheses.rs:2:9
+   |
+LL | #![deny(unused_parens)]
+   |         ^^^^^^^^^^^^^
+help: remove these parentheses
+   |
+LL -     if let(Some(_))= Some(1) {}
+LL +     if let Some(_) = Some(1) {}
+   |
+
+error: unnecessary parentheses around pattern
+  --> $DIR/issue-103435-extra-parentheses.rs:8:8
+   |
+LL |     for(_x)in 1..10 {}
+   |        ^  ^
+   |
+help: remove these parentheses
+   |
+LL -     for(_x)in 1..10 {}
+LL +     for _x in 1..10 {}
+   |
+
+error: unnecessary parentheses around `if` condition
+  --> $DIR/issue-103435-extra-parentheses.rs:11:7
+   |
+LL |     if(2 == 1){}
+   |       ^      ^
+   |
+help: remove these parentheses
+   |
+LL -     if(2 == 1){}
+LL +     if 2 == 1 {}
+   |
+
+error: aborting due to 5 previous errors
+