Split arg/method checking into its own function

This commit is contained in:
sjwang05 2023-10-30 19:12:03 +00:00
parent 103200967e
commit 10f3977eba
No known key found for this signature in database
GPG key ID: AB262FD6FFBFCFFE

View file

@ -59,59 +59,65 @@ declare_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
impl LateLintPass<'_> for LinesFilterMapOk {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind &&
is_trait_method(cx, expr, sym::Iterator) &&
let fm_method_str = fm_method.ident.as_str() &&
matches!(fm_method_str, "filter_map" | "flat_map" | "flatten") &&
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines)
if let ExprKind::MethodCall(fm_method, fm_receiver, fm_args, fm_span) = expr.kind
&& is_trait_method(cx, expr, sym::Iterator)
&& let fm_method_str = fm_method.ident.as_str()
&& matches!(fm_method_str, "filter_map" | "flat_map" | "flatten")
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines)
&& should_lint(cx, fm_args, fm_method_str)
{
let lint = match fm_args {
[] => fm_method_str == "flatten",
[fm_arg] => {
match &fm_arg.kind {
// Detect `Result::ok`
ExprKind::Path(qpath) =>
cx.qpath_res(qpath, fm_arg.hir_id).opt_def_id().map(|did|
match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)).unwrap_or_default(),
// Detect `|x| x.ok()`
ExprKind::Closure(Closure { body, .. }) =>
if let Body { params: [param], value, .. } = cx.tcx.hir().body(*body) &&
let ExprKind::MethodCall(method, receiver, [], _) = value.kind &&
path_to_local_id(receiver, param.pat.hir_id) &&
let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
{
is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
} else {
false
},
_ => false,
}
}
_ => false,
};
if lint {
span_lint_and_then(
cx,
LINES_FILTER_MAP_OK,
fm_span,
&format!(
"`{}()` will run forever if the iterator repeatedly produces an `Err`",
fm_method.ident
),
|diag| {
diag.span_note(
fm_receiver.span,
"this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
diag.span_suggestion(
fm_span,
"replace with",
"map_while(Result::ok)",
Applicability::MaybeIncorrect,
);
},
);
}
span_lint_and_then(
cx,
LINES_FILTER_MAP_OK,
fm_span,
&format!(
"`{}()` will run forever if the iterator repeatedly produces an `Err`",
fm_method.ident
),
|diag| {
diag.span_note(
fm_receiver.span,
"this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
diag.span_suggestion(
fm_span,
"replace with",
"map_while(Result::ok)",
Applicability::MaybeIncorrect,
);
},
);
}
}
}
fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> bool {
match args {
[] => method_str == "flatten",
[fm_arg] => {
match &fm_arg.kind {
// Detect `Result::ok`
ExprKind::Path(qpath) => cx
.qpath_res(qpath, fm_arg.hir_id)
.opt_def_id()
.map(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD))
.unwrap_or_default(),
// Detect `|x| x.ok()`
ExprKind::Closure(Closure { body, .. }) => {
if let Body {
params: [param], value, ..
} = cx.tcx.hir().body(*body)
&& let ExprKind::MethodCall(method, receiver, [], _) = value.kind
&& path_to_local_id(receiver, param.pat.hir_id)
&& let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
{
is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
} else {
false
}
},
_ => false,
}
},
_ => false,
}
}