Skip to content

Commit cc8ead2

Browse files
committed
Auto merge of rust-lang#10570 - AlessioC31:redundant_type_annotations, r=xFrednet
Add redundant type annotations lint Hello, I'm trying to add the `redundat_type_annotations` lint. It's still WIP but I'd like to start gathering some feedbacks to be sure that I'm not doing things 100% wrong :) Right now it still misses lints like: - [x] `let foo: u32 = 5_u32`, - [x] `let foo: String = STest2::func()` - [x] `let foo: String = self.func()` (`MethodCall`) - [x] refs - [ ] Generics I've some problems regarding the second example above, in the `init` part of the `Local` I have: ```rust init: Some( Expr { hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).58), kind: Call( Expr { hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).59), kind: Path( TypeRelative( Ty { hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).61), kind: Path( Resolved( None, Path { span: src/main.rs:77:21: 77:27 (#0), res: Def( Struct, DefId(0:17 ~ playground[e1bd]::STest2), ), segments: [ PathSegment { ident: STest2#0, hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).60), res: Def( Struct, DefId(0:17 ~ playground[e1bd]::STest2), ), args: None, infer_args: true, }, ], }, ), ), span: src/main.rs:77:21: 77:27 (#0), }, PathSegment { ident: get_numb#0, hir_id: HirId(DefId(0:24 ~ playground[e1bd]::main).62), res: Err, args: None, infer_args: true, }, ), ), span: src/main.rs:77:21: 77:37 (#0), }, [], ), span: src/main.rs:77:21: 77:39 (#0), }, ), ``` And I'm not sure how to get the return type of the function `STest2::func()` since the resolved path `DefId` points to the struct itself and not the function. Do you have any idea on how I could get this information in this case? Thanks! changelog: changelog: [`redundant_type_annotations`]: New lint to warn on redundant type annotations fixes rust-lang#9155
2 parents 7ee3dcd + a9b468f commit cc8ead2

6 files changed

+496
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5110,6 +5110,7 @@ Released 2018-09-13
51105110
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
51115111
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
51125112
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
5113+
[`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations
51135114
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
51145115
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
51155116
[`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
@@ -543,6 +543,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
543543
crate::redundant_slicing::DEREF_BY_SLICING_INFO,
544544
crate::redundant_slicing::REDUNDANT_SLICING_INFO,
545545
crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO,
546+
crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO,
546547
crate::ref_option_ref::REF_OPTION_REF_INFO,
547548
crate::ref_patterns::REF_PATTERNS_INFO,
548549
crate::reference::DEREF_ADDROF_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ mod redundant_field_names;
267267
mod redundant_pub_crate;
268268
mod redundant_slicing;
269269
mod redundant_static_lifetimes;
270+
mod redundant_type_annotations;
270271
mod ref_option_ref;
271272
mod ref_patterns;
272273
mod reference;
@@ -1012,6 +1013,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10121013
store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
10131014
store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
10141015
store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
1016+
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
10151017
// add lints here, do not remove this comment, it's used in `new_lint`
10161018
}
10171019

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use rustc_ast::LitKind;
3+
use rustc_hir as hir;
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_middle::ty::Ty;
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Warns about needless / redundant type annotations.
11+
///
12+
/// ### Why is this bad?
13+
/// Code without type annotations is shorter and in most cases
14+
/// more idiomatic and easier to modify.
15+
///
16+
/// ### Limitations
17+
/// This lint doesn't support:
18+
///
19+
/// - Generics
20+
/// - Refs returned from anything else than a `MethodCall`
21+
/// - Complex types (tuples, arrays, etc...)
22+
/// - `Path` to anything else than a primitive type.
23+
///
24+
/// ### Example
25+
/// ```rust
26+
/// let foo: String = String::new();
27+
/// ```
28+
/// Use instead:
29+
/// ```rust
30+
/// let foo = String::new();
31+
/// ```
32+
#[clippy::version = "1.70.0"]
33+
pub REDUNDANT_TYPE_ANNOTATIONS,
34+
restriction,
35+
"warns about needless / redundant type annotations."
36+
}
37+
declare_lint_pass!(RedundantTypeAnnotations => [REDUNDANT_TYPE_ANNOTATIONS]);
38+
39+
fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, func_return_type: Ty<'tcx>) -> bool {
40+
// type annotation is primitive
41+
if let hir::def::Res::PrimTy(primty) = ty_resolved_path
42+
&& func_return_type.is_primitive()
43+
&& let Some(func_return_type_sym) = func_return_type.primitive_symbol()
44+
{
45+
return primty.name() == func_return_type_sym;
46+
}
47+
48+
// type annotation is any other non generic type
49+
if let hir::def::Res::Def(_, defid) = ty_resolved_path
50+
&& let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars()
51+
{
52+
return annotation_ty == func_return_type;
53+
}
54+
55+
false
56+
}
57+
58+
fn func_hir_id_to_func_ty<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::hir_id::HirId) -> Option<Ty<'tcx>> {
59+
if let Some((defkind, func_defid)) = cx.typeck_results().type_dependent_def(hir_id)
60+
&& defkind == hir::def::DefKind::AssocFn
61+
&& let Some(init_ty) = cx.tcx.type_of(func_defid).no_bound_vars()
62+
{
63+
Some(init_ty)
64+
} else {
65+
None
66+
}
67+
}
68+
69+
fn func_ty_to_return_type<'tcx>(cx: &LateContext<'tcx>, func_ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
70+
if func_ty.is_fn() {
71+
Some(func_ty.fn_sig(cx.tcx).output().skip_binder())
72+
} else {
73+
None
74+
}
75+
}
76+
77+
/// Extracts the fn Ty, e.g. `fn() -> std::string::String {f}`
78+
fn extract_fn_ty<'tcx>(
79+
cx: &LateContext<'tcx>,
80+
call: &hir::Expr<'tcx>,
81+
func_return_path: &hir::QPath<'tcx>,
82+
) -> Option<Ty<'tcx>> {
83+
match func_return_path {
84+
// let a: String = f(); where f: fn f() -> String
85+
hir::QPath::Resolved(_, resolved_path) => {
86+
if let hir::def::Res::Def(_, defid) = resolved_path.res
87+
&& let Some(middle_ty_init) = cx.tcx.type_of(defid).no_bound_vars()
88+
{
89+
Some(middle_ty_init)
90+
} else {
91+
None
92+
}
93+
},
94+
// Associated functions like
95+
// let a: String = String::new();
96+
// let a: String = String::get_string();
97+
hir::QPath::TypeRelative(..) => func_hir_id_to_func_ty(cx, call.hir_id),
98+
hir::QPath::LangItem(..) => None,
99+
}
100+
}
101+
102+
fn is_redundant_in_func_call<'tcx>(
103+
cx: &LateContext<'tcx>,
104+
ty_resolved_path: hir::def::Res,
105+
call: &hir::Expr<'tcx>,
106+
) -> bool {
107+
if let hir::ExprKind::Path(init_path) = &call.kind {
108+
let func_type = extract_fn_ty(cx, call, init_path);
109+
110+
if let Some(func_type) = func_type
111+
&& let Some(init_return_type) = func_ty_to_return_type(cx, func_type)
112+
{
113+
return is_same_type(cx, ty_resolved_path, init_return_type);
114+
}
115+
}
116+
117+
false
118+
}
119+
120+
fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option<hir::PrimTy> {
121+
if let hir::TyKind::Path(ty_path) = ty_kind
122+
&& let hir::QPath::Resolved(_, resolved_path_ty) = ty_path
123+
&& let hir::def::Res::PrimTy(primty) = resolved_path_ty.res
124+
{
125+
Some(primty)
126+
} else {
127+
None
128+
}
129+
}
130+
131+
impl LateLintPass<'_> for RedundantTypeAnnotations {
132+
fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) {
133+
// type annotation part
134+
if !local.span.from_expansion()
135+
&& let Some(ty) = &local.ty
136+
137+
// initialization part
138+
&& let Some(init) = local.init
139+
{
140+
match &init.kind {
141+
// When the initialization is a call to a function
142+
hir::ExprKind::Call(init_call, _) => {
143+
if let hir::TyKind::Path(ty_path) = &ty.kind
144+
&& let hir::QPath::Resolved(_, resolved_path_ty) = ty_path
145+
146+
&& is_redundant_in_func_call(cx, resolved_path_ty.res, init_call) {
147+
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
148+
}
149+
},
150+
hir::ExprKind::MethodCall(_, _, _, _) => {
151+
let mut is_ref = false;
152+
let mut ty_kind = &ty.kind;
153+
154+
// If the annotation is a ref we "peel" it
155+
if let hir::TyKind::Ref(_, mut_ty) = &ty.kind {
156+
is_ref = true;
157+
ty_kind = &mut_ty.ty.kind;
158+
}
159+
160+
if let hir::TyKind::Path(ty_path) = ty_kind
161+
&& let hir::QPath::Resolved(_, resolved_path_ty) = ty_path
162+
&& let Some(func_ty) = func_hir_id_to_func_ty(cx, init.hir_id)
163+
&& let Some(return_type) = func_ty_to_return_type(cx, func_ty)
164+
&& is_same_type(cx, resolved_path_ty.res, if is_ref {
165+
return_type.peel_refs()
166+
} else {
167+
return_type
168+
})
169+
{
170+
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
171+
}
172+
},
173+
// When the initialization is a path for example u32::MAX
174+
hir::ExprKind::Path(init_path) => {
175+
// TODO: check for non primty
176+
if let Some(primty) = extract_primty(&ty.kind)
177+
178+
&& let hir::QPath::TypeRelative(init_ty, _) = init_path
179+
&& let Some(primty_init) = extract_primty(&init_ty.kind)
180+
181+
&& primty == primty_init
182+
{
183+
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
184+
}
185+
},
186+
hir::ExprKind::Lit(init_lit) => {
187+
match init_lit.node {
188+
// In these cases the annotation is redundant
189+
LitKind::Str(..)
190+
| LitKind::ByteStr(..)
191+
| LitKind::Byte(..)
192+
| LitKind::Char(..)
193+
| LitKind::Bool(..)
194+
| LitKind::CStr(..) => {
195+
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
196+
},
197+
LitKind::Int(..) | LitKind::Float(..) => {
198+
// If the initialization value is a suffixed literal we lint
199+
if init_lit.node.is_suffixed() {
200+
span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
201+
}
202+
},
203+
LitKind::Err => (),
204+
}
205+
}
206+
_ => ()
207+
}
208+
};
209+
}
210+
}

0 commit comments

Comments
 (0)