lint: while loop: detect if no var from the condition is mutated

This commit is contained in:
Karim Snj
2018-03-01 23:23:41 +01:00
parent 37eca59438
commit 7d35fab304
3 changed files with 131 additions and 18 deletions

View File

@@ -2140,23 +2140,45 @@ fn path_name(e: &Expr) -> Option<Name> {
None
}
fn check_infinite_loop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cond: &'tcx Expr, _block: &'tcx Block, _expr: &'tcx Expr) {
fn check_infinite_loop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, cond: &'tcx Expr, block: &'tcx Block, expr: &'tcx Expr) {
let mut mut_var_visitor = MutableVarsVisitor {
cx,
ids: HashSet::new(),
skip: false,
};
walk_expr(&mut mut_var_visitor, cond);
if !mut_var_visitor.skip && mut_var_visitor.ids.len() == 0 {
walk_expr(&mut mut_var_visitor, expr);
if mut_var_visitor.skip {
return;
}
if mut_var_visitor.ids.len() == 0 {
span_lint(
cx,
WHILE_IMMUTABLE_CONDITION,
cond.span,
"all variables in condition are immutable. This might lead to infinite loops.",
)
);
return;
}
let mut use_visitor = MutablyUsedVisitor {
cx,
ids: mut_var_visitor.ids,
any_used: false,
};
walk_block(&mut use_visitor, block);
if !use_visitor.any_used {
span_lint(
cx,
WHILE_IMMUTABLE_CONDITION,
expr.span,
"Variable in the condition are not mutated in the loop body. This might lead to infinite loops.",
);
}
}
/// Collects the set of mutable variable in an expression
/// Stops analysis if a function call is found
struct MutableVarsVisitor<'a, 'tcx: 'a> {
cx: &'a LateContext<'a, 'tcx>,
ids: HashSet<NodeId>,
@@ -2171,12 +2193,46 @@ impl<'a, 'tcx> Visitor<'tcx> for MutableVarsVisitor<'a, 'tcx> {
},
// If there is any fuction/method call… we just stop analysis
ExprCall(_, _) | ExprMethodCall(_, _, _) => self.skip = true,
ExprCall(..) | ExprMethodCall(..) => self.skip = true,
_ => walk_expr(self, ex),
}
}
fn visit_block(&mut self, _b: &'tcx Block) {}
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
}
/// checks within an expression/statement if any of the variables are used mutably
struct MutablyUsedVisitor<'a, 'tcx: 'a> {
cx: &'a LateContext<'a, 'tcx>,
ids: HashSet<NodeId>,
any_used: bool,
}
impl<'a, 'tcx> Visitor<'tcx> for MutablyUsedVisitor<'a, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr) {
if self.any_used { return; }
match ex.node {
ExprAddrOf(MutMutable, ref p) | ExprAssign(ref p, _) | ExprAssignOp(_, ref p, _) =>
if let Some(id) = check_for_mutability(self.cx, p) {
self.any_used = self.ids.contains(&id);
}
_ => walk_expr(self, ex)
}
}
fn visit_stmt(&mut self, s: &'tcx Stmt) {
match s.node {
StmtExpr(..) | StmtSemi (..) => walk_stmt(self, s),
_ => {}
}
}
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}