Skip to content

Commit ef3867f

Browse files
committed
Auto merge of rust-lang#9102 - botahamec:unused-box, r=xFrednet
Added the `[unnecessary_box_returns]` lint fixes #5 I'm not confident in the name of this lint. Let me know if you can think of something better --- changelog: New lint: ``[`unnecessary_box_returns`]`` [rust-lang#9102](rust-lang/rust-clippy#9102) <!-- changelog_checked -->
2 parents c5011e9 + 76d13bb commit ef3867f

8 files changed

+225
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4975,6 +4975,7 @@ Released 2018-09-13
49754975
[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
49764976
[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
49774977
[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
4978+
[`unnecessary_box_returns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns
49784979
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
49794980
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
49804981
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map

book/src/lint_configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
130130
* [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
131131
* [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
132132
* [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
133+
* [unnecessary_box_returns](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
133134

134135

135136
### msrv

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
617617
crate::unit_types::UNIT_CMP_INFO,
618618
crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
619619
crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
620+
crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
620621
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
621622
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
622623
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,

clippy_lints/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ mod uninit_vec;
300300
mod unit_return_expecting_ord;
301301
mod unit_types;
302302
mod unnamed_address;
303+
mod unnecessary_box_returns;
303304
mod unnecessary_owned_empty_strings;
304305
mod unnecessary_self_imports;
305306
mod unnecessary_struct_initialization;
@@ -940,6 +941,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
940941
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
941942
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
942943
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
944+
store.register_late_pass(move |_| {
945+
Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(
946+
avoid_breaking_exported_api,
947+
))
948+
});
943949
// add lints here, do not remove this comment, it's used in `new_lint`
944950
}
945951

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use rustc_errors::Applicability;
3+
use rustc_hir::{def_id::LocalDefId, FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_session::{declare_tool_lint, impl_lint_pass};
6+
use rustc_span::Symbol;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
///
11+
/// Checks for a return type containing a `Box<T>` where `T` implements `Sized`
12+
///
13+
/// ### Why is this bad?
14+
///
15+
/// It's better to just return `T` in these cases. The caller may not need
16+
/// the value to be boxed, and it's expensive to free the memory once the
17+
/// `Box<T>` been dropped.
18+
///
19+
/// ### Example
20+
/// ```rust
21+
/// fn foo() -> Box<String> {
22+
/// Box::new(String::from("Hello, world!"))
23+
/// }
24+
/// ```
25+
/// Use instead:
26+
/// ```rust
27+
/// fn foo() -> String {
28+
/// String::from("Hello, world!")
29+
/// }
30+
/// ```
31+
#[clippy::version = "1.70.0"]
32+
pub UNNECESSARY_BOX_RETURNS,
33+
pedantic,
34+
"Needlessly returning a Box"
35+
}
36+
37+
pub struct UnnecessaryBoxReturns {
38+
avoid_breaking_exported_api: bool,
39+
}
40+
41+
impl_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]);
42+
43+
impl UnnecessaryBoxReturns {
44+
pub fn new(avoid_breaking_exported_api: bool) -> Self {
45+
Self {
46+
avoid_breaking_exported_api,
47+
}
48+
}
49+
50+
fn check_fn_item(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: LocalDefId, name: Symbol) {
51+
// we don't want to tell someone to break an exported function if they ask us not to
52+
if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
53+
return;
54+
}
55+
56+
// functions which contain the word "box" are exempt from this lint
57+
if name.as_str().contains("box") {
58+
return;
59+
}
60+
61+
let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
62+
63+
let return_ty = cx
64+
.tcx
65+
.erase_late_bound_regions(cx.tcx.fn_sig(def_id).skip_binder())
66+
.output();
67+
68+
if !return_ty.is_box() {
69+
return;
70+
}
71+
72+
let boxed_ty = return_ty.boxed_ty();
73+
74+
// it's sometimes useful to return Box<T> if T is unsized, so don't lint those
75+
if boxed_ty.is_sized(cx.tcx, cx.param_env) {
76+
span_lint_and_then(
77+
cx,
78+
UNNECESSARY_BOX_RETURNS,
79+
return_ty_hir.span,
80+
format!("boxed return of the sized type `{boxed_ty}`").as_str(),
81+
|diagnostic| {
82+
diagnostic.span_suggestion(
83+
return_ty_hir.span,
84+
"try",
85+
boxed_ty.to_string(),
86+
// the return value and function callers also needs to
87+
// be changed, so this can't be MachineApplicable
88+
Applicability::Unspecified,
89+
);
90+
diagnostic.help("changing this also requires a change to the return expressions in this function");
91+
},
92+
);
93+
}
94+
}
95+
}
96+
97+
impl LateLintPass<'_> for UnnecessaryBoxReturns {
98+
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
99+
let TraitItemKind::Fn(signature, _) = &item.kind else { return };
100+
self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
101+
}
102+
103+
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
104+
// Ignore implementations of traits, because the lint should be on the
105+
// trait, not on the implmentation of it.
106+
let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
107+
let ItemKind::Impl(parent) = parent.kind else { return };
108+
if parent.of_trait.is_some() {
109+
return;
110+
}
111+
112+
let ImplItemKind::Fn(signature, ..) = &item.kind else { return };
113+
self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
114+
}
115+
116+
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
117+
let ItemKind::Fn(signature, ..) = &item.kind else { return };
118+
self.check_fn_item(cx, signature.decl, item.owner_id.def_id, item.ident.name);
119+
}
120+
}

clippy_lints/src/utils/conf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ define_Conf! {
249249
/// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
250250
/// ```
251251
(arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
252-
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
252+
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS.
253253
///
254254
/// Suppress lints whenever the suggested change would cause breakage for other crates.
255255
(avoid_breaking_exported_api: bool = true),

tests/ui/unnecessary_box_returns.rs

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![warn(clippy::unnecessary_box_returns)]
2+
3+
trait Bar {
4+
// lint
5+
fn baz(&self) -> Box<usize>;
6+
}
7+
8+
pub struct Foo {}
9+
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+
24+
// lint
25+
fn bxed_usize() -> Box<usize> {
26+
Box::new(5)
27+
}
28+
29+
// lint
30+
fn _bxed_foo() -> Box<Foo> {
31+
Box::new(Foo {})
32+
}
33+
34+
// don't lint: this is exported
35+
pub fn bxed_foo() -> Box<Foo> {
36+
Box::new(Foo {})
37+
}
38+
39+
// don't lint: str is unsized
40+
fn bxed_str() -> Box<str> {
41+
"Hello, world!".to_string().into_boxed_str()
42+
}
43+
44+
// don't lint: function contains the word, "box"
45+
fn boxed_usize() -> Box<usize> {
46+
Box::new(7)
47+
}
48+
49+
// don't lint: this has an unspecified return type
50+
fn default() {}
51+
52+
// don't lint: this doesn't return a Box
53+
fn string() -> String {
54+
String::from("Hello, world")
55+
}
56+
57+
fn main() {
58+
// don't lint: this is a closure
59+
let a = || -> Box<usize> { Box::new(5) };
60+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: boxed return of the sized type `usize`
2+
--> $DIR/unnecessary_box_returns.rs:5:22
3+
|
4+
LL | fn baz(&self) -> Box<usize>;
5+
| ^^^^^^^^^^ help: try: `usize`
6+
|
7+
= help: changing this also requires a change to the return expressions in this function
8+
= note: `-D clippy::unnecessary-box-returns` implied by `-D warnings`
9+
10+
error: boxed return of the sized type `usize`
11+
--> $DIR/unnecessary_box_returns.rs:18:22
12+
|
13+
LL | fn baz(&self) -> Box<usize> {
14+
| ^^^^^^^^^^ help: try: `usize`
15+
|
16+
= help: changing this also requires a change to the return expressions in this function
17+
18+
error: boxed return of the sized type `usize`
19+
--> $DIR/unnecessary_box_returns.rs:25:20
20+
|
21+
LL | fn bxed_usize() -> Box<usize> {
22+
| ^^^^^^^^^^ help: try: `usize`
23+
|
24+
= help: changing this also requires a change to the return expressions in this function
25+
26+
error: boxed return of the sized type `Foo`
27+
--> $DIR/unnecessary_box_returns.rs:30:19
28+
|
29+
LL | fn _bxed_foo() -> Box<Foo> {
30+
| ^^^^^^^^ help: try: `Foo`
31+
|
32+
= help: changing this also requires a change to the return expressions in this function
33+
34+
error: aborting due to 4 previous errors
35+

0 commit comments

Comments
 (0)