Skip to content

Commit

Permalink
Rollup merge of rust-lang#103468 - chenyukang:yukang/fix-103435-extra…
Browse files Browse the repository at this point in the history
…-parentheses, r=estebank

Fix unused lint and parser caring about spaces to won't produce invalid code

Fixes rust-lang#103435
  • Loading branch information
Manishearth authored Nov 11, 2022
2 parents 8048504 + a46af18 commit 6c35e1a
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 20 deletions.
42 changes: 29 additions & 13 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,10 +581,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,
Expand Down Expand Up @@ -781,6 +795,7 @@ impl UnusedParens {
value: &ast::Pat,
avoid_or: bool,
avoid_mut: bool,
keep_space: (bool, bool),
) {
use ast::{BindingAnnotation, PatKind};

Expand All @@ -805,7 +820,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);
}
}
}
Expand All @@ -814,7 +829,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
Expand Down Expand Up @@ -858,40 +873,41 @@ 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(_)
// The other cases do not contain sub-patterns.
| 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) {
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
24 changes: 19 additions & 5 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 18 additions & 0 deletions src/test/ui/lint/issue-103435-extra-parentheses.fixed
Original file line number Diff line number Diff line change
@@ -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
}
18 changes: 18 additions & 0 deletions src/test/ui/lint/issue-103435-extra-parentheses.rs
Original file line number Diff line number Diff line change
@@ -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
}
61 changes: 61 additions & 0 deletions src/test/ui/lint/issue-103435-extra-parentheses.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 6c35e1a

Please sign in to comment.