}
}
- let mut pre_order = FxHashMap::default();
-
- visitor.graph.iter().for_each(|(_, next)| {
- next.iter().for_each(|i| {
- *pre_order.entry(*i).or_insert(0) += 1;
- });
- });
-
for (id, span, ident) in param_span {
// if the variable is not used in recursion, it would be marked as unused
- if !visitor.has_side_effect.contains(&id)
- && *pre_order.get(&id).unwrap_or(&0) > 0
- && visitor.graph.contains_key(&id)
- {
- span_lint_and_sugg(
- cx,
- ONLY_USED_IN_RECURSION,
- span,
- "parameter is only used in recursion with no side-effects",
- "if this is intentional, prefix with an underscore",
- format!("_{}", ident.name.as_str()),
- Applicability::MaybeIncorrect,
- );
+ if !visitor.has_side_effect.contains(&id) {
+ let mut queue = VecDeque::new();
+ let mut visited = FxHashSet::default();
+
+ queue.push_back(id);
+
+ while let Some(id) = queue.pop_front() {
+ if let Some(next) = visitor.graph.get(&id) {
+ for i in next {
+ if !visited.contains(i) {
+ visited.insert(id);
+ queue.push_back(*i);
+ }
+ }
+ }
+ }
+
+ if visited.contains(&id) {
+ span_lint_and_sugg(
+ cx,
+ ONLY_USED_IN_RECURSION,
+ span,
+ "parameter is only used in recursion with no side-effects",
+ "if this is intentional, prefix with an underscore",
+ format!("_{}", ident.name.as_str()),
+ Applicability::MaybeIncorrect,
+ );
+ }
}
}
}
StmtKind::Local(Local {
pat, init: Some(init), ..
}) => {
- self.visit_pat_expr(pat, init);
+ self.visit_pat_expr(pat, init, false);
},
StmtKind::Item(i) => {
let item = self.ty_ctx.hir().item(i);
self.visit_bin_op(lhs, rhs);
},
ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
- ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init),
+ ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
ExprKind::If(bind, then_expr, else_expr) => {
self.visit_if(bind, then_expr, else_expr);
},
let lhs_vars = std::mem::take(&mut self.ret_vars);
self.visit_expr(rhs);
let rhs_vars = std::mem::take(&mut self.ret_vars);
- self.connect_assign(&lhs_vars, &rhs_vars);
+ self.connect_assign(&lhs_vars, &rhs_vars, false);
},
}
}
}
}
- fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>) {
+ fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
match (&pat.kind, &expr.kind) {
(PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
self.ret_vars = izip!(*pats, *exprs)
.flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr);
+ self.visit_pat_expr(pat, expr, connect_self);
std::mem::take(&mut self.ret_vars)
})
.collect();
(PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
let mut vars = izip!(*front_exprs, *exprs)
.flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr);
+ self.visit_pat_expr(pat, expr, connect_self);
std::mem::take(&mut self.ret_vars)
})
.collect();
self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
.flat_map(|(pat, expr)| {
- self.visit_pat_expr(pat, expr);
+ self.visit_pat_expr(pat, expr, connect_self);
std::mem::take(&mut self.ret_vars)
})
.collect();
pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
self.visit_expr(expr);
let rhs_vars = std::mem::take(&mut self.ret_vars);
- self.connect_assign(&lhs_vars, &rhs_vars);
+ self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
self.ret_vars = rhs_vars;
},
}
then {
izip!(self.params.clone(), args)
.for_each(|(pat, expr)| {
- self.visit_pat_expr(pat, expr);
+ self.visit_pat_expr(pat, expr, true);
self.ret_vars.clear();
});
} else {
then {
izip!(self.params.clone(), args.iter())
.for_each(|(pat, expr)| {
- self.visit_pat_expr(pat, expr);
+ self.visit_pat_expr(pat, expr, true);
self.ret_vars.clear();
});
} else {
self.contains_side_effect = false;
// this would visit `expr` multiple times
// but couldn't think of a better way
- self.visit_pat_expr(arm.pat, expr);
+ self.visit_pat_expr(arm.pat, expr, false);
let mut vars = std::mem::take(&mut self.ret_vars);
let _ = arm.guard.as_ref().map(|guard| {
self.visit_expr(match guard {
self.ret_vars.append(&mut expr_vars);
}
- fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)]) {
+ fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
// if mutable dereference is on assignment it can have side-effect
// (this can lead to parameter mutable dereference and change the original value)
// too hard to detect whether this value is from parameter, so this would all
// unwrap is possible since rhs is not empty
let rhs_first = rhs.first().unwrap();
for (id, _) in lhs.iter() {
- self.graph
- .entry(*id)
- .or_insert_with(FxHashSet::default)
- .insert(rhs_first.0);
+ if connect_self || *id != rhs_first.0 {
+ self.graph
+ .entry(*id)
+ .or_insert_with(FxHashSet::default)
+ .insert(rhs_first.0);
+ }
}
let rhs = rhs.iter();
izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
- self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
+ if connect_self || from.0 != to.0 {
+ self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
+ }
});
}