about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/len_zero.rs17
-rw-r--r--clippy_lints/src/lib.rs5
-rw-r--r--clippy_lints/src/utils/paths.rs1
-rw-r--r--clippy_lints/src/vec_resize_to_zero.rs59
-rw-r--r--src/lintlist/mod.rs7
-rw-r--r--tests/ui/crashes/ice-3969.rs51
-rw-r--r--tests/ui/crashes/ice-3969.stderr22
-rw-r--r--tests/ui/len_zero.fixed8
-rw-r--r--tests/ui/len_zero.rs8
-rw-r--r--tests/ui/len_zero_ranges.fixed14
-rw-r--r--tests/ui/len_zero_ranges.rs14
-rw-r--r--tests/ui/len_zero_ranges.stderr10
-rw-r--r--tests/ui/vec_resize_to_zero.rs15
-rw-r--r--tests/ui/vec_resize_to_zero.stderr13
15 files changed, 244 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2ac9057199f..f7dae3dcfff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1630,6 +1630,7 @@ Released 2018-09-13
 [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
+[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs
index 2ec0b5a8d6f..f5bfede75a7 100644
--- a/clippy_lints/src/len_zero.rs
+++ b/clippy_lints/src/len_zero.rs
@@ -1,4 +1,4 @@
-use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -259,6 +259,17 @@ fn check_len(
 
 /// Checks if this type has an `is_empty` method.
 fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
+    /// Special case ranges until `range_is_empty` is stabilized. See issue 3807.
+    fn should_skip_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
+        higher::range(cx, expr).map_or(false, |_| {
+            !cx.tcx
+                .features()
+                .declared_lib_features
+                .iter()
+                .any(|(name, _)| name.as_str() == "range_is_empty")
+        })
+    }
+
     /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
     fn is_is_empty(cx: &LateContext<'_, '_>, item: &ty::AssocItem) -> bool {
         if let ty::AssocKind::Fn = item.kind {
@@ -284,6 +295,10 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
         })
     }
 
+    if should_skip_range(cx, expr) {
+        return false;
+    }
+
     let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr));
     match ty.kind {
         ty::Dynamic(ref tt, ..) => {
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 902f3d56c1e..4f0ecab393d 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -325,6 +325,7 @@ mod unwrap;
 mod use_self;
 mod useless_conversion;
 mod vec;
+mod vec_resize_to_zero;
 mod verbose_file_reads;
 mod wildcard_dependencies;
 mod wildcard_imports;
@@ -847,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &utils::internal_lints::OUTER_EXPN_EXPN_DATA,
         &utils::internal_lints::PRODUCE_ICE,
         &vec::USELESS_VEC,
+        &vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
         &verbose_file_reads::VERBOSE_FILE_READS,
         &wildcard_dependencies::WILDCARD_DEPENDENCIES,
         &wildcard_imports::ENUM_GLOB_USE,
@@ -1062,6 +1064,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
     store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
     store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
+    store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
     store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
         single_char_binding_names_threshold,
@@ -1430,6 +1433,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&unwrap::UNNECESSARY_UNWRAP),
         LintId::of(&useless_conversion::USELESS_CONVERSION),
         LintId::of(&vec::USELESS_VEC),
+        LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
         LintId::of(&write::PRINTLN_EMPTY_STRING),
         LintId::of(&write::PRINT_LITERAL),
         LintId::of(&write::PRINT_WITH_NEWLINE),
@@ -1677,6 +1681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
         LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
         LintId::of(&unwrap::PANICKING_UNWRAP),
+        LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
     ]);
 
     store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs
index 779da7e6bf2..3b7e9739211 100644
--- a/clippy_lints/src/utils/paths.rs
+++ b/clippy_lints/src/utils/paths.rs
@@ -138,5 +138,6 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
 pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
+pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs
new file mode 100644
index 00000000000..86cbfa8203d
--- /dev/null
+++ b/clippy_lints/src/vec_resize_to_zero.rs
@@ -0,0 +1,59 @@
+use crate::utils::span_lint_and_then;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+
+use crate::utils::{match_def_path, paths};
+use rustc_ast::ast::LitKind;
+use rustc_hir as hir;
+
+declare_clippy_lint! {
+    /// **What it does:** Finds occurences of `Vec::resize(0, an_int)`
+    ///
+    /// **Why is this bad?** This is probably an argument inversion mistake.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
+    /// ```
+    pub VEC_RESIZE_TO_ZERO,
+    correctness,
+    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
+}
+
+declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VecResizeToZero {
+    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
+        if_chain! {
+            if let hir::ExprKind::MethodCall(path_segment, _, ref args) = expr.kind;
+            if let Some(method_def_id) = cx.tables.type_dependent_def_id(expr.hir_id);
+            if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3;
+            if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind;
+            if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind;
+            then {
+                let method_call_span = expr.span.with_lo(path_segment.ident.span.lo());
+                span_lint_and_then(
+                    cx,
+                    VEC_RESIZE_TO_ZERO,
+                    expr.span,
+                    "emptying a vector with `resize`",
+                    |db| {
+                        db.help("the arguments may be inverted...");
+                        db.span_suggestion(
+                            method_call_span,
+                            "...or you can empty the vector with",
+                            "clear()".to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    },
+                );
+            }
+        }
+    }
+}
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index f63301c7db0..1e94ca00c14 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -2461,6 +2461,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         module: "types",
     },
     Lint {
+        name: "vec_resize_to_zero",
+        group: "correctness",
+        desc: "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake",
+        deprecation: None,
+        module: "vec_resize_to_zero",
+    },
+    Lint {
         name: "verbose_bit_mask",
         group: "style",
         desc: "expressions where a bit mask is less readable than the corresponding method call",
diff --git a/tests/ui/crashes/ice-3969.rs b/tests/ui/crashes/ice-3969.rs
new file mode 100644
index 00000000000..4feab7910b7
--- /dev/null
+++ b/tests/ui/crashes/ice-3969.rs
@@ -0,0 +1,51 @@
+// https://github.com/rust-lang/rust-clippy/issues/3969
+// used to crash: error: internal compiler error:
+// src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `<i32 as
+// std::iter::Iterator>::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs
+
+// Check that tautalogically false bounds are accepted, and are used
+// in type inference.
+#![feature(trivial_bounds)]
+#![allow(unused)]
+
+trait A {}
+
+impl A for i32 {}
+
+struct Dst<X: ?Sized> {
+    x: X,
+}
+
+struct TwoStrs(str, str)
+where
+    str: Sized;
+
+fn unsized_local()
+where
+    for<'a> Dst<A + 'a>: Sized,
+{
+    let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
+}
+
+fn return_str() -> str
+where
+    str: Sized,
+{
+    *"Sized".to_string().into_boxed_str()
+}
+
+fn use_op(s: String) -> String
+where
+    String: ::std::ops::Neg<Output = String>,
+{
+    -s
+}
+
+fn use_for()
+where
+    i32: Iterator,
+{
+    for _ in 2i32 {}
+}
+
+fn main() {}
diff --git a/tests/ui/crashes/ice-3969.stderr b/tests/ui/crashes/ice-3969.stderr
new file mode 100644
index 00000000000..923db0664a7
--- /dev/null
+++ b/tests/ui/crashes/ice-3969.stderr
@@ -0,0 +1,22 @@
+error: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/ice-3969.rs:25:17
+   |
+LL |     for<'a> Dst<A + 'a>: Sized,
+   |                 ^^^^^^ help: use `dyn`: `dyn A + 'a`
+   |
+   = note: `-D bare-trait-objects` implied by `-D warnings`
+
+error: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/ice-3969.rs:27:16
+   |
+LL |     let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
+   |                ^ help: use `dyn`: `dyn A`
+
+error: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/ice-3969.rs:27:57
+   |
+LL |     let x: Dst<A> = *(Box::new(Dst { x: 1 }) as Box<Dst<A>>);
+   |                                                         ^ help: use `dyn`: `dyn A`
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed
index 624e5ef8fcf..a29b832eb60 100644
--- a/tests/ui/len_zero.fixed
+++ b/tests/ui/len_zero.fixed
@@ -141,3 +141,11 @@ fn main() {
 fn test_slice(b: &[u8]) {
     if !b.is_empty() {}
 }
+
+mod issue_3807 {
+    // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`.
+    // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965
+    fn no_suggestion() {
+        let _ = (0..42).len() == 0;
+    }
+}
diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs
index 7fba971cfd8..8fd0093f4fd 100644
--- a/tests/ui/len_zero.rs
+++ b/tests/ui/len_zero.rs
@@ -141,3 +141,11 @@ fn main() {
 fn test_slice(b: &[u8]) {
     if b.len() != 0 {}
 }
+
+mod issue_3807 {
+    // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`.
+    // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965
+    fn no_suggestion() {
+        let _ = (0..42).len() == 0;
+    }
+}
diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed
new file mode 100644
index 00000000000..7da26f8ff4d
--- /dev/null
+++ b/tests/ui/len_zero_ranges.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+
+#![feature(range_is_empty)]
+#![warn(clippy::len_zero)]
+#![allow(unused)]
+
+mod issue_3807 {
+    // With the feature enabled, `is_empty` should be suggested
+    fn suggestion_is_fine() {
+        let _ = (0..42).is_empty();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs
new file mode 100644
index 00000000000..be7b4244bc0
--- /dev/null
+++ b/tests/ui/len_zero_ranges.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+
+#![feature(range_is_empty)]
+#![warn(clippy::len_zero)]
+#![allow(unused)]
+
+mod issue_3807 {
+    // With the feature enabled, `is_empty` should be suggested
+    fn suggestion_is_fine() {
+        let _ = (0..42).len() == 0;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr
new file mode 100644
index 00000000000..6e5fa41fb08
--- /dev/null
+++ b/tests/ui/len_zero_ranges.stderr
@@ -0,0 +1,10 @@
+error: length comparison to zero
+  --> $DIR/len_zero_ranges.rs:10:17
+   |
+LL |         let _ = (0..42).len() == 0;
+   |                 ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()`
+   |
+   = note: `-D clippy::len-zero` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/vec_resize_to_zero.rs b/tests/ui/vec_resize_to_zero.rs
new file mode 100644
index 00000000000..0263e2f5f20
--- /dev/null
+++ b/tests/ui/vec_resize_to_zero.rs
@@ -0,0 +1,15 @@
+#![warn(clippy::vec_resize_to_zero)]
+
+fn main() {
+    // applicable here
+    vec![1, 2, 3, 4, 5].resize(0, 5);
+
+    // not applicable
+    vec![1, 2, 3, 4, 5].resize(2, 5);
+
+    // applicable here, but only implemented for integer litterals for now
+    vec!["foo", "bar", "baz"].resize(0, "bar");
+
+    // not applicable
+    vec!["foo", "bar", "baz"].resize(2, "bar")
+}
diff --git a/tests/ui/vec_resize_to_zero.stderr b/tests/ui/vec_resize_to_zero.stderr
new file mode 100644
index 00000000000..feb846298c6
--- /dev/null
+++ b/tests/ui/vec_resize_to_zero.stderr
@@ -0,0 +1,13 @@
+error: emptying a vector with `resize`
+  --> $DIR/vec_resize_to_zero.rs:5:5
+   |
+LL |     vec![1, 2, 3, 4, 5].resize(0, 5);
+   |     ^^^^^^^^^^^^^^^^^^^^------------
+   |                         |
+   |                         help: ...or you can empty the vector with: `clear()`
+   |
+   = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings`
+   = help: the arguments may be inverted...
+
+error: aborting due to previous error
+