rustc: update the unnecessary parens lint for struct literals.

Things like `match X { x: 1 } { ... }` now need to be written with
parentheses, so the lint should avoid warning in cases like that.
This commit is contained in:
Huon Wilson 2014-06-25 09:00:46 +10:00
parent 8fe47bc3bb
commit 64019e764f
2 changed files with 74 additions and 13 deletions

View file

@ -975,14 +975,52 @@ declare_lint!(UNNECESSARY_PARENS, Warn,
pub struct UnnecessaryParens; pub struct UnnecessaryParens;
impl UnnecessaryParens { impl UnnecessaryParens {
fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str) { fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str,
struct_lit_needs_parens: bool) {
match value.node { match value.node {
ast::ExprParen(_) => { ast::ExprParen(ref inner) => {
let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&**inner);
if !necessary {
cx.span_lint(UNNECESSARY_PARENS, value.span, cx.span_lint(UNNECESSARY_PARENS, value.span,
format!("unnecessary parentheses around {}", msg).as_slice()) format!("unnecessary parentheses around {}",
msg).as_slice())
}
} }
_ => {} _ => {}
} }
/// Expressions that syntatically contain an "exterior" struct
/// literal i.e. not surrounded by any parens or other
/// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo
/// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X {
/// y: 1 }) == foo` does not.
fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
match value.node {
ast::ExprStruct(..) => true,
ast::ExprAssign(ref lhs, ref rhs) |
ast::ExprAssignOp(_, ref lhs, ref rhs) |
ast::ExprBinary(_, ref lhs, ref rhs) => {
// X { y: 1 } + X { y: 2 }
contains_exterior_struct_lit(&**lhs) ||
contains_exterior_struct_lit(&**rhs)
}
ast::ExprUnary(_, ref x) |
ast::ExprCast(ref x, _) |
ast::ExprField(ref x, _, _) |
ast::ExprIndex(ref x, _) => {
// &X { y: 1 }, X { y: 1 }.y
contains_exterior_struct_lit(&**x)
}
ast::ExprMethodCall(_, _, ref exprs) => {
// X { y: 1 }.bar(...)
contains_exterior_struct_lit(&**exprs.get(0))
}
_ => false
}
}
} }
} }
@ -992,16 +1030,16 @@ impl LintPass for UnnecessaryParens {
} }
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) { fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
let (value, msg) = match e.node { let (value, msg, struct_lit_needs_parens) = match e.node {
ast::ExprIf(cond, _, _) => (cond, "`if` condition"), ast::ExprIf(cond, _, _) => (cond, "`if` condition", true),
ast::ExprWhile(cond, _) => (cond, "`while` condition"), ast::ExprWhile(cond, _) => (cond, "`while` condition", true),
ast::ExprMatch(head, _) => (head, "`match` head expression"), ast::ExprMatch(head, _) => (head, "`match` head expression", true),
ast::ExprRet(Some(value)) => (value, "`return` value"), ast::ExprRet(Some(value)) => (value, "`return` value", false),
ast::ExprAssign(_, value) => (value, "assigned value"), ast::ExprAssign(_, value) => (value, "assigned value", false),
ast::ExprAssignOp(_, _, value) => (value, "assigned value"), ast::ExprAssignOp(_, _, value) => (value, "assigned value", false),
_ => return _ => return
}; };
self.check_unnecessary_parens_core(cx, &*value, msg); self.check_unnecessary_parens_core(cx, &*value, msg, struct_lit_needs_parens);
} }
fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) { fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
@ -1015,7 +1053,7 @@ impl LintPass for UnnecessaryParens {
}, },
_ => return _ => return
}; };
self.check_unnecessary_parens_core(cx, &*value, msg); self.check_unnecessary_parens_core(cx, &*value, msg, false);
} }
} }

View file

@ -10,18 +10,41 @@
#![deny(unnecessary_parens)] #![deny(unnecessary_parens)]
#[deriving(Eq, PartialEq)]
struct X { y: bool }
impl X {
fn foo(&self) -> bool { self.y }
}
fn foo() -> int { fn foo() -> int {
return (1); //~ ERROR unnecessary parentheses around `return` value return (1); //~ ERROR unnecessary parentheses around `return` value
} }
fn bar() -> X {
return (X { y: true }); //~ ERROR unnecessary parentheses around `return` value
}
fn main() { fn main() {
foo(); foo();
bar();
if (true) {} //~ ERROR unnecessary parentheses around `if` condition if (true) {} //~ ERROR unnecessary parentheses around `if` condition
while (true) {} //~ ERROR unnecessary parentheses around `while` condition while (true) {} //~ ERROR unnecessary parentheses around `while` condition
match (true) { //~ ERROR unnecessary parentheses around `match` head expression match (true) { //~ ERROR unnecessary parentheses around `match` head expression
_ => {} _ => {}
} }
let v = X { y: false };
// struct lits needs parens, so these shouldn't warn.
if (v == X { y: true }) {}
if (X { y: true } == v) {}
if (X { y: false }.y) {}
while (X { y: false }.foo()) {}
while (true | X { y: false }.y) {}
match (X { y: false }) {
_ => {}
}
let mut _a = (0); //~ ERROR unnecessary parentheses around assigned value let mut _a = (0); //~ ERROR unnecessary parentheses around assigned value
_a = (0); //~ ERROR unnecessary parentheses around assigned value _a = (0); //~ ERROR unnecessary parentheses around assigned value
_a += (1); //~ ERROR unnecessary parentheses around assigned value _a += (1); //~ ERROR unnecessary parentheses around assigned value