use std::collections::VecDeque;
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lint_allowed;
use itertools::{izip, Itertools};
use rustc_ast::{walk_list, Label, Mutability};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
+use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
use rustc_hir::{
- Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
+ Arm, Closure, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
QPath, Stmt, StmtKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
/// and the assigned variables are also only in recursion, it is useless.
///
/// ### Known problems
+ /// Too many code paths in the linting code are currently untested and prone to produce false
+ /// positives or are prone to have performance implications.
+ ///
/// In some cases, this would not catch all useless arguments.
///
/// ```rust
/// # print!("{}", f(1));
/// # }
/// ```
- #[clippy::version = "1.60.0"]
+ #[clippy::version = "1.61.0"]
pub ONLY_USED_IN_RECURSION,
- complexity,
+ nursery,
"arguments that is only used in recursion can be removed"
}
declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
_: Span,
id: HirId,
) {
+ if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
+ return;
+ }
if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
let def_id = id.owner.to_def_id();
let data = cx.tcx.def_path(def_id).data;
is_method: matches!(kind, FnKind::Method(..)),
has_self,
ty_res,
- ty_ctx: cx.tcx,
+ tcx: cx.tcx,
+ visited_exprs: FxHashSet::default(),
};
visitor.visit_expr(&body.value);
}
pub fn is_primitive(ty: Ty<'_>) -> bool {
- match ty.kind() {
- ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
- ty::Ref(_, t, _) => is_primitive(*t),
- _ => false,
- }
+ let ty = ty.peel_refs();
+ ty.is_primitive() || ty.is_str()
}
pub fn is_array(ty: Ty<'_>) -> bool {
- match ty.kind() {
- ty::Array(..) | ty::Slice(..) => true,
- ty::Ref(_, t, _) => is_array(*t),
- _ => false,
- }
+ let ty = ty.peel_refs();
+ ty.is_array() || ty.is_array_slice()
}
/// This builds the graph of side effect.
/// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
///
-/// There are some exmaple in following code:
+/// There are some example in following code:
/// ```rust, ignore
/// let b = 1;
/// let a = b; // a -> b
is_method: bool,
has_self: bool,
ty_res: &'tcx TypeckResults<'tcx>,
- ty_ctx: TyCtxt<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ visited_exprs: FxHashSet<HirId>,
}
impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
- fn visit_block(&mut self, b: &'tcx Block<'tcx>) {
- b.stmts.iter().for_each(|stmt| {
- self.visit_stmt(stmt);
- self.ret_vars.clear();
- });
- walk_list!(self, visit_expr, b.expr);
- }
-
fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
match s.kind {
StmtKind::Local(Local {
pat, init: Some(init), ..
}) => {
self.visit_pat_expr(pat, init, false);
- self.ret_vars.clear();
},
- StmtKind::Item(i) => {
- let item = self.ty_ctx.hir().item(i);
- self.visit_item(item);
- self.ret_vars.clear();
- },
- StmtKind::Expr(e) | StmtKind::Semi(e) => {
- self.visit_expr(e);
- self.ret_vars.clear();
+ StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
+ walk_stmt(self, s);
},
StmtKind::Local(_) => {},
}
+ self.ret_vars.clear();
}
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+ if !self.visited_exprs.insert(ex.hir_id) {
+ return;
+ }
match ex.kind {
ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
self.ret_vars = exprs
},
ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
// since analysing the closure is not easy, just set all variables in it to side-effect
- ExprKind::Closure(_, _, body_id, _, _) => {
- let body = self.ty_ctx.hir().body(body_id);
+ ExprKind::Closure(&Closure { body, .. }) => {
+ let body = self.tcx.hir().body(body);
self.visit_body(body);
let vars = std::mem::take(&mut self.ret_vars);
self.add_side_effect(vars);
let mut vars = std::mem::take(&mut self.ret_vars);
let _ = arm.guard.as_ref().map(|guard| {
self.visit_expr(match guard {
- Guard::If(expr) | Guard::IfLet(_, expr) => expr,
+ Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
});
vars.append(&mut self.ret_vars);
});