Skip to content

Commit 453e6b9

Browse files
nahuakangY-Nak
authored andcommitted
Add check_needless_collect to its own module
1 parent 9520cba commit 453e6b9

File tree

2 files changed

+289
-275
lines changed

2 files changed

+289
-275
lines changed

clippy_lints/src/loops/mod.rs

Lines changed: 7 additions & 275 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ mod for_mut_range_bound;
66
mod for_single_element_loop;
77
mod infinite_loop;
88
mod manual_flatten;
9+
mod needless_collect;
910
mod utils;
1011

1112
use crate::utils::sugg::Sugg;
1213
use crate::utils::usage::mutated_variables;
1314
use crate::utils::{
1415
get_enclosing_block, get_parent_expr, get_trait_def_id, higher, implements_trait, is_in_panic_handler,
1516
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
16-
match_type, path_to_local, path_to_local_id, paths, snippet, snippet_with_applicability,
17-
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
17+
path_to_local, path_to_local_id, paths, snippet, snippet_with_applicability, snippet_with_macro_callsite,
18+
span_lint, span_lint_and_help, span_lint_and_sugg, sugg,
1819
};
1920
use if_chain::if_chain;
2021
use rustc_ast::ast;
@@ -23,16 +24,16 @@ use rustc_errors::Applicability;
2324
use rustc_hir::def::{DefKind, Res};
2425
use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
2526
use rustc_hir::{
26-
BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand, Local,
27-
LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
27+
BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, MatchSource,
28+
Mutability, Node, Pat, PatKind, Stmt, StmtKind,
2829
};
2930
use rustc_lint::{LateContext, LateLintPass, LintContext};
3031
use rustc_middle::hir::map::Map;
3132
use rustc_middle::lint::in_external_macro;
3233
use rustc_middle::ty::{self, Ty};
3334
use rustc_session::{declare_lint_pass, declare_tool_lint};
3435
use rustc_span::source_map::Span;
35-
use rustc_span::symbol::{sym, Ident, Symbol};
36+
use rustc_span::symbol::{sym, Symbol};
3637
use std::iter::{once, Iterator};
3738
use utils::{get_span_of_entire_for_loop, make_iterator_snippet};
3839

@@ -686,7 +687,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
686687
infinite_loop::check_infinite_loop(cx, cond, body);
687688
}
688689

689-
check_needless_collect(expr, cx);
690+
needless_collect::check_needless_collect(expr, cx);
690691
}
691692
}
692693

@@ -1894,272 +1895,3 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
18941895
NestedVisitorMap::None
18951896
}
18961897
}
1897-
1898-
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
1899-
1900-
fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
1901-
check_needless_collect_direct_usage(expr, cx);
1902-
check_needless_collect_indirect_usage(expr, cx);
1903-
}
1904-
fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
1905-
if_chain! {
1906-
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
1907-
if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind;
1908-
if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR);
1909-
if let Some(ref generic_args) = chain_method.args;
1910-
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
1911-
then {
1912-
let ty = cx.typeck_results().node_type(ty.hir_id);
1913-
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
1914-
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
1915-
match_type(cx, ty, &paths::BTREEMAP) ||
1916-
is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
1917-
if method.ident.name == sym!(len) {
1918-
let span = shorten_needless_collect_span(expr);
1919-
span_lint_and_sugg(
1920-
cx,
1921-
NEEDLESS_COLLECT,
1922-
span,
1923-
NEEDLESS_COLLECT_MSG,
1924-
"replace with",
1925-
"count()".to_string(),
1926-
Applicability::MachineApplicable,
1927-
);
1928-
}
1929-
if method.ident.name == sym!(is_empty) {
1930-
let span = shorten_needless_collect_span(expr);
1931-
span_lint_and_sugg(
1932-
cx,
1933-
NEEDLESS_COLLECT,
1934-
span,
1935-
NEEDLESS_COLLECT_MSG,
1936-
"replace with",
1937-
"next().is_none()".to_string(),
1938-
Applicability::MachineApplicable,
1939-
);
1940-
}
1941-
if method.ident.name == sym!(contains) {
1942-
let contains_arg = snippet(cx, args[1].span, "??");
1943-
let span = shorten_needless_collect_span(expr);
1944-
span_lint_and_then(
1945-
cx,
1946-
NEEDLESS_COLLECT,
1947-
span,
1948-
NEEDLESS_COLLECT_MSG,
1949-
|diag| {
1950-
let (arg, pred) = contains_arg
1951-
.strip_prefix('&')
1952-
.map_or(("&x", &*contains_arg), |s| ("x", s));
1953-
diag.span_suggestion(
1954-
span,
1955-
"replace with",
1956-
format!(
1957-
"any(|{}| x == {})",
1958-
arg, pred
1959-
),
1960-
Applicability::MachineApplicable,
1961-
);
1962-
}
1963-
);
1964-
}
1965-
}
1966-
}
1967-
}
1968-
}
1969-
1970-
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
1971-
if let ExprKind::Block(ref block, _) = expr.kind {
1972-
for ref stmt in block.stmts {
1973-
if_chain! {
1974-
if let StmtKind::Local(
1975-
Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
1976-
init: Some(ref init_expr), .. }
1977-
) = stmt.kind;
1978-
if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
1979-
if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR);
1980-
if let Some(ref generic_args) = method_name.args;
1981-
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
1982-
if let ty = cx.typeck_results().node_type(ty.hir_id);
1983-
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
1984-
is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
1985-
match_type(cx, ty, &paths::LINKED_LIST);
1986-
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
1987-
if iter_calls.len() == 1;
1988-
then {
1989-
let mut used_count_visitor = UsedCountVisitor {
1990-
cx,
1991-
id: *pat_id,
1992-
count: 0,
1993-
};
1994-
walk_block(&mut used_count_visitor, block);
1995-
if used_count_visitor.count > 1 {
1996-
return;
1997-
}
1998-
1999-
// Suggest replacing iter_call with iter_replacement, and removing stmt
2000-
let iter_call = &iter_calls[0];
2001-
span_lint_and_then(
2002-
cx,
2003-
NEEDLESS_COLLECT,
2004-
stmt.span.until(iter_call.span),
2005-
NEEDLESS_COLLECT_MSG,
2006-
|diag| {
2007-
let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
2008-
diag.multipart_suggestion(
2009-
iter_call.get_suggestion_text(),
2010-
vec![
2011-
(stmt.span, String::new()),
2012-
(iter_call.span, iter_replacement)
2013-
],
2014-
Applicability::MachineApplicable,// MaybeIncorrect,
2015-
).emit();
2016-
},
2017-
);
2018-
}
2019-
}
2020-
}
2021-
}
2022-
}
2023-
2024-
struct IterFunction {
2025-
func: IterFunctionKind,
2026-
span: Span,
2027-
}
2028-
impl IterFunction {
2029-
fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
2030-
match &self.func {
2031-
IterFunctionKind::IntoIter => String::new(),
2032-
IterFunctionKind::Len => String::from(".count()"),
2033-
IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
2034-
IterFunctionKind::Contains(span) => {
2035-
let s = snippet(cx, *span, "..");
2036-
if let Some(stripped) = s.strip_prefix('&') {
2037-
format!(".any(|x| x == {})", stripped)
2038-
} else {
2039-
format!(".any(|x| x == *{})", s)
2040-
}
2041-
},
2042-
}
2043-
}
2044-
fn get_suggestion_text(&self) -> &'static str {
2045-
match &self.func {
2046-
IterFunctionKind::IntoIter => {
2047-
"use the original Iterator instead of collecting it and then producing a new one"
2048-
},
2049-
IterFunctionKind::Len => {
2050-
"take the original Iterator's count instead of collecting it and finding the length"
2051-
},
2052-
IterFunctionKind::IsEmpty => {
2053-
"check if the original Iterator has anything instead of collecting it and seeing if it's empty"
2054-
},
2055-
IterFunctionKind::Contains(_) => {
2056-
"check if the original Iterator contains an element instead of collecting then checking"
2057-
},
2058-
}
2059-
}
2060-
}
2061-
enum IterFunctionKind {
2062-
IntoIter,
2063-
Len,
2064-
IsEmpty,
2065-
Contains(Span),
2066-
}
2067-
2068-
struct IterFunctionVisitor {
2069-
uses: Vec<IterFunction>,
2070-
seen_other: bool,
2071-
target: Ident,
2072-
}
2073-
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
2074-
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
2075-
// Check function calls on our collection
2076-
if_chain! {
2077-
if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind;
2078-
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0);
2079-
if let &[name] = &path.segments;
2080-
if name.ident == self.target;
2081-
then {
2082-
let len = sym!(len);
2083-
let is_empty = sym!(is_empty);
2084-
let contains = sym!(contains);
2085-
match method_name.ident.name {
2086-
sym::into_iter => self.uses.push(
2087-
IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
2088-
),
2089-
name if name == len => self.uses.push(
2090-
IterFunction { func: IterFunctionKind::Len, span: expr.span }
2091-
),
2092-
name if name == is_empty => self.uses.push(
2093-
IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
2094-
),
2095-
name if name == contains => self.uses.push(
2096-
IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
2097-
),
2098-
_ => self.seen_other = true,
2099-
}
2100-
return
2101-
}
2102-
}
2103-
// Check if the collection is used for anything else
2104-
if_chain! {
2105-
if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr;
2106-
if let &[name] = &path.segments;
2107-
if name.ident == self.target;
2108-
then {
2109-
self.seen_other = true;
2110-
} else {
2111-
walk_expr(self, expr);
2112-
}
2113-
}
2114-
}
2115-
2116-
type Map = Map<'tcx>;
2117-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
2118-
NestedVisitorMap::None
2119-
}
2120-
}
2121-
2122-
struct UsedCountVisitor<'a, 'tcx> {
2123-
cx: &'a LateContext<'tcx>,
2124-
id: HirId,
2125-
count: usize,
2126-
}
2127-
2128-
impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
2129-
type Map = Map<'tcx>;
2130-
2131-
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
2132-
if path_to_local_id(expr, self.id) {
2133-
self.count += 1;
2134-
} else {
2135-
walk_expr(self, expr);
2136-
}
2137-
}
2138-
2139-
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
2140-
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
2141-
}
2142-
}
2143-
2144-
/// Detect the occurrences of calls to `iter` or `into_iter` for the
2145-
/// given identifier
2146-
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
2147-
let mut visitor = IterFunctionVisitor {
2148-
uses: Vec::new(),
2149-
target: identifier,
2150-
seen_other: false,
2151-
};
2152-
visitor.visit_block(block);
2153-
if visitor.seen_other { None } else { Some(visitor.uses) }
2154-
}
2155-
2156-
fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
2157-
if_chain! {
2158-
if let ExprKind::MethodCall(.., args, _) = &expr.kind;
2159-
if let ExprKind::MethodCall(_, span, ..) = &args[0].kind;
2160-
then {
2161-
return expr.span.with_lo(span.lo());
2162-
}
2163-
}
2164-
unreachable!();
2165-
}

0 commit comments

Comments
 (0)