mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-12-22 11:03:16 +00:00
78 lines
3.1 KiB
Rust
78 lines
3.1 KiB
Rust
use super::utils::make_iterator_snippet;
|
|
use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id, span_lint_and_then};
|
|
use if_chain::if_chain;
|
|
use rustc_errors::Applicability;
|
|
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
|
|
use rustc_lint::LateContext;
|
|
use rustc_span::source_map::Span;
|
|
|
|
/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
|
|
/// iterator element is used.
|
|
pub(super) fn check_manual_flatten<'tcx>(
|
|
cx: &LateContext<'tcx>,
|
|
pat: &'tcx Pat<'_>,
|
|
arg: &'tcx Expr<'_>,
|
|
body: &'tcx Expr<'_>,
|
|
span: Span,
|
|
) {
|
|
if let ExprKind::Block(ref block, _) = body.kind {
|
|
// Ensure the `if let` statement is the only expression or statement in the for-loop
|
|
let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
|
|
let match_stmt = &block.stmts[0];
|
|
if let StmtKind::Semi(inner_expr) = match_stmt.kind {
|
|
Some(inner_expr)
|
|
} else {
|
|
None
|
|
}
|
|
} else if block.stmts.is_empty() {
|
|
block.expr
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if_chain! {
|
|
if let Some(inner_expr) = inner_expr;
|
|
if let ExprKind::Match(
|
|
ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false }
|
|
) = inner_expr.kind;
|
|
// Ensure match_expr in `if let` statement is the same as the pat from the for-loop
|
|
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
|
|
if path_to_local_id(match_expr, pat_hir_id);
|
|
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
|
|
if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
|
|
let some_ctor = is_some_ctor(cx, path.res);
|
|
let ok_ctor = is_ok_ctor(cx, path.res);
|
|
if some_ctor || ok_ctor;
|
|
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
|
|
|
then {
|
|
// Prepare the error message
|
|
let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
|
|
|
|
// Prepare the help message
|
|
let mut applicability = Applicability::MaybeIncorrect;
|
|
let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
|
|
|
|
span_lint_and_then(
|
|
cx,
|
|
super::MANUAL_FLATTEN,
|
|
span,
|
|
&msg,
|
|
|diag| {
|
|
let sugg = format!("{}.flatten()", arg_snippet);
|
|
diag.span_suggestion(
|
|
arg.span,
|
|
"try",
|
|
sugg,
|
|
Applicability::MaybeIncorrect,
|
|
);
|
|
diag.span_help(
|
|
inner_expr.span,
|
|
"...and remove the `if let` statement in the for loop",
|
|
);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|