Skip to content

Commit be625e6

Browse files
committed
Avoid breaking exported API
1 parent ed368ff commit be625e6

File tree

4 files changed

+65
-50
lines changed

4 files changed

+65
-50
lines changed

clippy_lints/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
926926
});
927927
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
928928
store.register_late_pass(|_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns));
929+
store.register_late_pass(move |_| {
930+
Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(
931+
avoid_breaking_exported_api,
932+
))
933+
});
929934
// add lints here, do not remove this comment, it's used in `new_lint`
930935
}
931936

Lines changed: 45 additions & 40 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::{intravisit::FnKind, Body, FnDecl, FnRetTy, HirId, TraitItem, TraitItemKind};
3+
use rustc_hir::{def_id::LocalDefId, FnDecl, FnRetTy, Item, ItemKind, TraitItem, TraitItemKind};
44
use rustc_hir_analysis::hir_ty_to_ty;
55
use rustc_lint::{LateContext, LateLintPass};
6-
use rustc_session::{declare_lint_pass, declare_tool_lint};
7-
use rustc_span::Span;
6+
use rustc_session::{declare_tool_lint, impl_lint_pass};
87

98
declare_clippy_lint! {
109
/// ### What it does
@@ -34,56 +33,62 @@ declare_clippy_lint! {
3433
nursery,
3534
"Needlessly returning a Box"
3635
}
37-
declare_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]);
3836

39-
fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
40-
let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
37+
pub struct UnnecessaryBoxReturns {
38+
avoid_breaking_exported_api: bool,
39+
}
4140

42-
// this is safe, since we're not in a body
43-
let return_ty = hir_ty_to_ty(cx.tcx, return_ty_hir);
41+
impl_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]);
4442

45-
if !return_ty.is_box() {
46-
return;
43+
impl UnnecessaryBoxReturns {
44+
pub fn new(avoid_breaking_exported_api: bool) -> Self {
45+
Self {
46+
avoid_breaking_exported_api,
47+
}
4748
}
4849

49-
let boxed_ty = return_ty.boxed_ty();
50-
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else { return };
50+
fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: LocalDefId) {
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+
let FnRetTy::Return(return_ty_hir) = &decl.output else { return };
57+
58+
// this is safe, since we're not in a body
59+
let return_ty = hir_ty_to_ty(cx.tcx, return_ty_hir);
5160

52-
// it's sometimes useful to return Box<T> if T is unsized, so don't lint those
53-
if implements_trait(cx, boxed_ty, sized_trait, &[]) {
54-
span_lint_and_sugg(
55-
cx,
56-
UNNECESSARY_BOX_RETURNS,
57-
return_ty_hir.span,
58-
format!("boxed return of the sized type `{boxed_ty}`").as_str(),
59-
"try",
60-
boxed_ty.to_string(),
61-
// the return value and function callers also needs to be changed, so this can't be MachineApplicable
62-
Applicability::Unspecified,
63-
);
61+
if !return_ty.is_box() {
62+
return;
63+
}
64+
65+
let boxed_ty = return_ty.boxed_ty();
66+
let Some(sized_trait) = cx.tcx.lang_items().sized_trait() else { return };
67+
68+
// it's sometimes useful to return Box<T> if T is unsized, so don't lint those
69+
if implements_trait(cx, boxed_ty, sized_trait, &[]) {
70+
span_lint_and_sugg(
71+
cx,
72+
UNNECESSARY_BOX_RETURNS,
73+
return_ty_hir.span,
74+
format!("boxed return of the sized type `{boxed_ty}`").as_str(),
75+
"try",
76+
boxed_ty.to_string(),
77+
// the return value and function callers also needs to be changed, so this can't be MachineApplicable
78+
Applicability::Unspecified,
79+
);
80+
}
6481
}
6582
}
6683

6784
impl LateLintPass<'_> for UnnecessaryBoxReturns {
6885
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
6986
let TraitItemKind::Fn(signature, _) = &item.kind else { return };
70-
check_fn_decl(cx, signature.decl);
87+
self.check_fn_decl(cx, signature.decl, item.owner_id.def_id);
7188
}
7289

73-
fn check_fn(
74-
&mut self,
75-
cx: &LateContext<'_>,
76-
fn_kind: FnKind<'_>,
77-
decl: &FnDecl<'_>,
78-
_: &Body<'_>,
79-
_: Span,
80-
_: HirId,
81-
) {
82-
// It's unclear what part of a closure you would span, so for now it's ignored.
83-
// Trait implementations should also not be linted.
84-
// If this is changed, please also make sure not to call `hir_ty_to_ty` below.
85-
let FnKind::ItemFn(..) = fn_kind else { return };
86-
87-
check_fn_decl(cx, decl);
90+
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
91+
let ItemKind::Fn(signature, ..) = &item.kind else { return };
92+
self.check_fn_decl(cx, signature.decl, item.owner_id.def_id);
8893
}
8994
}

tests/ui/unnecessary_box_returns.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait Bar {
55
fn baz(&self) -> Box<usize>;
66
}
77

8-
struct Foo {}
8+
pub struct Foo {}
99

1010
impl Bar for Foo {
1111
// don't lint: this is a problem with the trait, not the implementation
@@ -20,7 +20,12 @@ fn boxed_usize() -> Box<usize> {
2020
}
2121

2222
// lint
23-
fn boxed_foo() -> Box<Foo> {
23+
fn _boxed_foo() -> Box<Foo> {
24+
Box::new(Foo {})
25+
}
26+
27+
// don't lint: this is exported
28+
pub fn boxed_foo() -> Box<Foo> {
2429
Box::new(Foo {})
2530
}
2631

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
error: function returns `Box<usize>` when `usize` implements `Sized`
1+
error: boxed return of the sized type `usize`
22
--> $DIR/unnecessary_box_returns.rs:5:22
33
|
44
LL | fn baz(&self) -> Box<usize>;
5-
| ^^^^^^^^^^ help: change the return type to: `usize`
5+
| ^^^^^^^^^^ help: try: `usize`
66
|
77
= note: `-D clippy::unnecessary-box-returns` implied by `-D warnings`
88

9-
error: function returns `Box<usize>` when `usize` implements `Sized`
9+
error: boxed return of the sized type `usize`
1010
--> $DIR/unnecessary_box_returns.rs:18:21
1111
|
1212
LL | fn boxed_usize() -> Box<usize> {
13-
| ^^^^^^^^^^ help: change the return type to: `usize`
13+
| ^^^^^^^^^^ help: try: `usize`
1414

15-
error: function returns `Box<Foo>` when `Foo` implements `Sized`
16-
--> $DIR/unnecessary_box_returns.rs:23:19
15+
error: boxed return of the sized type `Foo`
16+
--> $DIR/unnecessary_box_returns.rs:23:20
1717
|
18-
LL | fn boxed_foo() -> Box<Foo> {
19-
| ^^^^^^^^ help: change the return type to: `Foo`
18+
LL | fn _boxed_foo() -> Box<Foo> {
19+
| ^^^^^^^^ help: try: `Foo`
2020

2121
error: aborting due to 3 previous errors
2222

0 commit comments

Comments
 (0)