Skip to content

Commit 62ac4bd

Browse files
nahuakangY-Nak
authored andcommitted
create loops dir; arrange manual_flatten lint and utils
1 parent e50afa4 commit 62ac4bd

File tree

3 files changed

+127
-112
lines changed

3 files changed

+127
-112
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use super::utils::make_iterator_snippet;
2+
use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id, span_lint_and_then};
3+
use if_chain::if_chain;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
6+
use rustc_lint::LateContext;
7+
use rustc_span::source_map::Span;
8+
9+
/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
10+
/// iterator element is used.
11+
pub(super) fn lint<'tcx>(
12+
cx: &LateContext<'tcx>,
13+
pat: &'tcx Pat<'_>,
14+
arg: &'tcx Expr<'_>,
15+
body: &'tcx Expr<'_>,
16+
span: Span,
17+
) {
18+
if let ExprKind::Block(ref block, _) = body.kind {
19+
// Ensure the `if let` statement is the only expression or statement in the for-loop
20+
let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
21+
let match_stmt = &block.stmts[0];
22+
if let StmtKind::Semi(inner_expr) = match_stmt.kind {
23+
Some(inner_expr)
24+
} else {
25+
None
26+
}
27+
} else if block.stmts.is_empty() {
28+
block.expr
29+
} else {
30+
None
31+
};
32+
33+
if_chain! {
34+
if let Some(inner_expr) = inner_expr;
35+
if let ExprKind::Match(
36+
ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false }
37+
) = inner_expr.kind;
38+
// Ensure match_expr in `if let` statement is the same as the pat from the for-loop
39+
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
40+
if path_to_local_id(match_expr, pat_hir_id);
41+
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
42+
if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
43+
let some_ctor = is_some_ctor(cx, path.res);
44+
let ok_ctor = is_ok_ctor(cx, path.res);
45+
if some_ctor || ok_ctor;
46+
let if_let_type = if some_ctor { "Some" } else { "Ok" };
47+
48+
then {
49+
// Prepare the error message
50+
let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
51+
52+
// Prepare the help message
53+
let mut applicability = Applicability::MaybeIncorrect;
54+
let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
55+
56+
span_lint_and_then(
57+
cx,
58+
super::MANUAL_FLATTEN,
59+
span,
60+
&msg,
61+
|diag| {
62+
let sugg = format!("{}.flatten()", arg_snippet);
63+
diag.span_suggestion(
64+
arg.span,
65+
"try",
66+
sugg,
67+
Applicability::MaybeIncorrect,
68+
);
69+
diag.span_help(
70+
inner_expr.span,
71+
"...and remove the `if let` statement in the for loop",
72+
);
73+
}
74+
);
75+
}
76+
}
77+
}
78+
}

clippy_lints/src/loops.rs renamed to clippy_lints/src/loops/mod.rs

Lines changed: 9 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
mod manual_flatten;
2+
mod utils;
3+
14
use crate::consts::constant;
25
use crate::utils::sugg::Sugg;
36
use crate::utils::usage::mutated_variables;
47
use crate::utils::visitors::LocalUsedVisitor;
58
use crate::utils::{
69
contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
7-
indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_ok_ctor, is_refutable, is_some_ctor,
8-
is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, multispan_sugg, path_to_local,
9-
path_to_local_id, paths, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite,
10-
span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
10+
indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item,
11+
last_path_segment, match_trait_method, match_type, multispan_sugg, path_to_local, path_to_local_id, paths,
12+
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
13+
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
1114
};
1215
use if_chain::if_chain;
1316
use rustc_ast::ast;
@@ -31,6 +34,7 @@ use rustc_span::symbol::{sym, Ident, Symbol};
3134
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
3235
use std::iter::{once, Iterator};
3336
use std::mem;
37+
use utils::make_iterator_snippet;
3438

3539
declare_clippy_lint! {
3640
/// **What it does:** Checks for for-loops that manually copy items between
@@ -862,7 +866,7 @@ fn check_for_loop<'tcx>(
862866
check_for_mut_range_bound(cx, arg, body);
863867
check_for_single_element_loop(cx, pat, arg, body, expr);
864868
detect_same_item_push(cx, pat, arg, body, expr);
865-
check_manual_flatten(cx, pat, arg, body, span);
869+
manual_flatten::lint(cx, pat, arg, body, span);
866870
}
867871

868872
// this function assumes the given expression is a `for` loop.
@@ -1839,42 +1843,6 @@ fn check_for_loop_explicit_counter<'tcx>(
18391843
}
18401844
}
18411845

1842-
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
1843-
/// actual `Iterator` that the loop uses.
1844-
fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
1845-
let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR).map_or(false, |id| {
1846-
implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
1847-
});
1848-
if impls_iterator {
1849-
format!(
1850-
"{}",
1851-
sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
1852-
)
1853-
} else {
1854-
// (&x).into_iter() ==> x.iter()
1855-
// (&mut x).into_iter() ==> x.iter_mut()
1856-
match &arg.kind {
1857-
ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner)
1858-
if has_iter_method(cx, cx.typeck_results().expr_ty(&arg_inner)).is_some() =>
1859-
{
1860-
let meth_name = match mutability {
1861-
Mutability::Mut => "iter_mut",
1862-
Mutability::Not => "iter",
1863-
};
1864-
format!(
1865-
"{}.{}()",
1866-
sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(),
1867-
meth_name,
1868-
)
1869-
}
1870-
_ => format!(
1871-
"{}.into_iter()",
1872-
sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
1873-
),
1874-
}
1875-
}
1876-
}
1877-
18781846
/// Checks for the `FOR_KV_MAP` lint.
18791847
fn check_for_loop_over_map_kv<'tcx>(
18801848
cx: &LateContext<'tcx>,
@@ -1964,77 +1932,6 @@ fn check_for_single_element_loop<'tcx>(
19641932
}
19651933
}
19661934

1967-
/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
1968-
/// iterator element is used.
1969-
fn check_manual_flatten<'tcx>(
1970-
cx: &LateContext<'tcx>,
1971-
pat: &'tcx Pat<'_>,
1972-
arg: &'tcx Expr<'_>,
1973-
body: &'tcx Expr<'_>,
1974-
span: Span,
1975-
) {
1976-
if let ExprKind::Block(ref block, _) = body.kind {
1977-
// Ensure the `if let` statement is the only expression or statement in the for-loop
1978-
let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
1979-
let match_stmt = &block.stmts[0];
1980-
if let StmtKind::Semi(inner_expr) = match_stmt.kind {
1981-
Some(inner_expr)
1982-
} else {
1983-
None
1984-
}
1985-
} else if block.stmts.is_empty() {
1986-
block.expr
1987-
} else {
1988-
None
1989-
};
1990-
1991-
if_chain! {
1992-
if let Some(inner_expr) = inner_expr;
1993-
if let ExprKind::Match(
1994-
ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false }
1995-
) = inner_expr.kind;
1996-
// Ensure match_expr in `if let` statement is the same as the pat from the for-loop
1997-
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
1998-
if path_to_local_id(match_expr, pat_hir_id);
1999-
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
2000-
if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
2001-
let some_ctor = is_some_ctor(cx, path.res);
2002-
let ok_ctor = is_ok_ctor(cx, path.res);
2003-
if some_ctor || ok_ctor;
2004-
let if_let_type = if some_ctor { "Some" } else { "Ok" };
2005-
2006-
then {
2007-
// Prepare the error message
2008-
let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
2009-
2010-
// Prepare the help message
2011-
let mut applicability = Applicability::MaybeIncorrect;
2012-
let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
2013-
2014-
span_lint_and_then(
2015-
cx,
2016-
MANUAL_FLATTEN,
2017-
span,
2018-
&msg,
2019-
|diag| {
2020-
let sugg = format!("{}.flatten()", arg_snippet);
2021-
diag.span_suggestion(
2022-
arg.span,
2023-
"try",
2024-
sugg,
2025-
Applicability::MaybeIncorrect,
2026-
);
2027-
diag.span_help(
2028-
inner_expr.span,
2029-
"...and remove the `if let` statement in the for loop",
2030-
);
2031-
}
2032-
);
2033-
}
2034-
}
2035-
}
2036-
}
2037-
20381935
struct MutatePairDelegate<'a, 'tcx> {
20391936
cx: &'a LateContext<'tcx>,
20401937
hir_id_low: Option<HirId>,

clippy_lints/src/loops/utils.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::utils::{get_trait_def_id, has_iter_method, implements_trait, paths, sugg};
2+
use rustc_errors::Applicability;
3+
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
4+
use rustc_lint::LateContext;
5+
6+
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
7+
/// actual `Iterator` that the loop uses.
8+
pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
9+
let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR).map_or(false, |id| {
10+
implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
11+
});
12+
if impls_iterator {
13+
format!(
14+
"{}",
15+
sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
16+
)
17+
} else {
18+
// (&x).into_iter() ==> x.iter()
19+
// (&mut x).into_iter() ==> x.iter_mut()
20+
match &arg.kind {
21+
ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner)
22+
if has_iter_method(cx, cx.typeck_results().expr_ty(&arg_inner)).is_some() =>
23+
{
24+
let meth_name = match mutability {
25+
Mutability::Mut => "iter_mut",
26+
Mutability::Not => "iter",
27+
};
28+
format!(
29+
"{}.{}()",
30+
sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(),
31+
meth_name,
32+
)
33+
}
34+
_ => format!(
35+
"{}.into_iter()",
36+
sugg::Sugg::hir_with_applicability(cx, arg, "_", applic_ref).maybe_par()
37+
),
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)