Skip to content

Commit 3569f87

Browse files
committed
Lint on trait declarations, not implementations
1 parent 858b3b0 commit 3569f87

File tree

3 files changed

+80
-41
lines changed

3 files changed

+80
-41
lines changed
Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
22
use rustc_errors::Applicability;
3-
use rustc_hir::{def_id::LocalDefId, intravisit::FnKind, Body, FnDecl, FnRetTy};
3+
use rustc_hir::{FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
44
use rustc_hir_analysis::hir_ty_to_ty;
55
use rustc_lint::{LateContext, LateLintPass};
66
use rustc_session::{declare_lint_pass, declare_tool_lint};
7-
use rustc_span::Span;
87

98
declare_clippy_lint! {
109
/// ### What it does
@@ -36,46 +35,55 @@ declare_clippy_lint! {
3635
}
3736
declare_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]);
3837

39-
impl LateLintPass<'_> for UnnecessaryBoxReturns {
40-
fn check_fn(
41-
&mut self,
42-
cx: &LateContext<'_>,
43-
fn_kind: FnKind<'_>,
44-
decl: &FnDecl<'_>,
45-
_: &Body<'_>,
46-
_: Span,
47-
_: LocalDefId,
48-
) {
49-
// it's unclear what part of a closure you would span, so for now it's ignored
50-
// if this is changed, please also make sure not to call `hir_ty_to_ty` below
51-
if matches!(fn_kind, FnKind::Closure) {
52-
return;
53-
}
38+
fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
39+
let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
5440

55-
let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
41+
// this is safe, since we're not in a body
42+
let return_ty = hir_ty_to_ty(cx.tcx, return_ty_hir);
5643

57-
// this is safe, since we're not in a body
58-
let return_ty = hir_ty_to_ty(cx.tcx, return_ty_hir);
44+
if !return_ty.is_box() {
45+
return;
46+
}
5947

60-
if !return_ty.is_box() {
48+
let boxed_ty = return_ty.boxed_ty();
49+
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else { return };
50+
51+
// it's sometimes useful to return Box<T> if T is unsized, so don't lint those
52+
if implements_trait(cx, boxed_ty, sized_trait, &[]) {
53+
span_lint_and_sugg(
54+
cx,
55+
UNNECESSARY_BOX_RETURNS,
56+
return_ty_hir.span,
57+
format!("boxed return of the sized type `{boxed_ty}`").as_str(),
58+
"try",
59+
boxed_ty.to_string(),
60+
// the return value and function callers also needs to be changed, so this can't be MachineApplicable
61+
Applicability::Unspecified,
62+
);
63+
}
64+
}
65+
66+
impl LateLintPass<'_> for UnnecessaryBoxReturns {
67+
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
68+
let TraitItemKind::Fn(signature, _) = &item.kind else { return };
69+
check_fn_decl(cx, signature.decl);
70+
}
71+
72+
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
73+
// Ignore implementations of traits, because the lint should be on the
74+
// trait, not on the implmentation of it.
75+
let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
76+
let ItemKind::Impl(parent) = parent.kind else { return };
77+
if parent.of_trait.is_some() {
6178
return;
6279
}
6380

64-
let boxed_ty = return_ty.boxed_ty();
65-
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else { return };
81+
let ImplItemKind::Fn(signature, ..) = &item.kind else { return };
82+
check_fn_decl(cx, signature.decl);
83+
}
6684

67-
// it's sometimes useful to return Box<T> if T is unsized, so don't lint those
68-
if implements_trait(cx, boxed_ty, sized_trait, &[]) {
69-
span_lint_and_sugg(
70-
cx,
71-
UNNECESSARY_BOX_RETURNS,
72-
return_ty_hir.span,
73-
format!("boxed return of the sized type `{boxed_ty}`").as_str(),
74-
"try",
75-
boxed_ty.to_string(),
76-
// the return value and function callers also needs to be changed, so this can't be MachineApplicable
77-
Applicability::Unspecified,
78-
);
79-
}
85+
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
86+
let ItemKind::Fn(signature, ..) = &item.kind else { return };
87+
check_fn_decl(cx, signature.decl);
8088
}
8189
}

tests/ui/unnecessary_box_returns.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
11
#![warn(clippy::unnecessary_box_returns)]
22

3+
trait Bar {
4+
// lint
5+
fn baz(&self) -> Box<usize>;
6+
}
7+
38
struct Foo {}
49

10+
impl Bar for Foo {
11+
// don't lint: this is a problem with the trait, not the implementation
12+
fn baz(&self) -> Box<usize> {
13+
Box::new(42)
14+
}
15+
}
16+
17+
impl Foo {
18+
fn baz(&self) -> Box<usize> {
19+
// lint
20+
Box::new(13)
21+
}
22+
}
23+
524
// lint
625
fn boxed_usize() -> Box<usize> {
726
Box::new(5)
Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
error: boxed return of the sized type `usize`
2-
--> $DIR/unnecessary_box_returns.rs:6:21
2+
--> $DIR/unnecessary_box_returns.rs:5:22
33
|
4-
LL | fn boxed_usize() -> Box<usize> {
5-
| ^^^^^^^^^^ help: try: `usize`
4+
LL | fn baz(&self) -> Box<usize>;
5+
| ^^^^^^^^^^ help: try: `usize`
66
|
77
= note: `-D clippy::unnecessary-box-returns` implied by `-D warnings`
88

9+
error: boxed return of the sized type `usize`
10+
--> $DIR/unnecessary_box_returns.rs:18:22
11+
|
12+
LL | fn baz(&self) -> Box<usize> {
13+
| ^^^^^^^^^^ help: try: `usize`
14+
15+
error: boxed return of the sized type `usize`
16+
--> $DIR/unnecessary_box_returns.rs:25:21
17+
|
18+
LL | fn boxed_usize() -> Box<usize> {
19+
| ^^^^^^^^^^ help: try: `usize`
20+
921
error: boxed return of the sized type `Foo`
10-
--> $DIR/unnecessary_box_returns.rs:11:19
22+
--> $DIR/unnecessary_box_returns.rs:30:19
1123
|
1224
LL | fn boxed_foo() -> Box<Foo> {
1325
| ^^^^^^^^ help: try: `Foo`
1426

15-
error: aborting due to 2 previous errors
27+
error: aborting due to 4 previous errors
1628

0 commit comments

Comments
 (0)