]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '09bd400243ed6f7059fedc0c1623aae3792521d6' into clippyup
authorflip1995 <hello@philkrones.com>
Tue, 11 Aug 2020 13:43:21 +0000 (15:43 +0200)
committerflip1995 <hello@philkrones.com>
Tue, 11 Aug 2020 15:50:45 +0000 (17:50 +0200)
127 files changed:
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/clippy_dev/src/ra_setup.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/bytecount.rs
src/tools/clippy/clippy_lints/src/checked_conversions.rs
src/tools/clippy/clippy_lints/src/default_trait_access.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/double_comparison.rs
src/tools/clippy/clippy_lints/src/double_parens.rs
src/tools/clippy/clippy_lints/src/drop_bounds.rs
src/tools/clippy/clippy_lints/src/functions.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/lifetimes.rs
src/tools/clippy/clippy_lints/src/loops.rs
src/tools/clippy/clippy_lints/src/manual_async_fn.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/minmax.rs
src/tools/clippy/clippy_lints/src/misc_early.rs
src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/needless_bool.rs
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
src/tools/clippy/clippy_lints/src/neg_cmp_op_on_partial_ord.rs
src/tools/clippy/clippy_lints/src/neg_multiply.rs
src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/reference.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute.rs
src/tools/clippy/clippy_lints/src/try_err.rs
src/tools/clippy/clippy_lints/src/unwrap.rs
src/tools/clippy/clippy_lints/src/utils/ast_utils.rs [changed mode: 0755->0644]
src/tools/clippy/clippy_lints/src/utils/attrs.rs
src/tools/clippy/clippy_lints/src/utils/mod.rs
src/tools/clippy/clippy_lints/src/utils/paths.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/doc/basics.md [new file with mode: 0644]
src/tools/clippy/src/lintlist/mod.rs
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
src/tools/clippy/tests/ui/await_holding_lock.rs
src/tools/clippy/tests/ui/await_holding_lock.stderr
src/tools/clippy/tests/ui/bool_comparison.stderr
src/tools/clippy/tests/ui/builtin-type-shadow.stderr
src/tools/clippy/tests/ui/bytecount.stderr
src/tools/clippy/tests/ui/checked_conversions.stderr
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr
src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
src/tools/clippy/tests/ui/cmp_null.stderr
src/tools/clippy/tests/ui/crashes/ice-5872.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-5872.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/default_trait_access.stderr
src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/double_comparison.stderr
src/tools/clippy/tests/ui/double_parens.stderr
src/tools/clippy/tests/ui/drop_bounds.stderr
src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr
src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
src/tools/clippy/tests/ui/functions_maxlines.stderr
src/tools/clippy/tests/ui/iter_skip_next.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/iter_skip_next.rs
src/tools/clippy/tests/ui/iter_skip_next.stderr
src/tools/clippy/tests/ui/len_without_is_empty.rs
src/tools/clippy/tests/ui/len_without_is_empty.stderr
src/tools/clippy/tests/ui/len_zero.fixed
src/tools/clippy/tests/ui/len_zero.rs
src/tools/clippy/tests/ui/manual_async_fn.fixed
src/tools/clippy/tests/ui/manual_async_fn.rs
src/tools/clippy/tests/ui/manual_async_fn.stderr
src/tools/clippy/tests/ui/map_flatten.fixed
src/tools/clippy/tests/ui/map_flatten.rs
src/tools/clippy/tests/ui/map_flatten.stderr
src/tools/clippy/tests/ui/min_max.rs
src/tools/clippy/tests/ui/min_max.stderr
src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_collect.fixed
src/tools/clippy/tests/ui/needless_collect.rs
src/tools/clippy/tests/ui/needless_collect.stderr
src/tools/clippy/tests/ui/needless_collect_indirect.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/needless_collect_indirect.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr
src/tools/clippy/tests/ui/neg_multiply.stderr
src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed
src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.rs
src/tools/clippy/tests/ui/overflow_check_conditional.stderr
src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr
src/tools/clippy/tests/ui/range.stderr
src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr
src/tools/clippy/tests/ui/renamed_builtin_attr.stderr
src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed
src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs
src/tools/clippy/tests/ui/same_item_push.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/same_item_push.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/stable_sort_primitive.fixed [new file with mode: 0644]
src/tools/clippy/tests/ui/stable_sort_primitive.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/stable_sort_primitive.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs
src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr
src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
src/tools/clippy/tests/ui/try_err.fixed
src/tools/clippy/tests/ui/try_err.rs
src/tools/clippy/tests/ui/try_err.stderr
src/tools/clippy/tests/ui/unknown_attribute.stderr
src/tools/clippy/tests/ui/unnecessary_ref.stderr
src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
src/tools/clippy/tests/ui/unnecessary_sort_by.rs
src/tools/clippy/tests/ui/unnecessary_sort_by.stderr
src/tools/clippy/tests/ui/unneeded_field_pattern.stderr
src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs

index a6e6949101775cebc31d7fb7b807991d90a3f831..5e9ed54c848205e499dd85cb43146d9b1d3f6f1e 100644 (file)
@@ -6,13 +6,13 @@ document.
 
 ## Unreleased / In Rust Nightly
 
-[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master)
+[c2c07fa...master](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...master)
 
 ## Rust 1.46
 
 Current beta, release 2020-08-27
 
-[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...master)
+[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 
 ### New lints
 
@@ -1454,6 +1454,7 @@ Released 2018-09-13
 [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
+[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@@ -1615,6 +1616,7 @@ Released 2018-09-13
 [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
+[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
@@ -1686,6 +1688,7 @@ Released 2018-09-13
 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
+[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
@@ -1701,6 +1704,7 @@ Released 2018-09-13
 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
+[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
@@ -1723,6 +1727,7 @@ Released 2018-09-13
 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
+[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 [`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
index 69a734e4ee4c2126a6bef72218d0b9a4b5e4aafc..5f7b1e85ee9a1769f6de383ca49bd5c433e47293 100644 (file)
@@ -28,11 +28,14 @@ All contributors are expected to follow the [Rust Code of Conduct].
 
 ## Getting started
 
-High level approach:
+**Note: If this is your first time contributing to Clippy, you should
+first read the [Basics docs](doc/basics.md).**
+
+### High level approach
 
 1. Find something to fix/improve
 2. Change code (likely some file in `clippy_lints/src/`)
-3. Follow the instructions in the [docs for writing lints](doc/adding_lints.md) such as running the `setup-toolchain.sh` script
+3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up
 4. Run `cargo test` in the root directory and wiggle code until it passes
 5. Open a PR (also can be done after 2. if you run into problems)
 
@@ -95,16 +98,16 @@ quick read.
 
 ## Getting code-completion for rustc internals to work
 
-Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals 
-using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not 
-available via a `rustup` component at the time of writing.  
-To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via  
-`git clone https://github.com/rust-lang/rust/`.  
-Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies 
-which rust-analyzer will be able to understand.  
-Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo 
-you just cloned.  
-The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to 
+Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals
+using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
+available via a `rustup` component at the time of writing.
+To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
+`git clone https://github.com/rust-lang/rust/`.
+Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
+which rust-analyzer will be able to understand.
+Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
+you just cloned.
+The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
 Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
 Just make sure to remove the dependencies again before finally making a pull request!
 
index 8617445c8a60030147556c0e221fe9155a09287e..f2bd651ab253c6419ce6395e22d7d5a18562dd78 100644 (file)
@@ -68,10 +68,11 @@ fn inject_deps_into_manifest(
     });
 
     // format a new [dependencies]-block with the new deps we need to inject
-    let mut all_deps = String::from("[dependencies]\n");
+    let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
     new_deps.for_each(|dep_line| {
         all_deps.push_str(&dep_line);
     });
+    all_deps.push_str("\n[dependencies]\n");
 
     // replace "[dependencies]" with
     // [dependencies]
index 40af6bb3d7bcff06c605c0bd4a04238a3fc583e2..376ac55f9c98ca60b1a96cf637174108a6d221c4 100644 (file)
@@ -1,6 +1,5 @@
 //! checks for attributes
 
-use crate::reexport::Name;
 use crate::utils::{
     first_line_of_span, is_present_in_source, match_def_path, paths, snippet_opt, span_lint, span_lint_and_help,
     span_lint_and_sugg, span_lint_and_then, without_block_comments,
@@ -517,7 +516,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>
     }
 }
 
-fn check_attrs(cx: &LateContext<'_>, span: Span, name: Name, attrs: &[Attribute]) {
+fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
     if span.from_expansion() {
         return;
     }
@@ -606,7 +605,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as
                         cx,
                         EMPTY_LINE_AFTER_OUTER_ATTR,
                         begin_of_attr_to_item,
-                        "Found an empty line after an outer attribute. \
+                        "found an empty line after an outer attribute. \
                         Perhaps you forgot to add a `!` to make it an inner attribute?",
                     );
                 }
index dde799fcae4cc4165b82ca6f153c97d4286f5cbb..cdb49d777d8dae2aeee008054b61dee8c293f092 100644 (file)
@@ -82,8 +82,8 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                             cx,
                             NAIVE_BYTECOUNT,
                             expr.span,
-                            "You appear to be counting bytes the naive way",
-                            "Consider using the bytecount crate",
+                            "you appear to be counting bytes the naive way",
+                            "consider using the bytecount crate",
                             format!("bytecount::count({}, {})",
                                     snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
                                     snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
index 841902943f00245cdf7b0a706b024bfb379bda2e..28c1a54d2c5a6a1c1ecc2749737c668b5da8f182 100644 (file)
@@ -66,7 +66,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
                     cx,
                     CHECKED_CONVERSIONS,
                     item.span,
-                    "Checked cast can be simplified.",
+                    "checked cast can be simplified",
                     "try",
                     format!("{}::try_from({}).is_ok()", to_type, snippet),
                     applicability,
index ea2447681293de4d1e438e15330fdc7d239f8a7e..874e19d9e9fb356d96db39f9079a6e5ecec157c7 100644 (file)
@@ -61,7 +61,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                                 cx,
                                 DEFAULT_TRAIT_ACCESS,
                                 expr.span,
-                                &format!("Calling `{}` is more clear than this expression", replacement),
+                                &format!("calling `{}` is more clear than this expression", replacement),
                                 "try",
                                 replacement,
                                 Applicability::Unspecified, // First resolve the TODO above
index 59c62f1ae94400089db996df65de04ac133e2d04..80a0675898240633e73830d6dc6a649df326c7ef 100644 (file)
@@ -1,6 +1,7 @@
 use crate::utils::paths;
 use crate::utils::{
-    is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then,
+    get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help,
+    span_lint_and_note, span_lint_and_then,
 };
 use if_chain::if_chain;
 use rustc_hir::def_id::DefId;
     "deriving `Hash` but implementing `PartialEq` explicitly"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for deriving `Ord` but implementing `PartialOrd`
+    /// explicitly or vice versa.
+    ///
+    /// **Why is this bad?** The implementation of these traits must agree (for
+    /// example for use with `sort`) so it’s probably a bad idea to use a
+    /// default-generated `Ord` implementation with an explicitly defined
+    /// `PartialOrd`. In particular, the following must hold for any type
+    /// implementing `Ord`:
+    ///
+    /// ```text
+    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
+    /// ```
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust,ignore
+    /// #[derive(Ord, PartialEq, Eq)]
+    /// struct Foo;
+    ///
+    /// impl PartialOrd for Foo {
+    ///     ...
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// #[derive(PartialEq, Eq)]
+    /// struct Foo;
+    ///
+    /// impl PartialOrd for Foo {
+    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
+    ///        Some(self.cmp(other))
+    ///     }
+    /// }
+    ///
+    /// impl Ord for Foo {
+    ///     ...
+    /// }
+    /// ```
+    /// or, if you don't need a custom ordering:
+    /// ```rust,ignore
+    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
+    /// struct Foo;
+    /// ```
+    pub DERIVE_ORD_XOR_PARTIAL_ORD,
+    correctness,
+    "deriving `Ord` but implementing `PartialOrd` explicitly"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for explicit `Clone` implementations for `Copy`
     /// types.
     "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 }
 
-declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, UNSAFE_DERIVE_DESERIALIZE]);
+declare_lint_pass!(Derive => [
+    EXPL_IMPL_CLONE_ON_COPY,
+    DERIVE_HASH_XOR_EQ,
+    DERIVE_ORD_XOR_PARTIAL_ORD,
+    UNSAFE_DERIVE_DESERIALIZE
+]);
 
 impl<'tcx> LateLintPass<'tcx> for Derive {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -116,6 +173,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
             let is_automatically_derived = is_automatically_derived(&*item.attrs);
 
             check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
+            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
 
             if is_automatically_derived {
                 check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
@@ -180,6 +238,60 @@ fn check_hash_peq<'tcx>(
     }
 }
 
+/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
+fn check_ord_partial_ord<'tcx>(
+    cx: &LateContext<'tcx>,
+    span: Span,
+    trait_ref: &TraitRef<'_>,
+    ty: Ty<'tcx>,
+    ord_is_automatically_derived: bool,
+) {
+    if_chain! {
+        if let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD);
+        if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
+        if let Some(def_id) = &trait_ref.trait_def_id();
+        if *def_id == ord_trait_def_id;
+        then {
+            // Look for the PartialOrd implementations for `ty`
+            cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
+                let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
+
+                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
+                    return;
+                }
+
+                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
+
+                // Only care about `impl PartialOrd<Foo> for Foo`
+                // For `impl PartialOrd<B> for A, input_types is [A, B]
+                if trait_ref.substs.type_at(1) == ty {
+                    let mess = if partial_ord_is_automatically_derived {
+                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
+                    } else {
+                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
+                    };
+
+                    span_lint_and_then(
+                        cx,
+                        DERIVE_ORD_XOR_PARTIAL_ORD,
+                        span,
+                        mess,
+                        |diag| {
+                            if let Some(local_def_id) = impl_id.as_local() {
+                                let hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
+                                diag.span_note(
+                                    cx.tcx.hir().span(hir_id),
+                                    "`PartialOrd` implemented here"
+                                );
+                            }
+                        }
+                    );
+                }
+            });
+        }
+    }
+}
+
 /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
 fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
     if match_path(&trait_ref.path, &paths::CLONE_TRAIT) {
@@ -242,7 +354,9 @@ fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
     if_chain! {
         if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
         if let ty::Adt(def, _) = ty.kind;
-        if def.did.is_local();
+        if let Some(local_def_id) = def.did.as_local();
+        let adt_hir_id = cx.tcx.hir().as_local_hir_id(local_def_id);
+        if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
         if cx.tcx.inherent_impls(def.did)
             .iter()
             .map(|imp_did| item_from_def_id(cx, *imp_did))
index 5d16192b7543bd3ab7a368a050b7e239b7bd80b8..bae7c4647d487a898c944adbaa0d62aa2c124dd3 100644 (file)
@@ -60,7 +60,7 @@ macro_rules! lint_double_comparison {
                     cx,
                     DOUBLE_COMPARISONS,
                     span,
-                    "This binary expression can be simplified",
+                    "this binary expression can be simplified",
                     "try",
                     sugg,
                     applicability,
index 1eb380a22cc6ba765ba1988e33308020bffbe691..abbcaf43f4151adcdfc44c0954d6e797531667f4 100644 (file)
@@ -45,15 +45,12 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
             return;
         }
 
+        let msg: &str = "consider removing unnecessary double parentheses";
+
         match expr.kind {
             ExprKind::Paren(ref in_paren) => match in_paren.kind {
                 ExprKind::Paren(_) | ExprKind::Tup(_) => {
-                    span_lint(
-                        cx,
-                        DOUBLE_PARENS,
-                        expr.span,
-                        "Consider removing unnecessary double parentheses",
-                    );
+                    span_lint(cx, DOUBLE_PARENS, expr.span, &msg);
                 },
                 _ => {},
             },
@@ -61,12 +58,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
                 if params.len() == 1 {
                     let param = &params[0];
                     if let ExprKind::Paren(_) = param.kind {
-                        span_lint(
-                            cx,
-                            DOUBLE_PARENS,
-                            param.span,
-                            "Consider removing unnecessary double parentheses",
-                        );
+                        span_lint(cx, DOUBLE_PARENS, param.span, &msg);
                     }
                 }
             },
@@ -74,12 +66,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
                 if params.len() == 2 {
                     let param = &params[1];
                     if let ExprKind::Paren(_) = param.kind {
-                        span_lint(
-                            cx,
-                            DOUBLE_PARENS,
-                            param.span,
-                            "Consider removing unnecessary double parentheses",
-                        );
+                        span_lint(cx, DOUBLE_PARENS, param.span, &msg);
                     }
                 }
             },
index 4afbd1ed0e59b03b43d30ea6d957dd9751f37463..ec3b6afa6300f13b76f17e40337b3efaeabfbeec 100644 (file)
     /// ```
     pub DROP_BOUNDS,
     correctness,
-    "Bounds of the form `T: Drop` are useless"
+    "bounds of the form `T: Drop` are useless"
 }
 
-const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \
-                                   Use `std::mem::needs_drop` to detect if a type has drop glue.";
+const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \
+                                   use `std::mem::needs_drop` to detect if a type has drop glue";
 
 declare_lint_pass!(DropBounds => [DROP_BOUNDS]);
 
index 6a141f1fc786d2548f11241faa5793d7111ff7e9..ac1c7aa9bbbe998977c3484eef37d1d8893572c4 100644 (file)
@@ -294,7 +294,8 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitIte
                 let body = cx.tcx.hir().body(eid);
                 Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
 
-                if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) {
+                if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs)
+                {
                     check_must_use_candidate(
                         cx,
                         &sig.decl,
@@ -373,7 +374,7 @@ fn check_line_number(self, cx: &LateContext<'_>, span: Span, body: &'tcx hir::Bo
         }
 
         if line_count > self.max_lines {
-            span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.")
+            span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines")
         }
     }
 
index 828ee91059601d2f9a5361c1b404b5e05cca217a..6ad525d762058c0f5b1d2b9c9ab576d34e2375e0 100644 (file)
@@ -250,6 +250,7 @@ macro_rules! declare_clippy_lint {
 mod mut_reference;
 mod mutable_debug_assertion;
 mod mutex_atomic;
+mod needless_arbitrary_self_type;
 mod needless_bool;
 mod needless_borrow;
 mod needless_borrowed_ref;
@@ -288,6 +289,7 @@ macro_rules! declare_clippy_lint {
 mod shadow;
 mod single_component_path_imports;
 mod slow_vector_initialization;
+mod stable_sort_primitive;
 mod strings;
 mod suspicious_trait_impl;
 mod swap;
@@ -322,10 +324,6 @@ macro_rules! declare_clippy_lint {
 
 pub use crate::utils::conf::Conf;
 
-mod reexport {
-    pub use rustc_span::Symbol as Name;
-}
-
 /// Register all pre expansion lints
 ///
 /// Pre-expansion lints run before any macro expansion has happened.
@@ -513,6 +511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &default_trait_access::DEFAULT_TRAIT_ACCESS,
         &dereference::EXPLICIT_DEREF_METHODS,
         &derive::DERIVE_HASH_XOR_EQ,
+        &derive::DERIVE_ORD_XOR_PARTIAL_ORD,
         &derive::EXPL_IMPL_CLONE_ON_COPY,
         &derive::UNSAFE_DERIVE_DESERIALIZE,
         &doc::DOC_MARKDOWN,
@@ -610,6 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &loops::NEEDLESS_COLLECT,
         &loops::NEEDLESS_RANGE_LOOP,
         &loops::NEVER_LOOP,
+        &loops::SAME_ITEM_PUSH,
         &loops::WHILE_IMMUTABLE_CONDITION,
         &loops::WHILE_LET_LOOP,
         &loops::WHILE_LET_ON_ITERATOR,
@@ -719,6 +719,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
         &mutex_atomic::MUTEX_ATOMIC,
         &mutex_atomic::MUTEX_INTEGER,
+        &needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
         &needless_bool::BOOL_COMPARISON,
         &needless_bool::NEEDLESS_BOOL,
         &needless_borrow::NEEDLESS_BORROW,
@@ -776,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &shadow::SHADOW_UNRELATED,
         &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
         &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
+        &stable_sort_primitive::STABLE_SORT_PRIMITIVE,
         &strings::STRING_ADD,
         &strings::STRING_ADD_ASSIGN,
         &strings::STRING_LIT_AS_BYTES,
@@ -786,6 +788,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
         &temporary_assignment::TEMPORARY_ASSIGNMENT,
         &to_digit_is_some::TO_DIGIT_IS_SOME,
+        &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
         &trait_bounds::TYPE_REPETITION_IN_BOUNDS,
         &transmute::CROSSPOINTER_TRANSMUTE,
         &transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
@@ -1028,6 +1031,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
     store.register_early_pass(|| box precedence::Precedence);
     store.register_early_pass(|| box needless_continue::NeedlessContinue);
+    store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
     store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
     store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
     store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
@@ -1079,6 +1083,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box macro_use::MacroUseImports::default());
     store.register_late_pass(|| box map_identity::MapIdentity);
     store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
+    store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
     store.register_late_pass(|| box repeat_once::RepeatOnce);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
@@ -1175,6 +1180,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&ranges::RANGE_PLUS_ONE),
         LintId::of(&shadow::SHADOW_UNRELATED),
         LintId::of(&strings::STRING_ADD_ASSIGN),
+        LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
         LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
         LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
         LintId::of(&types::CAST_LOSSLESS),
@@ -1231,6 +1237,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&copies::IFS_SAME_COND),
         LintId::of(&copies::IF_SAME_THEN_ELSE),
         LintId::of(&derive::DERIVE_HASH_XOR_EQ),
+        LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
         LintId::of(&doc::MISSING_SAFETY_DOC),
         LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
         LintId::of(&double_comparison::DOUBLE_COMPARISONS),
@@ -1293,6 +1300,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::NEEDLESS_COLLECT),
         LintId::of(&loops::NEEDLESS_RANGE_LOOP),
         LintId::of(&loops::NEVER_LOOP),
+        LintId::of(&loops::SAME_ITEM_PUSH),
         LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
         LintId::of(&loops::WHILE_LET_LOOP),
         LintId::of(&loops::WHILE_LET_ON_ITERATOR),
@@ -1369,6 +1377,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&mut_key::MUTABLE_KEY_TYPE),
         LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
         LintId::of(&mutex_atomic::MUTEX_ATOMIC),
+        LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
         LintId::of(&needless_bool::BOOL_COMPARISON),
         LintId::of(&needless_bool::NEEDLESS_BOOL),
         LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
@@ -1409,6 +1418,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&serde_api::SERDE_API_MISUSE),
         LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
         LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
+        LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
         LintId::of(&strings::STRING_LIT_AS_BYTES),
         LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
         LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
@@ -1495,6 +1505,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::EMPTY_LOOP),
         LintId::of(&loops::FOR_KV_MAP),
         LintId::of(&loops::NEEDLESS_RANGE_LOOP),
+        LintId::of(&loops::SAME_ITEM_PUSH),
         LintId::of(&loops::WHILE_LET_ON_ITERATOR),
         LintId::of(&main_recursion::MAIN_RECURSION),
         LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
@@ -1600,6 +1611,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&misc::SHORT_CIRCUIT_STATEMENT),
         LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
         LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
+        LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
         LintId::of(&needless_bool::BOOL_COMPARISON),
         LintId::of(&needless_bool::NEEDLESS_BOOL),
         LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
@@ -1651,6 +1663,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&copies::IFS_SAME_COND),
         LintId::of(&copies::IF_SAME_THEN_ELSE),
         LintId::of(&derive::DERIVE_HASH_XOR_EQ),
+        LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD),
         LintId::of(&drop_bounds::DROP_BOUNDS),
         LintId::of(&drop_forget_ref::DROP_COPY),
         LintId::of(&drop_forget_ref::DROP_REF),
@@ -1726,6 +1739,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&mutex_atomic::MUTEX_ATOMIC),
         LintId::of(&redundant_clone::REDUNDANT_CLONE),
         LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
+        LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
         LintId::of(&types::BOX_VEC),
         LintId::of(&types::REDUNDANT_ALLOCATION),
         LintId::of(&vec::USELESS_VEC),
index 168f9f953e4d899b70174f8db6d06b55610f1a09..4df6827d77f94027551ba7663b1cace0af537c88 100644 (file)
@@ -13,9 +13,8 @@
 use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
-use rustc_span::symbol::kw;
+use rustc_span::symbol::{kw, Symbol};
 
-use crate::reexport::Name;
 use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
 
 declare_clippy_lint! {
@@ -113,7 +112,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
 enum RefLt {
     Unnamed,
     Static,
-    Named(Name),
+    Named(Symbol),
 }
 
 fn check_fn_inner<'tcx>(
@@ -456,7 +455,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
 }
 
 struct LifetimeChecker {
-    map: FxHashMap<Name, Span>,
+    map: FxHashMap<Symbol, Span>,
 }
 
 impl<'tcx> Visitor<'tcx> for LifetimeChecker {
index 7e3876ff49b462fcc71810ad11684343cf9a188f..8352a8a3d2c69573c3956bbf7bbb93f77318a4f7 100644 (file)
@@ -1,14 +1,14 @@
 use crate::consts::constant;
-use crate::reexport::Name;
 use crate::utils::paths;
+use crate::utils::sugg::Sugg;
 use crate::utils::usage::{is_unused, mutated_variables};
 use crate::utils::{
     get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
-    is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var,
-    multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help,
-    span_lint_and_sugg, span_lint_and_then, SpanlessEq,
+    is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
+    match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
+    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
+    SpanlessEq,
 };
-use crate::utils::{is_type_diagnostic_item, qpath_res, sugg};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -17,7 +17,7 @@
 use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
 use rustc_hir::{
     def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand,
-    LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
+    Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind,
 };
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -27,7 +27,7 @@
 use rustc_middle::ty::{self, Ty, TyS};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use std::iter::{once, Iterator};
 use std::mem;
     "variables used within while expression are not mutated in the body"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks whether a for loop is being used to push a constant
+    /// value into a Vec.
+    ///
+    /// **Why is this bad?** This kind of operation can be expressed more succinctly with
+    /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
+    /// have better performance.
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    /// ```rust
+    /// let item1 = 2;
+    /// let item2 = 3;
+    /// let mut vec: Vec<u8> = Vec::new();
+    /// for _ in 0..20 {
+    ///    vec.push(item1);
+    /// }
+    /// for _ in 0..30 {
+    ///     vec.push(item2);
+    /// }
+    /// ```
+    /// could be written as
+    /// ```rust
+    /// let item1 = 2;
+    /// let item2 = 3;
+    /// let mut vec: Vec<u8> = vec![item1; 20];
+    /// vec.resize(20 + 30, item2);
+    /// ```
+    pub SAME_ITEM_PUSH,
+    style,
+    "the same item is pushed inside of a for loop"
+}
+
 declare_lint_pass!(Loops => [
     MANUAL_MEMCPY,
     NEEDLESS_RANGE_LOOP,
     NEVER_LOOP,
     MUT_RANGE_BOUND,
     WHILE_IMMUTABLE_CONDITION,
+    SAME_ITEM_PUSH,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -741,6 +775,7 @@ fn check_for_loop<'tcx>(
     check_for_loop_over_map_kv(cx, pat, arg, body, expr);
     check_for_mut_range_bound(cx, arg, body);
     detect_manual_memcpy(cx, pat, arg, body, expr);
+    detect_same_item_push(cx, pat, arg, body, expr);
 }
 
 fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
@@ -1017,6 +1052,117 @@ fn detect_manual_memcpy<'tcx>(
     }
 }
 
+// Scans the body of the for loop and determines whether lint should be given
+struct SameItemPushVisitor<'a, 'tcx> {
+    should_lint: bool,
+    // this field holds the last vec push operation visited, which should be the only push seen
+    vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>,
+    cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        match &expr.kind {
+            // Non-determinism may occur ... don't give a lint
+            ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false,
+            ExprKind::Block(block, _) => self.visit_block(block),
+            _ => {},
+        }
+    }
+
+    fn visit_block(&mut self, b: &'tcx Block<'_>) {
+        for stmt in b.stmts.iter() {
+            self.visit_stmt(stmt);
+        }
+    }
+
+    fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) {
+        let vec_push_option = get_vec_push(self.cx, s);
+        if vec_push_option.is_none() {
+            // Current statement is not a push so visit inside
+            match &s.kind {
+                StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr),
+                _ => {},
+            }
+        } else {
+            // Current statement is a push ...check whether another
+            // push had been previously done
+            if self.vec_push.is_none() {
+                self.vec_push = vec_push_option;
+            } else {
+                // There are multiple pushes ... don't lint
+                self.should_lint = false;
+            }
+        }
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+// Given some statement, determine if that statement is a push on a Vec. If it is, return
+// the Vec being pushed into and the item being pushed
+fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
+    if_chain! {
+            // Extract method being called
+            if let StmtKind::Semi(semi_stmt) = &stmt.kind;
+            if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind;
+            // Figure out the parameters for the method call
+            if let Some(self_expr) = args.get(0);
+            if let Some(pushed_item) = args.get(1);
+            // Check that the method being called is push() on a Vec
+            if match_type(cx, cx.typeck_results().expr_ty(self_expr), &paths::VEC);
+            if path.ident.name.as_str() == "push";
+            then {
+                return Some((self_expr, pushed_item))
+            }
+    }
+    None
+}
+
+/// Detects for loop pushing the same item into a Vec
+fn detect_same_item_push<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &'tcx Pat<'_>,
+    _: &'tcx Expr<'_>,
+    body: &'tcx Expr<'_>,
+    _: &'tcx Expr<'_>,
+) {
+    // Determine whether it is safe to lint the body
+    let mut same_item_push_visitor = SameItemPushVisitor {
+        should_lint: true,
+        vec_push: None,
+        cx,
+    };
+    walk_expr(&mut same_item_push_visitor, body);
+    if same_item_push_visitor.should_lint {
+        if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
+            // Make sure that the push does not involve possibly mutating values
+            if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
+                if let PatKind::Wild = pat.kind {
+                    let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
+                    let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
+
+                    span_lint_and_help(
+                        cx,
+                        SAME_ITEM_PUSH,
+                        vec.span,
+                        "it looks like the same item is being pushed into this Vec",
+                        None,
+                        &format!(
+                            "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
+                            item_str, vec_str, item_str
+                        ),
+                    )
+                }
+            }
+        }
+    }
+}
+
 /// Checks for looping over a range and then indexing a sequence with it.
 /// The iteratee must be a range literal.
 #[allow(clippy::too_many_lines)]
@@ -1184,7 +1330,7 @@ fn check_for_loop_range<'tcx>(
     }
 }
 
-fn is_len_call(expr: &Expr<'_>, var: Name) -> bool {
+fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
     if_chain! {
         if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind;
         if len_args.len() == 1;
@@ -1640,15 +1786,15 @@ struct VarVisitor<'a, 'tcx> {
     /// var name to look for as index
     var: HirId,
     /// indexed variables that are used mutably
-    indexed_mut: FxHashSet<Name>,
+    indexed_mut: FxHashSet<Symbol>,
     /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
-    indexed_indirectly: FxHashMap<Name, Option<region::Scope>>,
+    indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
     /// subset of `indexed` of vars that are indexed directly: `v[i]`
     /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
-    indexed_directly: FxHashMap<Name, (Option<region::Scope>, Ty<'tcx>)>,
+    indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
     /// Any names that are used outside an index operation.
     /// Used to detect things like `&mut vec` used together with `vec[i]`
-    referenced: FxHashSet<Name>,
+    referenced: FxHashSet<Symbol>,
     /// has the loop variable been used in expressions other than the index of
     /// an index op?
     nonindex: bool,
@@ -2004,7 +2150,7 @@ struct InitializeVisitor<'a, 'tcx> {
     end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here.
     var_id: HirId,
     state: VarState,
-    name: Option<Name>,
+    name: Option<Symbol>,
     depth: u32, // depth of conditional expressions
     past_loop: bool,
 }
@@ -2167,7 +2313,7 @@ enum Nesting {
 
 struct LoopNestVisitor {
     hir_id: HirId,
-    iterator: Name,
+    iterator: Symbol,
     nesting: Nesting,
 }
 
@@ -2218,7 +2364,7 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
     }
 }
 
-fn path_name(e: &Expr<'_>) -> Option<Name> {
+fn path_name(e: &Expr<'_>) -> Option<Symbol> {
     if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind {
         let segments = &path.segments;
         if segments.len() == 1 {
@@ -2358,6 +2504,10 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
 
 fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
+    check_needless_collect_direct_usage(expr, cx);
+    check_needless_collect_indirect_usage(expr, cx);
+}
+fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
     if_chain! {
         if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
         if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind;
@@ -2371,7 +2521,7 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
                 match_type(cx, ty, &paths::BTREEMAP) ||
                 is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) {
                 if method.ident.name == sym!(len) {
-                    let span = shorten_span(expr, sym!(collect));
+                    let span = shorten_needless_collect_span(expr);
                     span_lint_and_sugg(
                         cx,
                         NEEDLESS_COLLECT,
@@ -2383,20 +2533,20 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
                     );
                 }
                 if method.ident.name == sym!(is_empty) {
-                    let span = shorten_span(expr, sym!(iter));
+                    let span = shorten_needless_collect_span(expr);
                     span_lint_and_sugg(
                         cx,
                         NEEDLESS_COLLECT,
                         span,
                         NEEDLESS_COLLECT_MSG,
                         "replace with",
-                        "get(0).is_none()".to_string(),
+                        "next().is_none()".to_string(),
                         Applicability::MachineApplicable,
                     );
                 }
                 if method.ident.name == sym!(contains) {
                     let contains_arg = snippet(cx, args[1].span, "??");
-                    let span = shorten_span(expr, sym!(collect));
+                    let span = shorten_needless_collect_span(expr);
                     span_lint_and_then(
                         cx,
                         NEEDLESS_COLLECT,
@@ -2425,13 +2575,164 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
     }
 }
 
-fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span {
-    let mut current_expr = expr;
-    while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind {
-        if path.ident.name == target_fn_name {
+fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
+    if let ExprKind::Block(ref block, _) = expr.kind {
+        for ref stmt in block.stmts {
+            if_chain! {
+                if let StmtKind::Local(
+                    Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. },
+                    init: Some(ref init_expr), .. }
+                ) = stmt.kind;
+                if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
+                if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR);
+                if let Some(ref generic_args) = method_name.args;
+                if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
+                if let ty = cx.typeck_results().node_type(ty.hir_id);
+                if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
+                    is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) ||
+                    match_type(cx, ty, &paths::LINKED_LIST);
+                if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
+                if iter_calls.len() == 1;
+                then {
+                    // Suggest replacing iter_call with iter_replacement, and removing stmt
+                    let iter_call = &iter_calls[0];
+                    span_lint_and_then(
+                        cx,
+                        NEEDLESS_COLLECT,
+                        stmt.span.until(iter_call.span),
+                        NEEDLESS_COLLECT_MSG,
+                        |diag| {
+                            let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
+                            diag.multipart_suggestion(
+                                iter_call.get_suggestion_text(),
+                                vec![
+                                    (stmt.span, String::new()),
+                                    (iter_call.span, iter_replacement)
+                                ],
+                                Applicability::MachineApplicable,// MaybeIncorrect,
+                            ).emit();
+                        },
+                    );
+                }
+            }
+        }
+    }
+}
+
+struct IterFunction {
+    func: IterFunctionKind,
+    span: Span,
+}
+impl IterFunction {
+    fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
+        match &self.func {
+            IterFunctionKind::IntoIter => String::new(),
+            IterFunctionKind::Len => String::from(".count()"),
+            IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
+            IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")),
+        }
+    }
+    fn get_suggestion_text(&self) -> &'static str {
+        match &self.func {
+            IterFunctionKind::IntoIter => {
+                "Use the original Iterator instead of collecting it and then producing a new one"
+            },
+            IterFunctionKind::Len => {
+                "Take the original Iterator's count instead of collecting it and finding the length"
+            },
+            IterFunctionKind::IsEmpty => {
+                "Check if the original Iterator has anything instead of collecting it and seeing if it's empty"
+            },
+            IterFunctionKind::Contains(_) => {
+                "Check if the original Iterator contains an element instead of collecting then checking"
+            },
+        }
+    }
+}
+enum IterFunctionKind {
+    IntoIter,
+    Len,
+    IsEmpty,
+    Contains(Span),
+}
+
+struct IterFunctionVisitor {
+    uses: Vec<IterFunction>,
+    seen_other: bool,
+    target: Ident,
+}
+impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        // Check function calls on our collection
+        if_chain! {
+            if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind;
+            if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0);
+            if let &[name] = &path.segments;
+            if name.ident == self.target;
+            then {
+                let len = sym!(len);
+                let is_empty = sym!(is_empty);
+                let contains = sym!(contains);
+                match method_name.ident.name {
+                    sym::into_iter => self.uses.push(
+                        IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
+                    ),
+                    name if name == len => self.uses.push(
+                        IterFunction { func: IterFunctionKind::Len, span: expr.span }
+                    ),
+                    name if name == is_empty => self.uses.push(
+                        IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
+                    ),
+                    name if name == contains => self.uses.push(
+                        IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
+                    ),
+                    _ => self.seen_other = true,
+                }
+                return
+            }
+        }
+        // Check if the collection is used for anything else
+        if_chain! {
+            if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr;
+            if let &[name] = &path.segments;
+            if name.ident == self.target;
+            then {
+                self.seen_other = true;
+            } else {
+                walk_expr(self, expr);
+            }
+        }
+    }
+
+    type Map = Map<'tcx>;
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+/// Detect the occurences of calls to `iter` or `into_iter` for the
+/// given identifier
+fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
+    let mut visitor = IterFunctionVisitor {
+        uses: Vec::new(),
+        target: identifier,
+        seen_other: false,
+    };
+    visitor.visit_block(block);
+    if visitor.seen_other {
+        None
+    } else {
+        Some(visitor.uses)
+    }
+}
+
+fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span {
+    if_chain! {
+        if let ExprKind::MethodCall(.., args, _) = &expr.kind;
+        if let ExprKind::MethodCall(_, span, ..) = &args[0].kind;
+        then {
             return expr.span.with_lo(span.lo());
         }
-        current_expr = &args[0];
     }
-    unreachable!()
+    unreachable!();
 }
index c19fb148cda590ef3f7f9fb34d78eee800f4bf23..864d1ea87f57572d45fa9e8f7dfcc5063fd5b1d8 100644 (file)
@@ -4,8 +4,8 @@
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync,
-    ItemKind, TraitRef, Ty, TyKind, TypeBindingKind,
+    AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId,
+    IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -27,8 +27,6 @@
     /// ```
     /// Use instead:
     /// ```rust
-    /// use std::future::Future;
-    ///
     /// async fn foo() -> i32 { 42 }
     /// ```
     pub MANUAL_ASYNC_FN,
@@ -53,8 +51,9 @@ fn check_fn(
             if let IsAsync::NotAsync = header.asyncness;
             // Check that this function returns `impl Future`
             if let FnRetTy::Return(ret_ty) = decl.output;
-            if let Some(trait_ref) = future_trait_ref(cx, ret_ty);
+            if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
             if let Some(output) = future_output_ty(trait_ref);
+            if captures_all_lifetimes(decl.inputs, &output_lifetimes);
             // Check that the body of the function consists of one async block
             if let ExprKind::Block(block, _) = body.value.kind;
             if block.stmts.is_empty();
@@ -97,16 +96,35 @@ fn check_fn(
     }
 }
 
-fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
+fn future_trait_ref<'tcx>(
+    cx: &LateContext<'tcx>,
+    ty: &'tcx Ty<'tcx>,
+) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
     if_chain! {
-        if let TyKind::OpaqueDef(item_id, _) = ty.kind;
+        if let TyKind::OpaqueDef(item_id, bounds) = ty.kind;
         let item = cx.tcx.hir().item(item_id.id);
         if let ItemKind::OpaqueTy(opaque) = &item.kind;
-        if opaque.bounds.len() == 1;
-        if let GenericBound::Trait(poly, _) = &opaque.bounds[0];
-        if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
+        if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
+            if let GenericBound::Trait(poly, _) = bound {
+                Some(&poly.trait_ref)
+            } else {
+                None
+            }
+        });
+        if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
         then {
-            return Some(&poly.trait_ref);
+            let output_lifetimes = bounds
+                .iter()
+                .filter_map(|bound| {
+                    if let GenericArg::Lifetime(lt) = bound {
+                        Some(lt.name)
+                    } else {
+                        None
+                    }
+                })
+                .collect();
+
+            return Some((trait_ref, output_lifetimes));
         }
     }
 
@@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
     None
 }
 
+fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
+    let input_lifetimes: Vec<LifetimeName> = inputs
+        .iter()
+        .filter_map(|ty| {
+            if let TyKind::Rptr(lt, _) = ty.kind {
+                Some(lt.name)
+            } else {
+                None
+            }
+        })
+        .collect();
+
+    // The lint should trigger in one of these cases:
+    // - There are no input lifetimes
+    // - There's only one output lifetime bound using `+ '_`
+    // - All input lifetimes are explicitly bound to the output
+    input_lifetimes.is_empty()
+        || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore))
+        || input_lifetimes
+            .iter()
+            .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
+}
+
 fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
     if_chain! {
         if let Some(block_expr) = block.expr;
index 2c70183d87666d7aa0ac5ea93fc94d4406c7579d..570ae66d595ea0348b42175bc04c097a96259c96 100644 (file)
     ///
     /// **Why is this bad?** Readability.
     ///
-    /// **Known problems:** False positive in pattern guards. Will be resolved once
-    /// non-lexical lifetimes are stable.
+    /// **Known problems:** None.
     ///
     /// **Example:**
     /// ```rust
@@ -1408,7 +1407,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
             ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
             ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
-            ["next", "skip"] => lint_iter_skip_next(cx, expr),
+            ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]),
             ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
             ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
             ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
@@ -2433,17 +2432,21 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
     );
 }
 
-fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
+fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) {
     // lint if caller of skip is an Iterator
     if match_trait_method(cx, expr, &paths::ITERATOR) {
-        span_lint_and_help(
-            cx,
-            ITER_SKIP_NEXT,
-            expr.span,
-            "called `skip(x).next()` on an iterator",
-            None,
-            "this is more succinctly expressed by calling `nth(x)`",
-        );
+        if let [caller, n] = skip_args {
+            let hint = format!(".nth({})", snippet(cx, n.span, ".."));
+            span_lint_and_sugg(
+                cx,
+                ITER_SKIP_NEXT,
+                expr.span.trim_start(caller.span).unwrap(),
+                "called `skip(x).next()` on an iterator",
+                "use `nth` instead",
+                hint,
+                Applicability::MachineApplicable,
+            );
+        }
     }
 }
 
@@ -2565,17 +2568,34 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex
 fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
     // lint if caller of `.map().flatten()` is an Iterator
     if match_trait_method(cx, expr, &paths::ITERATOR) {
-        let msg = "called `map(..).flatten()` on an `Iterator`. \
-                    This is more succinctly expressed by calling `.flat_map(..)`";
-        let self_snippet = snippet(cx, map_args[0].span, "..");
+        let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
+        let is_map_to_option = match map_closure_ty.kind {
+            ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
+                let map_closure_sig = match map_closure_ty.kind {
+                    ty::Closure(_, substs) => substs.as_closure().sig(),
+                    _ => map_closure_ty.fn_sig(cx.tcx),
+                };
+                let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output());
+                is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type))
+            },
+            _ => false,
+        };
+
+        let method_to_use = if is_map_to_option {
+            // `(...).map(...)` has type `impl Iterator<Item=Option<...>>
+            "filter_map"
+        } else {
+            // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
+            "flat_map"
+        };
         let func_snippet = snippet(cx, map_args[1].span, "..");
-        let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet);
+        let hint = format!(".{0}({1})", method_to_use, func_snippet);
         span_lint_and_sugg(
             cx,
             MAP_FLATTEN,
-            expr.span,
-            msg,
-            "try using `flat_map` instead",
+            expr.span.with_lo(map_args[0].span.hi()),
+            "called `map(..).flatten()` on an `Iterator`",
+            &format!("try using `{}` instead", method_to_use),
             hint,
             Applicability::MachineApplicable,
         );
@@ -2583,16 +2603,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
 
     // lint if caller of `.map().flatten()` is an Option
     if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) {
-        let msg = "called `map(..).flatten()` on an `Option`. \
-                    This is more succinctly expressed by calling `.and_then(..)`";
-        let self_snippet = snippet(cx, map_args[0].span, "..");
         let func_snippet = snippet(cx, map_args[1].span, "..");
-        let hint = format!("{0}.and_then({1})", self_snippet, func_snippet);
+        let hint = format!(".and_then({})", func_snippet);
         span_lint_and_sugg(
             cx,
             MAP_FLATTEN,
-            expr.span,
-            msg,
+            expr.span.with_lo(map_args[0].span.hi()),
+            "called `map(..).flatten()` on an `Option`",
             "try using `and_then` instead",
             hint,
             Applicability::MachineApplicable,
index dae39aaf5e2165e6ffa9e31e5f8ffc5ffd0062c4..004dd50a31be8716b7af5bc1727051261b2f2b84 100644 (file)
@@ -1,5 +1,6 @@
 use crate::consts::{constant_simple, Constant};
-use crate::utils::{match_def_path, paths, span_lint};
+use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
+use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
     /// ```ignore
     /// min(0, max(100, x))
     /// ```
+    /// or
+    /// ```ignore
+    /// x.max(100).min(0)
+    /// ```
     /// It will always be equal to `0`. Probably the author meant to clamp the value
     /// between 0 and 100, but has erroneously swapped `min` and `max`.
     pub MIN_MAX,
@@ -60,25 +65,43 @@ enum MinMax {
 }
 
 fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
-    if let ExprKind::Call(ref path, ref args) = expr.kind {
-        if let ExprKind::Path(ref qpath) = path.kind {
-            cx.typeck_results()
-                .qpath_res(qpath, path.hir_id)
-                .opt_def_id()
-                .and_then(|def_id| {
-                    if match_def_path(cx, def_id, &paths::CMP_MIN) {
-                        fetch_const(cx, args, MinMax::Min)
-                    } else if match_def_path(cx, def_id, &paths::CMP_MAX) {
+    match expr.kind {
+        ExprKind::Call(ref path, ref args) => {
+            if let ExprKind::Path(ref qpath) = path.kind {
+                cx.typeck_results()
+                    .qpath_res(qpath, path.hir_id)
+                    .opt_def_id()
+                    .and_then(|def_id| {
+                        if match_def_path(cx, def_id, &paths::CMP_MIN) {
+                            fetch_const(cx, args, MinMax::Min)
+                        } else if match_def_path(cx, def_id, &paths::CMP_MAX) {
+                            fetch_const(cx, args, MinMax::Max)
+                        } else {
+                            None
+                        }
+                    })
+            } else {
+                None
+            }
+        },
+        ExprKind::MethodCall(ref path, _, ref args, _) => {
+            if_chain! {
+                if let [obj, _] = args;
+                if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
+                then {
+                    if path.ident.as_str() == sym!(max).as_str() {
                         fetch_const(cx, args, MinMax::Max)
+                    } else if path.ident.as_str() == sym!(min).as_str() {
+                        fetch_const(cx, args, MinMax::Min)
                     } else {
                         None
                     }
-                })
-        } else {
-            None
-        }
-    } else {
-        None
+                } else {
+                    None
+                }
+            }
+        },
+        _ => None,
     }
 }
 
index 29aba7c121873bbd6c57aa3d24d2758942040ba3..02789735c17a313b1aa152aa01eadc3df3bf1692 100644 (file)
@@ -271,7 +271,7 @@ fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
                         cx,
                         BUILTIN_TYPE_SHADOW,
                         param.ident.span,
-                        &format!("This generic shadows the built-in type `{}`", name),
+                        &format!("this generic shadows the built-in type `{}`", name),
                     );
                 }
             }
@@ -298,9 +298,9 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
                     cx,
                     UNNEEDED_FIELD_PATTERN,
                     pat.span,
-                    "All the struct fields are matched to a wildcard pattern, consider using `..`.",
+                    "all the struct fields are matched to a wildcard pattern, consider using `..`",
                     None,
-                    &format!("Try with `{} {{ .. }}` instead", type_name),
+                    &format!("try with `{} {{ .. }}` instead", type_name),
                 );
                 return;
             }
@@ -313,7 +313,7 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
                                 cx,
                                 UNNEEDED_FIELD_PATTERN,
                                 field.span,
-                                "You matched a field with a wildcard pattern. Consider using `..` instead",
+                                "you matched a field with a wildcard pattern, consider using `..` instead",
                             );
                         } else {
                             let mut normal = vec![];
@@ -333,10 +333,10 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
                                 cx,
                                 UNNEEDED_FIELD_PATTERN,
                                 field.span,
-                                "You matched a field with a wildcard pattern. Consider using `..` \
+                                "you matched a field with a wildcard pattern, consider using `..` \
                                  instead",
                                 None,
-                                &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
+                                &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
                             );
                         }
                     }
diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
new file mode 100644 (file)
index 0000000..38bdd0f
--- /dev/null
@@ -0,0 +1,118 @@
+use crate::utils::span_lint_and_sugg;
+use if_chain::if_chain;
+use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// **What it does:** The lint checks for `self` in fn parameters that
+    /// specify the `Self`-type explicitly
+    /// **Why is this bad?** Increases the amount and decreases the readability of code
+    ///
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    /// ```rust
+    /// enum ValType {
+    ///     I32,
+    ///     I64,
+    ///     F32,
+    ///     F64,
+    /// }
+    ///
+    /// impl ValType {
+    ///     pub fn bytes(self: Self) -> usize {
+    ///         match self {
+    ///             Self::I32 | Self::F32 => 4,
+    ///             Self::I64 | Self::F64 => 8,
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// Could be rewritten as
+    ///
+    /// ```rust
+    /// enum ValType {
+    ///     I32,
+    ///     I64,
+    ///     F32,
+    ///     F64,
+    /// }
+    ///
+    /// impl ValType {
+    ///     pub fn bytes(self) -> usize {
+    ///         match self {
+    ///             Self::I32 | Self::F32 => 4,
+    ///             Self::I64 | Self::F64 => 8,
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    pub NEEDLESS_ARBITRARY_SELF_TYPE,
+    complexity,
+    "type of `self` parameter is already by default `Self`"
+}
+
+declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]);
+
+enum Mode {
+    Ref(Option<Lifetime>),
+    Value,
+}
+
+fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) {
+    if_chain! {
+        if let [segment] = &path.segments[..];
+        if segment.ident.name == kw::SelfUpper;
+        then {
+            let self_param = match (binding_mode, mutbl) {
+                (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
+                (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name),
+                (Mode::Ref(None), Mutability::Not) => "&self".to_string(),
+                (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name),
+                (Mode::Value, Mutability::Mut) => "mut self".to_string(),
+                (Mode::Value, Mutability::Not) => "self".to_string(),
+            };
+
+            span_lint_and_sugg(
+                cx,
+                NEEDLESS_ARBITRARY_SELF_TYPE,
+                span,
+                "the type of the `self` parameter does not need to be arbitrary",
+                "consider to change this parameter to",
+                self_param,
+                Applicability::MachineApplicable,
+            )
+        }
+    }
+}
+
+impl EarlyLintPass for NeedlessArbitrarySelfType {
+    fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
+        if !p.is_self() {
+            return;
+        }
+
+        match &p.ty.kind {
+            TyKind::Path(None, path) => {
+                if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind {
+                    check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl)
+                }
+            },
+            TyKind::Rptr(lifetime, mut_ty) => {
+                if_chain! {
+                if let TyKind::Path(None, path) = &mut_ty.ty.kind;
+                if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind;
+                    then {
+                        check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl)
+                    }
+                }
+            },
+            _ => {},
+        }
+    }
+}
index 8e44f2ec2408c8807cc55d5889040346c3c65a76..dc5aa6691396b14220376178adcac0afe5a374af 100644 (file)
@@ -243,7 +243,7 @@ fn check_comparison<'a, 'tcx>(
                         cx,
                         BOOL_COMPARISON,
                         e.span,
-                        "This comparison might be written more concisely",
+                        "this comparison might be written more concisely",
                         "try simplifying it as shown",
                         format!(
                             "{} != {}",
index a7f7c97fc487cf0e937a681c84c32662c3b37e5e..047a78b087841b7224c83536bfbb4eaa488f7908 100644 (file)
@@ -40,9 +40,8 @@
     ///     assert_eq!(v.len(), 42);
     /// }
     /// ```
-    ///
+    /// should be
     /// ```rust
-    /// // should be
     /// fn foo(v: &[i32]) {
     ///     assert_eq!(v.len(), 42);
     /// }
@@ -159,26 +158,19 @@ fn check_fn(
                 }
             }
 
+            //
             // * Exclude a type that is specifically bounded by `Borrow`.
             // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
             //   `serde::Serialize`)
             let (implements_borrow_trait, all_borrowable_trait) = {
-                let preds = preds
-                    .iter()
-                    .filter(|t| t.self_ty() == ty)
-                    .collect::<Vec<_>>();
+                let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();
 
                 (
                     preds.iter().any(|t| t.def_id() == borrow_trait),
                     !preds.is_empty() && {
                         let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
                         preds.iter().all(|t| {
-                            let ty_params = t
-                                .trait_ref
-                                .substs
-                                .iter()
-                                .skip(1)
-                                .collect::<Vec<_>>();
+                            let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
                             implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
                         })
                     },
index 95613a1b82ef0f32101c061c696730dabea56ea7..4fb899125e8ad9f0612149d10b97ecf360bc778c 100644 (file)
@@ -79,10 +79,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         cx,
                         NEG_CMP_OP_ON_PARTIAL_ORD,
                         expr.span,
-                        "The use of negated comparison operators on partially ordered \
-                        types produces code that is hard to read and refactor. Please \
+                        "the use of negated comparison operators on partially ordered \
+                        types produces code that is hard to read and refactor, please \
                         consider using the `partial_cmp` method instead, to make it \
-                        clear that the two values could be incomparable."
+                        clear that the two values could be incomparable"
                     )
                 }
             }
index 6b6c950e0abee23231a72782d11db11e1771e9a7..aa550510867f9af5df50165cdf9ef18845ca9a4f 100644 (file)
@@ -47,7 +47,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
         if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit));
         if cx.typeck_results().expr_ty(exp).is_integral();
         then {
-            span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`");
+            span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`");
         }
     }
 }
index 4d4a9676654826b36360539531c092a5b85de056..3c041bac234a589f8034f1dd5ede89db0b6c1c8b 100644 (file)
@@ -42,13 +42,13 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 if let BinOpKind::Lt = op.node {
                     if let BinOpKind::Add = op2.node {
                         span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
-                            "You are trying to use classic C overflow conditions that will fail in Rust.");
+                            "you are trying to use classic C overflow conditions that will fail in Rust");
                     }
                 }
                 if let BinOpKind::Gt = op.node {
                     if let BinOpKind::Sub = op2.node {
                         span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
-                            "You are trying to use classic C underflow conditions that will fail in Rust.");
+                            "you are trying to use classic C underflow conditions that will fail in Rust");
                     }
                 }
             }
@@ -67,13 +67,13 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 if let BinOpKind::Gt = op.node {
                     if let BinOpKind::Add = op2.node {
                         span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
-                            "You are trying to use classic C overflow conditions that will fail in Rust.");
+                            "you are trying to use classic C overflow conditions that will fail in Rust");
                     }
                 }
                 if let BinOpKind::Lt = op.node {
                     if let BinOpKind::Sub = op2.node {
                         span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
-                            "You are trying to use classic C underflow conditions that will fail in Rust.");
+                            "you are trying to use classic C underflow conditions that will fail in Rust");
                     }
                 }
             }
index 66a145a7f14b39b2cd81eec5c976033afd5bc3a3..b8583402928b424013200de3d1680352c3f34b12 100644 (file)
@@ -60,7 +60,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     cx,
                     PATH_BUF_PUSH_OVERWRITE,
                     lit.span,
-                    "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
+                    "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
                     "try",
                     format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
                     Applicability::MachineApplicable,
index 7b6bd69ffca5c6d82d66001187f5e5262a0e1507..460d631fab0fdc62ac9207e3112c9bfb65383937 100644 (file)
@@ -145,7 +145,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     cx,
                     CMP_NULL,
                     expr.span,
-                    "Comparing with null is better expressed by the `.is_null()` method",
+                    "comparing with null is better expressed by the `.is_null()` method",
                 );
             }
         }
index 4c1f2e8e01a8c9b485399aa994a8a1ada33ff8aa..f88075798ca7515d61b6f478933956c8f3804add 100644 (file)
@@ -160,7 +160,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                          span_lint(cx,
                                    RANGE_ZIP_WITH_LEN,
                                    expr.span,
-                                   &format!("It is more idiomatic to use `{}.iter().enumerate()`",
+                                   &format!("it is more idiomatic to use `{}.iter().enumerate()`",
                                             snippet(cx, iter_args[0].span, "_")));
                     }
                 }
index c6f57298c2601a7287e8608fe2b536dff326bc78..7bbcc67aa2ddf13db6eb565df72047355c2f1666 100644 (file)
@@ -86,13 +86,13 @@ impl EarlyLintPass for RedundantStaticLifetimes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
         if !item.span.from_expansion() {
             if let ItemKind::Const(_, ref var_type, _) = item.kind {
-                self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime");
+                self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
                 // Don't check associated consts because `'static` cannot be elided on those (issue
                 // #2438)
             }
 
             if let ItemKind::Static(ref var_type, _, _) = item.kind {
-                self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime");
+                self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
             }
         }
     }
index fe457aad50e368adb6605b7fbcfc912bfcb1cd17..3fda00403c611890e9053cb60bb7b9cfcdcba720 100644 (file)
@@ -92,7 +92,7 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
                     cx,
                     REF_IN_DEREF,
                     object.span,
-                    "Creating a reference that is immediately dereferenced.",
+                    "creating a reference that is immediately dereferenced",
                     "try this",
                     snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(),
                     applicability,
index 6f03e92bde36fcb64a353e23ab731379f14c2ca7..2610157763a8b97dcdb9a818c1ee1cfb7417190f 100644 (file)
@@ -1,4 +1,3 @@
-use crate::reexport::Name;
 use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
@@ -10,6 +9,7 @@
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
+use rustc_span::symbol::Symbol;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for bindings that shadow other bindings already in
@@ -123,7 +123,7 @@ fn check_fn<'tcx>(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Bo
     check_expr(cx, &body.value, &mut bindings);
 }
 
-fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Name, Span)>) {
+fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &mut Vec<(Symbol, Span)>) {
     let len = bindings.len();
     for stmt in block.stmts {
         match stmt.kind {
@@ -138,7 +138,7 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: &
     bindings.truncate(len);
 }
 
-fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Name, Span)>) {
+fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: &mut Vec<(Symbol, Span)>) {
     if in_external_macro(cx.sess(), local.span) {
         return;
     }
@@ -173,7 +173,7 @@ fn check_pat<'tcx>(
     pat: &'tcx Pat<'_>,
     init: Option<&'tcx Expr<'_>>,
     span: Span,
-    bindings: &mut Vec<(Name, Span)>,
+    bindings: &mut Vec<(Symbol, Span)>,
 ) {
     // TODO: match more stuff / destructuring
     match pat.kind {
@@ -254,7 +254,7 @@ fn check_pat<'tcx>(
 
 fn lint_shadow<'tcx>(
     cx: &LateContext<'tcx>,
-    name: Name,
+    name: Symbol,
     span: Span,
     pattern_span: Span,
     init: Option<&'tcx Expr<'_>>,
@@ -315,7 +315,7 @@ fn lint_shadow<'tcx>(
     }
 }
 
-fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Name, Span)>) {
+fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut Vec<(Symbol, Span)>) {
     if in_external_macro(cx.sess(), expr.span) {
         return;
     }
@@ -351,7 +351,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut
     }
 }
 
-fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Name, Span)>) {
+fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) {
     match ty.kind {
         TyKind::Slice(ref sty) => check_ty(cx, sty, bindings),
         TyKind::Array(ref fty, ref anon_const) => {
@@ -371,7 +371,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(
     }
 }
 
-fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
+fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner),
         ExprKind::Block(ref block, _) => {
@@ -383,6 +383,6 @@ fn is_self_shadow(name: Name, expr: &Expr<'_>) -> bool {
     }
 }
 
-fn path_eq_name(name: Name, path: &Path<'_>) -> bool {
+fn path_eq_name(name: Symbol, path: &Path<'_>) -> bool {
     !path.is_global() && path.segments.len() == 1 && path.segments[0].ident.as_str() == name.as_str()
 }
diff --git a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs
new file mode 100644 (file)
index 0000000..cd70566
--- /dev/null
@@ -0,0 +1,130 @@
+use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg};
+
+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};
+
+declare_clippy_lint! {
+    /// **What it does:**
+    /// When sorting primitive values (integers, bools, chars, as well
+    /// as arrays, slices, and tuples of such items), it is better to
+    /// use an unstable sort than a stable sort.
+    ///
+    /// **Why is this bad?**
+    /// Using a stable sort consumes more memory and cpu cycles. Because
+    /// values which compare equal are identical, preserving their
+    /// relative order (the guarantee that a stable sort provides) means
+    /// nothing, while the extra costs still apply.
+    ///
+    /// **Known problems:**
+    /// None
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let mut vec = vec![2, 1, 3];
+    /// vec.sort();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut vec = vec![2, 1, 3];
+    /// vec.sort_unstable();
+    /// ```
+    pub STABLE_SORT_PRIMITIVE,
+    perf,
+    "use of sort() when sort_unstable() is equivalent"
+}
+
+declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
+
+/// The three "kinds" of sorts
+enum SortingKind {
+    Vanilla,
+    /* The other kinds of lint are currently commented out because they
+     * can map distinct values to equal ones. If the key function is
+     * provably one-to-one, or if the Cmp function conserves equality,
+     * then they could be linted on, but I don't know if we can check
+     * for that. */
+
+    /* ByKey,
+     * ByCmp, */
+}
+impl SortingKind {
+    /// The name of the stable version of this kind of sort
+    fn stable_name(&self) -> &str {
+        match self {
+            SortingKind::Vanilla => "sort",
+            /* SortingKind::ByKey => "sort_by_key",
+             * SortingKind::ByCmp => "sort_by", */
+        }
+    }
+    /// The name of the unstable version of this kind of sort
+    fn unstable_name(&self) -> &str {
+        match self {
+            SortingKind::Vanilla => "sort_unstable",
+            /* SortingKind::ByKey => "sort_unstable_by_key",
+             * SortingKind::ByCmp => "sort_unstable_by", */
+        }
+    }
+    /// Takes the name of a function call and returns the kind of sort
+    /// that corresponds to that function name (or None if it isn't)
+    fn from_stable_name(name: &str) -> Option<SortingKind> {
+        match name {
+            "sort" => Some(SortingKind::Vanilla),
+            // "sort_by" => Some(SortingKind::ByCmp),
+            // "sort_by_key" => Some(SortingKind::ByKey),
+            _ => None,
+        }
+    }
+}
+
+/// A detected instance of this lint
+struct LintDetection {
+    slice_name: String,
+    method: SortingKind,
+    method_args: String,
+}
+
+fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
+    if_chain! {
+        if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
+        if let Some(slice) = &args.get(0);
+        if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str());
+        if is_slice_of_primitives(cx, slice);
+        then {
+            let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
+            Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str })
+        } else {
+            None
+        }
+    }
+}
+
+impl LateLintPass<'_> for StableSortPrimitive {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
+            span_lint_and_sugg(
+                cx,
+                STABLE_SORT_PRIMITIVE,
+                expr.span,
+                format!(
+                    "Use {} instead of {}",
+                    detection.method.unstable_name(),
+                    detection.method.stable_name()
+                )
+                .as_str(),
+                "try",
+                format!(
+                    "{}.{}({})",
+                    detection.slice_name,
+                    detection.method.unstable_name(),
+                    detection.method_args
+                ),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
index 6d1d083fa8d40a145f45ecaabc13c8fc41f4c7b2..4e335a0222f2067c3fd33e05f9bed56c5fa91bff 100644 (file)
@@ -64,26 +64,22 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                 | hir::BinOpKind::Gt => return,
                 _ => {},
             }
-            // Check if the binary expression is part of another bi/unary expression
-            // or operator assignment as a child node
-            let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id);
-            while parent_expr != hir::CRATE_HIR_ID {
-                if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) {
-                    match e.kind {
-                        hir::ExprKind::Binary(..)
-                        | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
-                        | hir::ExprKind::AssignOp(..) => return,
-                        _ => {},
+
+            // Check for more than one binary operation in the implemented function
+            // Linting when multiple operations are involved can result in false positives
+            if_chain! {
+                let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
+                if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn);
+                if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
+                let body = cx.tcx.hir().body(body_id);
+                let mut visitor = BinaryExprVisitor { nb_binops: 0 };
+
+                then {
+                    walk_expr(&mut visitor, &body.value);
+                    if visitor.nb_binops > 1 {
+                        return;
                     }
                 }
-                parent_expr = cx.tcx.hir().get_parent_node(parent_expr);
-            }
-            // as a parent node
-            let mut visitor = BinaryExprVisitor { in_binary_expr: false };
-            walk_expr(&mut visitor, expr);
-
-            if visitor.in_binary_expr {
-                return;
             }
 
             if let Some(impl_trait) = check_binop(
@@ -102,7 +98,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                     cx,
                     SUSPICIOUS_ARITHMETIC_IMPL,
                     binop.span,
-                    &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
+                    &format!("suspicious use of binary operator in `{}` impl", impl_trait),
                 );
             }
 
@@ -139,7 +135,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                     cx,
                     SUSPICIOUS_OP_ASSIGN_IMPL,
                     binop.span,
-                    &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait),
+                    &format!("suspicious use of binary operator in `{}` impl", impl_trait),
                 );
             }
         }
@@ -181,7 +177,7 @@ fn check_binop(
 }
 
 struct BinaryExprVisitor {
-    in_binary_expr: bool,
+    nb_binops: u32,
 }
 
 impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
@@ -191,12 +187,13 @@ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
         match expr.kind {
             hir::ExprKind::Binary(..)
             | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _)
-            | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true,
+            | hir::ExprKind::AssignOp(..) => self.nb_binops += 1,
             _ => {},
         }
 
         walk_expr(self, expr);
     }
+
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
         NestedVisitorMap::None
     }
index 0ef70311fb1cde5a80e9f21b20f21ed511d1093f..d4acf8df46d8a5323d64613c38de7ef7abbb8dd1 100644 (file)
@@ -2,9 +2,10 @@
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
-use rustc_hir::{GenericBound, Generics, WherePredicate};
+use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
 
 declare_clippy_lint! {
     /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for cases where generics are being used and multiple
+    /// syntax specifications for trait bounds are used simultaneously.
+    ///
+    /// **Why is this bad?** Duplicate bounds makes the code
+    /// less readable than specifing them only once.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
+    /// ```
+    ///
+    /// Could be written as:
+    ///
+    /// ```rust
+    /// fn func<T: Clone + Default>(arg: T) {}
+    /// ```
+    /// or
+    ///
+    /// ```rust
+    /// fn func<T>(arg: T) where T: Clone + Default {}
+    /// ```
+    pub TRAIT_DUPLICATION_IN_BOUNDS,
+    pedantic,
+    "Check if the same trait bounds are specified twice during a function declaration"
+}
+
 #[derive(Copy, Clone)]
 pub struct TraitBounds {
     max_trait_bounds: u64,
@@ -41,10 +71,25 @@ pub fn new(max_trait_bounds: u64) -> Self {
     }
 }
 
-impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
+impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
 
 impl<'tcx> LateLintPass<'tcx> for TraitBounds {
     fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
+        self.check_type_repetition(cx, gen);
+        check_trait_bound_duplication(cx, gen);
+    }
+}
+
+fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> {
+    if let GenericBound::Trait(t, _) = bound {
+        Some((t.trait_ref.path.res, t.span))
+    } else {
+        None
+    }
+}
+
+impl TraitBounds {
+    fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
         if in_macro(gen.span) {
             return;
         }
@@ -101,3 +146,48 @@ fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
         }
     }
 }
+
+fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
+    if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
+        return;
+    }
+
+    let mut map = FxHashMap::default();
+    for param in gen.params {
+        if let ParamName::Plain(ref ident) = param.name {
+            let res = param
+                .bounds
+                .iter()
+                .filter_map(get_trait_res_span_from_bound)
+                .collect::<Vec<_>>();
+            map.insert(*ident, res);
+        }
+    }
+
+    for predicate in gen.where_clause.predicates {
+        if_chain! {
+            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
+            if !in_macro(bound_predicate.span);
+            if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind;
+            if let QPath::Resolved(_, Path { ref segments, .. }) = path;
+            if let Some(segment) = segments.first();
+            if let Some(trait_resolutions_direct) = map.get(&segment.ident);
+            then {
+                for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) {
+                    if let Some((_, span_direct)) = trait_resolutions_direct
+                                                .iter()
+                                                .find(|(res_direct, _)| *res_direct == res_where) {
+                        span_lint_and_help(
+                            cx,
+                            TRAIT_DUPLICATION_IN_BOUNDS,
+                            *span_direct,
+                            "this trait bound is already specified in the where clause",
+                            None,
+                            "consider removing this trait bound",
+                        );
+                    }
+                }
+            }
+        }
+    }
+}
index f077c14618316e51a305d848015ebc2ab2a36e4d..7b5e92eb5ee1b896882bdf6b6195e0dbc1636113 100644 (file)
     ///
     /// **Example:**
     ///
-    /// ```rust,ignore
-    /// core::intrinsics::transmute::<*const [i32], *const [u16]>(p)
+    /// ```rust
+    /// # let p: *const [i32] = &[];
+    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
     /// ```
     /// Use instead:
     /// ```rust
-    /// p as *const [u16]
+    /// # let p: *const [i32] = &[];
+    /// p as *const [u16];
     /// ```
     pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     complexity,
@@ -704,14 +706,14 @@ fn can_be_expressed_as_pointer_cast<'tcx>(
     from_ty: Ty<'tcx>,
     to_ty: Ty<'tcx>,
 ) -> bool {
-    use CastKind::*;
+    use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
     matches!(
         check_cast(cx, e, from_ty, to_ty),
         Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
     )
 }
 
-/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of
+/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
 /// the cast. In certain cases, including some invalid casts from array references
 /// to pointers, this may cause additional errors to be emitted and/or ICE error
 /// messages. This function will panic if that occurs.
index d3b351f30ef7c9c0b4f8211fd711f01e8221e972..3bd73d9f21a98f98deea34846f8a18e1a10a92d9 100644 (file)
@@ -1,10 +1,13 @@
-use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg};
+use crate::utils::{
+    is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
+    span_lint_and_sugg,
+};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
+use rustc_hir::{Expr, ExprKind, MatchSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -65,19 +68,39 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if let Some(ref err_arg) = err_args.get(0);
             if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
             if match_qpath(err_fun_path, &paths::RESULT_ERR);
-            if let Some(return_type) = find_err_return_type(cx, &expr.kind);
-
+            if let Some(return_ty) = find_return_type(cx, &expr.kind);
             then {
-                let err_type = cx.typeck_results().expr_ty(err_arg);
+                let prefix;
+                let suffix;
+                let err_ty;
+
+                if let Some(ty) = result_error_type(cx, return_ty) {
+                    prefix = "Err(";
+                    suffix = ")";
+                    err_ty = ty;
+                } else if let Some(ty) = poll_result_error_type(cx, return_ty) {
+                    prefix = "Poll::Ready(Err(";
+                    suffix = "))";
+                    err_ty = ty;
+                } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
+                    prefix = "Poll::Ready(Some(Err(";
+                    suffix = ")))";
+                    err_ty = ty;
+                } else {
+                    return;
+                };
+
+                let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
+
                 let origin_snippet = if err_arg.span.from_expansion() {
                     snippet_with_macro_callsite(cx, err_arg.span, "_")
                 } else {
                     snippet(cx, err_arg.span, "_")
                 };
-                let suggestion = if err_type == return_type {
-                    format!("return Err({})", origin_snippet)
+                let suggestion = if err_ty == expr_err_ty {
+                    format!("return {}{}{}", prefix, origin_snippet, suffix)
                 } else {
-                    format!("return Err({}.into())", origin_snippet)
+                    format!("return {}{}.into(){}", prefix, origin_snippet, suffix)
                 };
 
                 span_lint_and_sugg(
@@ -94,27 +117,68 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     }
 }
 
-// In order to determine whether to suggest `.into()` or not, we need to find the error type the
-// function returns. To do that, we look for the From::from call (see tree above), and capture
-// its output type.
-fn find_err_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
+/// Finds function return type by examining return expressions in match arms.
+fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
     if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr {
-        arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty))
-    } else {
-        None
+        for arm in arms.iter() {
+            if let ExprKind::Ret(Some(ref ret)) = arm.body.kind {
+                return Some(cx.typeck_results().expr_ty(ret));
+            }
+        }
     }
+    None
 }
 
-// Check for From::from in one of the match arms.
-fn find_err_return_type_arm<'tcx>(cx: &LateContext<'tcx>, arm: &'tcx Arm<'_>) -> Option<Ty<'tcx>> {
+/// Extracts the error type from Result<T, E>.
+fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     if_chain! {
-        if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind;
-        if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind;
-        if let ExprKind::Path(ref from_error_fn) = from_error_path.kind;
-        if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR);
-        if let Some(from_error_arg) = from_error_args.get(0);
+        if let ty::Adt(_, subst) = ty.kind;
+        if is_type_diagnostic_item(cx, ty, sym!(result_type));
+        let err_ty = subst.type_at(1);
+        then {
+            Some(err_ty)
+        } else {
+            None
+        }
+    }
+}
+
+/// Extracts the error type from Poll<Result<T, E>>.
+fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+    if_chain! {
+        if let ty::Adt(def, subst) = ty.kind;
+        if match_def_path(cx, def.did, &paths::POLL);
+        let ready_ty = subst.type_at(0);
+
+        if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
+        if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did);
+        let err_ty = ready_subst.type_at(1);
+
+        then {
+            Some(err_ty)
+        } else {
+            None
+        }
+    }
+}
+
+/// Extracts the error type from Poll<Option<Result<T, E>>>.
+fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+    if_chain! {
+        if let ty::Adt(def, subst) = ty.kind;
+        if match_def_path(cx, def.did, &paths::POLL);
+        let ready_ty = subst.type_at(0);
+
+        if let ty::Adt(ready_def, ready_subst) = ready_ty.kind;
+        if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did);
+        let some_ty = ready_subst.type_at(0);
+
+        if let ty::Adt(some_def, some_subst) = some_ty.kind;
+        if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did);
+        let err_ty = some_subst.type_at(1);
+
         then {
-            Some(cx.typeck_results().expr_ty(from_error_arg))
+            Some(err_ty)
         } else {
             None
         }
index f2bbde28c2abc81793b852069926c0a718e85342..ea4b8172c9c2e8bb6f5b61bdb7a8e407b8612cb8 100644 (file)
@@ -181,8 +181,8 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
                             self.cx,
                             UNNECESSARY_UNWRAP,
                             expr.span,
-                            &format!("You checked before that `{}()` cannot fail. \
-                            Instead of checking and unwrapping, it's better to use `if let` or `match`.",
+                            &format!("you checked before that `{}()` cannot fail, \
+                            instead of checking and unwrapping, it's better to use `if let` or `match`",
                             method_name.ident.name),
                             |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); },
                         );
@@ -191,7 +191,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
                             self.cx,
                             PANICKING_UNWRAP,
                             expr.span,
-                            &format!("This call to `{}()` will always panic.",
+                            &format!("this call to `{}()` will always panic",
                             method_name.ident.name),
                             |diag| { diag.span_label(unwrappable.check.span, "because of this check"); },
                         );
index 407527251da225d4d2b454c8f5eb77f5a84b7812..a3975683cb3021395a7a9f4a29c6ef585455bb09 100644 (file)
@@ -75,12 +75,12 @@ pub fn get_attr<'a>(
                 })
                 .map_or_else(
                     || {
-                        sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
+                        sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
                         false
                     },
                     |deprecation_status| {
                         let mut diag =
-                            sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
+                            sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
                         match *deprecation_status {
                             DeprecationStatus::Deprecated => {
                                 diag.emit();
index 95a12fe193547d863783a5e6a2fbd4af9e8fed91..223628cc610da038d91f1856ea638dc56ce892b7 100644 (file)
@@ -43,6 +43,7 @@
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable};
+use rustc_mir::const_eval;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::original_sp;
 use rustc_span::symbol::{self, kw, Symbol};
@@ -52,7 +53,6 @@
 use smallvec::SmallVec;
 
 use crate::consts::{constant, Constant};
-use crate::reexport::Name;
 
 /// Returns `true` if the two spans come from differing expansions (i.e., one is
 /// from a macro and one isn't).
@@ -150,7 +150,7 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
 }
 
 /// Checks if an expression references a variable of the given name.
-pub fn match_var(expr: &Expr<'_>, var: Name) -> bool {
+pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
     if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
         if let [p] = path.segments {
             return p.ident.name == var;
@@ -420,7 +420,7 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 }
 
 /// Gets the name of the item the expression is in, if available.
-pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Name> {
+pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
     let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
     match cx.tcx.hir().find(parent_id) {
         Some(
@@ -433,7 +433,7 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Name> {
 }
 
 /// Gets the name of a `Pat`, if any.
-pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
+pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
     match pat.kind {
         PatKind::Binding(.., ref spname, _) => Some(spname.name),
         PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
@@ -443,14 +443,14 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option<Name> {
 }
 
 struct ContainsName {
-    name: Name,
+    name: Symbol,
     result: bool,
 }
 
 impl<'tcx> Visitor<'tcx> for ContainsName {
     type Map = Map<'tcx>;
 
-    fn visit_name(&mut self, _: Span, name: Name) {
+    fn visit_name(&mut self, _: Span, name: Symbol) {
         if self.name == name {
             self.result = true;
         }
@@ -461,7 +461,7 @@ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 }
 
 /// Checks if an `Expr` contains a certain name.
-pub fn contains_name(name: Name, expr: &Expr<'_>) -> bool {
+pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
     let mut cn = ContainsName { name, result: false };
     cn.visit_expr(expr);
     cn.result
@@ -869,11 +869,19 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 
 /// Checks if an expression is constructing a tuple-like enum variant or struct
 pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool {
+        cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty()
+    }
+
     if let ExprKind::Call(ref fun, _) = expr.kind {
         if let ExprKind::Path(ref qp) = fun.kind {
             let res = cx.qpath_res(qp, fun.hir_id);
             return match res {
                 def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
+                // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210
+                def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => {
+                    const_eval::is_const_fn(cx.tcx, def_id)
+                },
                 def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
                 _ => false,
             };
@@ -1027,7 +1035,7 @@ pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool
     cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 }
 
-pub fn get_arg_name(pat: &Pat<'_>) -> Option<Name> {
+pub fn get_arg_name(pat: &Pat<'_>) -> Option<Symbol> {
     match pat.kind {
         PatKind::Binding(.., ident, None) => Some(ident.name),
         PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
@@ -1378,6 +1386,36 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo
     })
 }
 
+/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
+/// number type, a str, or an array, slice, or tuple of those types).
+pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
+    match ty.kind {
+        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
+        ty::Ref(_, inner, _) if inner.kind == ty::Str => true,
+        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+        ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
+        _ => false,
+    }
+}
+
+/// Returns true iff the given expression is a slice of primitives (as defined in the
+/// `is_recursively_primitive_type` function).
+pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
+    match expr_type.kind {
+        ty::Slice(ref element_type)
+        | ty::Ref(
+            _,
+            ty::TyS {
+                kind: ty::Slice(ref element_type),
+                ..
+            },
+            _,
+        ) => is_recursively_primitive_type(element_type),
+        _ => false,
+    }
+}
+
 #[macro_export]
 macro_rules! unwrap_cargo_metadata {
     ($cx: ident, $lint: ident, $deps: expr) => {{
index a515ee29c82adc64e2678302137a26ac40254157..923b319d7778e2b3f0741eff765eca54a331bbba 100644 (file)
@@ -80,6 +80,7 @@
 pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
 pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
+pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
 pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
 pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
 pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
-pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
 pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
 pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
 pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
index 8092be277cca01af4bfbf8905cee24e1bfc567c5..168092f7329cc0caed65ea30f0edfe20ac91644c 100644 (file)
@@ -27,10 +27,7 @@ because that's clearly a non-descriptive name.
 
 ## Setup
 
-When working on Clippy, you will need the current git master version of rustc,
-which can change rapidly. Make sure you're working near rust-clippy's master,
-and use the `setup-toolchain.sh` script to configure the appropriate toolchain
-for the Clippy directory.
+See the [Basics](basics.md#get-the-code) documentation.
 
 ## Getting Started
 
@@ -38,12 +35,14 @@ There is a bit of boilerplate code that needs to be set up when creating a new
 lint. Fortunately, you can use the clippy dev tools to handle this for you. We
 are naming our new lint `foo_functions` (lints are generally written in snake
 case), and we don't need type information so it will have an early pass type
-(more on this later on). To get started on this lint you can run
-`cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
-(category will default to nursery if not provided). This command will create
-two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
-as well as run `cargo dev update_lints` to register the new lint. For cargo lints,
-two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
+(more on this later on). If you're not sure if the name you chose fits the lint,
+take a look at our [lint naming guidelines][lint_naming]. To get started on this
+lint you can run `cargo dev new_lint --name=foo_functions --pass=early
+--category=pedantic` (category will default to nursery if not provided). This
+command will create two files: `tests/ui/foo_functions.rs` and
+`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to
+register the new lint. For cargo lints, two project hierarchies (fail/pass) will
+be created by default under `tests/ui-cargo`.
 
 Next, we'll open up these files and add our lint!
 
@@ -113,7 +112,7 @@ For cargo lints, the process of testing differs in that we are interested in
 the `Cargo.toml` manifest file. We also need a minimal crate associated
 with that manifest.
 
-If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` 
+If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
 we will find by default two new crates, each with its manifest file:
 
 * `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
diff --git a/src/tools/clippy/doc/basics.md b/src/tools/clippy/doc/basics.md
new file mode 100644 (file)
index 0000000..c81e7f6
--- /dev/null
@@ -0,0 +1,112 @@
+# Basics for hacking on Clippy
+
+This document explains the basics for hacking on Clippy. Besides others, this
+includes how to set-up the development environment, how to build and how to test
+Clippy. For a more in depth description on the codebase take a look at [Adding
+Lints] or [Common Tools].
+
+[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
+
+- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
+  - [Get the code](#get-the-code)
+  - [Setup](#setup)
+  - [Building and Testing](#building-and-testing)
+  - [`cargo dev`](#cargo-dev)
+
+## Get the Code
+
+First, make sure you have checked out the latest version of Clippy. If this is
+your first time working on Clippy, create a fork of the repository and clone it
+afterwards with the following command:
+
+```bash
+git clone git@github.com:<your-username>/rust-clippy
+```
+
+If you've already cloned Clippy in the past, update it to the latest version:
+
+```bash
+# upstream has to be the remote of the rust-lang/rust-clippy repo
+git fetch upstream
+# make sure that you are on the master branch
+git checkout master
+# rebase your master branch on the upstream master
+git rebase upstream/master
+# push to the master branch of your fork
+git push
+```
+
+## Setup
+
+Next we need to setup the toolchain to compile Clippy. Since Clippy heavily
+relies on compiler internals it is build with the latest rustc master. To get
+this toolchain, you can just use the `setup-toolchain.sh` script or use
+`rustup-toolchain-install-master`:
+
+```bash
+sh setup-toolchain.sh
+# OR
+cargo install rustup-toolchain-install-master
+# For better IDE integration also add `-c rustfmt -c rust-src` (optional)
+rustup-toolchain-install-master -f -n master -c rustc-dev -c llvm-tools
+rustup override set master
+```
+
+_Note:_ Sometimes you may get compiler errors when building Clippy, even if you
+didn't change anything. Normally those will be fixed by a maintainer in a few hours. 
+
+## Building and Testing
+
+Once the `master` toolchain is installed, you can build and test Clippy like
+every other Rust project:
+
+```bash
+cargo build  # builds Clippy
+cargo test   # tests Clippy
+```
+
+Since Clippy's test suite is pretty big, there are some commands that only run a
+subset of Clippy's tests:
+
+```bash
+# only run UI tests
+cargo uitest
+# only run UI tests starting with `test_`
+TESTNAME="test_" cargo uitest
+# only run dogfood tests
+cargo test --test dogfood
+```
+
+If the output of a [UI test] differs from the expected output, you can update the
+reference file with:
+
+```bash
+sh tests/ui/update-all-references.sh
+```
+
+For example, this is necessary, if you fix a typo in an error message of a lint
+or if you modify a test file to add a test case.
+
+_Note:_ This command may update more files than you intended. In that case only
+commit the files you wanted to update.
+
+[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests
+
+## `cargo dev`
+
+Clippy has some dev tools to make working on Clippy more convenient. These tools
+can be accessed through the `cargo dev` command. Available tools are listed
+below. To get more information about these commands, just call them with
+`--help`.
+
+```bash
+# formats the whole Clippy codebase and all tests
+cargo dev fmt
+# register or update lint names/groups/...
+cargo dev update_lints
+# create a new lint and register it
+cargo dev new_lint
+# (experimental) Setup Clippy to work with rust-analyzer
+cargo dev ra-setup
+```
index 1f3f70631fb29ca5422b47e596cbe007d9a1f49f..bbb300296be9720ab629d8426e03fef89e6c5379 100644 (file)
         deprecation: None,
         module: "derive",
     },
+    Lint {
+        name: "derive_ord_xor_partial_ord",
+        group: "correctness",
+        desc: "deriving `Ord` but implementing `PartialOrd` explicitly",
+        deprecation: None,
+        module: "derive",
+    },
     Lint {
         name: "diverging_sub_expression",
         group: "complexity",
     Lint {
         name: "drop_bounds",
         group: "correctness",
-        desc: "Bounds of the form `T: Drop` are useless",
+        desc: "bounds of the form `T: Drop` are useless",
         deprecation: None,
         module: "drop_bounds",
     },
         deprecation: None,
         module: "bytecount",
     },
+    Lint {
+        name: "needless_arbitrary_self_type",
+        group: "complexity",
+        desc: "type of `self` parameter is already by default `Self`",
+        deprecation: None,
+        module: "needless_arbitrary_self_type",
+    },
     Lint {
         name: "needless_bool",
         group: "complexity",
         deprecation: None,
         module: "copies",
     },
+    Lint {
+        name: "same_item_push",
+        group: "style",
+        desc: "the same item is pushed inside of a for loop",
+        deprecation: None,
+        module: "loops",
+    },
     Lint {
         name: "search_is_some",
         group: "complexity",
         deprecation: None,
         module: "slow_vector_initialization",
     },
+    Lint {
+        name: "stable_sort_primitive",
+        group: "perf",
+        desc: "use of sort() when sort_unstable() is equivalent",
+        deprecation: None,
+        module: "stable_sort_primitive",
+    },
     Lint {
         name: "string_add",
         group: "restriction",
         deprecation: None,
         module: "misc",
     },
+    Lint {
+        name: "trait_duplication_in_bounds",
+        group: "pedantic",
+        desc: "Check if the same trait bounds are specified twice during a function declaration",
+        deprecation: None,
+        module: "trait_bounds",
+    },
     Lint {
         name: "transmute_bytes_to_str",
         group: "complexity",
index 26a47d237065a9f0c16cc3db6638a014b9ae86b7..697823712bf05e4ef411709915b1100fea7672cc 100644 (file)
@@ -3,7 +3,7 @@
 use compiletest_rs as compiletest;
 use compiletest_rs::common::Mode as TestMode;
 
-use std::env::{self, set_var};
+use std::env::{self, set_var, var};
 use std::ffi::OsStr;
 use std::fs;
 use std::io;
@@ -136,7 +136,9 @@ fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>
 
     let tests = compiletest::make_tests(&config);
 
+    let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default();
     let res = run_tests(&config, tests);
+    set_var("CARGO_MANIFEST_DIR", &manifest_dir);
     match res {
         Ok(true) => {},
         Ok(false) => panic!("Some tests failed"),
@@ -147,9 +149,6 @@ fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>
 }
 
 fn run_ui_cargo(config: &mut compiletest::Config) {
-    if cargo::is_rustc_test_suite() {
-        return;
-    }
     fn run_tests(
         config: &compiletest::Config,
         filter: &Option<String>,
@@ -217,6 +216,10 @@ fn run_tests(
         Ok(result)
     }
 
+    if cargo::is_rustc_test_suite() {
+        return;
+    }
+
     config.mode = TestMode::Ui;
     config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
 
index 4b77ac551e770f174b59e666841fbb2267cc2b76..fb12257021a1e7e0f0751c65c233392c0d8572fa 100644 (file)
@@ -1,4 +1,4 @@
-error: This function has a large number of lines.
+error: this function has a large number of lines
   --> $DIR/test.rs:18:1
    |
 LL | / fn too_many_lines() {
@@ -9,7 +9,7 @@ LL | | }
    |
    = note: `-D clippy::too-many-lines` implied by `-D warnings`
 
-error: This function has a large number of lines.
+error: this function has a large number of lines
   --> $DIR/test.rs:38:1
    |
 LL | / fn comment_before_code() {
index 5c1fdd83efb0da25f4578355532ee4101126cb11..0458950edee1c9660d41a4e15a974038f3949eac 100644 (file)
@@ -47,6 +47,7 @@ async fn not_good(x: &Mutex<u32>) -> u32 {
     first + second + third
 }
 
+#[allow(clippy::manual_async_fn)]
 fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
     async move {
         let guard = x.lock().unwrap();
index 8c47cb37d8c997230423bf3c4b899fa31e455ac1..21bf49d16f04877d862ff702ef527575cd537d2f 100644 (file)
@@ -46,13 +46,13 @@ LL | |     };
    | |_____^
 
 error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.
-  --> $DIR/await_holding_lock.rs:52:13
+  --> $DIR/await_holding_lock.rs:53:13
    |
 LL |         let guard = x.lock().unwrap();
    |             ^^^^^
    |
 note: these are all the await points this lock is held through
-  --> $DIR/await_holding_lock.rs:52:9
+  --> $DIR/await_holding_lock.rs:53:9
    |
 LL | /         let guard = x.lock().unwrap();
 LL | |         baz().await
index eeb1f20ee894d89b0d267ff67bb4098054f6ab04..55d94b8257dba3b96408dad7eb849811623e0114 100644 (file)
@@ -84,25 +84,25 @@ error: order comparisons between booleans can be simplified
 LL |     if x > y {
    |        ^^^^^ help: try simplifying it as shown: `x & !y`
 
-error: This comparison might be written more concisely
+error: this comparison might be written more concisely
   --> $DIR/bool_comparison.rs:120:8
    |
 LL |     if a == !b {};
    |        ^^^^^^^ help: try simplifying it as shown: `a != b`
 
-error: This comparison might be written more concisely
+error: this comparison might be written more concisely
   --> $DIR/bool_comparison.rs:121:8
    |
 LL |     if !a == b {};
    |        ^^^^^^^ help: try simplifying it as shown: `a != b`
 
-error: This comparison might be written more concisely
+error: this comparison might be written more concisely
   --> $DIR/bool_comparison.rs:125:8
    |
 LL |     if b == !a {};
    |        ^^^^^^^ help: try simplifying it as shown: `b != a`
 
-error: This comparison might be written more concisely
+error: this comparison might be written more concisely
   --> $DIR/bool_comparison.rs:126:8
    |
 LL |     if !b == a {};
index bc785b075e0282641b3f095be8e6c4665398f198..f42b246afd29319233c12bde09cb35726f959776 100644 (file)
@@ -1,4 +1,4 @@
-error: This generic shadows the built-in type `u32`
+error: this generic shadows the built-in type `u32`
   --> $DIR/builtin-type-shadow.rs:4:8
    |
 LL | fn foo<u32>(a: u32) -> u32 {
index 436f5d86a062760926b60ad31cf8ec8211bbed06..1dc37fc8b259f576d046aeec7090a998465cabd0 100644 (file)
@@ -1,8 +1,8 @@
-error: You appear to be counting bytes the naive way
+error: you appear to be counting bytes the naive way
   --> $DIR/bytecount.rs:5:13
    |
 LL |     let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)`
    |
 note: the lint level is defined here
   --> $DIR/bytecount.rs:1:8
@@ -10,17 +10,17 @@ note: the lint level is defined here
 LL | #[deny(clippy::naive_bytecount)]
    |        ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: You appear to be counting bytes the naive way
+error: you appear to be counting bytes the naive way
   --> $DIR/bytecount.rs:7:13
    |
 LL |     let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)`
 
-error: You appear to be counting bytes the naive way
+error: you appear to be counting bytes the naive way
   --> $DIR/bytecount.rs:19:13
    |
 LL |     let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)`
 
 error: aborting due to 3 previous errors
 
index 648ba3ccd01dbfbc4e9af53ef7301df4de88d355..18518def0acbe42e596234de18964803d6059a18 100644 (file)
@@ -1,4 +1,4 @@
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:17:13
    |
 LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
@@ -6,91 +6,91 @@ LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
    |
    = note: `-D clippy::checked-conversions` implied by `-D warnings`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:18:13
    |
 LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:22:13
    |
 LL |     let _ = value <= i64::from(u16::max_value()) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:23:13
    |
 LL |     let _ = value <= i64::from(u16::MAX) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:27:13
    |
 LL |     let _ = value <= (u8::max_value() as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:28:13
    |
 LL |     let _ = value <= (u8::MAX as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:34:13
    |
 LL |     let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:35:13
    |
 LL |     let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:39:13
    |
 LL |     let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:40:13
    |
 LL |     let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:46:13
    |
 LL |     let _ = value <= i32::max_value() as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:47:13
    |
 LL |     let _ = value <= i32::MAX as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:51:13
    |
 LL |     let _ = value <= isize::max_value() as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:52:13
    |
 LL |     let _ = value <= isize::MAX as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:56:13
    |
 LL |     let _ = value <= u16::max_value() as u32 && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
-error: Checked cast can be simplified.
+error: checked cast can be simplified
   --> $DIR/checked_conversions.rs:57:13
    |
 LL |     let _ = value <= u16::MAX as u32 && value as i32 == 5;
index dc666bab4603960fdb4928c1d05bf8495bb5e96e..33bb5136ef8e70ff8c5a24fd0a1f90fb9b75ac27 100644 (file)
@@ -1,4 +1,4 @@
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:8:9
    |
 LL |     if x.is_ok() && y.is_err() {
@@ -12,7 +12,7 @@ note: the lint level is defined here
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: This call to `unwrap_err()` will always panic.
+error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:9:9
    |
 LL |     if x.is_ok() && y.is_err() {
@@ -27,7 +27,7 @@ note: the lint level is defined here
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:10:9
    |
 LL |     if x.is_ok() && y.is_err() {
@@ -36,7 +36,7 @@ LL |     if x.is_ok() && y.is_err() {
 LL |         y.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:11:9
    |
 LL |     if x.is_ok() && y.is_err() {
@@ -45,7 +45,7 @@ LL |     if x.is_ok() && y.is_err() {
 LL |         y.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:25:9
    |
 LL |     if x.is_ok() || y.is_ok() {
@@ -54,7 +54,7 @@ LL |     if x.is_ok() || y.is_ok() {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:26:9
    |
 LL |     if x.is_ok() || y.is_ok() {
@@ -63,7 +63,7 @@ LL |     if x.is_ok() || y.is_ok() {
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:27:9
    |
 LL |     if x.is_ok() || y.is_ok() {
@@ -72,7 +72,7 @@ LL |     if x.is_ok() || y.is_ok() {
 LL |         y.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:28:9
    |
 LL |     if x.is_ok() || y.is_ok() {
@@ -81,7 +81,7 @@ LL |     if x.is_ok() || y.is_ok() {
 LL |         y.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:32:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -89,7 +89,7 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
-error: This call to `unwrap_err()` will always panic.
+error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:33:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -98,7 +98,7 @@ LL |         x.unwrap(); // unnecessary
 LL |         x.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:34:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -107,7 +107,7 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 LL |         y.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:35:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -116,7 +116,7 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 LL |         y.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:36:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -125,7 +125,7 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 LL |         z.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
-error: This call to `unwrap_err()` will always panic.
+error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:37:9
    |
 LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
@@ -134,7 +134,7 @@ LL |     if x.is_ok() && !(y.is_ok() || z.is_err()) {
 LL |         z.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:45:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -143,7 +143,7 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:46:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -152,7 +152,7 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:47:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -161,7 +161,7 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 LL |         y.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
-error: This call to `unwrap_err()` will always panic.
+error: this call to `unwrap_err()` will always panic
   --> $DIR/complex_conditionals.rs:48:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -170,7 +170,7 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 LL |         y.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals.rs:49:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
@@ -179,7 +179,7 @@ LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
 LL |         z.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals.rs:50:9
    |
 LL |     if x.is_ok() || !(y.is_ok() && z.is_err()) {
index e4d085470c3b4200af404d3260974caca34ca731..a01f7f956f629c80ac8affb2f702a1b148b794d2 100644 (file)
@@ -1,4 +1,4 @@
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/complex_conditionals_nested.rs:8:13
    |
 LL |         if x.is_some() {
@@ -12,7 +12,7 @@ note: the lint level is defined here
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/complex_conditionals_nested.rs:10:13
    |
 LL |         if x.is_some() {
index 4013d1ed667f16470d8abe497bfeff6562bb9ac2..416ec1a01ab3aa27e59b9ed44bd9d8cde45c911e 100644 (file)
@@ -1,4 +1,4 @@
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/simple_conditionals.rs:39:9
    |
 LL |     if x.is_some() {
@@ -12,7 +12,7 @@ note: the lint level is defined here
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/simple_conditionals.rs:41:9
    |
 LL |     if x.is_some() {
@@ -27,7 +27,7 @@ note: the lint level is defined here
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/simple_conditionals.rs:44:9
    |
 LL |     if x.is_none() {
@@ -35,7 +35,7 @@ LL |     if x.is_none() {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/simple_conditionals.rs:46:9
    |
 LL |     if x.is_none() {
@@ -44,7 +44,7 @@ LL |     if x.is_none() {
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/simple_conditionals.rs:7:13
    |
 LL |         if $a.is_some() {
@@ -57,7 +57,7 @@ LL |     m!(x);
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/simple_conditionals.rs:54:9
    |
 LL |     if x.is_ok() {
@@ -65,7 +65,7 @@ LL |     if x.is_ok() {
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
-error: This call to `unwrap_err()` will always panic.
+error: this call to `unwrap_err()` will always panic
   --> $DIR/simple_conditionals.rs:55:9
    |
 LL |     if x.is_ok() {
@@ -74,7 +74,7 @@ LL |         x.unwrap(); // unnecessary
 LL |         x.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/simple_conditionals.rs:57:9
    |
 LL |     if x.is_ok() {
@@ -83,7 +83,7 @@ LL |     if x.is_ok() {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/simple_conditionals.rs:58:9
    |
 LL |     if x.is_ok() {
@@ -92,7 +92,7 @@ LL |     if x.is_ok() {
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: This call to `unwrap()` will always panic.
+error: this call to `unwrap()` will always panic
   --> $DIR/simple_conditionals.rs:61:9
    |
 LL |     if x.is_err() {
@@ -100,7 +100,7 @@ LL |     if x.is_err() {
 LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
-error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/simple_conditionals.rs:62:9
    |
 LL |     if x.is_err() {
@@ -109,7 +109,7 @@ LL |         x.unwrap(); // will panic
 LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
-error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`.
+error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match`
   --> $DIR/simple_conditionals.rs:64:9
    |
 LL |     if x.is_err() {
@@ -118,7 +118,7 @@ LL |     if x.is_err() {
 LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
-error: This call to `unwrap_err()` will always panic.
+error: this call to `unwrap_err()` will always panic
   --> $DIR/simple_conditionals.rs:65:9
    |
 LL |     if x.is_err() {
index b563a2ebec2d2fd1b55f7f436799f6efe6920864..a1f4c70fb2786ede59a99564e629a4715ab3fca7 100644 (file)
@@ -1,4 +1,4 @@
-error: Comparing with null is better expressed by the `.is_null()` method
+error: comparing with null is better expressed by the `.is_null()` method
   --> $DIR/cmp_null.rs:9:8
    |
 LL |     if p == ptr::null() {
@@ -6,7 +6,7 @@ LL |     if p == ptr::null() {
    |
    = note: `-D clippy::cmp-null` implied by `-D warnings`
 
-error: Comparing with null is better expressed by the `.is_null()` method
+error: comparing with null is better expressed by the `.is_null()` method
   --> $DIR/cmp_null.rs:14:8
    |
 LL |     if m == ptr::null_mut() {
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5872.rs b/src/tools/clippy/tests/ui/crashes/ice-5872.rs
new file mode 100644 (file)
index 0000000..68afa8f
--- /dev/null
@@ -0,0 +1,5 @@
+#![warn(clippy::needless_collect)]
+
+fn main() {
+    let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-5872.stderr b/src/tools/clippy/tests/ui/crashes/ice-5872.stderr
new file mode 100644 (file)
index 0000000..a60ca34
--- /dev/null
@@ -0,0 +1,10 @@
+error: avoid using `collect()` when not needed
+  --> $DIR/ice-5872.rs:4:39
+   |
+LL |     let _ = vec![1, 2, 3].into_iter().collect::<Vec<_>>().is_empty();
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
+   |
+   = note: `-D clippy::needless-collect` implied by `-D warnings`
+
+error: aborting due to previous error
+
index 453760c6b9211c338130d9fadad206af116440e2..26b2057548bd9e46b3dc94c7a6ff6366a30b33ce 100644 (file)
@@ -1,4 +1,4 @@
-error: Calling `std::string::String::default()` is more clear than this expression
+error: calling `std::string::String::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:8:22
    |
 LL |     let s1: String = Default::default();
@@ -6,43 +6,43 @@ LL |     let s1: String = Default::default();
    |
    = note: `-D clippy::default-trait-access` implied by `-D warnings`
 
-error: Calling `std::string::String::default()` is more clear than this expression
+error: calling `std::string::String::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:12:22
    |
 LL |     let s3: String = D2::default();
    |                      ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
 
-error: Calling `std::string::String::default()` is more clear than this expression
+error: calling `std::string::String::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:14:22
    |
 LL |     let s4: String = std::default::Default::default();
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
 
-error: Calling `std::string::String::default()` is more clear than this expression
+error: calling `std::string::String::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:18:22
    |
 LL |     let s6: String = default::Default::default();
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
 
-error: Calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
+error: calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:28:46
    |
 LL |     let s11: GenericDerivedDefault<String> = Default::default();
    |                                              ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()`
 
-error: Calling `TupleDerivedDefault::default()` is more clear than this expression
+error: calling `TupleDerivedDefault::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:34:36
    |
 LL |     let s14: TupleDerivedDefault = Default::default();
    |                                    ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()`
 
-error: Calling `ArrayDerivedDefault::default()` is more clear than this expression
+error: calling `ArrayDerivedDefault::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:36:36
    |
 LL |     let s15: ArrayDerivedDefault = Default::default();
    |                                    ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()`
 
-error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression
+error: calling `TupleStructDerivedDefault::default()` is more clear than this expression
   --> $DIR/default_trait_access.rs:40:42
    |
 LL |     let s17: TupleStructDerivedDefault = Default::default();
diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs
new file mode 100644 (file)
index 0000000..b82dc51
--- /dev/null
@@ -0,0 +1,68 @@
+#![warn(clippy::derive_ord_xor_partial_ord)]
+
+use std::cmp::Ordering;
+
+#[derive(PartialOrd, Ord, PartialEq, Eq)]
+struct DeriveBoth;
+
+impl PartialEq<u64> for DeriveBoth {
+    fn eq(&self, _: &u64) -> bool {
+        true
+    }
+}
+
+impl PartialOrd<u64> for DeriveBoth {
+    fn partial_cmp(&self, _: &u64) -> Option<Ordering> {
+        Some(Ordering::Equal)
+    }
+}
+
+#[derive(Ord, PartialEq, Eq)]
+struct DeriveOrd;
+
+impl PartialOrd for DeriveOrd {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(other.cmp(self))
+    }
+}
+
+#[derive(Ord, PartialEq, Eq)]
+struct DeriveOrdWithExplicitTypeVariable;
+
+impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(other.cmp(self))
+    }
+}
+
+#[derive(PartialOrd, PartialEq, Eq)]
+struct DerivePartialOrd;
+
+impl std::cmp::Ord for DerivePartialOrd {
+    fn cmp(&self, other: &Self) -> Ordering {
+        Ordering::Less
+    }
+}
+
+#[derive(PartialOrd, PartialEq, Eq)]
+struct ImplUserOrd;
+
+trait Ord {}
+
+// We don't want to lint on user-defined traits called `Ord`
+impl Ord for ImplUserOrd {}
+
+mod use_ord {
+    use std::cmp::{Ord, Ordering};
+
+    #[derive(PartialOrd, PartialEq, Eq)]
+    struct DerivePartialOrdInUseOrd;
+
+    impl Ord for DerivePartialOrdInUseOrd {
+        fn cmp(&self, other: &Self) -> Ordering {
+            Ordering::Less
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.stderr
new file mode 100644 (file)
index 0000000..66bc4d4
--- /dev/null
@@ -0,0 +1,71 @@
+error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
+  --> $DIR/derive_ord_xor_partial_ord.rs:20:10
+   |
+LL | #[derive(Ord, PartialEq, Eq)]
+   |          ^^^
+   |
+   = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings`
+note: `PartialOrd` implemented here
+  --> $DIR/derive_ord_xor_partial_ord.rs:23:1
+   |
+LL | / impl PartialOrd for DeriveOrd {
+LL | |     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+LL | |         Some(other.cmp(self))
+LL | |     }
+LL | | }
+   | |_^
+   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are deriving `Ord` but have implemented `PartialOrd` explicitly
+  --> $DIR/derive_ord_xor_partial_ord.rs:29:10
+   |
+LL | #[derive(Ord, PartialEq, Eq)]
+   |          ^^^
+   |
+note: `PartialOrd` implemented here
+  --> $DIR/derive_ord_xor_partial_ord.rs:32:1
+   |
+LL | / impl PartialOrd<DeriveOrdWithExplicitTypeVariable> for DeriveOrdWithExplicitTypeVariable {
+LL | |     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+LL | |         Some(other.cmp(self))
+LL | |     }
+LL | | }
+   | |_^
+   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are implementing `Ord` explicitly but have derived `PartialOrd`
+  --> $DIR/derive_ord_xor_partial_ord.rs:41:1
+   |
+LL | / impl std::cmp::Ord for DerivePartialOrd {
+LL | |     fn cmp(&self, other: &Self) -> Ordering {
+LL | |         Ordering::Less
+LL | |     }
+LL | | }
+   | |_^
+   |
+note: `PartialOrd` implemented here
+  --> $DIR/derive_ord_xor_partial_ord.rs:38:10
+   |
+LL | #[derive(PartialOrd, PartialEq, Eq)]
+   |          ^^^^^^^^^^
+   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are implementing `Ord` explicitly but have derived `PartialOrd`
+  --> $DIR/derive_ord_xor_partial_ord.rs:61:5
+   |
+LL | /     impl Ord for DerivePartialOrdInUseOrd {
+LL | |         fn cmp(&self, other: &Self) -> Ordering {
+LL | |             Ordering::Less
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+note: `PartialOrd` implemented here
+  --> $DIR/derive_ord_xor_partial_ord.rs:58:14
+   |
+LL |     #[derive(PartialOrd, PartialEq, Eq)]
+   |              ^^^^^^^^^^
+   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
index 5dcda7b3af4ac48187d6d84a3559793199f87593..05ef4e25f7f870b8b8c9056ff6f540de89617b1b 100644 (file)
@@ -1,4 +1,4 @@
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:6:8
    |
 LL |     if x == y || x < y {
@@ -6,43 +6,43 @@ LL |     if x == y || x < y {
    |
    = note: `-D clippy::double-comparisons` implied by `-D warnings`
 
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:9:8
    |
 LL |     if x < y || x == y {
    |        ^^^^^^^^^^^^^^^ help: try: `x <= y`
 
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:12:8
    |
 LL |     if x == y || x > y {
    |        ^^^^^^^^^^^^^^^ help: try: `x >= y`
 
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:15:8
    |
 LL |     if x > y || x == y {
    |        ^^^^^^^^^^^^^^^ help: try: `x >= y`
 
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:18:8
    |
 LL |     if x < y || x > y {
    |        ^^^^^^^^^^^^^^ help: try: `x != y`
 
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:21:8
    |
 LL |     if x > y || x < y {
    |        ^^^^^^^^^^^^^^ help: try: `x != y`
 
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:24:8
    |
 LL |     if x <= y && x >= y {
    |        ^^^^^^^^^^^^^^^^ help: try: `x == y`
 
-error: This binary expression can be simplified
+error: this binary expression can be simplified
   --> $DIR/double_comparison.rs:27:8
    |
 LL |     if x >= y && x <= y {
index 0e4c9b5682dfb4748f74c808cdf56321583451ee..40fcad2ab1d4a64b5c88a7a451d08f9180379784 100644 (file)
@@ -1,4 +1,4 @@
-error: Consider removing unnecessary double parentheses
+error: consider removing unnecessary double parentheses
   --> $DIR/double_parens.rs:15:5
    |
 LL |     ((0))
@@ -6,31 +6,31 @@ LL |     ((0))
    |
    = note: `-D clippy::double-parens` implied by `-D warnings`
 
-error: Consider removing unnecessary double parentheses
+error: consider removing unnecessary double parentheses
   --> $DIR/double_parens.rs:19:14
    |
 LL |     dummy_fn((0));
    |              ^^^
 
-error: Consider removing unnecessary double parentheses
+error: consider removing unnecessary double parentheses
   --> $DIR/double_parens.rs:23:20
    |
 LL |     x.dummy_method((0));
    |                    ^^^
 
-error: Consider removing unnecessary double parentheses
+error: consider removing unnecessary double parentheses
   --> $DIR/double_parens.rs:27:5
    |
 LL |     ((1, 2))
    |     ^^^^^^^^
 
-error: Consider removing unnecessary double parentheses
+error: consider removing unnecessary double parentheses
   --> $DIR/double_parens.rs:31:5
    |
 LL |     (())
    |     ^^^^
 
-error: Consider removing unnecessary double parentheses
+error: consider removing unnecessary double parentheses
   --> $DIR/double_parens.rs:53:16
    |
 LL |     assert_eq!(((1, 2)), (1, 2), "Error");
index 5d360ef30a1d89048c017a061e64167a2acbd9a5..8208c0ed7e3985e6e4059d4373574622f8540fb4 100644 (file)
@@ -1,4 +1,4 @@
-error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
+error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue
   --> $DIR/drop_bounds.rs:2:11
    |
 LL | fn foo<T: Drop>() {}
@@ -6,7 +6,7 @@ LL | fn foo<T: Drop>() {}
    |
    = note: `#[deny(clippy::drop_bounds)]` on by default
 
-error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue.
+error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue
   --> $DIR/drop_bounds.rs:5:8
    |
 LL |     T: Drop,
index bf753a732f000f78c956833f7ea3f7f25bed7286..594fca44a321071ac46cdc9ad5bcac5676f69218 100644 (file)
@@ -1,4 +1,4 @@
-error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
   --> $DIR/empty_line_after_outer_attribute.rs:11:1
    |
 LL | / #[crate_type = "lib"]
@@ -9,7 +9,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) }
    |
    = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
 
-error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
   --> $DIR/empty_line_after_outer_attribute.rs:23:1
    |
 LL | / #[crate_type = "lib"]
@@ -17,7 +17,7 @@ LL | |
 LL | | fn with_one_newline() { assert!(true) }
    | |_
 
-error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
   --> $DIR/empty_line_after_outer_attribute.rs:28:1
    |
 LL | / #[crate_type = "lib"]
@@ -26,7 +26,7 @@ LL | |
 LL | | fn with_two_newlines() { assert!(true) }
    | |_
 
-error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
   --> $DIR/empty_line_after_outer_attribute.rs:35:1
    |
 LL | / #[crate_type = "lib"]
@@ -34,7 +34,7 @@ LL | |
 LL | | enum Baz {
    | |_
 
-error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
   --> $DIR/empty_line_after_outer_attribute.rs:43:1
    |
 LL | / #[crate_type = "lib"]
@@ -42,7 +42,7 @@ LL | |
 LL | | struct Foo {
    | |_
 
-error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
+error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
   --> $DIR/empty_line_after_outer_attribute.rs:51:1
    |
 LL | / #[crate_type = "lib"]
index ddbf4e98c51ab6921808eb14b64055975b4557e8..150acfbfee75996fd89b882784a26fcbc3a5e002 100644 (file)
@@ -1,4 +1,10 @@
-#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)]
+#![allow(
+    unused,
+    dead_code,
+    clippy::needless_lifetimes,
+    clippy::needless_pass_by_value,
+    clippy::needless_arbitrary_self_type
+)]
 #![warn(clippy::extra_unused_lifetimes)]
 
 fn empty() {}
index 16bbb1c037d84c6f3a86c442edfc639eddc534ef..ebdb8e749520fed2bd95045043b76e60f5829543 100644 (file)
@@ -1,5 +1,5 @@
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:8:14
+  --> $DIR/extra_unused_lifetimes.rs:14:14
    |
 LL | fn unused_lt<'a>(x: u8) {}
    |              ^^
@@ -7,19 +7,19 @@ LL | fn unused_lt<'a>(x: u8) {}
    = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
 
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:10:25
+  --> $DIR/extra_unused_lifetimes.rs:16:25
    |
 LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
    |                         ^^
 
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:35:10
+  --> $DIR/extra_unused_lifetimes.rs:41:10
    |
 LL |     fn x<'a>(&self) {}
    |          ^^
 
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:61:22
+  --> $DIR/extra_unused_lifetimes.rs:67:22
    |
 LL |         fn unused_lt<'a>(x: u8) {}
    |                      ^^
index 9b0e7550cc314b774f400ffc095ef77921800430..c640c82d6d7c991e9df76d297118ea3de576a4cf 100644 (file)
@@ -1,4 +1,4 @@
-error: This function has a large number of lines.
+error: this function has a large number of lines
   --> $DIR/functions_maxlines.rs:58:1
    |
 LL | / fn bad_lines() {
diff --git a/src/tools/clippy/tests/ui/iter_skip_next.fixed b/src/tools/clippy/tests/ui/iter_skip_next.fixed
new file mode 100644 (file)
index 0000000..928b6ac
--- /dev/null
@@ -0,0 +1,22 @@
+// run-rustfix
+// aux-build:option_helpers.rs
+
+#![warn(clippy::iter_skip_next)]
+#![allow(clippy::blacklisted_name)]
+#![allow(clippy::iter_nth)]
+
+extern crate option_helpers;
+
+use option_helpers::IteratorFalsePositives;
+
+/// Checks implementation of `ITER_SKIP_NEXT` lint
+fn main() {
+    let some_vec = vec![0, 1, 2, 3];
+    let _ = some_vec.iter().nth(42);
+    let _ = some_vec.iter().cycle().nth(42);
+    let _ = (1..10).nth(10);
+    let _ = &some_vec[..].iter().nth(3);
+    let foo = IteratorFalsePositives { foo: 0 };
+    let _ = foo.skip(42).next();
+    let _ = foo.filter().skip(42).next();
+}
index a65ca3bbb131b149d4328612bcef41931c18a773..7075e2598ebee2903cdb6d70ab922190ccc2ec62 100644 (file)
@@ -1,15 +1,17 @@
+// run-rustfix
 // aux-build:option_helpers.rs
 
 #![warn(clippy::iter_skip_next)]
 #![allow(clippy::blacklisted_name)]
+#![allow(clippy::iter_nth)]
 
 extern crate option_helpers;
 
 use option_helpers::IteratorFalsePositives;
 
 /// Checks implementation of `ITER_SKIP_NEXT` lint
-fn iter_skip_next() {
-    let mut some_vec = vec![0, 1, 2, 3];
+fn main() {
+    let some_vec = vec![0, 1, 2, 3];
     let _ = some_vec.iter().skip(42).next();
     let _ = some_vec.iter().cycle().skip(42).next();
     let _ = (1..10).skip(10).next();
@@ -18,5 +20,3 @@ fn iter_skip_next() {
     let _ = foo.skip(42).next();
     let _ = foo.filter().skip(42).next();
 }
-
-fn main() {}
index 5709f3355298bb396337aa7a259dc6040c3d5da4..feedc2f288a2bf81855ae6a2b49507c0fcac44fa 100644 (file)
@@ -1,35 +1,28 @@
 error: called `skip(x).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:13:13
+  --> $DIR/iter_skip_next.rs:15:28
    |
 LL |     let _ = some_vec.iter().skip(42).next();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                            ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
    |
    = note: `-D clippy::iter-skip-next` implied by `-D warnings`
-   = help: this is more succinctly expressed by calling `nth(x)`
 
 error: called `skip(x).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:14:13
+  --> $DIR/iter_skip_next.rs:16:36
    |
 LL |     let _ = some_vec.iter().cycle().skip(42).next();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: this is more succinctly expressed by calling `nth(x)`
+   |                                    ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
 
 error: called `skip(x).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:15:13
+  --> $DIR/iter_skip_next.rs:17:20
    |
 LL |     let _ = (1..10).skip(10).next();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: this is more succinctly expressed by calling `nth(x)`
+   |                    ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
 
 error: called `skip(x).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:16:14
+  --> $DIR/iter_skip_next.rs:18:33
    |
 LL |     let _ = &some_vec[..].iter().skip(3).next();
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: this is more succinctly expressed by calling `nth(x)`
+   |                                 ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
 
 error: aborting due to 4 previous errors
 
index 3ef29dd63880b7f62757d82ab6a2f6bdf2dfc341..b5211318a15047e4e9c6f9f079cd2835d34fa01e 100644 (file)
@@ -4,14 +4,14 @@
 pub struct PubOne;
 
 impl PubOne {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 }
 
 impl PubOne {
     // A second impl for this struct -- the error span shouldn't mention this.
-    pub fn irrelevant(self: &Self) -> bool {
+    pub fn irrelevant(&self) -> bool {
         false
     }
 }
@@ -21,7 +21,7 @@ pub fn irrelevant(self: &Self) -> bool {
 
 #[allow(clippy::len_without_is_empty)]
 impl PubAllowed {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 }
@@ -29,17 +29,17 @@ pub fn len(self: &Self) -> isize {
 // No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the
 // impl containing `len`.
 impl PubAllowed {
-    pub fn irrelevant(self: &Self) -> bool {
+    pub fn irrelevant(&self) -> bool {
         false
     }
 }
 
 pub trait PubTraitsToo {
-    fn len(self: &Self) -> isize;
+    fn len(&self) -> isize;
 }
 
 impl PubTraitsToo for One {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         0
     }
 }
@@ -47,11 +47,11 @@ fn len(self: &Self) -> isize {
 pub struct HasIsEmpty;
 
 impl HasIsEmpty {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 
-    fn is_empty(self: &Self) -> bool {
+    fn is_empty(&self) -> bool {
         false
     }
 }
@@ -59,11 +59,11 @@ fn is_empty(self: &Self) -> bool {
 pub struct HasWrongIsEmpty;
 
 impl HasWrongIsEmpty {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 
-    pub fn is_empty(self: &Self, x: u32) -> bool {
+    pub fn is_empty(&self, x: u32) -> bool {
         false
     }
 }
@@ -71,7 +71,7 @@ pub fn is_empty(self: &Self, x: u32) -> bool {
 struct NotPubOne;
 
 impl NotPubOne {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         // No error; `len` is pub but `NotPubOne` is not exported anyway.
         1
     }
@@ -80,19 +80,19 @@ pub fn len(self: &Self) -> isize {
 struct One;
 
 impl One {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         // No error; `len` is private; see issue #1085.
         1
     }
 }
 
 trait TraitsToo {
-    fn len(self: &Self) -> isize;
+    fn len(&self) -> isize;
     // No error; `len` is private; see issue #1085.
 }
 
 impl TraitsToo for One {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         0
     }
 }
@@ -100,11 +100,11 @@ fn len(self: &Self) -> isize {
 struct HasPrivateIsEmpty;
 
 impl HasPrivateIsEmpty {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 
-    fn is_empty(self: &Self) -> bool {
+    fn is_empty(&self) -> bool {
         false
     }
 }
@@ -112,16 +112,16 @@ fn is_empty(self: &Self) -> bool {
 struct Wither;
 
 pub trait WithIsEmpty {
-    fn len(self: &Self) -> isize;
-    fn is_empty(self: &Self) -> bool;
+    fn len(&self) -> isize;
+    fn is_empty(&self) -> bool;
 }
 
 impl WithIsEmpty for Wither {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         1
     }
 
-    fn is_empty(self: &Self) -> bool {
+    fn is_empty(&self) -> bool {
         false
     }
 }
index 4493b17a4b4e5672a0a494165ee78788955bc206..d79c300c07445054ec7ca5da083bb4aaf8202f1d 100644 (file)
@@ -2,7 +2,7 @@ error: item `PubOne` has a public `len` method but no corresponding `is_empty` m
   --> $DIR/len_without_is_empty.rs:6:1
    |
 LL | / impl PubOne {
-LL | |     pub fn len(self: &Self) -> isize {
+LL | |     pub fn len(&self) -> isize {
 LL | |         1
 LL | |     }
 LL | | }
@@ -14,7 +14,7 @@ error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_e
   --> $DIR/len_without_is_empty.rs:37:1
    |
 LL | / pub trait PubTraitsToo {
-LL | |     fn len(self: &Self) -> isize;
+LL | |     fn len(&self) -> isize;
 LL | | }
    | |_^
 
@@ -22,7 +22,7 @@ error: item `HasIsEmpty` has a public `len` method but a private `is_empty` meth
   --> $DIR/len_without_is_empty.rs:49:1
    |
 LL | / impl HasIsEmpty {
-LL | |     pub fn len(self: &Self) -> isize {
+LL | |     pub fn len(&self) -> isize {
 LL | |         1
 LL | |     }
 ...  |
@@ -34,7 +34,7 @@ error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is
   --> $DIR/len_without_is_empty.rs:61:1
    |
 LL | / impl HasWrongIsEmpty {
-LL | |     pub fn len(self: &Self) -> isize {
+LL | |     pub fn len(&self) -> isize {
 LL | |         1
 LL | |     }
 ...  |
index a29b832eb6019af0dda1ffc160fe49ebfac08f32..d81676a3d9f4801d06f1884e1473c8730bd7597f 100644 (file)
@@ -7,12 +7,12 @@ pub struct One;
 struct Wither;
 
 trait TraitsToo {
-    fn len(self: &Self) -> isize;
+    fn len(&self) -> isize;
     // No error; `len` is private; see issue #1085.
 }
 
 impl TraitsToo for One {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         0
     }
 }
@@ -20,11 +20,11 @@ impl TraitsToo for One {
 pub struct HasIsEmpty;
 
 impl HasIsEmpty {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 
-    fn is_empty(self: &Self) -> bool {
+    fn is_empty(&self) -> bool {
         false
     }
 }
@@ -32,26 +32,26 @@ impl HasIsEmpty {
 pub struct HasWrongIsEmpty;
 
 impl HasWrongIsEmpty {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 
-    pub fn is_empty(self: &Self, x: u32) -> bool {
+    pub fn is_empty(&self, x: u32) -> bool {
         false
     }
 }
 
 pub trait WithIsEmpty {
-    fn len(self: &Self) -> isize;
-    fn is_empty(self: &Self) -> bool;
+    fn len(&self) -> isize;
+    fn is_empty(&self) -> bool;
 }
 
 impl WithIsEmpty for Wither {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         1
     }
 
-    fn is_empty(self: &Self) -> bool {
+    fn is_empty(&self) -> bool {
         false
     }
 }
index 8fd0093f4fdbbc13e31205003209de35fa9a21df..ecdba921a5d0fde97fa67b5a3a455d953d3cdd38 100644 (file)
@@ -7,12 +7,12 @@
 struct Wither;
 
 trait TraitsToo {
-    fn len(self: &Self) -> isize;
+    fn len(&self) -> isize;
     // No error; `len` is private; see issue #1085.
 }
 
 impl TraitsToo for One {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         0
     }
 }
@@ -20,11 +20,11 @@ fn len(self: &Self) -> isize {
 pub struct HasIsEmpty;
 
 impl HasIsEmpty {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 
-    fn is_empty(self: &Self) -> bool {
+    fn is_empty(&self) -> bool {
         false
     }
 }
@@ -32,26 +32,26 @@ fn is_empty(self: &Self) -> bool {
 pub struct HasWrongIsEmpty;
 
 impl HasWrongIsEmpty {
-    pub fn len(self: &Self) -> isize {
+    pub fn len(&self) -> isize {
         1
     }
 
-    pub fn is_empty(self: &Self, x: u32) -> bool {
+    pub fn is_empty(&self, x: u32) -> bool {
         false
     }
 }
 
 pub trait WithIsEmpty {
-    fn len(self: &Self) -> isize;
-    fn is_empty(self: &Self) -> bool;
+    fn len(&self) -> isize;
+    fn is_empty(&self) -> bool;
 }
 
 impl WithIsEmpty for Wither {
-    fn len(self: &Self) -> isize {
+    fn len(&self) -> isize {
         1
     }
 
-    fn is_empty(self: &Self) -> bool {
+    fn is_empty(&self) -> bool {
         false
     }
 }
index 27222cc0869c83cf50a37d5c66fb63ee2ff92f62..4f551690c4370ab3b7f2428237f3a0d5e3548180 100644 (file)
@@ -43,10 +43,6 @@ impl S {
         42
     }
 
-    async fn meth_fut(&self) -> i32 { 42 }
-
-    async fn empty_fut(&self)  {}
-
     // should be ignored
     fn not_fut(&self) -> i32 {
         42
@@ -64,4 +60,40 @@ impl S {
     }
 }
 
+// Tests related to lifetime capture
+
+async fn elided(_: &i32) -> i32 { 42 }
+
+// should be ignored
+fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
+    async { 42 }
+}
+
+async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 }
+
+// should be ignored
+#[allow(clippy::needless_lifetimes)]
+fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
+    async { 42 }
+}
+
+// should be ignored
+mod issue_5765 {
+    use std::future::Future;
+
+    struct A;
+    impl A {
+        fn f(&self) -> impl Future<Output = ()> {
+            async {}
+        }
+    }
+
+    fn test() {
+        let _future = {
+            let a = A;
+            a.f()
+        };
+    }
+}
+
 fn main() {}
index 6a0f1b26c88388e18dfe49375799e928b578b049..6ed60309947a81b27f5e2a2b7a559cdbbacf9b7e 100644 (file)
@@ -51,14 +51,6 @@ fn inh_fut() -> impl Future<Output = i32> {
         }
     }
 
-    fn meth_fut(&self) -> impl Future<Output = i32> {
-        async { 42 }
-    }
-
-    fn empty_fut(&self) -> impl Future<Output = ()> {
-        async {}
-    }
-
     // should be ignored
     fn not_fut(&self) -> i32 {
         42
@@ -76,4 +68,44 @@ async fn already_async(&self) -> impl Future<Output = i32> {
     }
 }
 
+// Tests related to lifetime capture
+
+fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
+    async { 42 }
+}
+
+// should be ignored
+fn elided_not_bound(_: &i32) -> impl Future<Output = i32> {
+    async { 42 }
+}
+
+fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
+    async { 42 }
+}
+
+// should be ignored
+#[allow(clippy::needless_lifetimes)]
+fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> {
+    async { 42 }
+}
+
+// should be ignored
+mod issue_5765 {
+    use std::future::Future;
+
+    struct A;
+    impl A {
+        fn f(&self) -> impl Future<Output = ()> {
+            async {}
+        }
+    }
+
+    fn test() {
+        let _future = {
+            let a = A;
+            a.f()
+        };
+    }
+}
+
 fn main() {}
index a1904c904d0f4930672ec80fc40887a33769cf2f..ccd828674276ba48cf9c6ba2a0f357edaecc5331 100644 (file)
@@ -65,34 +65,34 @@ LL |             let c = 21;
  ...
 
 error: this function can be simplified using the `async fn` syntax
-  --> $DIR/manual_async_fn.rs:54:5
+  --> $DIR/manual_async_fn.rs:73:1
    |
-LL |     fn meth_fut(&self) -> impl Future<Output = i32> {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: make the function `async` and return the output of the future directly
    |
-LL |     async fn meth_fut(&self) -> i32 {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | async fn elided(_: &i32) -> i32 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: move the body of the async block to the enclosing function
    |
-LL |     fn meth_fut(&self) -> impl Future<Output = i32> { 42 }
-   |                                                     ^^^^^^
+LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { 42 }
+   |                                                      ^^^^^^
 
 error: this function can be simplified using the `async fn` syntax
-  --> $DIR/manual_async_fn.rs:58:5
+  --> $DIR/manual_async_fn.rs:82:1
    |
-LL |     fn empty_fut(&self) -> impl Future<Output = ()> {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: make the function `async` and remove the return type
+help: make the function `async` and return the output of the future directly
    |
-LL |     async fn empty_fut(&self)  {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: move the body of the async block to the enclosing function
    |
-LL |     fn empty_fut(&self) -> impl Future<Output = ()> {}
-   |                                                     ^^
+LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { 42 }
+   |                                                                                    ^^^^^^
 
 error: aborting due to 6 previous errors
 
index 4171d80f48a3f51826fe8c59870e75ebdd2e488b..a5fdf7df613d325d6b5bf80ed09647806581adb0 100644 (file)
@@ -5,6 +5,20 @@
 #![allow(clippy::map_identity)]
 
 fn main() {
+    // mapping to Option on Iterator
+    fn option_id(x: i8) -> Option<i8> {
+        Some(x)
+    }
+    let option_id_ref: fn(i8) -> Option<i8> = option_id;
+    let option_id_closure = |x| Some(x);
+    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
+
+    // mapping to Iterator on Iterator
     let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
+
+    // mapping to Option on Option
     let _: Option<_> = (Some(Some(1))).and_then(|x| x);
 }
index 16a0fd090ad04533e0fc1fde345d8c11ea930b5d..abbc4e16e567951af8a952810e2a7bce9724d832 100644 (file)
@@ -5,6 +5,20 @@
 #![allow(clippy::map_identity)]
 
 fn main() {
+    // mapping to Option on Iterator
+    fn option_id(x: i8) -> Option<i8> {
+        Some(x)
+    }
+    let option_id_ref: fn(i8) -> Option<i8> = option_id;
+    let option_id_closure = |x| Some(x);
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+
+    // mapping to Iterator on Iterator
     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+
+    // mapping to Option on Option
     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
 }
index 00bc41c15e9b8d892ee6eb4ee30d3a5ad62a81eb..b6479cd69eac4f49f6a0abf08c55dc3ba7f0224d 100644 (file)
@@ -1,16 +1,40 @@
-error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)`
-  --> $DIR/map_flatten.rs:8:21
+error: called `map(..).flatten()` on an `Iterator`
+  --> $DIR/map_flatten.rs:14:46
    |
-LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)`
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
+   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)`
    |
    = note: `-D clippy::map-flatten` implied by `-D warnings`
 
-error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)`
-  --> $DIR/map_flatten.rs:9:24
+error: called `map(..).flatten()` on an `Iterator`
+  --> $DIR/map_flatten.rs:15:46
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
+   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)`
+
+error: called `map(..).flatten()` on an `Iterator`
+  --> $DIR/map_flatten.rs:16:46
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
+   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)`
+
+error: called `map(..).flatten()` on an `Iterator`
+  --> $DIR/map_flatten.rs:17:46
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
+   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))`
+
+error: called `map(..).flatten()` on an `Iterator`
+  --> $DIR/map_flatten.rs:20:46
+   |
+LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
+   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)`
+
+error: called `map(..).flatten()` on an `Option`
+  --> $DIR/map_flatten.rs:23:39
    |
 LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)`
+   |                                       ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)`
 
-error: aborting due to 2 previous errors
+error: aborting due to 6 previous errors
 
index 8307d4b3019f7cf077fc673c34bc7481da55c3ae..f7ed72a11cf684b64f8d584cbaa990ae4aa0fa39 100644 (file)
@@ -6,6 +6,18 @@
 
 const LARGE: usize = 3;
 
+struct NotOrd(u64);
+
+impl NotOrd {
+    fn min(self, x: u64) -> NotOrd {
+        NotOrd(x)
+    }
+
+    fn max(self, x: u64) -> NotOrd {
+        NotOrd(x)
+    }
+}
+
 fn main() {
     let x;
     x = 2usize;
@@ -30,4 +42,24 @@ fn main() {
     max(min(s, "Apple"), "Zoo");
 
     max("Apple", min(s, "Zoo")); // ok
+
+    let f = 3f32;
+    x.min(1).max(3);
+    x.max(3).min(1);
+    f.max(3f32).min(1f32);
+
+    x.max(1).min(3); // ok
+    x.min(3).max(1); // ok
+    f.min(3f32).max(1f32); // ok
+
+    max(x.min(1), 3);
+    min(x.max(1), 3); // ok
+
+    s.max("Zoo").min("Apple");
+    s.min("Apple").max("Zoo");
+
+    s.min("Zoo").max("Apple"); // ok
+
+    let not_ord = NotOrd(1);
+    not_ord.min(1).max(3); // ok
 }
index b552c137f7c7c8513799936ce51fe45c7f8c14b3..9f8e26fa406f0e59c7458ec5b54ca234386abf6d 100644 (file)
@@ -1,5 +1,5 @@
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:12:5
+  --> $DIR/min_max.rs:24:5
    |
 LL |     min(1, max(3, x));
    |     ^^^^^^^^^^^^^^^^^
@@ -7,40 +7,76 @@ LL |     min(1, max(3, x));
    = note: `-D clippy::min-max` implied by `-D warnings`
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:13:5
+  --> $DIR/min_max.rs:25:5
    |
 LL |     min(max(3, x), 1);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:14:5
+  --> $DIR/min_max.rs:26:5
    |
 LL |     max(min(x, 1), 3);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:15:5
+  --> $DIR/min_max.rs:27:5
    |
 LL |     max(3, min(x, 1));
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:17:5
+  --> $DIR/min_max.rs:29:5
    |
 LL |     my_max(3, my_min(x, 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:29:5
+  --> $DIR/min_max.rs:41:5
    |
 LL |     min("Apple", max("Zoo", s));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:30:5
+  --> $DIR/min_max.rs:42:5
    |
 LL |     max(min(s, "Apple"), "Zoo");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: this `min`/`max` combination leads to constant result
+  --> $DIR/min_max.rs:47:5
+   |
+LL |     x.min(1).max(3);
+   |     ^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+  --> $DIR/min_max.rs:48:5
+   |
+LL |     x.max(3).min(1);
+   |     ^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+  --> $DIR/min_max.rs:49:5
+   |
+LL |     f.max(3f32).min(1f32);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+  --> $DIR/min_max.rs:55:5
+   |
+LL |     max(x.min(1), 3);
+   |     ^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+  --> $DIR/min_max.rs:58:5
+   |
+LL |     s.max("Zoo").min("Apple");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this `min`/`max` combination leads to constant result
+  --> $DIR/min_max.rs:59:5
+   |
+LL |     s.min("Apple").max("Zoo");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed
new file mode 100644 (file)
index 0000000..9da21eb
--- /dev/null
@@ -0,0 +1,69 @@
+// run-rustfix
+
+#![warn(clippy::needless_arbitrary_self_type)]
+#![allow(unused_mut, clippy::needless_lifetimes)]
+
+pub enum ValType {
+    A,
+    B,
+}
+
+impl ValType {
+    pub fn bad(self) {
+        unimplemented!();
+    }
+
+    pub fn good(self) {
+        unimplemented!();
+    }
+
+    pub fn mut_bad(mut self) {
+        unimplemented!();
+    }
+
+    pub fn mut_good(mut self) {
+        unimplemented!();
+    }
+
+    pub fn ref_bad(&self) {
+        unimplemented!();
+    }
+
+    pub fn ref_good(&self) {
+        unimplemented!();
+    }
+
+    pub fn ref_bad_with_lifetime<'a>(&'a self) {
+        unimplemented!();
+    }
+
+    pub fn ref_good_with_lifetime<'a>(&'a self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_bad(&mut self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_good(&mut self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_mut_good(mut self: &mut Self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
+        unimplemented!();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs
new file mode 100644 (file)
index 0000000..17aeaaf
--- /dev/null
@@ -0,0 +1,69 @@
+// run-rustfix
+
+#![warn(clippy::needless_arbitrary_self_type)]
+#![allow(unused_mut, clippy::needless_lifetimes)]
+
+pub enum ValType {
+    A,
+    B,
+}
+
+impl ValType {
+    pub fn bad(self: Self) {
+        unimplemented!();
+    }
+
+    pub fn good(self) {
+        unimplemented!();
+    }
+
+    pub fn mut_bad(mut self: Self) {
+        unimplemented!();
+    }
+
+    pub fn mut_good(mut self) {
+        unimplemented!();
+    }
+
+    pub fn ref_bad(self: &Self) {
+        unimplemented!();
+    }
+
+    pub fn ref_good(&self) {
+        unimplemented!();
+    }
+
+    pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
+        unimplemented!();
+    }
+
+    pub fn ref_good_with_lifetime<'a>(&'a self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_bad(self: &mut Self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_good(&mut self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_mut_good(mut self: &mut Self) {
+        unimplemented!();
+    }
+
+    pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) {
+        unimplemented!();
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr
new file mode 100644 (file)
index 0000000..f4c645d
--- /dev/null
@@ -0,0 +1,40 @@
+error: the type of the `self` parameter does not need to be arbitrary
+  --> $DIR/needless_arbitrary_self_type.rs:12:16
+   |
+LL |     pub fn bad(self: Self) {
+   |                ^^^^^^^^^^ help: consider to change this parameter to: `self`
+   |
+   = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
+
+error: the type of the `self` parameter does not need to be arbitrary
+  --> $DIR/needless_arbitrary_self_type.rs:20:20
+   |
+LL |     pub fn mut_bad(mut self: Self) {
+   |                    ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+  --> $DIR/needless_arbitrary_self_type.rs:28:20
+   |
+LL |     pub fn ref_bad(self: &Self) {
+   |                    ^^^^^^^^^^^ help: consider to change this parameter to: `&self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+  --> $DIR/needless_arbitrary_self_type.rs:36:38
+   |
+LL |     pub fn ref_bad_with_lifetime<'a>(self: &'a Self) {
+   |                                      ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+  --> $DIR/needless_arbitrary_self_type.rs:44:24
+   |
+LL |     pub fn mut_ref_bad(self: &mut Self) {
+   |                        ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self`
+
+error: the type of the `self` parameter does not need to be arbitrary
+  --> $DIR/needless_arbitrary_self_type.rs:52:42
+   |
+LL |     pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) {
+   |                                          ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self`
+
+error: aborting due to 6 previous errors
+
index be37dc16b9a3effcc6aec5b4fd92f1de7a2412c5..7f2fcf02f6b5bfb6140c7af508af3ee4e8f62765 100644 (file)
@@ -5,11 +5,11 @@
 use std::collections::{BTreeSet, HashMap, HashSet};
 
 #[warn(clippy::needless_collect)]
-#[allow(unused_variables, clippy::iter_cloned_collect)]
+#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
 fn main() {
     let sample = [1; 5];
     let len = sample.iter().count();
-    if sample.get(0).is_none() {
+    if sample.iter().next().is_none() {
         // Empty
     }
     sample.iter().cloned().any(|x| x == 1);
index 7ee603afeb0778e74bc67e24d25919660c89ee87..788a9eb3264ee6c6cf972eeccc45d2c45b7542e2 100644 (file)
@@ -5,7 +5,7 @@
 use std::collections::{BTreeSet, HashMap, HashSet};
 
 #[warn(clippy::needless_collect)]
-#[allow(unused_variables, clippy::iter_cloned_collect)]
+#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
 fn main() {
     let sample = [1; 5];
     let len = sample.iter().collect::<Vec<_>>().len();
index 9113aad90dd7cc120e89748f5df590f74878f174..2a9539d59759d6f73798582868f6f6da667bf6b7 100644 (file)
@@ -7,10 +7,10 @@ LL |     let len = sample.iter().collect::<Vec<_>>().len();
    = note: `-D clippy::needless-collect` implied by `-D warnings`
 
 error: avoid using `collect()` when not needed
-  --> $DIR/needless_collect.rs:12:15
+  --> $DIR/needless_collect.rs:12:22
    |
 LL |     if sample.iter().collect::<Vec<_>>().is_empty() {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()`
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
 
 error: avoid using `collect()` when not needed
   --> $DIR/needless_collect.rs:15:28
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
new file mode 100644 (file)
index 0000000..4cf03e8
--- /dev/null
@@ -0,0 +1,19 @@
+use std::collections::{HashMap, VecDeque};
+
+fn main() {
+    let sample = [1; 5];
+    let indirect_iter = sample.iter().collect::<Vec<_>>();
+    indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
+    let indirect_len = sample.iter().collect::<VecDeque<_>>();
+    indirect_len.len();
+    let indirect_empty = sample.iter().collect::<VecDeque<_>>();
+    indirect_empty.is_empty();
+    let indirect_contains = sample.iter().collect::<VecDeque<_>>();
+    indirect_contains.contains(&&5);
+    let indirect_negative = sample.iter().collect::<Vec<_>>();
+    indirect_negative.len();
+    indirect_negative
+        .into_iter()
+        .map(|x| (*x, *x + 1))
+        .collect::<HashMap<_, _>>();
+}
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
new file mode 100644 (file)
index 0000000..0c1e61d
--- /dev/null
@@ -0,0 +1,55 @@
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:5:5
+   |
+LL | /     let indirect_iter = sample.iter().collect::<Vec<_>>();
+LL | |     indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
+   | |____^
+   |
+   = note: `-D clippy::needless-collect` implied by `-D warnings`
+help: Use the original Iterator instead of collecting it and then producing a new one
+   |
+LL |     
+LL |     sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:7:5
+   |
+LL | /     let indirect_len = sample.iter().collect::<VecDeque<_>>();
+LL | |     indirect_len.len();
+   | |____^
+   |
+help: Take the original Iterator's count instead of collecting it and finding the length
+   |
+LL |     
+LL |     sample.iter().count();
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:9:5
+   |
+LL | /     let indirect_empty = sample.iter().collect::<VecDeque<_>>();
+LL | |     indirect_empty.is_empty();
+   | |____^
+   |
+help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty
+   |
+LL |     
+LL |     sample.iter().next().is_none();
+   |
+
+error: avoid using `collect()` when not needed
+  --> $DIR/needless_collect_indirect.rs:11:5
+   |
+LL | /     let indirect_contains = sample.iter().collect::<VecDeque<_>>();
+LL | |     indirect_contains.contains(&&5);
+   | |____^
+   |
+help: Check if the original Iterator contains an element instead of collecting then checking
+   |
+LL |     
+LL |     sample.iter().any(|x| x == &&5);
+   |
+
+error: aborting due to 4 previous errors
+
index 8c5d548222e0dc789aeb3bc980bb980cc21058d4..c78560007217df78dd4f78a65810cb4956cabfef 100644 (file)
@@ -1,4 +1,4 @@
-error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
   --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21
    |
 LL |     let _not_less = !(a_value < another_value);
@@ -6,19 +6,19 @@ LL |     let _not_less = !(a_value < another_value);
    |
    = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings`
 
-error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
   --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30
    |
 LL |     let _not_less_or_equal = !(a_value <= another_value);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
   --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24
    |
 LL |     let _not_greater = !(a_value > another_value);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
+error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable
   --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33
    |
 LL |     let _not_greater_or_equal = !(a_value >= another_value);
index f08bbd6a12c597e4b0912db1a23cbe0bd8bf9bd4..ad677f6d6fb9bf46c897fb122dc798883cb21ac4 100644 (file)
@@ -1,4 +1,4 @@
-error: Negation by multiplying with `-1`
+error: negation by multiplying with `-1`
   --> $DIR/neg_multiply.rs:27:5
    |
 LL |     x * -1;
@@ -6,7 +6,7 @@ LL |     x * -1;
    |
    = note: `-D clippy::neg-multiply` implied by `-D warnings`
 
-error: Negation by multiplying with `-1`
+error: negation by multiplying with `-1`
   --> $DIR/neg_multiply.rs:29:5
    |
 LL |     -1 * x;
index 9a0da404cb6dbbbb65aa13f54fbd14587ccf149d..96d1c54946c0b40a7cee773d20f4bc6dfe0408ab 100644 (file)
@@ -22,9 +22,9 @@ struct HasOption {
 }
 
 impl HasOption {
-    fn do_option_nothing(self: &Self, value: usize) {}
+    fn do_option_nothing(&self, value: usize) {}
 
-    fn do_option_plus_one(self: &Self, value: usize) -> usize {
+    fn do_option_plus_one(&self, value: usize) -> usize {
         value + 1
     }
 }
index 58041b62df35a6b564e1850be6782d8d50ce2808..931ffc18665938cd3af7a9b592ad71f67a4df96d 100644 (file)
@@ -22,9 +22,9 @@ struct HasOption {
 }
 
 impl HasOption {
-    fn do_option_nothing(self: &Self, value: usize) {}
+    fn do_option_nothing(&self, value: usize) {}
 
-    fn do_option_plus_one(self: &Self, value: usize) -> usize {
+    fn do_option_plus_one(&self, value: usize) -> usize {
         value + 1
     }
 }
index 2045ffdb5f09d38996c36d57ffc9c56967859d96..67faa8bd4a0aa1f8b2c5eb2a8f86f43df6fd2798 100644 (file)
@@ -116,4 +116,12 @@ fn f() -> Option<()> {
     Some(())
 }
 
+// Issue 5886 - const fn (with no arguments)
+pub fn skip_const_fn_with_no_args() {
+    const fn foo() -> Option<i32> {
+        Some(42)
+    }
+    let _ = None.or(foo());
+}
+
 fn main() {}
index 522f31b72d01f4d94a590dd179a86c287d1c6db4..9867e2eedcff5253eaa86e20bbb24fabd9a046bd 100644 (file)
@@ -116,4 +116,12 @@ fn f() -> Option<()> {
     Some(())
 }
 
+// Issue 5886 - const fn (with no arguments)
+pub fn skip_const_fn_with_no_args() {
+    const fn foo() -> Option<i32> {
+        Some(42)
+    }
+    let _ = None.or(foo());
+}
+
 fn main() {}
index ad66135d326bdcf51538f817b48a4455a396dc7c..19e843c2c0a50d5b468a9a2a4447db3bcac7f6e2 100644 (file)
@@ -1,4 +1,4 @@
-error: You are trying to use classic C overflow conditions that will fail in Rust.
+error: you are trying to use classic C overflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:8:8
    |
 LL |     if a + b < a {}
@@ -6,43 +6,43 @@ LL |     if a + b < a {}
    |
    = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
 
-error: You are trying to use classic C overflow conditions that will fail in Rust.
+error: you are trying to use classic C overflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:9:8
    |
 LL |     if a > a + b {}
    |        ^^^^^^^^^
 
-error: You are trying to use classic C overflow conditions that will fail in Rust.
+error: you are trying to use classic C overflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:10:8
    |
 LL |     if a + b < b {}
    |        ^^^^^^^^^
 
-error: You are trying to use classic C overflow conditions that will fail in Rust.
+error: you are trying to use classic C overflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:11:8
    |
 LL |     if b > a + b {}
    |        ^^^^^^^^^
 
-error: You are trying to use classic C underflow conditions that will fail in Rust.
+error: you are trying to use classic C underflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:12:8
    |
 LL |     if a - b > b {}
    |        ^^^^^^^^^
 
-error: You are trying to use classic C underflow conditions that will fail in Rust.
+error: you are trying to use classic C underflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:13:8
    |
 LL |     if b < a - b {}
    |        ^^^^^^^^^
 
-error: You are trying to use classic C underflow conditions that will fail in Rust.
+error: you are trying to use classic C underflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:14:8
    |
 LL |     if a - b > a {}
    |        ^^^^^^^^^
 
-error: You are trying to use classic C underflow conditions that will fail in Rust.
+error: you are trying to use classic C underflow conditions that will fail in Rust
   --> $DIR/overflow_check_conditional.rs:15:8
    |
 LL |     if a < a - b {}
index 09b18d71baf93bcc77f8edc51b73fadc54520a8d..bb8dce2bbba4b3f4d560b34bd76577cdd5a6b17e 100644 (file)
@@ -1,4 +1,4 @@
-error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
+error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
   --> $DIR/path_buf_push_overwrite.rs:7:12
    |
 LL |     x.push("/bar");
index d53c1edecac018f5d605b0653cc8f9c71603dd8e..dcb5061371f80e94f416288eb13e8fcd7c13d222 100644 (file)
@@ -1,4 +1,4 @@
-error: It is more idiomatic to use `v1.iter().enumerate()`
+error: it is more idiomatic to use `v1.iter().enumerate()`
   --> $DIR/range.rs:5:14
    |
 LL |     let _x = v1.iter().zip(0..v1.len());
index 3c3d2eacd8d9c51c64a1deaf1e4decb4b18dcc0e..649831f9c069af3d30a60cbf1d06a14f3b83ab9b 100644 (file)
@@ -1,4 +1,4 @@
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:8:17
    |
 LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
@@ -6,91 +6,91 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin
    |
    = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:12:21
    |
 LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
    |                    -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:14:32
    |
 LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                               -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:14:47
    |
 LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                                              -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:16:17
    |
 LL | const VAR_SIX: &'static u8 = &5;
    |                -^^^^^^^--- help: consider removing `'static`: `&u8`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:18:20
    |
 LL | const VAR_HEIGHT: &'static Foo = &Foo {};
    |                   -^^^^^^^---- help: consider removing `'static`: `&Foo`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:20:19
    |
 LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
    |                  -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:22:19
    |
 LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
    |                  -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:24:19
    |
 LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
    |                  -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:26:25
    |
 LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
    |                        -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:30:29
    |
 LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
    |                            -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:32:25
    |
 LL | static STATIC_VAR_SIX: &'static u8 = &5;
    |                        -^^^^^^^--- help: consider removing `'static`: `&u8`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:34:28
    |
 LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
    |                           -^^^^^^^---- help: consider removing `'static`: `&Foo`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:36:27
    |
 LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
    |                          -^^^^^^^----- help: consider removing `'static`: `&[u8]`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:38:27
    |
 LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
    |                          -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes.rs:40:27
    |
 LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
index afc853dcfce8357fd01e4c66f85422c5f473f5a9..cc7e55a757a32895778366d2974b2cef05075183 100644 (file)
@@ -1,4 +1,4 @@
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:3:18
    |
 LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
@@ -6,55 +6,55 @@ LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]];
    |
    = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:3:30
    |
 LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
    |                             -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:5:29
    |
 LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
    |                            -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
 
-error: Constants have by default a `'static` lifetime
+error: constants have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:5:39
    |
 LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
    |                                      -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:7:40
    |
 LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                                       -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:7:55
    |
 LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
    |                                                      -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:9:26
    |
 LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
    |                         -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:9:38
    |
 LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static
    |                                     -^^^^^^^---- help: consider removing `'static`: `&str`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:11:37
    |
 LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
    |                                    -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]`
 
-error: Statics have by default a `'static` lifetime
+error: statics have by default a `'static` lifetime
   --> $DIR/redundant_static_lifetimes_multiple.rs:11:47
    |
 LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])];
index a399ff52fb8b607dc9500afeb05cf0c7dd1f8be7..88046762483567d267329223c4c81aa6fc87b8f8 100644 (file)
@@ -1,4 +1,4 @@
-error: Usage of deprecated attribute
+error: usage of deprecated attribute
   --> $DIR/renamed_builtin_attr.rs:3:11
    |
 LL | #[clippy::cyclomatic_complexity = "1"]
index 1d0a3ecd0ff8df69cee72d525dd6e60646d910b3..631042c616bc0cd261579f32aa06c43e9526b151 100644 (file)
@@ -18,9 +18,9 @@ struct HasResult {
 }
 
 impl HasResult {
-    fn do_result_nothing(self: &Self, value: usize) {}
+    fn do_result_nothing(&self, value: usize) {}
 
-    fn do_result_plus_one(self: &Self, value: usize) -> usize {
+    fn do_result_plus_one(&self, value: usize) -> usize {
         value + 1
     }
 }
index 2fe18f923f08fa1143195d968f01d8cc2bf5590d..679eb2326268c7a62fba3a04b228d9bbf4a827ee 100644 (file)
@@ -18,9 +18,9 @@ struct HasResult {
 }
 
 impl HasResult {
-    fn do_result_nothing(self: &Self, value: usize) {}
+    fn do_result_nothing(&self, value: usize) {}
 
-    fn do_result_plus_one(self: &Self, value: usize) -> usize {
+    fn do_result_plus_one(&self, value: usize) -> usize {
         value + 1
     }
 }
diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs
new file mode 100644 (file)
index 0000000..ff1088f
--- /dev/null
@@ -0,0 +1,89 @@
+#![warn(clippy::same_item_push)]
+
+fn mutate_increment(x: &mut u8) -> u8 {
+    *x += 1;
+    *x
+}
+
+fn increment(x: u8) -> u8 {
+    x + 1
+}
+
+fn main() {
+    // Test for basic case
+    let mut spaces = Vec::with_capacity(10);
+    for _ in 0..10 {
+        spaces.push(vec![b' ']);
+    }
+
+    let mut vec2: Vec<u8> = Vec::new();
+    let item = 2;
+    for _ in 5..=20 {
+        vec2.push(item);
+    }
+
+    let mut vec3: Vec<u8> = Vec::new();
+    for _ in 0..15 {
+        let item = 2;
+        vec3.push(item);
+    }
+
+    let mut vec4: Vec<u8> = Vec::new();
+    for _ in 0..15 {
+        vec4.push(13);
+    }
+
+    // Suggestion should not be given as pushed variable can mutate
+    let mut vec5: Vec<u8> = Vec::new();
+    let mut item: u8 = 2;
+    for _ in 0..30 {
+        vec5.push(mutate_increment(&mut item));
+    }
+
+    let mut vec6: Vec<u8> = Vec::new();
+    let mut item: u8 = 2;
+    let mut item2 = &mut mutate_increment(&mut item);
+    for _ in 0..30 {
+        vec6.push(mutate_increment(item2));
+    }
+
+    let mut vec7: Vec<usize> = Vec::new();
+    for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
+        vec7.push(a);
+    }
+
+    let mut vec8: Vec<u8> = Vec::new();
+    for i in 0..30 {
+        vec8.push(increment(i));
+    }
+
+    let mut vec9: Vec<u8> = Vec::new();
+    for i in 0..30 {
+        vec9.push(i + i * i);
+    }
+
+    // Suggestion should not be given as there are multiple pushes that are not the same
+    let mut vec10: Vec<u8> = Vec::new();
+    let item: u8 = 2;
+    for _ in 0..30 {
+        vec10.push(item);
+        vec10.push(item * 2);
+    }
+
+    // Suggestion should not be given as Vec is not involved
+    for _ in 0..5 {
+        println!("Same Item Push");
+    }
+
+    struct A {
+        kind: u32,
+    }
+    let mut vec_a: Vec<A> = Vec::new();
+    for i in 0..30 {
+        vec_a.push(A { kind: i });
+    }
+    let mut vec12: Vec<u8> = Vec::new();
+    for a in vec_a {
+        vec12.push(2u8.pow(a.kind));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/same_item_push.stderr b/src/tools/clippy/tests/ui/same_item_push.stderr
new file mode 100644 (file)
index 0000000..ddc5d48
--- /dev/null
@@ -0,0 +1,35 @@
+error: it looks like the same item is being pushed into this Vec
+  --> $DIR/same_item_push.rs:16:9
+   |
+LL |         spaces.push(vec![b' ']);
+   |         ^^^^^^
+   |
+   = note: `-D clippy::same-item-push` implied by `-D warnings`
+   = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' '])
+
+error: it looks like the same item is being pushed into this Vec
+  --> $DIR/same_item_push.rs:22:9
+   |
+LL |         vec2.push(item);
+   |         ^^^^
+   |
+   = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item)
+
+error: it looks like the same item is being pushed into this Vec
+  --> $DIR/same_item_push.rs:28:9
+   |
+LL |         vec3.push(item);
+   |         ^^^^
+   |
+   = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item)
+
+error: it looks like the same item is being pushed into this Vec
+  --> $DIR/same_item_push.rs:33:9
+   |
+LL |         vec4.push(13);
+   |         ^^^^
+   |
+   = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13)
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.fixed b/src/tools/clippy/tests/ui/stable_sort_primitive.fixed
new file mode 100644 (file)
index 0000000..8f8f566
--- /dev/null
@@ -0,0 +1,32 @@
+// run-rustfix
+#![warn(clippy::stable_sort_primitive)]
+
+fn main() {
+    // positive examples
+    let mut vec = vec![1, 3, 2];
+    vec.sort_unstable();
+    let mut vec = vec![false, false, true];
+    vec.sort_unstable();
+    let mut vec = vec!['a', 'A', 'c'];
+    vec.sort_unstable();
+    let mut vec = vec!["ab", "cd", "ab", "bc"];
+    vec.sort_unstable();
+    let mut vec = vec![(2, 1), (1, 2), (2, 5)];
+    vec.sort_unstable();
+    let mut vec = vec![[2, 1], [1, 2], [2, 5]];
+    vec.sort_unstable();
+    let mut arr = [1, 3, 2];
+    arr.sort_unstable();
+    // Negative examples: behavior changes if made unstable
+    let mut vec = vec![1, 3, 2];
+    vec.sort_by_key(|i| i / 2);
+    vec.sort_by(|a, b| (a + b).cmp(&b));
+    // negative examples - Not of a primitive type
+    let mut vec_of_complex = vec![String::from("hello"), String::from("world!")];
+    vec_of_complex.sort();
+    vec_of_complex.sort_by_key(String::len);
+    let mut vec = vec![(String::from("hello"), String::from("world"))];
+    vec.sort();
+    let mut vec = vec![[String::from("hello"), String::from("world")]];
+    vec.sort();
+}
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.rs b/src/tools/clippy/tests/ui/stable_sort_primitive.rs
new file mode 100644 (file)
index 0000000..f9bd977
--- /dev/null
@@ -0,0 +1,32 @@
+// run-rustfix
+#![warn(clippy::stable_sort_primitive)]
+
+fn main() {
+    // positive examples
+    let mut vec = vec![1, 3, 2];
+    vec.sort();
+    let mut vec = vec![false, false, true];
+    vec.sort();
+    let mut vec = vec!['a', 'A', 'c'];
+    vec.sort();
+    let mut vec = vec!["ab", "cd", "ab", "bc"];
+    vec.sort();
+    let mut vec = vec![(2, 1), (1, 2), (2, 5)];
+    vec.sort();
+    let mut vec = vec![[2, 1], [1, 2], [2, 5]];
+    vec.sort();
+    let mut arr = [1, 3, 2];
+    arr.sort();
+    // Negative examples: behavior changes if made unstable
+    let mut vec = vec![1, 3, 2];
+    vec.sort_by_key(|i| i / 2);
+    vec.sort_by(|a, b| (a + b).cmp(&b));
+    // negative examples - Not of a primitive type
+    let mut vec_of_complex = vec![String::from("hello"), String::from("world!")];
+    vec_of_complex.sort();
+    vec_of_complex.sort_by_key(String::len);
+    let mut vec = vec![(String::from("hello"), String::from("world"))];
+    vec.sort();
+    let mut vec = vec![[String::from("hello"), String::from("world")]];
+    vec.sort();
+}
diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.stderr b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr
new file mode 100644 (file)
index 0000000..b0b729e
--- /dev/null
@@ -0,0 +1,46 @@
+error: Use sort_unstable instead of sort
+  --> $DIR/stable_sort_primitive.rs:7:5
+   |
+LL |     vec.sort();
+   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+   |
+   = note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
+
+error: Use sort_unstable instead of sort
+  --> $DIR/stable_sort_primitive.rs:9:5
+   |
+LL |     vec.sort();
+   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+  --> $DIR/stable_sort_primitive.rs:11:5
+   |
+LL |     vec.sort();
+   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+  --> $DIR/stable_sort_primitive.rs:13:5
+   |
+LL |     vec.sort();
+   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+  --> $DIR/stable_sort_primitive.rs:15:5
+   |
+LL |     vec.sort();
+   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+  --> $DIR/stable_sort_primitive.rs:17:5
+   |
+LL |     vec.sort();
+   |     ^^^^^^^^^^ help: try: `vec.sort_unstable()`
+
+error: Use sort_unstable instead of sort
+  --> $DIR/stable_sort_primitive.rs:19:5
+   |
+LL |     arr.sort();
+   |     ^^^^^^^^^^ help: try: `arr.sort_unstable()`
+
+error: aborting due to 7 previous errors
+
index 1f5b981188706fd579b77f761c5563a79bdaa7f6..60c2f3ec9b652159db0b19696b1139ca9a8bff4c 100644 (file)
@@ -88,3 +88,33 @@ fn main() {}
 fn do_nothing(x: u32) -> u32 {
     x
 }
+
+struct MultipleBinops(u32);
+
+impl Add for MultipleBinops {
+    type Output = MultipleBinops;
+
+    // OK: multiple Binops in `add` impl
+    fn add(self, other: Self) -> Self::Output {
+        let mut result = self.0 + other.0;
+        if result >= u32::max_value() {
+            result -= u32::max_value();
+        }
+        MultipleBinops(result)
+    }
+}
+
+impl Mul for MultipleBinops {
+    type Output = MultipleBinops;
+
+    // OK: multiple Binops in `mul` impl
+    fn mul(self, other: Self) -> Self::Output {
+        let mut result: u32 = 0;
+        let size = std::cmp::max(self.0, other.0) as usize;
+        let mut v = vec![0; size + 1];
+        for i in 0..size + 1 {
+            result *= i as u32;
+        }
+        MultipleBinops(result)
+    }
+}
index 7e42d72c30b2cefd6503f6315ac316e0e372053f..23d47e3f1ff085b647451daa653918cf31093863 100644 (file)
@@ -1,4 +1,4 @@
-error: Suspicious use of binary operator in `Add` impl
+error: suspicious use of binary operator in `Add` impl
   --> $DIR/suspicious_arithmetic_impl.rs:11:20
    |
 LL |         Foo(self.0 - other.0)
@@ -6,7 +6,7 @@ LL |         Foo(self.0 - other.0)
    |
    = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings`
 
-error: Suspicious use of binary operator in `AddAssign` impl
+error: suspicious use of binary operator in `AddAssign` impl
   --> $DIR/suspicious_arithmetic_impl.rs:17:23
    |
 LL |         *self = *self - other;
@@ -14,7 +14,7 @@ LL |         *self = *self - other;
    |
    = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default
 
-error: Suspicious use of binary operator in `MulAssign` impl
+error: suspicious use of binary operator in `MulAssign` impl
   --> $DIR/suspicious_arithmetic_impl.rs:30:16
    |
 LL |         self.0 /= other.0;
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
new file mode 100644 (file)
index 0000000..cb2b005
--- /dev/null
@@ -0,0 +1,31 @@
+#![deny(clippy::trait_duplication_in_bounds)]
+
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+where
+    T: Clone,
+    T: Default,
+{
+    unimplemented!();
+}
+
+fn good_bar<T: Clone + Default>(arg: T) {
+    unimplemented!();
+}
+
+fn good_foo<T>(arg: T)
+where
+    T: Clone + Default,
+{
+    unimplemented!();
+}
+
+fn good_foobar<T: Default>(arg: T)
+where
+    T: Clone,
+{
+    unimplemented!();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
new file mode 100644 (file)
index 0000000..027e1c7
--- /dev/null
@@ -0,0 +1,23 @@
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:5:15
+   |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+   |               ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/trait_duplication_in_bounds.rs:1:9
+   |
+LL | #![deny(clippy::trait_duplication_in_bounds)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:5:23
+   |
+LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
+   |                       ^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: aborting due to 2 previous errors
+
index 98288dde6d845acbad6d27df59e2a1732ba3c5a8..b6f1e83181ccb4cb2bce5d89d5ec24df478fcdc5 100644 (file)
@@ -9,60 +9,48 @@
 
 use std::mem::{size_of, transmute};
 
-// rustc_typeck::check::cast contains documentation about when a cast `e as U` is 
+// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
 // valid, which we quote from below.
 fn main() {
     // We should see an error message for each transmute, and no error messages for
     // the casts, since the casts are the recommended fixes.
 
     // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
-    let _ptr_i32_transmute = unsafe {
-        usize::MAX as *const i32
-    };
+    let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 };
     let ptr_i32 = usize::MAX as *const i32;
 
     // e has type *T, U is *U_0, and either U_0: Sized ...
-    let _ptr_i8_transmute = unsafe {
-        ptr_i32 as *const i8
-    };
+    let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 };
     let _ptr_i8 = ptr_i32 as *const i8;
 
-    let slice_ptr = &[0,1,2,3] as *const [i32];
+    let slice_ptr = &[0, 1, 2, 3] as *const [i32];
 
     // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
-    let _ptr_to_unsized_transmute = unsafe {
-        slice_ptr as *const [u16]
-    };
+    let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u16] };
     let _ptr_to_unsized = slice_ptr as *const [u16];
     // TODO: We could try testing vtable casts here too, but maybe
     // we should wait until std::raw::TraitObject is stabilized?
 
     // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
-    let _usize_from_int_ptr_transmute = unsafe {
-        ptr_i32 as usize
-    };
+    let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize };
     let _usize_from_int_ptr = ptr_i32 as usize;
 
-    let array_ref: &[i32; 4] = &[1,2,3,4];
+    let array_ref: &[i32; 4] = &[1, 2, 3, 4];
 
     // e has type &[T; n] and U is *const T; array-ptr-cast
-    let _array_ptr_transmute = unsafe {
-        array_ref as *const [i32; 4]
-    };
+    let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] };
     let _array_ptr = array_ref as *const [i32; 4];
 
-    fn foo(_: usize) -> u8 { 42 }
+    fn foo(_: usize) -> u8 {
+        42
+    }
 
     // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
-    let _usize_ptr_transmute = unsafe {
-        foo as *const usize
-    };
+    let _usize_ptr_transmute = unsafe { foo as *const usize };
     let _usize_ptr_transmute = foo as *const usize;
 
     // e is a function pointer type and U is an integer; fptr-addr-cast
-    let _usize_from_fn_ptr_transmute = unsafe {
-        foo as usize
-    };
+    let _usize_from_fn_ptr_transmute = unsafe { foo as usize };
     let _usize_from_fn_ptr = foo as *const usize;
 }
 
index fd5055c08f63379f2bdf86da911a7922a166e605..0205d1ece60d58c3e3fab1c55de5638b8f8011e6 100644 (file)
@@ -9,60 +9,48 @@
 
 use std::mem::{size_of, transmute};
 
-// rustc_typeck::check::cast contains documentation about when a cast `e as U` is 
+// rustc_typeck::check::cast contains documentation about when a cast `e as U` is
 // valid, which we quote from below.
 fn main() {
     // We should see an error message for each transmute, and no error messages for
     // the casts, since the casts are the recommended fixes.
 
     // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
-    let _ptr_i32_transmute = unsafe {
-        transmute::<usize, *const i32>(usize::MAX)
-    };
+    let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
     let ptr_i32 = usize::MAX as *const i32;
 
     // e has type *T, U is *U_0, and either U_0: Sized ...
-    let _ptr_i8_transmute = unsafe {
-        transmute::<*const i32, *const i8>(ptr_i32)
-    };
+    let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
     let _ptr_i8 = ptr_i32 as *const i8;
 
-    let slice_ptr = &[0,1,2,3] as *const [i32];
+    let slice_ptr = &[0, 1, 2, 3] as *const [i32];
 
     // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
-    let _ptr_to_unsized_transmute = unsafe {
-        transmute::<*const [i32], *const [u16]>(slice_ptr)
-    };
+    let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) };
     let _ptr_to_unsized = slice_ptr as *const [u16];
     // TODO: We could try testing vtable casts here too, but maybe
     // we should wait until std::raw::TraitObject is stabilized?
 
     // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
-    let _usize_from_int_ptr_transmute = unsafe {
-        transmute::<*const i32, usize>(ptr_i32)
-    };
+    let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
     let _usize_from_int_ptr = ptr_i32 as usize;
 
-    let array_ref: &[i32; 4] = &[1,2,3,4];
+    let array_ref: &[i32; 4] = &[1, 2, 3, 4];
 
     // e has type &[T; n] and U is *const T; array-ptr-cast
-    let _array_ptr_transmute = unsafe {
-        transmute::<&[i32; 4], *const [i32; 4]>(array_ref)
-    };
+    let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
     let _array_ptr = array_ref as *const [i32; 4];
 
-    fn foo(_: usize) -> u8 { 42 }
+    fn foo(_: usize) -> u8 {
+        42
+    }
 
     // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
-    let _usize_ptr_transmute = unsafe {
-        transmute::<fn(usize) -> u8, *const usize>(foo)
-    };
+    let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
     let _usize_ptr_transmute = foo as *const usize;
 
     // e is a function pointer type and U is an integer; fptr-addr-cast
-    let _usize_from_fn_ptr_transmute = unsafe {
-        transmute::<fn(usize) -> u8, usize>(foo)
-    };
+    let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
     let _usize_from_fn_ptr = foo as *const usize;
 }
 
index 46597acc6c0d272ce9c7175855290c94593559bf..1157b179317e20e9725fe4ef403cd7e9bd320c9e 100644 (file)
@@ -1,53 +1,53 @@
 error: transmute from an integer to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:20:9
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39
    |
-LL |         transmute::<usize, *const i32>(usize::MAX)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
+LL |     let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
    |
    = note: `-D clippy::useless-transmute` implied by `-D warnings`
 
 error: transmute from a pointer to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:26:9
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38
    |
-LL |         transmute::<*const i32, *const i8>(ptr_i32)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
+LL |     let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
+   |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
    |
    = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
 
 error: transmute from a pointer to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:9
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46
    |
-LL |         transmute::<*const [i32], *const [u16]>(slice_ptr)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]`
+LL |     let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) };
+   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]`
 
 error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:42:9
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50
    |
-LL |         transmute::<*const i32, usize>(ptr_i32)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
+LL |     let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
    |
    = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings`
 
 error: transmute from a reference to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:50:9
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41
    |
-LL |         transmute::<&[i32; 4], *const [i32; 4]>(array_ref)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
+LL |     let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
+   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
 
 error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:58:9
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41
    |
-LL |         transmute::<fn(usize) -> u8, *const usize>(foo)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
+LL |     let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
+   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
 
 error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:9
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49
    |
-LL |         transmute::<fn(usize) -> u8, usize>(foo)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
+LL |     let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
 
 error: transmute from a reference to a pointer
-  --> $DIR/transmutes_expressible_as_ptr_casts.rs:77:14
+  --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14
    |
 LL |     unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
index 29d9139d3e346e87e13cb62dbe96cfb9a08c9ebd..9e77dcd8731642567072e5ff8ac0a8a12db38cd5 100644 (file)
@@ -6,6 +6,9 @@
 #[macro_use]
 extern crate macro_rules;
 
+use std::io;
+use std::task::Poll;
+
 // Tests that a simple case works
 // Should flag `Err(err)?`
 pub fn basic_test() -> Result<i32, i32> {
@@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result<i32, String> {
     }
     Ok(0)
 }
+
+pub fn poll_write(n: usize) -> Poll<io::Result<usize>> {
+    if n == 0 {
+        return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))
+    } else if n == 1 {
+        return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))
+    };
+
+    Poll::Ready(Ok(n))
+}
+
+pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
+    if !ready {
+        return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))
+    }
+
+    Poll::Ready(None)
+}
index 5e85d091a2ae73359046f06a82d7e3ce744d9672..41bcb0a189e76de98a567bcb3db02caf9bd82354 100644 (file)
@@ -6,6 +6,9 @@
 #[macro_use]
 extern crate macro_rules;
 
+use std::io;
+use std::task::Poll;
+
 // Tests that a simple case works
 // Should flag `Err(err)?`
 pub fn basic_test() -> Result<i32, i32> {
@@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result<i32, String> {
     }
     Ok(0)
 }
+
+pub fn poll_write(n: usize) -> Poll<io::Result<usize>> {
+    if n == 0 {
+        Err(io::ErrorKind::WriteZero)?
+    } else if n == 1 {
+        Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
+    };
+
+    Poll::Ready(Ok(n))
+}
+
+pub fn poll_next(ready: bool) -> Poll<Option<io::Result<()>>> {
+    if !ready {
+        Err(io::ErrorKind::NotFound)?
+    }
+
+    Poll::Ready(None)
+}
index 21e9d4048a5887bdc49d3a0e10f1a43f4f48df2f..3f1cbc17e72d08ff35e90bfc0e9333472d1473f8 100644 (file)
@@ -1,5 +1,5 @@
 error: returning an `Err(_)` with the `?` operator
-  --> $DIR/try_err.rs:15:9
+  --> $DIR/try_err.rs:18:9
    |
 LL |         Err(err)?;
    |         ^^^^^^^^^ help: try this: `return Err(err)`
@@ -11,28 +11,46 @@ LL | #![deny(clippy::try_err)]
    |         ^^^^^^^^^^^^^^^
 
 error: returning an `Err(_)` with the `?` operator
-  --> $DIR/try_err.rs:25:9
+  --> $DIR/try_err.rs:28:9
    |
 LL |         Err(err)?;
    |         ^^^^^^^^^ help: try this: `return Err(err.into())`
 
 error: returning an `Err(_)` with the `?` operator
-  --> $DIR/try_err.rs:45:17
+  --> $DIR/try_err.rs:48:17
    |
 LL |                 Err(err)?;
    |                 ^^^^^^^^^ help: try this: `return Err(err)`
 
 error: returning an `Err(_)` with the `?` operator
-  --> $DIR/try_err.rs:64:17
+  --> $DIR/try_err.rs:67:17
    |
 LL |                 Err(err)?;
    |                 ^^^^^^^^^ help: try this: `return Err(err.into())`
 
 error: returning an `Err(_)` with the `?` operator
-  --> $DIR/try_err.rs:103:9
+  --> $DIR/try_err.rs:106:9
    |
 LL |         Err(foo!())?;
    |         ^^^^^^^^^^^^ help: try this: `return Err(foo!())`
 
-error: aborting due to 5 previous errors
+error: returning an `Err(_)` with the `?` operator
+  --> $DIR/try_err.rs:113:9
+   |
+LL |         Err(io::ErrorKind::WriteZero)?
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))`
+
+error: returning an `Err(_)` with the `?` operator
+  --> $DIR/try_err.rs:115:9
+   |
+LL |         Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))`
+
+error: returning an `Err(_)` with the `?` operator
+  --> $DIR/try_err.rs:123:9
+   |
+LL |         Err(io::ErrorKind::NotFound)?
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`
+
+error: aborting due to 8 previous errors
 
index 47e37aed2464e4a5567ddb5ec62d16b2f67d8f50..618c5980d64e5bdc83b64b24f9405695cbf6cbef 100644 (file)
@@ -1,4 +1,4 @@
-error: Usage of unknown attribute
+error: usage of unknown attribute
   --> $DIR/unknown_attribute.rs:1:11
    |
 LL | #[clippy::unknown]
index 34ba167a947906602a4267f2a3125906f653432a..d0a0f219097e5f20b2aa325c345fc2b71128c4d8 100644 (file)
@@ -1,4 +1,4 @@
-error: Creating a reference that is immediately dereferenced.
+error: creating a reference that is immediately dereferenced
   --> $DIR/unnecessary_ref.rs:13:17
    |
 LL |     let inner = (&outer).inner;
index c017d1cf9a468bc03ddc55829e277442dfe87734..31c2ba0f9c5893a21dcaba76b1675b4577746173 100644 (file)
@@ -1,5 +1,7 @@
 // run-rustfix
 
+#![allow(clippy::stable_sort_primitive)]
+
 use std::cmp::Reverse;
 
 fn unnecessary_sort_by() {
index 1929c72b2f2cd3737194c2c52eba6aaa309b3380..a3c8ae468ede73d15a42f79c61141b60aac38e46 100644 (file)
@@ -1,5 +1,7 @@
 // run-rustfix
 
+#![allow(clippy::stable_sort_primitive)]
+
 use std::cmp::Reverse;
 
 fn unnecessary_sort_by() {
index 903b6e5099ce8b68c9aa1363a8b7999418ca52ee..70c6cf0a3b63138b25781730faac8a13b9b62efb 100644 (file)
@@ -1,5 +1,5 @@
 error: use Vec::sort here instead
-  --> $DIR/unnecessary_sort_by.rs:12:5
+  --> $DIR/unnecessary_sort_by.rs:14:5
    |
 LL |     vec.sort_by(|a, b| a.cmp(b));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()`
@@ -7,37 +7,37 @@ LL |     vec.sort_by(|a, b| a.cmp(b));
    = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings`
 
 error: use Vec::sort here instead
-  --> $DIR/unnecessary_sort_by.rs:13:5
+  --> $DIR/unnecessary_sort_by.rs:15:5
    |
 LL |     vec.sort_unstable_by(|a, b| a.cmp(b));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()`
 
 error: use Vec::sort_by_key here instead
-  --> $DIR/unnecessary_sort_by.rs:14:5
+  --> $DIR/unnecessary_sort_by.rs:16:5
    |
 LL |     vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())`
 
 error: use Vec::sort_by_key here instead
-  --> $DIR/unnecessary_sort_by.rs:15:5
+  --> $DIR/unnecessary_sort_by.rs:17:5
    |
 LL |     vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b)));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))`
 
 error: use Vec::sort_by_key here instead
-  --> $DIR/unnecessary_sort_by.rs:17:5
+  --> $DIR/unnecessary_sort_by.rs:19:5
    |
 LL |     vec.sort_by(|a, b| b.cmp(a));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))`
 
 error: use Vec::sort_by_key here instead
-  --> $DIR/unnecessary_sort_by.rs:18:5
+  --> $DIR/unnecessary_sort_by.rs:20:5
    |
 LL |     vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs()));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))`
 
 error: use Vec::sort_by_key here instead
-  --> $DIR/unnecessary_sort_by.rs:19:5
+  --> $DIR/unnecessary_sort_by.rs:21:5
    |
 LL |     vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a)));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))`
index e7b92ce1e197b3631cede7d5fb7090260f54f87f..b8d3c2945322b66e6e59859595fb44a12379ebc9 100644 (file)
@@ -1,19 +1,19 @@
-error: You matched a field with a wildcard pattern. Consider using `..` instead
+error: you matched a field with a wildcard pattern, consider using `..` instead
   --> $DIR/unneeded_field_pattern.rs:14:15
    |
 LL |         Foo { a: _, b: 0, .. } => {},
    |               ^^^^
    |
    = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings`
-   = help: Try with `Foo { b: 0, .. }`
+   = help: try with `Foo { b: 0, .. }`
 
-error: All the struct fields are matched to a wildcard pattern, consider using `..`.
+error: all the struct fields are matched to a wildcard pattern, consider using `..`
   --> $DIR/unneeded_field_pattern.rs:16:9
    |
 LL |         Foo { a: _, b: _, c: _ } => {},
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: Try with `Foo { .. }` instead
+   = help: try with `Foo { .. }` instead
 
 error: aborting due to 2 previous errors
 
index 7bee9c499e1f32373090b85cdbd4dcf5755f832d..690d705573d3f73ae3bba84c7dfb4a97d35e87fb 100644 (file)
@@ -57,4 +57,14 @@ unsafe fn inner() {}
 #[derive(Deserialize)]
 pub struct F {}
 
+// Check that we honor the `allow` attribute on the ADT
+#[allow(clippy::unsafe_derive_deserialize)]
+#[derive(Deserialize)]
+pub struct G {}
+impl G {
+    pub fn unsafe_block(&self) {
+        unsafe {}
+    }
+}
+
 fn main() {}