Skip to content

Commit

Permalink
New lint: borrowed_option
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-anders committed Sep 4, 2023
1 parent b9906ac commit 7f5486e
Show file tree
Hide file tree
Showing 19 changed files with 174 additions and 81 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4805,6 +4805,7 @@ Released 2018-09-13
[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
[`borrowed_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_option
[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default
[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::transmute::WRONG_TRANSMUTE_INFO,
crate::tuple_array_conversions::TUPLE_ARRAY_CONVERSIONS_INFO,
crate::types::BORROWED_BOX_INFO,
crate::types::BORROWED_OPTION_INFO,
crate::types::BOX_COLLECTION_INFO,
crate::types::LINKEDLIST_INFO,
crate::types::OPTION_OPTION_INFO,
Expand Down
18 changes: 12 additions & 6 deletions clippy_lints/src/types/borrowed_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_hir::{
use rustc_lint::LateContext;
use rustc_span::sym;

use super::BORROWED_BOX;
use super::{BORROWED_BOX, BORROWED_OPTION};

pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, mut_ty: &MutTy<'_>) -> bool {
match mut_ty.ty.kind {
Expand All @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
let def = cx.qpath_res(qpath, hir_id);
if_chain! {
if let Some(def_id) = def.opt_def_id();
if Some(def_id) == cx.tcx.lang_items().owned_box();
if Some(def_id) == cx.tcx.lang_items().owned_box() || Some(def_id) == cx.tcx.lang_items().option_type();
if let QPath::Resolved(None, path) = *qpath;
if let [ref bx] = *path.segments;
if let Some(params) = bx.args;
Expand All @@ -27,7 +27,8 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
_ => None,
});
then {
if is_any_trait(cx, inner) {
let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
if is_box && is_any_trait(cx, inner) {
// Ignore `Box<Any>` types; see issue #1884 for details.
return false;
}
Expand All @@ -41,6 +42,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
if mut_ty.mutbl == Mutability::Mut {
// Ignore `&mut Box<T>` types; see issue #2907 for
// details.
// Same reasoning applies for `&mut Option<T>` types.
return false;
}

Expand All @@ -62,11 +64,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
};
span_lint_and_sugg(
cx,
BORROWED_BOX,
if is_box { BORROWED_BOX } else { BORROWED_OPTION },
hir_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
if is_box {
"you seem to be trying to use `&Box<T>`. Consider using just `&T`"
} else {
"you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead"
},
"try",
suggestion,
if is_box { suggestion } else { format!("Option<{suggestion}>") },
// To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
// because the trait impls of it will break otherwise;
// and there may be other cases that result in invalid code.
Expand Down
34 changes: 33 additions & 1 deletion clippy_lints/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,38 @@ declare_clippy_lint! {
"a borrow of a boxed type"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `&Option<T>` anywhere in the code.
/// Check the [Option documentation](https://doc.rust-lang.org/std/option/index.html) for more information.
///
/// ### Why is this bad?
/// A `&Option<T>` parameter prevents calling the function if the caller holds a different type, e.g. `Result<T, E>`.
/// Using `Option<&T>` generalizes the function, e.g. allowing to pass `res.ok().as_ref()`
/// Returning `&Option<T>` needlessly exposes implementation details and has no advantage over `Option<&T>`.
///
/// ### Example
/// ```rust,compile_fail
/// fn foo(bar: &Option<i32>) -> &Option<i32> { bar }
/// fn call_foo(bar: &Result<i32, ()>) {
/// foo(bar.ok()); // does not work
/// }
/// ```
///
/// Better:
///
/// ```rust
/// fn foo(bar: Option<&i32>) -> Option<&i32> { bar }
/// fn call_foo(bar: &Result<i32, ()>) {
/// foo(bar.ok().as_ref()); // works!
/// }
/// ```
#[clippy::version = "1.74.0"]
pub BORROWED_OPTION,
complexity,
"`&Option<T>` instead of `Option<&T>`"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for usage of redundant allocations anywhere in the code.
Expand Down Expand Up @@ -309,7 +341,7 @@ pub struct Types {
avoid_breaking_exported_api: bool,
}

impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, BORROWED_OPTION, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);

impl<'tcx> LateLintPass<'tcx> for Types {
fn check_fn(
Expand Down
26 changes: 18 additions & 8 deletions tests/ui/borrow_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

use std::fmt::Display;

pub fn test1(foo: &mut Box<bool>) {
pub fn test1(foo: &mut Box<bool>, bar: &mut Option<bool>) {
// Although this function could be changed to "&mut bool",
// avoiding the Box, mutable references to boxes are not
// flagged by this lint.
Expand All @@ -18,26 +18,33 @@ pub fn test1(foo: &mut Box<bool>) {
// the memory location of the pointed-to object could be
// modified. By passing a mutable reference, the contents
// could change, but not the location.
println!("{:?}", foo)
//
// The same reasoning applies to ignoring &mut Option
println!("{:?} {:?}", foo, bar)
}

pub fn test2() {
let foo: &Box<bool>;
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`
let bar: &Option<bool>;
//~^ ERROR: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
}

struct Test3<'a> {
foo: &'a Box<bool>,
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`
bar: &'a Option<bool>,
//~^ ERROR: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
}

trait Test4 {
fn test4(a: &Box<bool>);
fn test4(a: &Box<bool>, b: &Option<bool>);
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`
//~| ERROR: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
}

impl<'a> Test4 for Test3<'a> {
fn test4(a: &Box<bool>) {
fn test4(a: &Box<bool>, b: &Option<bool>) {
unimplemented!();
}
}
Expand Down Expand Up @@ -106,12 +113,15 @@ pub fn test15(_display: &Box<dyn Display + Send>) {}
pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`

pub fn test17(_display: &Box<impl Display>) {}
pub fn test17(_display: &Box<impl Display>, _display2: &Option<impl Display>) {}
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`
pub fn test18(_display: &Box<impl Display + Send>) {}
//~| ERROR: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
pub fn test18(_display: &Box<impl Display + Send>, _display2: &Option<impl Display + Send>) {}
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`
pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
//~| ERROR: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
pub fn test19<'a>(_display: &'a Box<impl Display + 'a>, _display2: &'a Option<impl Display + 'a>) {}
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`
//~| ERROR: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead

// This exists only to check what happens when parentheses are already present.
// Even though the current implementation doesn't put extra parentheses,
Expand All @@ -120,7 +130,7 @@ pub fn test20(_display: &Box<(dyn Display + Send)>) {}
//~^ ERROR: you seem to be trying to use `&Box<T>`. Consider using just `&T`

fn main() {
test1(&mut Box::new(false));
test1(&mut Box::new(false), &mut Some(false));
test2();
test5(&mut (Box::new(false) as Box<dyn Any>));
test6();
Expand Down
68 changes: 53 additions & 15 deletions tests/ui/borrow_box.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:25:14
--> $DIR/borrow_box.rs:27:14
|
LL | let foo: &Box<bool>;
| ^^^^^^^^^^ help: try: `&bool`
Expand All @@ -10,59 +10,97 @@ note: the lint level is defined here
LL | #![deny(clippy::borrowed_box)]
| ^^^^^^^^^^^^^^^^^^^^

error: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
--> $DIR/borrow_box.rs:29:14
|
LL | let bar: &Option<bool>;
| ^^^^^^^^^^^^^ help: try: `Option<&bool>`
|
= note: `-D clippy::borrowed-option` implied by `-D warnings`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:30:10
--> $DIR/borrow_box.rs:34:10
|
LL | foo: &'a Box<bool>,
| ^^^^^^^^^^^^^ help: try: `&'a bool`

error: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
--> $DIR/borrow_box.rs:36:10
|
LL | bar: &'a Option<bool>,
| ^^^^^^^^^^^^^^^^ help: try: `Option<&'a bool>`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:35:17
--> $DIR/borrow_box.rs:41:17
|
LL | fn test4(a: &Box<bool>);
LL | fn test4(a: &Box<bool>, b: &Option<bool>);
| ^^^^^^^^^^ help: try: `&bool`

error: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
--> $DIR/borrow_box.rs:41:32
|
LL | fn test4(a: &Box<bool>, b: &Option<bool>);
| ^^^^^^^^^^^^^ help: try: `Option<&bool>`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:102:25
--> $DIR/borrow_box.rs:109:25
|
LL | pub fn test14(_display: &Box<dyn Display>) {}
| ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:104:25
--> $DIR/borrow_box.rs:111:25
|
LL | pub fn test15(_display: &Box<dyn Display + Send>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:106:29
--> $DIR/borrow_box.rs:113:29
|
LL | pub fn test16<'a>(_display: &'a Box<dyn Display + 'a>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:109:25
--> $DIR/borrow_box.rs:116:25
|
LL | pub fn test17(_display: &Box<impl Display>) {}
LL | pub fn test17(_display: &Box<impl Display>, _display2: &Option<impl Display>) {}
| ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display`

error: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
--> $DIR/borrow_box.rs:116:56
|
LL | pub fn test17(_display: &Box<impl Display>, _display2: &Option<impl Display>) {}
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&impl Display>`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:111:25
--> $DIR/borrow_box.rs:119:25
|
LL | pub fn test18(_display: &Box<impl Display + Send>) {}
LL | pub fn test18(_display: &Box<impl Display + Send>, _display2: &Option<impl Display + Send>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)`

error: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
--> $DIR/borrow_box.rs:119:63
|
LL | pub fn test18(_display: &Box<impl Display + Send>, _display2: &Option<impl Display + Send>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&(impl Display + Send)>`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:113:29
--> $DIR/borrow_box.rs:122:29
|
LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>) {}
LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>, _display2: &'a Option<impl Display + 'a>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)`

error: you seem to be trying to use `&Option<T>`. Consider using `Option<&T>` instead
--> $DIR/borrow_box.rs:122:68
|
LL | pub fn test19<'a>(_display: &'a Box<impl Display + 'a>, _display2: &'a Option<impl Display + 'a>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a (impl Display + 'a)>`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> $DIR/borrow_box.rs:119:25
--> $DIR/borrow_box.rs:129:25
|
LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)`

error: aborting due to 10 previous errors
error: aborting due to 16 previous errors

3 changes: 2 additions & 1 deletion tests/ui/clone_on_copy.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
clippy::unnecessary_operation,
clippy::vec_init_then_push,
clippy::toplevel_ref_arg,
clippy::needless_borrow
clippy::needless_borrow,
clippy::borrowed_option
)]

use std::cell::RefCell;
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/clone_on_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
clippy::unnecessary_operation,
clippy::vec_init_then_push,
clippy::toplevel_ref_arg,
clippy::needless_borrow
clippy::needless_borrow,
clippy::borrowed_option
)]

use std::cell::RefCell;
Expand Down
18 changes: 9 additions & 9 deletions tests/ui/clone_on_copy.stderr
Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@
error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:23:5
--> $DIR/clone_on_copy.rs:24:5
|
LL | 42.clone();
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
|
= note: `-D clippy::clone-on-copy` implied by `-D warnings`

error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:27:5
--> $DIR/clone_on_copy.rs:28:5
|
LL | (&42).clone();
| ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)`

error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:30:5
--> $DIR/clone_on_copy.rs:31:5
|
LL | rc.borrow().clone();
| ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()`

error: using `clone` on type `u32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:33:5
--> $DIR/clone_on_copy.rs:34:5
|
LL | x.clone().rotate_left(1);
| ^^^^^^^^^ help: try removing the `clone` call: `x`

error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:47:5
--> $DIR/clone_on_copy.rs:48:5
|
LL | m!(42).clone();
| ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)`

error: using `clone` on type `[u32; 2]` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:57:5
--> $DIR/clone_on_copy.rs:58:5
|
LL | x.clone()[0];
| ^^^^^^^^^ help: try dereferencing it: `(*x)`

error: using `clone` on type `char` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:67:14
--> $DIR/clone_on_copy.rs:68:14
|
LL | is_ascii('z'.clone());
| ^^^^^^^^^^^ help: try removing the `clone` call: `'z'`

error: using `clone` on type `i32` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:71:14
--> $DIR/clone_on_copy.rs:72:14
|
LL | vec.push(42.clone());
| ^^^^^^^^^^ help: try removing the `clone` call: `42`

error: using `clone` on type `Option<i32>` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:75:17
--> $DIR/clone_on_copy.rs:76:17
|
LL | let value = opt.clone()?; // operator precedence needed (*opt)?
| ^^^^^^^^^^^ help: try dereferencing it: `(*opt)`
Expand Down
Loading

0 comments on commit 7f5486e

Please sign in to comment.