Skip to content

Commit 2ddfc92

Browse files
authored
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
2 parents 0a07dc5 + ab5d55c commit 2ddfc92

File tree

5 files changed

+240
-42
lines changed

5 files changed

+240
-42
lines changed

clippy_lints/src/methods/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -4982,6 +4982,10 @@ impl Methods {
49824982
}
49834983
map_identity::check(cx, expr, recv, m_arg, name, span);
49844984
manual_inspect::check(cx, expr, m_arg, name, span, &self.msrv);
4985+
crate::useless_conversion::check_function_application(cx, expr, recv, m_arg);
4986+
},
4987+
("map_break" | "map_continue", [m_arg]) => {
4988+
crate::useless_conversion::check_function_application(cx, expr, recv, m_arg);
49854989
},
49864990
("map_or", [def, map]) => {
49874991
option_map_or_none::check(cx, expr, recv, def, map);

clippy_lints/src/useless_conversion.rs

+60-4
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
22
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
3-
use clippy_utils::sugg::Sugg;
3+
use clippy_utils::sugg::{DiagExt as _, Sugg};
44
use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
5-
use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, path_to_local};
5+
use clippy_utils::{
6+
get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local,
7+
};
68
use rustc_errors::Applicability;
79
use rustc_hir::def_id::DefId;
810
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
911
use rustc_infer::infer::TyCtxtInferExt;
1012
use rustc_infer::traits::Obligation;
1113
use rustc_lint::{LateContext, LateLintPass};
1214
use rustc_middle::traits::ObligationCause;
13-
use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
15+
use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt};
1416
use rustc_session::impl_lint_pass;
15-
use rustc_span::{Span, sym};
17+
use rustc_span::{Span, Symbol, sym};
1618
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1719

1820
declare_clippy_lint! {
@@ -382,3 +384,57 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
382384
}
383385
}
384386
}
387+
388+
/// Check if `arg` is a `Into::into` or `From::from` applied to `receiver` to give `expr`, through a
389+
/// higher-order mapping function.
390+
pub fn check_function_application(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
391+
if has_eligible_receiver(cx, recv, expr)
392+
&& (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From))
393+
&& let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind()
394+
&& let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice()
395+
&& same_type_and_consts(from_ty, to_ty)
396+
{
397+
span_lint_and_then(
398+
cx,
399+
USELESS_CONVERSION,
400+
expr.span.with_lo(recv.span.hi()),
401+
format!("useless conversion to the same type: `{from_ty}`"),
402+
|diag| {
403+
diag.suggest_remove_item(
404+
cx,
405+
expr.span.with_lo(recv.span.hi()),
406+
"consider removing",
407+
Applicability::MachineApplicable,
408+
);
409+
},
410+
);
411+
}
412+
}
413+
414+
fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) -> bool {
415+
let recv_ty = cx.typeck_results().expr_ty(recv);
416+
if is_inherent_method_call(cx, expr)
417+
&& let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
418+
{
419+
if let Some(diag_name) = cx.tcx.get_diagnostic_name(recv_ty_defid)
420+
&& matches!(diag_name, sym::Option | sym::Result)
421+
{
422+
return true;
423+
}
424+
425+
// FIXME: Add ControlFlow diagnostic item
426+
let def_path = cx.get_def_path(recv_ty_defid);
427+
if def_path
428+
.iter()
429+
.map(Symbol::as_str)
430+
.zip(["core", "ops", "control_flow", "ControlFlow"])
431+
.all(|(sym, s)| sym == s)
432+
{
433+
return true;
434+
}
435+
}
436+
if is_trait_method(cx, expr, sym::Iterator) {
437+
return true;
438+
}
439+
false
440+
}

tests/ui/useless_conversion.fixed

+45
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
44
#![allow(static_mut_refs)]
55

6+
use std::ops::ControlFlow;
7+
68
fn test_generic<T: Copy>(val: T) -> T {
79
let _ = val;
810
val
@@ -297,3 +299,46 @@ impl From<Foo<'a'>> for Foo<'b'> {
297299
Foo
298300
}
299301
}
302+
303+
fn direct_application() {
304+
let _: Result<(), std::io::Error> = test_issue_3913();
305+
//~^ useless_conversion
306+
let _: Result<(), std::io::Error> = test_issue_3913();
307+
//~^ useless_conversion
308+
let _: Result<(), std::io::Error> = test_issue_3913();
309+
//~^ useless_conversion
310+
let _: Result<(), std::io::Error> = test_issue_3913();
311+
//~^ useless_conversion
312+
313+
let c: ControlFlow<()> = ControlFlow::Continue(());
314+
let _: ControlFlow<()> = c;
315+
//~^ useless_conversion
316+
let c: ControlFlow<()> = ControlFlow::Continue(());
317+
let _: ControlFlow<()> = c;
318+
//~^ useless_conversion
319+
320+
struct Absorb;
321+
impl From<()> for Absorb {
322+
fn from(_: ()) -> Self {
323+
Self
324+
}
325+
}
326+
impl From<std::io::Error> for Absorb {
327+
fn from(_: std::io::Error) -> Self {
328+
Self
329+
}
330+
}
331+
let _: Vec<u32> = [1u32].into_iter().collect();
332+
//~^ useless_conversion
333+
334+
// No lint for those
335+
let _: Result<Absorb, std::io::Error> = test_issue_3913().map(Into::into);
336+
let _: Result<(), Absorb> = test_issue_3913().map_err(Into::into);
337+
let _: Result<Absorb, std::io::Error> = test_issue_3913().map(From::from);
338+
let _: Result<(), Absorb> = test_issue_3913().map_err(From::from);
339+
}
340+
341+
fn gen_identity<T>(x: [T; 3]) -> Vec<T> {
342+
x.into_iter().collect()
343+
//~^ useless_conversion
344+
}

tests/ui/useless_conversion.rs

+45
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
44
#![allow(static_mut_refs)]
55

6+
use std::ops::ControlFlow;
7+
68
fn test_generic<T: Copy>(val: T) -> T {
79
let _ = T::from(val);
810
val.into()
@@ -297,3 +299,46 @@ impl From<Foo<'a'>> for Foo<'b'> {
297299
Foo
298300
}
299301
}
302+
303+
fn direct_application() {
304+
let _: Result<(), std::io::Error> = test_issue_3913().map(Into::into);
305+
//~^ useless_conversion
306+
let _: Result<(), std::io::Error> = test_issue_3913().map_err(Into::into);
307+
//~^ useless_conversion
308+
let _: Result<(), std::io::Error> = test_issue_3913().map(From::from);
309+
//~^ useless_conversion
310+
let _: Result<(), std::io::Error> = test_issue_3913().map_err(From::from);
311+
//~^ useless_conversion
312+
313+
let c: ControlFlow<()> = ControlFlow::Continue(());
314+
let _: ControlFlow<()> = c.map_break(Into::into);
315+
//~^ useless_conversion
316+
let c: ControlFlow<()> = ControlFlow::Continue(());
317+
let _: ControlFlow<()> = c.map_continue(Into::into);
318+
//~^ useless_conversion
319+
320+
struct Absorb;
321+
impl From<()> for Absorb {
322+
fn from(_: ()) -> Self {
323+
Self
324+
}
325+
}
326+
impl From<std::io::Error> for Absorb {
327+
fn from(_: std::io::Error) -> Self {
328+
Self
329+
}
330+
}
331+
let _: Vec<u32> = [1u32].into_iter().map(Into::into).collect();
332+
//~^ useless_conversion
333+
334+
// No lint for those
335+
let _: Result<Absorb, std::io::Error> = test_issue_3913().map(Into::into);
336+
let _: Result<(), Absorb> = test_issue_3913().map_err(Into::into);
337+
let _: Result<Absorb, std::io::Error> = test_issue_3913().map(From::from);
338+
let _: Result<(), Absorb> = test_issue_3913().map_err(From::from);
339+
}
340+
341+
fn gen_identity<T>(x: [T; 3]) -> Vec<T> {
342+
x.into_iter().map(Into::into).collect()
343+
//~^ useless_conversion
344+
}

0 commit comments

Comments
 (0)