Skip to content

Commit 4770ee1

Browse files
committed
Add redundant type annotations lint
1 parent 799732c commit 4770ee1

File tree

6 files changed

+266
-0
lines changed

6 files changed

+266
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4854,6 +4854,7 @@ Released 2018-09-13
48544854
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
48554855
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
48564856
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
4857+
[`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations
48574858
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
48584859
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
48594860
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
533533
crate::redundant_slicing::DEREF_BY_SLICING_INFO,
534534
crate::redundant_slicing::REDUNDANT_SLICING_INFO,
535535
crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO,
536+
crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO,
536537
crate::ref_option_ref::REF_OPTION_REF_INFO,
537538
crate::reference::DEREF_ADDROF_INFO,
538539
crate::regex::INVALID_REGEX_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ mod redundant_field_names;
263263
mod redundant_pub_crate;
264264
mod redundant_slicing;
265265
mod redundant_static_lifetimes;
266+
mod redundant_type_annotations;
266267
mod ref_option_ref;
267268
mod reference;
268269
mod regex;
@@ -949,6 +950,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
949950
avoid_breaking_exported_api,
950951
))
951952
});
953+
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
952954
// add lints here, do not remove this comment, it's used in `new_lint`
953955
}
954956

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use rustc_hir as hir;
3+
use rustc_lint::{LateContext, LateLintPass};
4+
use rustc_middle::ty::Ty;
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
7+
declare_clippy_lint! {
8+
/// ### What it does
9+
/// Warns about needless / redundant type annotations.
10+
///
11+
/// ### Why is this bad?
12+
/// Code is more idiomatic, shorter, and easier to modify.
13+
///
14+
/// ### Example
15+
/// ```rust
16+
/// let foo: String = String::new();
17+
/// ```
18+
/// Use instead:
19+
/// ```rust
20+
/// let foo = String::new();
21+
/// ```
22+
#[clippy::version = "1.70.0"]
23+
pub REDUNDANT_TYPE_ANNOTATIONS,
24+
pedantic,
25+
"Warns about needless / redundant type annotations."
26+
}
27+
declare_lint_pass!(RedundantTypeAnnotations => [REDUNDANT_TYPE_ANNOTATIONS]);
28+
29+
fn is_redundant_in_resolved_path<'tcx>(cx: &LateContext<'tcx>, res: hir::def::Res, func_return_type: Ty<'tcx>) -> bool {
30+
// type annotation is primitive
31+
if_chain! {
32+
if let hir::def::Res::PrimTy(primty) = res;
33+
34+
if func_return_type.is_primitive();
35+
if let Some(func_return_type_sym) = func_return_type.primitive_symbol();
36+
37+
then {
38+
return primty.name() == func_return_type_sym;
39+
}
40+
}
41+
42+
// type annotation is any other non generic type
43+
if_chain! {
44+
if let hir::def::Res::Def(_, defid) = res;
45+
if let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars();
46+
47+
then {
48+
return annotation_ty == func_return_type
49+
}
50+
}
51+
52+
false
53+
}
54+
55+
fn is_redundant_in_func_call<'tcx>(
56+
cx: &LateContext<'tcx>,
57+
res: hir::def::Res,
58+
func_return_path: &hir::QPath<'tcx>,
59+
) -> bool {
60+
// TODO: Problem with something like
61+
// let a: String = StructTest::func() where func returns String::from
62+
// The problem is that the DefId that I get refers to the struct itself and not to string
63+
64+
match func_return_path {
65+
// let a: String = f(); where f: fn f() -> String
66+
hir::QPath::Resolved(_, resolved_path) => {
67+
if_chain! {
68+
if let hir::def::Res::Def(_, defid) = resolved_path.res;
69+
if let Some(middle_ty_init) = cx.tcx.type_of(defid).no_bound_vars();
70+
if middle_ty_init.is_fn();
71+
if let Some(init_return_type) = middle_ty_init.fn_sig(cx.tcx).output().no_bound_vars();
72+
then {
73+
return is_redundant_in_resolved_path(cx, res, init_return_type);
74+
}
75+
}
76+
77+
false
78+
},
79+
// let a: String = String::new();
80+
hir::QPath::TypeRelative(func_hir_ty, _) => {
81+
if_chain! {
82+
if let hir::def::Res::Def(_, defid) = res;
83+
if let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars();
84+
85+
if let hir::TyKind::Path(init_ty_path) = &func_hir_ty.kind;
86+
if let hir::QPath::Resolved(_, resolved_init_ty_path) = init_ty_path;
87+
if let hir::def::Res::Def(_, init_defid) = resolved_init_ty_path.res;
88+
if let Some(init_ty) = cx.tcx.type_of(init_defid).no_bound_vars();
89+
90+
then {
91+
return annotation_ty == init_ty
92+
}
93+
}
94+
95+
false
96+
},
97+
hir::QPath::LangItem(..) => false,
98+
}
99+
}
100+
101+
impl LateLintPass<'_> for RedundantTypeAnnotations {
102+
fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'_>) {
103+
if_chain! {
104+
// type annotation part
105+
if let Some(ty) = &local.ty;
106+
if let hir::TyKind::Path(ty_path) = &ty.kind;
107+
if let hir::QPath::Resolved(_, resolved_path_ty) = ty_path;
108+
109+
// initialization part
110+
if let Some(init) = local.init;
111+
112+
then {
113+
match &init.kind {
114+
// When the initialization is a call to a function
115+
hir::ExprKind::Call(init_call, _) => {
116+
if let hir::ExprKind::Path(init_path) = &init_call.kind {
117+
if is_redundant_in_func_call(cx, resolved_path_ty.res, init_path) {
118+
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
119+
}
120+
}
121+
},
122+
// When the initialization is a path for example u32::MAX
123+
hir::ExprKind::Path(init_path) => {
124+
if_chain! {
125+
if let hir::def::Res::PrimTy(primty) = resolved_path_ty.res;
126+
127+
if let hir::QPath::TypeRelative(init_ty, _) = init_path;
128+
if let hir::TyKind::Path(init_ty_path) = &init_ty.kind;
129+
if let hir::QPath::Resolved(_, resolved_init_ty_path) = init_ty_path;
130+
if let hir::def::Res::PrimTy(primty_init) = resolved_init_ty_path.res;
131+
132+
if primty == primty_init;
133+
then {
134+
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
135+
}
136+
}
137+
}
138+
_ => ()
139+
}
140+
}
141+
};
142+
}
143+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#![allow(unused)]
2+
#![warn(clippy::redundant_type_annotations)]
3+
4+
struct A;
5+
enum E {
6+
One,
7+
Two,
8+
}
9+
10+
fn f() -> String {
11+
String::new()
12+
}
13+
14+
fn f_struct() -> A {
15+
A
16+
}
17+
18+
fn f_enum() -> E {
19+
E::One
20+
}
21+
22+
struct ATest2 {
23+
inner: u32,
24+
}
25+
26+
impl ATest2 {
27+
fn func(&self) -> u32 {
28+
self.inner
29+
}
30+
31+
fn a(&self) {
32+
let v: u32 = self.func(); // This should lint but doesn't (MethodCall)
33+
}
34+
35+
fn get_num() -> u32 {
36+
5
37+
}
38+
39+
fn new() -> Self {
40+
Self { inner: 5 }
41+
}
42+
}
43+
44+
fn f_prim() -> u32 {
45+
5
46+
}
47+
48+
fn main() {
49+
let a: String = f();
50+
51+
let a: A = f_struct();
52+
53+
let a: E = f_enum();
54+
55+
let a: u32 = f_prim();
56+
57+
let a: String = String::new();
58+
59+
let st: ATest2 = ATest2::new();
60+
let a: u32 = st.func(); // this should lint but doesn't (MethodCall)
61+
62+
let a: String = String::from("test");
63+
64+
let a: u32 = u32::MAX;
65+
66+
let a: u32 = ATest2::get_num(); // this should lint but doesn't (DefId refers to ATest2 and not to the func)
67+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: redundant type annotation
2+
--> $DIR/redundant_type_annotations.rs:49:5
3+
|
4+
LL | let a: String = f();
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::redundant-type-annotations` implied by `-D warnings`
8+
9+
error: redundant type annotation
10+
--> $DIR/redundant_type_annotations.rs:51:5
11+
|
12+
LL | let a: A = f_struct();
13+
| ^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: redundant type annotation
16+
--> $DIR/redundant_type_annotations.rs:53:5
17+
|
18+
LL | let a: E = f_enum();
19+
| ^^^^^^^^^^^^^^^^^^^^
20+
21+
error: redundant type annotation
22+
--> $DIR/redundant_type_annotations.rs:55:5
23+
|
24+
LL | let a: u32 = f_prim();
25+
| ^^^^^^^^^^^^^^^^^^^^^^
26+
27+
error: redundant type annotation
28+
--> $DIR/redundant_type_annotations.rs:57:5
29+
|
30+
LL | let a: String = String::new();
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
error: redundant type annotation
34+
--> $DIR/redundant_type_annotations.rs:59:5
35+
|
36+
LL | let st: ATest2 = ATest2::new();
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
39+
error: redundant type annotation
40+
--> $DIR/redundant_type_annotations.rs:62:5
41+
|
42+
LL | let a: String = String::from("test");
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
45+
error: redundant type annotation
46+
--> $DIR/redundant_type_annotations.rs:64:5
47+
|
48+
LL | let a: u32 = u32::MAX;
49+
| ^^^^^^^^^^^^^^^^^^^^^^
50+
51+
error: aborting due to 8 previous errors
52+

0 commit comments

Comments
 (0)