Add more cases to the useless_conversion lint (#13756)
The new cases are the application of `Into::into` or `From::from` through the following functions with no effect: - `Option::map()` - `Result::map()` and `Result::map_err()` - `ControlFlow::map_break()` and `ControlFlow::map_err()` - `Iterator::map()` changelog: [`useless_conversion`]: detect useless calls to `Into::into` and `From::from` application through `map*` methods
This commit is contained in:
commit
2ddfc92ea2
5 changed files with 240 additions and 42 deletions
|
|
@ -4982,6 +4982,10 @@ impl Methods {
|
|||
}
|
||||
map_identity::check(cx, expr, recv, m_arg, name, span);
|
||||
manual_inspect::check(cx, expr, m_arg, name, span, &self.msrv);
|
||||
crate::useless_conversion::check_function_application(cx, expr, recv, m_arg);
|
||||
},
|
||||
("map_break" | "map_continue", [m_arg]) => {
|
||||
crate::useless_conversion::check_function_application(cx, expr, recv, m_arg);
|
||||
},
|
||||
("map_or", [def, map]) => {
|
||||
option_map_or_none::check(cx, expr, recv, def, map);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::sugg::{DiagExt as _, Sugg};
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
|
||||
use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, path_to_local};
|
||||
use clippy_utils::{
|
||||
get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
|
||||
|
|
@ -10,9 +12,9 @@ use rustc_infer::infer::TyCtxtInferExt;
|
|||
use rustc_infer::traits::Obligation;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -382,3 +384,57 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if `arg` is a `Into::into` or `From::from` applied to `receiver` to give `expr`, through a
|
||||
/// higher-order mapping function.
|
||||
pub fn check_function_application(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
|
||||
if has_eligible_receiver(cx, recv, expr)
|
||||
&& (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From))
|
||||
&& let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind()
|
||||
&& let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice()
|
||||
&& same_type_and_consts(from_ty, to_ty)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
USELESS_CONVERSION,
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
format!("useless conversion to the same type: `{from_ty}`"),
|
||||
|diag| {
|
||||
diag.suggest_remove_item(
|
||||
cx,
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
"consider removing",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool {
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
if is_inherent_method_call(cx, expr)
|
||||
&& let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
|
||||
{
|
||||
if let Some(diag_name) = cx.tcx.get_diagnostic_name(recv_ty_defid)
|
||||
&& matches!(diag_name, sym::Option | sym::Result)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: Add ControlFlow diagnostic item
|
||||
let def_path = cx.get_def_path(recv_ty_defid);
|
||||
if def_path
|
||||
.iter()
|
||||
.map(Symbol::as_str)
|
||||
.zip(["core", "ops", "control_flow", "ControlFlow"])
|
||||
.all(|(sym, s)| sym == s)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue