Auto merge of #7289 - camsteffen:needless-collect-shadow, r=Manishearth
Fix needless_collect with binding shadowing changelog: Fix [`needless_collect`] weird output when a binding is shadowed Fixes #7200
This commit is contained in:
commit
9991040258
@ -7,10 +7,10 @@ use clippy_utils::{is_trait_method, path_to_local_id};
|
|||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty};
|
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
use rustc_span::symbol::{sym, Ident};
|
use rustc_span::sym;
|
||||||
use rustc_span::{MultiSpan, Span};
|
use rustc_span::{MultiSpan, Span};
|
||||||
|
|
||||||
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
|
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
|
||||||
@ -24,10 +24,8 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
|
|||||||
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
|
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
|
||||||
if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind;
|
if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind;
|
||||||
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
|
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
|
||||||
if let Some(generic_args) = chain_method.args;
|
|
||||||
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
|
|
||||||
if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id);
|
|
||||||
then {
|
then {
|
||||||
|
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let is_empty_sugg = "next().is_none()".to_string();
|
let is_empty_sugg = "next().is_none()".to_string();
|
||||||
let method_name = &*method.ident.name.as_str();
|
let method_name = &*method.ident.name.as_str();
|
||||||
@ -72,40 +70,25 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
|
||||||
fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option<HirId> {
|
|
||||||
if let Some(ty) = ty {
|
|
||||||
return Some(ty.hir_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(generic_args) = method_args {
|
|
||||||
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) {
|
|
||||||
return Some(ty.hir_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
if let ExprKind::Block(block, _) = expr.kind {
|
if let ExprKind::Block(block, _) = expr.kind {
|
||||||
for stmt in block.stmts {
|
for stmt in block.stmts {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let StmtKind::Local(
|
if let StmtKind::Local(local) = stmt.kind;
|
||||||
Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
|
if let PatKind::Binding(_, id, ..) = local.pat.kind;
|
||||||
init: Some(init_expr), ty, .. }
|
if let Some(init_expr) = local.init;
|
||||||
) = stmt.kind;
|
|
||||||
if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
|
if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
|
||||||
if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
|
if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
|
||||||
if let Some(hir_id) = get_hir_id(*ty, method_name.args);
|
let ty = cx.typeck_results().expr_ty(init_expr);
|
||||||
if let Some(ty) = cx.typeck_results().node_type_opt(hir_id);
|
|
||||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
||||||
is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
|
is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
|
||||||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
|
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
|
||||||
is_type_diagnostic_item(cx, ty, sym::LinkedList);
|
is_type_diagnostic_item(cx, ty, sym::LinkedList);
|
||||||
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
|
if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
|
||||||
if let [iter_call] = &*iter_calls;
|
if let [iter_call] = &*iter_calls;
|
||||||
then {
|
then {
|
||||||
let mut used_count_visitor = UsedCountVisitor {
|
let mut used_count_visitor = UsedCountVisitor {
|
||||||
cx,
|
cx,
|
||||||
id: *pat_id,
|
id,
|
||||||
count: 0,
|
count: 0,
|
||||||
};
|
};
|
||||||
walk_block(&mut used_count_visitor, block);
|
walk_block(&mut used_count_visitor, block);
|
||||||
@ -187,50 +170,42 @@ enum IterFunctionKind {
|
|||||||
struct IterFunctionVisitor {
|
struct IterFunctionVisitor {
|
||||||
uses: Vec<IterFunction>,
|
uses: Vec<IterFunction>,
|
||||||
seen_other: bool,
|
seen_other: bool,
|
||||||
target: Ident,
|
target: HirId,
|
||||||
}
|
}
|
||||||
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
|
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||||
// Check function calls on our collection
|
// Check function calls on our collection
|
||||||
if_chain! {
|
if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
|
||||||
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
|
if path_to_local_id(recv, self.target) {
|
||||||
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0);
|
match &*method_name.ident.name.as_str() {
|
||||||
if let &[name] = &path.segments;
|
"into_iter" => self.uses.push(IterFunction {
|
||||||
if name.ident == self.target;
|
func: IterFunctionKind::IntoIter,
|
||||||
then {
|
span: expr.span,
|
||||||
let len = sym!(len);
|
}),
|
||||||
let is_empty = sym!(is_empty);
|
"len" => self.uses.push(IterFunction {
|
||||||
let contains = sym!(contains);
|
func: IterFunctionKind::Len,
|
||||||
match method_name.ident.name {
|
span: expr.span,
|
||||||
sym::into_iter => self.uses.push(
|
}),
|
||||||
IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
|
"is_empty" => self.uses.push(IterFunction {
|
||||||
),
|
func: IterFunctionKind::IsEmpty,
|
||||||
name if name == len => self.uses.push(
|
span: expr.span,
|
||||||
IterFunction { func: IterFunctionKind::Len, span: expr.span }
|
}),
|
||||||
),
|
"contains" => self.uses.push(IterFunction {
|
||||||
name if name == is_empty => self.uses.push(
|
func: IterFunctionKind::Contains(args[0].span),
|
||||||
IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
|
span: expr.span,
|
||||||
),
|
}),
|
||||||
name if name == contains => self.uses.push(
|
|
||||||
IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
|
|
||||||
),
|
|
||||||
_ => self.seen_other = true,
|
_ => self.seen_other = true,
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if the collection is used for anything else
|
// Check if the collection is used for anything else
|
||||||
if_chain! {
|
if path_to_local_id(expr, self.target) {
|
||||||
if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr;
|
|
||||||
if let &[name] = &path.segments;
|
|
||||||
if name.ident == self.target;
|
|
||||||
then {
|
|
||||||
self.seen_other = true;
|
self.seen_other = true;
|
||||||
} else {
|
} else {
|
||||||
walk_expr(self, expr);
|
walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
type Map = Map<'tcx>;
|
type Map = Map<'tcx>;
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
@ -262,10 +237,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
|
|||||||
|
|
||||||
/// Detect the occurrences of calls to `iter` or `into_iter` for the
|
/// Detect the occurrences of calls to `iter` or `into_iter` for the
|
||||||
/// given identifier
|
/// given identifier
|
||||||
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
|
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
|
||||||
let mut visitor = IterFunctionVisitor {
|
let mut visitor = IterFunctionVisitor {
|
||||||
uses: Vec::new(),
|
uses: Vec::new(),
|
||||||
target: identifier,
|
target: id,
|
||||||
seen_other: false,
|
seen_other: false,
|
||||||
};
|
};
|
||||||
visitor.visit_block(block);
|
visitor.visit_block(block);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user