Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Types borrow box #1501

Merged
merged 13 commits into from
Jun 13, 2017
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ All notable changes to this project will be documented in this file.
[`block_in_if_condition_expr`]: https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr
[`block_in_if_condition_stmt`]: https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_stmt
[`bool_comparison`]: https://github.com/Manishearth/rust-clippy/wiki#bool_comparison
[`borrowed_box`]: https://github.com/Manishearth/rust-clippy/wiki#borrowed_box
[`box_vec`]: https://github.com/Manishearth/rust-clippy/wiki#box_vec
[`boxed_local`]: https://github.com/Manishearth/rust-clippy/wiki#boxed_local
[`builtin_type_shadow`]: https://github.com/Manishearth/rust-clippy/wiki#builtin_type_shadow
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ name
[block_in_if_condition_expr](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr) | warn | braces that can be eliminated in conditions, e.g. `if { true } ...`
[block_in_if_condition_stmt](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_stmt) | warn | complex blocks in conditions, e.g. `if { let x = true; x } ...`
[bool_comparison](https://github.com/Manishearth/rust-clippy/wiki#bool_comparison) | warn | comparing a variable to a boolean, e.g. `if x == true`
[borrowed_box](https://github.com/Manishearth/rust-clippy/wiki#borrowed_box) | warn | a borrow of a boxed type
[box_vec](https://github.com/Manishearth/rust-clippy/wiki#box_vec) | warn | usage of `Box<Vec<T>>`, vector elements are already on the heap
[boxed_local](https://github.com/Manishearth/rust-clippy/wiki#boxed_local) | warn | using `Box<T>` where unnecessary
[builtin_type_shadow](https://github.com/Manishearth/rust-clippy/wiki#builtin_type_shadow) | warn | shadowing a builtin type
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
transmute::USELESS_TRANSMUTE,
transmute::WRONG_TRANSMUTE,
types::ABSURD_EXTREME_COMPARISONS,
types::BORROWED_BOX,
types::BOX_VEC,
types::CHAR_LIT_AS_U8,
types::LET_UNIT_VALUE,
Expand Down
97 changes: 81 additions & 16 deletions clippy_lints/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::cmp::Ordering;
use syntax::ast::{IntTy, UintTy, FloatTy};
use syntax::attr::IntType;
use syntax::codemap::Span;
use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet, span_help_and_lint, span_lint,
use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet, span_help_and_lint, span_lint, span_lint_and_then,
opt_def_id, last_path_segment, type_size};
use utils::paths;

Expand Down Expand Up @@ -65,9 +65,25 @@ declare_lint! {
structure like a VecDeque"
}

/// **What it does:** Checks for use of `&Box<T>` anywhere in the code.
///
/// **Why is this bad?** Any `&Box<T>` can also be a `&T`, which is more general.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(bar: &Box<T>) { ... }
/// ```
declare_lint! {
pub BORROWED_BOX,
Warn,
"a borrow of a boxed type"
}

impl LintPass for TypePass {
fn get_lints(&self) -> LintArray {
lint_array!(BOX_VEC, LINKEDLIST)
lint_array!(BOX_VEC, LINKEDLIST, BORROWED_BOX)
}
}

Expand All @@ -84,35 +100,46 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypePass {
}

fn check_struct_field(&mut self, cx: &LateContext, field: &StructField) {
check_ty(cx, &field.ty);
check_ty(cx, &field.ty, false);
}

fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
match item.node {
TraitItemKind::Const(ref ty, _) |
TraitItemKind::Type(_, Some(ref ty)) => check_ty(cx, ty),
TraitItemKind::Type(_, Some(ref ty)) => check_ty(cx, ty, false),
TraitItemKind::Method(ref sig, _) => check_fn_decl(cx, &sig.decl),
_ => (),
}
}

fn check_local(&mut self, cx: &LateContext, local: &Local) {
if let Some(ref ty) = local.ty {
check_ty(cx, ty, true);
}
}
}

fn check_fn_decl(cx: &LateContext, decl: &FnDecl) {
for input in &decl.inputs {
check_ty(cx, input);
check_ty(cx, input, false);
}

if let FunctionRetTy::Return(ref ty) = decl.output {
check_ty(cx, ty);
check_ty(cx, ty, false);
}
}

fn check_ty(cx: &LateContext, ast_ty: &hir::Ty) {
/// Recursively check for `TypePass` lints in the given type. Stop at the first
/// lint found.
///
/// The parameter `is_local` distinguishes the context of the type; types from
/// local bindings should only be checked for the `BORROWED_BOX` lint.
fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
if in_macro(ast_ty.span) {
return;
}
match ast_ty.node {
TyPath(ref qpath) => {
TyPath(ref qpath) if !is_local => {
let def = cx.tables.qpath_def(qpath, ast_ty.id);
if let Some(def_id) = opt_def_id(def) {
if Some(def_id) == cx.tcx.lang_items.owned_box() {
Expand Down Expand Up @@ -143,32 +170,70 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty) {
}
match *qpath {
QPath::Resolved(Some(ref ty), ref p) => {
check_ty(cx, ty);
check_ty(cx, ty, is_local);
for ty in p.segments.iter().flat_map(|seg| seg.parameters.types()) {
check_ty(cx, ty);
check_ty(cx, ty, is_local);
}
},
QPath::Resolved(None, ref p) => {
for ty in p.segments.iter().flat_map(|seg| seg.parameters.types()) {
check_ty(cx, ty);
check_ty(cx, ty, is_local);
}
},
QPath::TypeRelative(ref ty, ref seg) => {
check_ty(cx, ty);
check_ty(cx, ty, is_local);
for ty in seg.parameters.types() {
check_ty(cx, ty);
check_ty(cx, ty, is_local);
}
},
}
},
TyRptr(ref lt, MutTy { ref ty, ref mutbl }) => {
match ty.node {
TyPath(ref qpath) => {
let def = cx.tables.qpath_def(qpath, ast_ty.id);
if_let_chain! {[
let Some(def_id) = opt_def_id(def),
Some(def_id) == cx.tcx.lang_items.owned_box(),
let QPath::Resolved(None, ref path) = *qpath,
let [ref bx] = *path.segments,
let PathParameters::AngleBracketedParameters(ref ab_data) = bx.parameters,
let [ref inner] = *ab_data.types
], {
let ltopt = if lt.is_elided() {
"".to_owned()
} else {
format!("{} ", lt.name.as_str())
};
let mutopt = if *mutbl == Mutability::MutMutable {
"mut "
} else {
""
};
span_lint_and_then(cx,
BORROWED_BOX,
ast_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
|db| {
db.span_suggestion(ast_ty.span,
"try",
format!("&{}{}{}", ltopt, mutopt, &snippet(cx, inner.span, "..")));
}
);
return; // don't recurse into the type
}};
check_ty(cx, ty, is_local);
},
_ => check_ty(cx, ty, is_local),
}
},
// recurse
TySlice(ref ty) |
TyArray(ref ty, _) |
TyPtr(MutTy { ref ty, .. }) |
TyRptr(_, MutTy { ref ty, .. }) => check_ty(cx, ty),
TyPtr(MutTy { ref ty, .. }) => check_ty(cx, ty, is_local),
TyTup(ref tys) => {
for ty in tys {
check_ty(cx, ty);
check_ty(cx, ty, is_local);
}
},
_ => {},
Expand Down
34 changes: 34 additions & 0 deletions clippy_tests/examples/borrow_box.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#![feature(plugin)]
#![plugin(clippy)]

#![deny(borrowed_box)]
#![allow(blacklisted_name)]
#![allow(unused_variables)]
#![allow(dead_code)]

pub fn test1(foo: &mut Box<bool>) {
println!("{:?}", foo)
}

pub fn test2() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a trait method taking a box reference and an impl to show that only the trait is linted and not the impl?

let foo: &Box<bool>;
}

struct Test3<'a> {
foo: &'a Box<bool>
}

trait Test4 {
fn test4(a: &Box<bool>);
}

impl<'a> Test4 for Test3<'a> {
fn test4(a: &Box<bool>) {
unimplemented!();
}
}

fn main(){
test1(&mut Box::new(false));
test2();
}
35 changes: 35 additions & 0 deletions clippy_tests/examples/borrow_box.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> borrow_box.rs:9:19
|
9 | pub fn test1(foo: &mut Box<bool>) {
| ^^^^^^^^^^^^^^ help: try `&mut bool`
|
note: lint level defined here
--> borrow_box.rs:4:9
|
4 | #![deny(borrowed_box)]
| ^^^^^^^^^^^^

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> borrow_box.rs:14:14
|
14 | let foo: &Box<bool>;
| ^^^^^^^^^^ help: try `&bool`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> borrow_box.rs:18:10
|
18 | foo: &'a Box<bool>
| ^^^^^^^^^^^^^ help: try `&'a bool`

error: you seem to be trying to use `&Box<T>`. Consider using just `&T`
--> borrow_box.rs:22:17
|
22 | fn test4(a: &Box<bool>);
| ^^^^^^^^^^ help: try `&bool`

error: aborting due to previous error(s)

error: Could not compile `clippy_tests`.

To learn more, run the command again with --verbose.
5 changes: 5 additions & 0 deletions clippy_tests/examples/box_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ pub fn test2(foo: Box<Fn(Vec<u32>)>) { // pass if #31 is fixed
foo(vec![1, 2, 3])
}

pub fn test_local_not_linted() {
let _: Box<Vec<bool>>;
}

fn main(){
test(Box::new(Vec::new()));
test2(Box::new(|v| println!("{:?}", v)));
test_macro();
test_local_not_linted();
}
5 changes: 5 additions & 0 deletions clippy_tests/examples/dlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ pub fn test_ret() -> Option<LinkedList<u8>> {
unimplemented!();
}

pub fn test_local_not_linted() {
let _: LinkedList<u8>;
}

fn main(){
test(LinkedList::new());
test_local_not_linted();
}