]> git.lizzy.rs Git - rust.git/commitdiff
Merge pull request #2985 from phansch/riir_update_lints
authorOliver S̶c̶h̶n̶e̶i̶d̶e̶r Scherer <github35764891676564198441@oli-obk.de>
Thu, 6 Sep 2018 07:24:26 +0000 (09:24 +0200)
committerGitHub <noreply@github.com>
Thu, 6 Sep 2018 07:24:26 +0000 (09:24 +0200)
update_lints rewrite: Add structure and --print-only

16 files changed:
PUBLISH.md
build.rs
ci/integration-tests.sh
clippy_lints/src/default_trait_access.rs
clippy_lints/src/lib.rs
clippy_lints/src/loops.rs
clippy_lints/src/ranges.rs
clippy_lints/src/utils/conf.rs
clippy_lints/src/utils/mod.rs
tests/ui/default_trait_access.rs
tests/ui/for_loop.stderr
tests/ui/needless_collect.rs [new file with mode: 0644]
tests/ui/needless_collect.stderr [new file with mode: 0644]
tests/ui/ptr_arg.rs
tests/ui/range_plus_minus_one.rs
tests/ui/range_plus_minus_one.stderr

index 749eae973047af002ef3b3f2c70a655d8184570d..b85605dc3b316f64d4f5ccd7eac6d0f909e98876 100644 (file)
@@ -2,7 +2,6 @@ Steps to publish a new Clippy version
 
 - Bump `package.version` in `./Cargo.toml` (no need to manually bump `dependencies.clippy_lints.version`).
 - Write a changelog entry.
-- If a nightly update is needed, update `min_version.txt` using `rustc -vV > min_version.txt`
 - Run `./pre_publish.sh`
 - Review and commit all changed files
 - `git push`
index 3b9f217c8848fa968dd5a70092ad2284c0f7ad78..1c930c1b2c95e531928f002e295c50f0eb31d782 100644 (file)
--- a/build.rs
+++ b/build.rs
@@ -1,18 +1,3 @@
-//! This build script ensures that Clippy is not compiled with an
-//! incompatible version of rust. It will panic with a descriptive
-//! error message instead.
-//!
-//! We specifially want to ensure that Clippy is only built with a
-//! rustc version that is newer or equal to the one specified in the
-//! `min_version.txt` file.
-//!
-//! `min_version.txt` is in the repo but also in the `.gitignore` to
-//! make sure that it is not updated manually by accident. Only CI
-//! should update that file.
-//!
-//! This build script was originally taken from the Rocket web framework:
-//! https://github.com/SergioBenitez/Rocket
-
 use std::env;
 
 fn main() {
index ac0d2f1614ba189ccdeb7ae1ac168cc6b42a8e69..18b91f6eae05b7ff1c848ec196d18ad007b49fbf 100755 (executable)
@@ -9,7 +9,7 @@ cd checkout
 
 function check() {
 # run clippy on a project, try to be verbose and trigger as many warnings as possible for greater coverage
-  RUST_BACKTRACE=full cargo clippy --all-targets --all-features -- --cap-lints warn -W clippy_pedantic -W clippy_nursery  &> clippy_output
+  RUST_BACKTRACE=full cargo clippy --all-targets --all-features -- --cap-lints warn -W clippy::pedantic -W clippy::nursery  &> clippy_output
   cat clippy_output
   ! cat clippy_output | grep -q "internal compiler error\|query stack during panic"
   if [[ $? != 0 ]]; then
index cb79883372a0a7a37d2e942ea67940ba8339e85b..e7c90f041889a591627fc3053f295428397ec167 100644 (file)
@@ -48,6 +48,17 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
             then {
                 match qpath {
                     QPath::Resolved(..) => {
+                        if_chain! {
+                            // Detect and ignore <Foo as Default>::default() because these calls do
+                            // explicitly name the type.
+                            if let ExprKind::Call(ref method, ref _args) = expr.node;
+                            if let ExprKind::Path(ref p) = method.node;
+                            if let QPath::Resolved(Some(_ty), _path) = p;
+                            then {
+                                return;
+                            }
+                        }
+
                         // TODO: Work out a way to put "whatever the imported way of referencing
                         // this type in this file" rather than a fully-qualified type.
                         let expr_ty = cx.tables.expr_ty(expr);
index d0e8def27868b315bb5e6fe138991fdcd8cc74f6..23fdbf648e3078a49b2e7a2ea6f681a7031f7c36 100644 (file)
@@ -449,6 +449,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
         if_not_else::IF_NOT_ELSE,
         infinite_iter::MAYBE_INFINITE_ITER,
         items_after_statements::ITEMS_AFTER_STATEMENTS,
+        loops::EXPLICIT_INTO_ITER_LOOP,
+        loops::EXPLICIT_ITER_LOOP,
         matches::SINGLE_MATCH_ELSE,
         methods::FILTER_MAP,
         methods::OPTION_MAP_UNWRAP_OR,
@@ -546,14 +548,13 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
         literal_representation::UNREADABLE_LITERAL,
         loops::EMPTY_LOOP,
         loops::EXPLICIT_COUNTER_LOOP,
-        loops::EXPLICIT_INTO_ITER_LOOP,
-        loops::EXPLICIT_ITER_LOOP,
         loops::FOR_KV_MAP,
         loops::FOR_LOOP_OVER_OPTION,
         loops::FOR_LOOP_OVER_RESULT,
         loops::ITER_NEXT_LOOP,
         loops::MANUAL_MEMCPY,
         loops::MUT_RANGE_BOUND,
+        loops::NEEDLESS_COLLECT,
         loops::NEEDLESS_RANGE_LOOP,
         loops::NEVER_LOOP,
         loops::REVERSE_RANGE_LOOP,
@@ -718,8 +719,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
         literal_representation::LARGE_DIGIT_GROUPS,
         literal_representation::UNREADABLE_LITERAL,
         loops::EMPTY_LOOP,
-        loops::EXPLICIT_INTO_ITER_LOOP,
-        loops::EXPLICIT_ITER_LOOP,
         loops::FOR_KV_MAP,
         loops::NEEDLESS_RANGE_LOOP,
         loops::WHILE_LET_ON_ITERATOR,
@@ -906,6 +905,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
         escape::BOXED_LOCAL,
         large_enum_variant::LARGE_ENUM_VARIANT,
         loops::MANUAL_MEMCPY,
+        loops::NEEDLESS_COLLECT,
         loops::UNUSED_COLLECT,
         methods::EXPECT_FUN_CALL,
         methods::ITER_NTH,
index 51e6327e728b4310498b2ab11870440d54c5df70..8a12530cb0d2591636f2490923d6d76c1cae190b 100644 (file)
 use rustc::middle::mem_categorization::cmt_;
 use rustc::ty::{self, Ty};
 use rustc::ty::subst::Subst;
+use rustc_errors::Applicability;
 use std::collections::{HashMap, HashSet};
 use std::iter::{once, Iterator};
 use syntax::ast;
 use syntax::source_map::Span;
+use syntax_pos::BytePos;
 use crate::utils::{sugg, sext};
 use crate::utils::usage::mutated_variables;
 use crate::consts::{constant, Constant};
@@ -85,7 +87,7 @@
 /// ```
 declare_clippy_lint! {
     pub EXPLICIT_ITER_LOOP,
-    style,
+    pedantic,
     "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
 }
 
 /// ```
 declare_clippy_lint! {
     pub EXPLICIT_INTO_ITER_LOOP,
-    style,
+    pedantic,
     "for-looping over `_.into_iter()` when `_` would do"
 }
 
      written as a for loop"
 }
 
+/// **What it does:** Checks for functions collecting an iterator when collect
+/// is not needed.
+///
+/// **Why is this bad?** `collect` causes the allocation of a new data structure,
+/// when this allocation may not be needed.
+///
+/// **Known problems:**
+/// None
+///
+/// **Example:**
+/// ```rust
+/// let len = iterator.collect::<Vec<_>>().len();
+/// // should be
+/// let len = iterator.count();
+/// ```
+declare_clippy_lint! {
+    pub NEEDLESS_COLLECT,
+    perf,
+    "collecting an iterator when collect is not needed"
+}
+
 /// **What it does:** Checks for loops over ranges `x..y` where both `x` and `y`
 /// are constant and `x` is greater or equal to `y`, unless the range is
 /// reversed or has a negative `.step_by(_)`.
@@ -400,6 +423,7 @@ fn get_lints(&self) -> LintArray {
             FOR_LOOP_OVER_OPTION,
             WHILE_LET_LOOP,
             UNUSED_COLLECT,
+            NEEDLESS_COLLECT,
             REVERSE_RANGE_LOOP,
             EXPLICIT_COUNTER_LOOP,
             EMPTY_LOOP,
@@ -523,6 +547,8 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
         if let ExprKind::While(ref cond, _, _) = expr.node {
             check_infinite_loop(cx, cond, expr);
         }
+
+        check_needless_collect(expr, cx);
     }
 
     fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
@@ -1209,7 +1235,7 @@ fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr], arg: &Expr, method_
         cx,
         EXPLICIT_ITER_LOOP,
         arg.span,
-        "it is more idiomatic to loop over references to containers instead of using explicit \
+        "it is more concise to loop over references to containers instead of using explicit \
          iteration methods",
         "to write this more concisely, try",
         format!("&{}{}", muta, object),
@@ -1247,7 +1273,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat, arg: &Expr, expr: &Ex
                         cx,
                         EXPLICIT_INTO_ITER_LOOP,
                         arg.span,
-                        "it is more idiomatic to loop over containers instead of using explicit \
+                        "it is more concise to loop over containers instead of using explicit \
                          iteration methods`",
                         "to write this more concisely, try",
                         object.to_string(),
@@ -2241,3 +2267,71 @@ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
         NestedVisitorMap::None
     }
 }
+
+const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
+
+fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr, cx: &LateContext<'a, 'tcx>) {
+    if_chain! {
+        if let ExprKind::MethodCall(ref method, _, ref args) = expr.node;
+        if let ExprKind::MethodCall(ref chain_method, _, _) = args[0].node;
+        if chain_method.ident.name == "collect" && match_trait_method(cx, &args[0], &paths::ITERATOR);
+        if let Some(ref generic_args) = chain_method.args;
+        if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
+        then {
+            let ty = cx.tables.node_id_to_type(ty.hir_id);
+            if match_type(cx, ty, &paths::VEC) ||
+                match_type(cx, ty, &paths::VEC_DEQUE) ||
+                match_type(cx, ty, &paths::BTREEMAP) ||
+                match_type(cx, ty, &paths::HASHMAP) {
+                if method.ident.name == "len" {
+                    let span = shorten_needless_collect_span(expr);
+                    span_lint_and_then(cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, |db| {
+                        db.span_suggestion_with_applicability(
+                            span,
+                            "replace with",
+                            ".count()".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    });
+                }
+                if method.ident.name == "is_empty" {
+                    let span = shorten_needless_collect_span(expr);
+                    span_lint_and_then(cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, |db| {
+                        db.span_suggestion_with_applicability(
+                            span,
+                            "replace with",
+                            ".next().is_none()".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    });
+                }
+                if method.ident.name == "contains" {
+                    let contains_arg = snippet(cx, args[1].span, "??");
+                    let span = shorten_needless_collect_span(expr);
+                    span_lint_and_then(cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, |db| {
+                        db.span_suggestion_with_applicability(
+                            span,
+                            "replace with",
+                            format!(
+                                ".any(|&x| x == {})",
+                                if contains_arg.starts_with('&') { &contains_arg[1..] } else { &contains_arg }
+                            ),
+                            Applicability::MachineApplicable,
+                        );
+                    });
+                }
+            }
+        }
+    }
+}
+
+fn shorten_needless_collect_span(expr: &Expr) -> Span {
+    if_chain! {
+        if let ExprKind::MethodCall(_, _, ref args) = expr.node;
+        if let ExprKind::MethodCall(_, ref span, _) = args[0].node;
+        then {
+            return expr.span.with_lo(span.lo() - BytePos(1));
+        }
+    }
+    unreachable!()
+}
index 95fd14162ace99df72f27fe70031233a50d8b72d..f9cdffea79c5096efe22f064cf9600015c3b5cee 100644 (file)
@@ -4,7 +4,7 @@
 use rustc::hir::*;
 use syntax::ast::RangeLimits;
 use syntax::source_map::Spanned;
-use crate::utils::{is_integer_literal, paths, snippet, span_lint, span_lint_and_then};
+use crate::utils::{is_integer_literal, paths, snippet, span_lint, span_lint_and_then, snippet_opt};
 use crate::utils::{get_trait_def_id, higher, implements_trait, SpanlessEq};
 use crate::utils::sugg::Sugg;
 
 /// **Why is this bad?** The code is more readable with an inclusive range
 /// like `x..=y`.
 ///
-/// **Known problems:** None.
+/// **Known problems:** Will add unnecessary pair of parentheses when the
+/// expression is not wrapped in a pair but starts with a opening parenthesis
+/// and ends with a closing one.
+/// I.e: `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
 ///
 /// **Example:**
 /// ```rust
@@ -145,9 +148,17 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
                     |db| {
                         let start = start.map_or("".to_owned(), |x| Sugg::hir(cx, x, "x").to_string());
                         let end = Sugg::hir(cx, y, "y");
-                        db.span_suggestion(expr.span,
+                        if let Some(is_wrapped) = &snippet_opt(cx, expr.span) {
+                            if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
+                                db.span_suggestion(expr.span,
+                                           "use",
+                                           format!("({}..={})", start, end));
+                            } else {
+                                db.span_suggestion(expr.span,
                                            "use",
                                            format!("{}..={}", start, end));
+                            }
+                        }
                     },
                 );
             }
index 8ec889a9fb6b329cfbdda66bbeaa33849581260e..16e39ff13ea2635913bff974685ec6d703fa175b 100644 (file)
@@ -3,6 +3,7 @@
 #![deny(clippy::missing_docs_in_private_items)]
 
 use lazy_static::lazy_static;
+use std::default::Default;
 use std::{env, fmt, fs, io, path};
 use std::io::Read;
 use syntax::{ast, source_map};
@@ -65,7 +66,7 @@ macro_rules! define_Conf {
         mod helpers {
             use serde_derive::Deserialize;
             /// Type used to store lint configuration.
-            #[derive(Default, Deserialize)]
+            #[derive(Deserialize)]
             #[serde(rename_all="kebab-case", deny_unknown_fields)]
             pub struct Conf {
                 $(#[$doc] #[serde(default=$rust_name_str)] #[serde(with=$rust_name_str)]
@@ -146,6 +147,12 @@ fn $rust_name() -> define_Conf!(TY $($ty)+) {
     (trivial_copy_size_limit, "trivial_copy_size_limit", None => Option<u64>),
 }
 
+impl Default for Conf {
+    fn default() -> Conf {
+        toml::from_str("").expect("we never error on empty config files")
+    }
+}
+
 /// Search for the configuration file.
 pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
     /// Possible filename to search for.
@@ -180,7 +187,7 @@ pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
 ///
 /// Used internally for convenience
 fn default(errors: Vec<Error>) -> (Conf, Vec<Error>) {
-    (toml::from_str("").expect("we never error on empty config files"), errors)
+    (Conf::default(), errors)
 }
 
 /// Read the `toml` configuration file.
index 650ea373d97e22ed0b915a319c199a7661d6b48f..ad033724fe125962228d3c1f371d94fd64d7d7e3 100644 (file)
@@ -499,7 +499,7 @@ fn docs_link(&mut self, lint: &'static Lint) {
             self.0.help(&format!(
                 "for further information visit https://rust-lang-nursery.github.io/rust-clippy/v{}/index.html#{}",
                 env!("CARGO_PKG_VERSION"),
-                lint.name_lower()
+                lint.name_lower().replacen("clippy::", "", 1)
             ));
         }
     }
@@ -1115,4 +1115,4 @@ fn test_without_block_comments_lines_without_block_comments() {
         let result = without_block_comments(vec!["foo", "bar", "baz"]);
         assert_eq!(result, vec!["foo", "bar", "baz"]);
     }
-}
\ No newline at end of file
+}
index ba8886cd6a416a0c6843b39658f79d741395f7c2..248b4ec0066ee2090d7032a8d0884a09baa9ddc7 100644 (file)
@@ -43,8 +43,10 @@ fn main() {
 
     let s18 = TupleStructDerivedDefault::default();
 
+    let s19 = <DerivedDefault as Default>::default();
+
     println!(
-        "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]",
+        "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
         s1,
         s2,
         s3,
@@ -63,6 +65,7 @@ fn main() {
         s16,
         s17,
         s18,
+        s19,
     );
 }
 
index 732dc2ab448bda5f2d82c8d04c9cf22497b4c5c1..bab7bdc77a4ccffb3338515b79cc5081427fe506 100644 (file)
@@ -264,7 +264,7 @@ error: this range is empty so this for loop will never run
 193 |     for i in (5 + 2)..(8 - 1) {
     |              ^^^^^^^^^^^^^^^^
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:215:15
     |
 215 |     for _v in vec.iter() {}
@@ -272,13 +272,13 @@ error: it is more idiomatic to loop over references to containers instead of usi
     |
     = note: `-D clippy::explicit-iter-loop` implied by `-D warnings`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:217:15
     |
 217 |     for _v in vec.iter_mut() {}
     |               ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
 
-error: it is more idiomatic to loop over containers instead of using explicit iteration methods`
+error: it is more concise to loop over containers instead of using explicit iteration methods`
    --> $DIR/for_loop.rs:220:15
     |
 220 |     for _v in out_vec.into_iter() {}
@@ -286,61 +286,61 @@ error: it is more idiomatic to loop over containers instead of using explicit it
     |
     = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:223:15
     |
 223 |     for _v in array.into_iter() {}
     |               ^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&array`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:228:15
     |
 228 |     for _v in [1, 2, 3].iter() {}
     |               ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:232:15
     |
 232 |     for _v in [0; 32].iter() {}
     |               ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:237:15
     |
 237 |     for _v in ll.iter() {}
     |               ^^^^^^^^^ help: to write this more concisely, try: `&ll`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:240:15
     |
 240 |     for _v in vd.iter() {}
     |               ^^^^^^^^^ help: to write this more concisely, try: `&vd`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:243:15
     |
 243 |     for _v in bh.iter() {}
     |               ^^^^^^^^^ help: to write this more concisely, try: `&bh`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:246:15
     |
 246 |     for _v in hm.iter() {}
     |               ^^^^^^^^^ help: to write this more concisely, try: `&hm`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:249:15
     |
 249 |     for _v in bt.iter() {}
     |               ^^^^^^^^^ help: to write this more concisely, try: `&bt`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:252:15
     |
 252 |     for _v in hs.iter() {}
     |               ^^^^^^^^^ help: to write this more concisely, try: `&hs`
 
-error: it is more idiomatic to loop over references to containers instead of using explicit iteration methods
+error: it is more concise to loop over references to containers instead of using explicit iteration methods
    --> $DIR/for_loop.rs:255:15
     |
 255 |     for _v in bs.iter() {}
diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs
new file mode 100644 (file)
index 0000000..b001f20
--- /dev/null
@@ -0,0 +1,19 @@
+#![feature(tool_lints)]
+
+use std::collections::{HashMap, HashSet, BTreeSet};
+
+#[warn(clippy::needless_collect)]
+#[allow(unused_variables, clippy::iter_cloned_collect)]
+fn main() {
+    let sample = [1; 5];
+    let len = sample.iter().collect::<Vec<_>>().len();
+    if sample.iter().collect::<Vec<_>>().is_empty() {
+        // Empty
+    }
+    sample.iter().cloned().collect::<Vec<_>>().contains(&1);
+    sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
+    // Notice the `HashSet`--this should not be linted
+    sample.iter().collect::<HashSet<_>>().len();
+    // Neither should this
+    sample.iter().collect::<BTreeSet<_>>().len();
+}
diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr
new file mode 100644 (file)
index 0000000..0124db3
--- /dev/null
@@ -0,0 +1,28 @@
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect.rs:9:28
+  |
+9 |     let len = sample.iter().collect::<Vec<_>>().len();
+  |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()`
+  |
+  = note: `-D clippy::needless-collect` implied by `-D warnings`
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect.rs:10:21
+   |
+10 |     if sample.iter().collect::<Vec<_>>().is_empty() {
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()`
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect.rs:13:27
+   |
+13 |     sample.iter().cloned().collect::<Vec<_>>().contains(&1);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|&x| x == 1)`
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect.rs:14:34
+   |
+14 |     sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()`
+
+error: aborting due to 4 previous errors
+
index e76221355aeaebd305ee6b23c25a5e96780ca754..7cd3c9f9c722549f0721a7b75aeb9be5dbca2b62 100644 (file)
@@ -77,3 +77,10 @@ fn test_cow_with_ref(c: &Cow<[i32]>) {
 fn test_cow(c: Cow<[i32]>) {
     let _c = c;
 }
+
+trait Foo2 {
+    fn do_string(&self);
+}
+
+// no error for &self references where self is of type String (#2293)
+impl Foo2 for String { fn do_string(&self) {} }
index 12a1312de36665d214f24f6f5aeb711709aa5d0d..1ee3637f266d7654eda12f7ebcc2ef476bd533f7 100644 (file)
@@ -27,6 +27,7 @@ fn main() {
     let _ = ..11-1;
     let _ = ..=11-1;
     let _ = ..=(11-1);
+    let _ = (1..11+1);
     let _ = (f()+1)..(f()+1);
 
     let mut vec: Vec<()> = std::vec::Vec::new();
index 083c153addbc8235e07706c3f8ddb3423feb80dc..3fe4e7ca073f990b6bfb6863397681b3bfa4b2e3 100644 (file)
@@ -41,8 +41,14 @@ error: an exclusive range would be more readable
 error: an inclusive range would be more readable
   --> $DIR/range_plus_minus_one.rs:30:13
    |
-30 |     let _ = (f()+1)..(f()+1);
-   |             ^^^^^^^^^^^^^^^^ help: use: `(f()+1)..=f()`
+30 |     let _ = (1..11+1);
+   |             ^^^^^^^^^ help: use: `(1..=11)`
 
-error: aborting due to 7 previous errors
+error: an inclusive range would be more readable
+  --> $DIR/range_plus_minus_one.rs:31:13
+   |
+31 |     let _ = (f()+1)..(f()+1);
+   |             ^^^^^^^^^^^^^^^^ help: use: `((f()+1)..=f())`
+
+error: aborting due to 8 previous errors