Skip to content

Commit

Permalink
Support associated functions
Browse files Browse the repository at this point in the history
  • Loading branch information
AlessioC31 committed Apr 17, 2023
1 parent efe7f8c commit b20100c
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 65 deletions.
113 changes: 62 additions & 51 deletions clippy_lints/src/redundant_type_annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -22,91 +23,101 @@ 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<Ty<'tcx>> {
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

// 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
Expand All @@ -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");
}
}
_ => ()
}
};
}
Expand Down
8 changes: 7 additions & 1 deletion tests/ui/redundant_type_annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ impl ATest2 {
fn new() -> Self {
Self { inner: 5 }
}

fn get_string() -> String {
String::from("")
}
}

fn f_prim() -> u32 {
Expand All @@ -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();
}
32 changes: 19 additions & 13 deletions tests/ui/redundant_type_annotations.stderr
Original file line number Diff line number Diff line change
@@ -1,52 +1,58 @@
error: redundant type annotation
--> $DIR/redundant_type_annotations.rs:49:5
--> $DIR/redundant_type_annotations.rs:53:5
|
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

0 comments on commit b20100c

Please sign in to comment.