]> git.lizzy.rs Git - rust.git/commitdiff
Merge pull request #1501 from scott-linder/types-borrow-box
authorOliver Schneider <oli-obk@users.noreply.github.com>
Tue, 13 Jun 2017 09:30:52 +0000 (11:30 +0200)
committerGitHub <noreply@github.com>
Tue, 13 Jun 2017 09:30:52 +0000 (11:30 +0200)
Types borrow box

CHANGELOG.md
README.md
clippy_lints/src/lib.rs
clippy_lints/src/types.rs
clippy_tests/examples/borrow_box.rs [new file with mode: 0644]
clippy_tests/examples/borrow_box.stderr [new file with mode: 0644]
clippy_tests/examples/box_vec.rs
clippy_tests/examples/dlist.rs

index 837f160f770d325ed64a203dc0fa88b4dfbed7f4..d2b5f6f2fe7c3dbe7c2dd2e4f33be82ef70e2e43 100644 (file)
@@ -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
index b650c64eb218b76c48ef82f4a233ad874b07ddb7..28dffcbea9411a00be6e0bd95ec27a16849f23ae 100644 (file)
--- a/README.md
+++ b/README.md
@@ -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
index fb0114f8f683ae0e1fffac08763de6fac8921c7d..c91492c4c4b276ecfe3bd47311ddcf26e263cef8 100644 (file)
@@ -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,
index f608c7808a0be84302bd2481bd7f98c57ac21ec3..0204bfff0d4362928339244b2193ab86cc05d992 100644 (file)
@@ -8,7 +8,7 @@
 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;
 
      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)
     }
 }
 
@@ -84,35 +100,46 @@ fn check_fn(&mut self, cx: &LateContext, _: FnKind, decl: &FnDecl, _: &Body, _:
     }
 
     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() {
@@ -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);
             }
         },
         _ => {},
diff --git a/clippy_tests/examples/borrow_box.rs b/clippy_tests/examples/borrow_box.rs
new file mode 100644 (file)
index 0000000..ef569ab
--- /dev/null
@@ -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() {
+    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();
+}
diff --git a/clippy_tests/examples/borrow_box.stderr b/clippy_tests/examples/borrow_box.stderr
new file mode 100644 (file)
index 0000000..6670a8c
--- /dev/null
@@ -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.
index 507d9e77da0356a383d030ca9504e7507fb300dd..f8c5a80c59dec3be92348ddbe4f727ddaaa02550 100644 (file)
@@ -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();
 }
index a41bf1b52e012b56b08931f1bf890663fc915cad..b23aeb70a63376dbbae181d6c7ae27fd4dc75f67 100644 (file)
@@ -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();
 }