diff --git a/clippy_lints/src/redundant_type_annotations.rs b/clippy_lints/src/redundant_type_annotations.rs index 17f413ebfea0..c4edb1bb6f9d 100644 --- a/clippy_lints/src/redundant_type_annotations.rs +++ b/clippy_lints/src/redundant_type_annotations.rs @@ -9,7 +9,8 @@ declare_clippy_lint! { /// Warns about needless / redundant type annotations. /// /// ### Why is this bad? - /// Code is more idiomatic, shorter, and easier to modify. + /// Code without type annotations is shorter and in most cases + /// more idiomatic and easier to modify. /// /// ### Example /// ```rust @@ -22,73 +23,85 @@ declare_clippy_lint! { #[clippy::version = "1.70.0"] pub REDUNDANT_TYPE_ANNOTATIONS, pedantic, - "Warns about needless / redundant type annotations." + "warns about needless / redundant type annotations." } declare_lint_pass!(RedundantTypeAnnotations => [REDUNDANT_TYPE_ANNOTATIONS]); -fn is_redundant_in_resolved_path<'tcx>(cx: &LateContext<'tcx>, res: hir::def::Res, func_return_type: Ty<'tcx>) -> bool { +fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, func_return_type: Ty<'tcx>) -> bool { // type annotation is primitive - if let hir::def::Res::PrimTy(primty) = res + if let hir::def::Res::PrimTy(primty) = ty_resolved_path && func_return_type.is_primitive() && let Some(func_return_type_sym) = func_return_type.primitive_symbol() { - return primty.name() == func_return_type_sym; - } + return primty.name() == func_return_type_sym; + } // type annotation is any other non generic type - if let hir::def::Res::Def(_, defid) = res + if let hir::def::Res::Def(_, defid) = ty_resolved_path && let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars() { - return annotation_ty == func_return_type - } + return annotation_ty == func_return_type; + } false } -fn is_redundant_in_func_call<'tcx>( +/// Extracts the fn Ty, e.g. `fn() -> std::string::String {f}` +fn extract_fn_ty<'tcx>( cx: &LateContext<'tcx>, - res: hir::def::Res, + call: &hir::Expr<'tcx>, func_return_path: &hir::QPath<'tcx>, -) -> bool { - // TODO: Problem with something like - // let a: String = StructTest::func() where func returns String::from - // The problem is that the DefId that I get refers to the struct itself and not to string - +) -> Option> { match func_return_path { // let a: String = f(); where f: fn f() -> String hir::QPath::Resolved(_, resolved_path) => { if let hir::def::Res::Def(_, defid) = resolved_path.res && let Some(middle_ty_init) = cx.tcx.type_of(defid).no_bound_vars() - && middle_ty_init.is_fn() - && let Some(init_return_type) = middle_ty_init.fn_sig(cx.tcx).output().no_bound_vars() { - return is_redundant_in_resolved_path(cx, res, init_return_type); - } - - false + Some(middle_ty_init) + } else { + None + } }, + // Associated functions like // let a: String = String::new(); - hir::QPath::TypeRelative(func_hir_ty, _) => { - if let hir::def::Res::Def(_, defid) = res - && let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars() - - && let hir::TyKind::Path(init_ty_path) = &func_hir_ty.kind - && let hir::QPath::Resolved(_, resolved_init_ty_path) = init_ty_path - && let hir::def::Res::Def(_, init_defid) = resolved_init_ty_path.res - && let Some(init_ty) = cx.tcx.type_of(init_defid).no_bound_vars() + // let a: String = String::get_string(); + hir::QPath::TypeRelative(..) => { + if let Some((defkind, func_defid)) = cx.typeck_results().type_dependent_def(call.hir_id) + && defkind == hir::def::DefKind::AssocFn + && let Some(init_ty) = cx.tcx.type_of(func_defid).no_bound_vars() { - return annotation_ty == init_ty + Some(init_ty) + } else { + None } - - false }, - hir::QPath::LangItem(..) => false, + hir::QPath::LangItem(..) => None, } } +fn is_redundant_in_func_call<'tcx>( + cx: &LateContext<'tcx>, + ty_resolved_path: hir::def::Res, + call: &hir::Expr<'tcx>, +) -> bool { + if let hir::ExprKind::Path(init_path) = &call.kind { + let func_type = extract_fn_ty(cx, call, init_path); + + if let Some(func_type) = func_type + && func_type.is_fn() + && let Some(init_return_type) = func_type.fn_sig(cx.tcx).output().no_bound_vars() + { + return is_same_type(cx, ty_resolved_path, init_return_type); + } + } + + false +} + impl LateLintPass<'_> for RedundantTypeAnnotations { - fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'_>) { - // type annotation part + fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) { + // type annotation part if let Some(ty) = &local.ty && let hir::TyKind::Path(ty_path) = &ty.kind && let hir::QPath::Resolved(_, resolved_path_ty) = ty_path @@ -96,17 +109,15 @@ impl LateLintPass<'_> for RedundantTypeAnnotations { // initialization part && let Some(init) = local.init { - match &init.kind { - // When the initialization is a call to a function - hir::ExprKind::Call(init_call, _) => { - if let hir::ExprKind::Path(init_path) = &init_call.kind - && is_redundant_in_func_call(cx, resolved_path_ty.res, init_path) - { - span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation"); - } - }, - // When the initialization is a path for example u32::MAX - hir::ExprKind::Path(init_path) => { + match &init.kind { + // When the initialization is a call to a function + hir::ExprKind::Call(init_call, _) => { + if is_redundant_in_func_call(cx, resolved_path_ty.res, init_call) { + span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation"); + } + }, + // When the initialization is a path for example u32::MAX + hir::ExprKind::Path(init_path) => { if let hir::def::Res::PrimTy(primty) = resolved_path_ty.res && let hir::QPath::TypeRelative(init_ty, _) = init_path @@ -116,10 +127,10 @@ impl LateLintPass<'_> for RedundantTypeAnnotations { && primty == primty_init { - span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation"); - } - } - _ => () + span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation"); + } + } + _ => () } }; } diff --git a/tests/ui/redundant_type_annotations.rs b/tests/ui/redundant_type_annotations.rs index e5dfbf4b74a5..ab9541b1a62b 100644 --- a/tests/ui/redundant_type_annotations.rs +++ b/tests/ui/redundant_type_annotations.rs @@ -39,6 +39,10 @@ impl ATest2 { fn new() -> Self { Self { inner: 5 } } + + fn get_string() -> String { + String::from("") + } } fn f_prim() -> u32 { @@ -63,5 +67,7 @@ fn main() { let a: u32 = u32::MAX; - let a: u32 = ATest2::get_num(); // this should lint but doesn't (DefId refers to ATest2 and not to the func) + let a: u32 = ATest2::get_num(); + + let a: String = ATest2::get_string(); } diff --git a/tests/ui/redundant_type_annotations.stderr b/tests/ui/redundant_type_annotations.stderr index 2fc2778c7130..c4aa0483bee3 100644 --- a/tests/ui/redundant_type_annotations.stderr +++ b/tests/ui/redundant_type_annotations.stderr @@ -1,5 +1,5 @@ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:49:5 + --> $DIR/redundant_type_annotations.rs:53:5 | LL | let a: String = f(); | ^^^^^^^^^^^^^^^^^^^^ @@ -7,46 +7,52 @@ LL | let a: String = f(); = note: `-D clippy::redundant-type-annotations` implied by `-D warnings` error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:51:5 + --> $DIR/redundant_type_annotations.rs:55:5 | LL | let a: A = f_struct(); | ^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:53:5 + --> $DIR/redundant_type_annotations.rs:57:5 | LL | let a: E = f_enum(); | ^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:55:5 + --> $DIR/redundant_type_annotations.rs:59:5 | LL | let a: u32 = f_prim(); | ^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:57:5 + --> $DIR/redundant_type_annotations.rs:61:5 | LL | let a: String = String::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:59:5 + --> $DIR/redundant_type_annotations.rs:63:5 | LL | let st: ATest2 = ATest2::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:62:5 + --> $DIR/redundant_type_annotations.rs:68:5 | -LL | let a: String = String::from("test"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let a: u32 = u32::MAX; + | ^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:64:5 + --> $DIR/redundant_type_annotations.rs:70:5 | -LL | let a: u32 = u32::MAX; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | let a: u32 = ATest2::get_num(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant type annotation + --> $DIR/redundant_type_annotations.rs:72:5 + | +LL | let a: String = ATest2::get_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors