]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'e636b88aa180e8cab9e28802aac90adbc984234d' into clippyup
authorflip1995 <hello@philkrones.com>
Thu, 24 Sep 2020 12:49:22 +0000 (14:49 +0200)
committerflip1995 <hello@philkrones.com>
Thu, 24 Sep 2020 12:49:22 +0000 (14:49 +0200)
110 files changed:
CHANGELOG.md
CONTRIBUTING.md
clippy_dev/src/ra_setup.rs
clippy_lints/Cargo.toml
clippy_lints/src/atomic_ordering.rs
clippy_lints/src/await_holding_lock.rs
clippy_lints/src/bit_mask.rs
clippy_lints/src/bytecount.rs
clippy_lints/src/consts.rs
clippy_lints/src/doc.rs
clippy_lints/src/duration_subsec.rs
clippy_lints/src/entry.rs
clippy_lints/src/fallible_impl_from.rs
clippy_lints/src/format.rs
clippy_lints/src/indexing_slicing.rs
clippy_lints/src/inherent_to_string.rs
clippy_lints/src/len_zero.rs
clippy_lints/src/lib.rs
clippy_lints/src/loops.rs
clippy_lints/src/manual_strip.rs [new file with mode: 0644]
clippy_lints/src/map_err_ignore.rs [new file with mode: 0644]
clippy_lints/src/match_on_vec_items.rs
clippy_lints/src/matches.rs
clippy_lints/src/methods/bind_instead_of_map.rs
clippy_lints/src/methods/mod.rs
clippy_lints/src/methods/unnecessary_lazy_eval.rs
clippy_lints/src/misc.rs
clippy_lints/src/misc_early.rs
clippy_lints/src/mut_key.rs
clippy_lints/src/non_copy_const.rs
clippy_lints/src/open_options.rs
clippy_lints/src/option_if_let_else.rs
clippy_lints/src/panic_in_result_fn.rs [new file with mode: 0644]
clippy_lints/src/path_buf_push_overwrite.rs
clippy_lints/src/redundant_clone.rs
clippy_lints/src/repeat_once.rs
clippy_lints/src/shadow.rs
clippy_lints/src/strings.rs
clippy_lints/src/swap.rs
clippy_lints/src/types.rs
clippy_lints/src/unwrap_in_result.rs
clippy_lints/src/useless_conversion.rs
clippy_lints/src/utils/diagnostics.rs
clippy_lints/src/utils/eager_or_lazy.rs [new file with mode: 0644]
clippy_lints/src/utils/internal_lints.rs
clippy_lints/src/utils/mod.rs
clippy_lints/src/utils/paths.rs
clippy_lints/src/utils/sugg.rs
clippy_lints/src/utils/usage.rs
clippy_lints/src/write.rs
doc/adding_lints.md
doc/common_tools_writing_lints.md
src/lintlist/mod.rs
tests/ui/atomic_ordering_exchange.rs [new file with mode: 0644]
tests/ui/atomic_ordering_exchange.stderr [new file with mode: 0644]
tests/ui/atomic_ordering_exchange_weak.rs [new file with mode: 0644]
tests/ui/atomic_ordering_exchange_weak.stderr [new file with mode: 0644]
tests/ui/atomic_ordering_fetch_update.rs [new file with mode: 0644]
tests/ui/atomic_ordering_fetch_update.stderr [new file with mode: 0644]
tests/ui/borrow_interior_mutable_const.rs
tests/ui/borrow_interior_mutable_const.stderr
tests/ui/crashes/ice-2636.rs [deleted file]
tests/ui/crashes/ice-2636.stderr [deleted file]
tests/ui/declare_interior_mutable_const.rs
tests/ui/declare_interior_mutable_const.stderr
tests/ui/drop_ref.rs
tests/ui/drop_ref.stderr
tests/ui/float_cmp.stderr
tests/ui/float_cmp_const.stderr
tests/ui/indexing_slicing_index.rs
tests/ui/indexing_slicing_index.stderr
tests/ui/indexing_slicing_slice.stderr
tests/ui/into_iter_on_ref.stderr
tests/ui/let_if_seq.rs
tests/ui/let_if_seq.stderr
tests/ui/manual_strip.rs [new file with mode: 0644]
tests/ui/manual_strip.stderr [new file with mode: 0644]
tests/ui/map_err.rs [new file with mode: 0644]
tests/ui/map_err.stderr [new file with mode: 0644]
tests/ui/match_type_on_diag_item.rs [new file with mode: 0644]
tests/ui/match_type_on_diag_item.stderr [new file with mode: 0644]
tests/ui/option_if_let_else.fixed
tests/ui/option_if_let_else.rs
tests/ui/option_if_let_else.stderr
tests/ui/panic_in_result_fn.rs [new file with mode: 0644]
tests/ui/panic_in_result_fn.stderr [new file with mode: 0644]
tests/ui/print_with_newline.rs
tests/ui/print_with_newline.stderr
tests/ui/rc_buffer.rs [new file with mode: 0644]
tests/ui/rc_buffer.stderr [new file with mode: 0644]
tests/ui/rc_buffer_arc.rs [new file with mode: 0644]
tests/ui/rc_buffer_arc.stderr [new file with mode: 0644]
tests/ui/rc_buffer_redefined_string.rs [new file with mode: 0644]
tests/ui/rc_buffer_redefined_string.stderr [new file with mode: 0644]
tests/ui/redundant_pattern_matching.fixed
tests/ui/redundant_pattern_matching.rs
tests/ui/redundant_pattern_matching.stderr
tests/ui/redundant_pattern_matching_option.fixed [new file with mode: 0644]
tests/ui/redundant_pattern_matching_option.rs [new file with mode: 0644]
tests/ui/redundant_pattern_matching_option.stderr [new file with mode: 0644]
tests/ui/trailing_zeros.rs
tests/ui/trailing_zeros.stderr
tests/ui/unnecessary_lazy_eval.fixed
tests/ui/unnecessary_lazy_eval.rs
tests/ui/unnecessary_lazy_eval.stderr
tests/ui/useless_conversion.stderr
tests/ui/useless_conversion_try.stderr
tests/ui/write_with_newline.rs
tests/ui/write_with_newline.stderr
triagebot.toml

index 64f9379b303cda15d6cd5919d4208718149b8348..d1dfe36ffd825d2c7828bb7b55369873d15f1c60 100644 (file)
@@ -1672,10 +1672,12 @@ Released 2018-09-13
 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
+[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 [`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
@@ -1755,6 +1757,7 @@ Released 2018-09-13
 [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
+[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
@@ -1774,6 +1777,7 @@ Released 2018-09-13
 [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
+[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
index 54777810abbdfac70346b952f2510388aa1e789c..100c9edb367241450780bd3fc27bb1d234db97f4 100644 (file)
@@ -8,7 +8,7 @@ something. We appreciate any sort of contributions, and don't want a wall of rul
 
 Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
 explains how you can contribute and how to get started.  If you have any questions about contributing or need help with
-anything, feel free to ask questions on issues or visit the `#clippy` on [Discord].
+anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip].
 
 All contributors are expected to follow the [Rust Code of Conduct].
 
@@ -23,7 +23,7 @@ All contributors are expected to follow the [Rust Code of Conduct].
   - [Bors and Homu](#bors-and-homu)
   - [Contributions](#contributions)
 
-[Discord]: https://discord.gg/rust-lang
+[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
 [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
 
 ## Getting started
@@ -242,7 +242,7 @@ to be run inside the `rust` directory):
     ```
 3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
    accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
-   ~~annoy~~ ask them in the [Discord] channel.)
+   ~~annoy~~ ask them in the [Zulip] stream.)
    
 ### Syncing back changes in Clippy to [`rust-lang/rust`]
 
index f2bd651ab253c6419ce6395e22d7d5a18562dd78..c67efc10f15783d28e3ad5e3443e48a96a33a2b6 100644 (file)
@@ -14,7 +14,7 @@ pub fn run(rustc_path: Option<&str>) {
     // we can unwrap here because the arg is required here
     let rustc_path = PathBuf::from(rustc_path.unwrap());
     assert!(rustc_path.is_dir(), "path is not a directory");
-    let rustc_source_basedir = rustc_path.join("src");
+    let rustc_source_basedir = rustc_path.join("compiler");
     assert!(
         rustc_source_basedir.is_dir(),
         "are you sure the path leads to a rustc repo?"
@@ -61,7 +61,7 @@ fn inject_deps_into_manifest(
     let new_deps = extern_crates.map(|dep| {
         // format the dependencies that are going to be put inside the Cargo.toml
         format!(
-            "{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n",
+            "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
             dep = dep,
             source_path = rustc_source_dir.display()
         )
index cc7d3a04f003ee0728feb89aa83ef147fe12e7ac..341d9e601ee6fe656946bda93fbc367b5a2bf1f4 100644 (file)
@@ -21,7 +21,7 @@ cargo_metadata = "0.11.1"
 if_chain = "1.0.0"
 itertools = "0.9"
 lazy_static = "1.0.2"
-pulldown-cmark = { version = "0.7.1", default-features = false }
+pulldown-cmark = { version = "0.8", default-features = false }
 quine-mc_cluskey = "0.2.2"
 regex-syntax = "0.6"
 serde = { version = "1.0", features = ["derive"] }
index 2d964ac2b9f64c7cae2f4a0bc511cfbb23ff5355..703d8a6f62bb1f07ab305dfec36e32097d10f070 100644 (file)
@@ -8,7 +8,8 @@
 
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of invalid atomic
-    /// ordering in atomic loads/stores and memory fences.
+    /// ordering in atomic loads/stores/exchanges/updates and
+    /// memory fences.
     ///
     /// **Why is this bad?** Using an invalid atomic ordering
     /// will cause a panic at run-time.
     ///
     /// **Example:**
     /// ```rust,no_run
-    /// # use std::sync::atomic::{self, AtomicBool, Ordering};
+    /// # use std::sync::atomic::{self, AtomicU8, Ordering};
     ///
-    /// let x = AtomicBool::new(true);
+    /// let x = AtomicU8::new(0);
     ///
+    /// // Bad: `Release` and `AcqRel` cannot be used for `load`.
     /// let _ = x.load(Ordering::Release);
     /// let _ = x.load(Ordering::AcqRel);
     ///
-    /// x.store(false, Ordering::Acquire);
-    /// x.store(false, Ordering::AcqRel);
+    /// // Bad: `Acquire` and `AcqRel` cannot be used for `store`.
+    /// x.store(1, Ordering::Acquire);
+    /// x.store(2, Ordering::AcqRel);
     ///
+    /// // Bad: `Relaxed` cannot be used as a fence's ordering.
     /// atomic::fence(Ordering::Relaxed);
     /// atomic::compiler_fence(Ordering::Relaxed);
+    ///
+    /// // Bad: `Release` and `AcqRel` are both always invalid
+    /// // for the failure ordering (the last arg).
+    /// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);
+    /// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);
+    ///
+    /// // Bad: The failure ordering is not allowed to be
+    /// // stronger than the success order, and `SeqCst` is
+    /// // stronger than `Relaxed`.
+    /// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));
     /// ```
     pub INVALID_ATOMIC_ORDERING,
     correctness,
-    "usage of invalid atomic ordering in atomic loads/stores and memory fences"
+    "usage of invalid atomic ordering in atomic operations and memory fences"
 }
 
 declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
@@ -127,9 +141,89 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
     }
 }
 
+fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
+    if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
+        cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
+    } else {
+        None
+    }
+}
+
+fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if_chain! {
+        if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
+        let method = method_path.ident.name.as_str();
+        if type_is_atomic(cx, &args[0]);
+        if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update";
+        let (success_order_arg, failure_order_arg) = if method == "fetch_update" {
+            (&args[1], &args[2])
+        } else {
+            (&args[3], &args[4])
+        };
+        if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg);
+        then {
+            // Helper type holding on to some checking and error reporting data. Has
+            // - (success ordering name,
+            // - list of failure orderings forbidden by the success order,
+            // - suggestion message)
+            type OrdLintInfo = (&'static str, &'static [&'static str], &'static str);
+            let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`");
+            let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`");
+            let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
+            let release = ("Release", relaxed.1, relaxed.2);
+            let acqrel = ("AcqRel", acquire.1, acquire.2);
+            let search = [relaxed, acquire, seq_cst, release, acqrel];
+
+            let success_lint_info = opt_ordering_defid(cx, success_order_arg)
+                .and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
+                    search
+                        .iter()
+                        .find(|(ordering, ..)| {
+                            match_def_path(cx, success_ord_def_id,
+                                &["core", "sync", "atomic", "Ordering", ordering])
+                        })
+                        .copied()
+                });
+
+            if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) {
+                // If we don't know the success order is, use what we'd suggest
+                // if it were maximally permissive.
+                let suggested = success_lint_info.unwrap_or(seq_cst).2;
+                span_lint_and_help(
+                    cx,
+                    INVALID_ATOMIC_ORDERING,
+                    failure_order_arg.span,
+                    &format!(
+                        "{}'s failure ordering may not be `Release` or `AcqRel`",
+                        method,
+                    ),
+                    None,
+                    &format!("consider using {} instead", suggested),
+                );
+            } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info {
+                if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) {
+                    span_lint_and_help(
+                        cx,
+                        INVALID_ATOMIC_ORDERING,
+                        failure_order_arg.span,
+                        &format!(
+                            "{}'s failure ordering may not be stronger than the success ordering of `{}`",
+                            method,
+                            success_ord_name,
+                        ),
+                        None,
+                        &format!("consider using {} instead", suggested),
+                    );
+                }
+            }
+        }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for AtomicOrdering {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         check_atomic_load_store(cx, expr);
         check_memory_fence(cx, expr);
+        check_atomic_compare_exchange(cx, expr);
     }
 }
index f18e7e5d99755d425d75ec2eab98574c409cb7a0..367534499fd020665de7e1efda36a2d0742d3005 100644 (file)
@@ -10,7 +10,7 @@
     /// **What it does:** Checks for calls to await while holding a
     /// non-async-aware MutexGuard.
     ///
-    /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot
+    /// **Why is this bad?** The Mutex types found in std::sync and parking_lot
     /// are not designed to operate in an async context across await points.
     ///
     /// There are two potential solutions. One is to use an asynx-aware Mutex
index 81a34021e8a018aa78faf6cf5b57deb9ba40bf23..a4ee54076ee98fc74b41f5fe67aa7d21e5dfcfed 100644 (file)
@@ -90,7 +90,7 @@
     /// if x & 0b1111 == 0 { }
     /// ```
     pub VERBOSE_BIT_MASK,
-    style,
+    pedantic,
     "expressions where a bit mask is less readable than the corresponding method call"
 }
 
index 189c07427ae99c82583ebab999557ebee40844f0..d7d02ebf985c81ae602d8c6e78eaf5fc1bb94b8d 100644 (file)
@@ -1,6 +1,5 @@
 use crate::utils::{
-    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability,
-    span_lint_and_sugg, walk_ptrs_ty,
+    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::UintTy;
@@ -53,7 +52,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                     if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
                     if op.node == BinOpKind::Eq;
                     if match_type(cx,
-                               walk_ptrs_ty(cx.typeck_results().expr_ty(&filter_args[0])),
+                               cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
                                &paths::SLICE_ITER);
                     then {
                         let needle = match get_path_name(l) {
@@ -63,7 +62,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                                 _ => { return; }
                             }
                         };
-                        if ty::Uint(UintTy::U8) != *walk_ptrs_ty(cx.typeck_results().expr_ty(needle)).kind() {
+                        if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
                             return;
                         }
                         let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
index 3ee022e4e68a0e5d20357b081eedcb5225b47525..0000d39263ed388013927885c1004db3dce5673b 100644 (file)
@@ -21,7 +21,7 @@ pub enum Constant {
     /// A `String` (e.g., "abc").
     Str(String),
     /// A binary string (e.g., `b"abc"`).
-    Binary(Lrc<Vec<u8>>),
+    Binary(Lrc<[u8]>),
     /// A single `char` (e.g., `'a'`).
     Char(char),
     /// An integer's bit representation.
@@ -155,7 +155,7 @@ pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
     match *lit {
         LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
         LitKind::Byte(b) => Constant::Int(u128::from(b)),
-        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
+        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::from(s.as_slice())),
         LitKind::Char(c) => Constant::Char(c),
         LitKind::Int(n, _) => Constant::Int(n),
         LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
index 50121a054c79830676aac61494779ce2445b1e66..62bb70af06e937e2e4522b6886d5d411706c670e 100644 (file)
@@ -534,7 +534,7 @@ fn is_camel_case(s: &str) -> bool {
             return false;
         }
 
-        let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s };
+        let s = s.strip_suffix('s').unwrap_or(s);
 
         s.chars().all(char::is_alphanumeric)
             && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
index 8ece44878fe32c6c7bcd1777e717cd6e185137ec..c0529a34cc411ed5fca26c63b8e4e0eefcca2b5d 100644 (file)
@@ -7,7 +7,7 @@
 
 use crate::consts::{constant, Constant};
 use crate::utils::paths;
-use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
@@ -43,7 +43,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
             if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind;
             if let ExprKind::MethodCall(ref method_path, _ , ref args, _) = left.kind;
-            if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::DURATION);
+            if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION);
             if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right);
             then {
                 let suggested_fn = match (method_path.ident.as_str().as_ref(), divisor) {
index d616502a82a0c07adbbbda1150fb47580ca3558e..35a5d00f4aa5ae6764c4501af5f52ce3afff05d1 100644 (file)
@@ -1,6 +1,6 @@
 use crate::utils::SpanlessEq;
 use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
-use crate::utils::{snippet_with_applicability, span_lint_and_then, walk_ptrs_ty};
+use crate::utils::{snippet_with_applicability, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
@@ -106,7 +106,7 @@ fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static
         if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind;
         then {
             let map = &params[0];
-            let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(map));
+            let obj_ty = cx.typeck_results().expr_ty(map).peel_refs();
 
             return if match_type(cx, obj_ty, &paths::BTREEMAP) {
                 Some(("BTreeMap", map, key))
index 000762334f61eb022944aa0ca69fb84e3a21bf41..a9e05fddbe7625dc7dc98d74dd39679c034982bf 100644 (file)
@@ -1,7 +1,5 @@
 use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT};
-use crate::utils::{
-    is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty,
-};
+use crate::utils::{is_expn_of, is_type_diagnostic_item, match_def_path, method_chain_args, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -96,7 +94,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 
             // check for `unwrap`
             if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
-                let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+                let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
                 if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
                     || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
                 {
index 8bd85af87682a3027c0b5e7eb3efee27952aca8c..d6541010bca2326691b8b3e961f14f2bdb77db3f 100644 (file)
@@ -1,7 +1,7 @@
 use crate::utils::paths;
 use crate::utils::{
     is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet,
-    span_lint_and_then, walk_ptrs_ty,
+    span_lint_and_then,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
@@ -90,7 +90,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &
         if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind;
         if pats.len() == 1;
         then {
-            let ty = walk_ptrs_ty(cx.typeck_results().pat_ty(&pats[0]));
+            let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs();
             if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym!(string_type)) {
                 return None;
             }
index a28eda8be15a4ec8a129bb44a9aa172fb10cc247..741195f3b10d5eee8f1da6577ec18919b95574b3 100644 (file)
@@ -88,7 +88,7 @@
 impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Index(ref array, ref index) = &expr.kind {
-            let ty = cx.typeck_results().expr_ty(array);
+            let ty = cx.typeck_results().expr_ty(array).peel_refs();
             if let Some(range) = higher::range(index) {
                 // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
                 if let ty::Array(_, s) = ty.kind() {
index f330fa8fab8f4ea08c1a004f62be5fa58c53bed5..0877b44d90138a3bbfe0bbf0b4d7bbfa82a3dc13 100644 (file)
@@ -5,7 +5,7 @@
 
 use crate::utils::{
     get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
-    trait_ref_of_method, walk_ptrs_ty,
+    trait_ref_of_method,
 };
 
 declare_clippy_lint! {
@@ -125,7 +125,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) {
     // Get the real type of 'self'
     let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id);
     let self_type = cx.tcx.fn_sig(fn_def_id).input(0);
-    let self_type = walk_ptrs_ty(self_type.skip_binder());
+    let self_type = self_type.skip_binder().peel_refs();
 
     // Emit either a warning or an error
     if implements_trait(cx, self_type, display_trait_id, &[]) {
index 42a98dc963d2084cfe93eca9ac759608bcbfd5d4..c9c4891bb08acf67f4161184424f50b4ac486ea8 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg};
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -285,7 +285,7 @@ fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
         })
     }
 
-    let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr));
+    let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
     match ty.kind() {
         ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {
             cx.tcx
index 2020ef78509b0c24f80f01f4105da0652462ebf9..58112ac8da5f12e024bea6581579e3fa3a0e851f 100644 (file)
@@ -230,7 +230,9 @@ macro_rules! declare_clippy_lint {
 mod main_recursion;
 mod manual_async_fn;
 mod manual_non_exhaustive;
+mod manual_strip;
 mod map_clone;
+mod map_err_ignore;
 mod map_identity;
 mod map_unit_fn;
 mod match_on_vec_items;
@@ -269,6 +271,7 @@ macro_rules! declare_clippy_lint {
 mod option_env_unwrap;
 mod option_if_let_else;
 mod overflow_check_conditional;
+mod panic_in_result_fn;
 mod panic_unimplemented;
 mod partialeq_ne_impl;
 mod path_buf_push_overwrite;
@@ -625,7 +628,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &main_recursion::MAIN_RECURSION,
         &manual_async_fn::MANUAL_ASYNC_FN,
         &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
+        &manual_strip::MANUAL_STRIP,
         &map_clone::MAP_CLONE,
+        &map_err_ignore::MAP_ERR_IGNORE,
         &map_identity::MAP_IDENTITY,
         &map_unit_fn::OPTION_MAP_UNIT_FN,
         &map_unit_fn::RESULT_MAP_UNIT_FN,
@@ -751,6 +756,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &option_env_unwrap::OPTION_ENV_UNWRAP,
         &option_if_let_else::OPTION_IF_LET_ELSE,
         &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
+        &panic_in_result_fn::PANIC_IN_RESULT_FN,
         &panic_unimplemented::PANIC,
         &panic_unimplemented::PANIC_PARAMS,
         &panic_unimplemented::TODO,
@@ -835,6 +841,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &types::LET_UNIT_VALUE,
         &types::LINKEDLIST,
         &types::OPTION_OPTION,
+        &types::RC_BUFFER,
         &types::REDUNDANT_ALLOCATION,
         &types::TYPE_COMPLEXITY,
         &types::UNIT_ARG,
@@ -863,6 +870,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &utils::internal_lints::COMPILER_LINT_FUNCTIONS,
         &utils::internal_lints::DEFAULT_LINT,
         &utils::internal_lints::LINT_WITHOUT_LINT_PASS,
+        &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
         &utils::internal_lints::OUTER_EXPN_EXPN_DATA,
         &utils::internal_lints::PRODUCE_ICE,
         &vec::USELESS_VEC,
@@ -918,6 +926,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
     store.register_late_pass(|| box methods::Methods);
     store.register_late_pass(|| box map_clone::MapClone);
+    store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
     store.register_late_pass(|| box shadow::Shadow);
     store.register_late_pass(|| box types::LetUnitValue);
     store.register_late_pass(|| box types::UnitCmp);
@@ -1091,6 +1100,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
     store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
     store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
+    store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
+
     let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
     store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
         single_char_binding_names_threshold,
@@ -1105,6 +1116,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box self_assignment::SelfAssignment);
     store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
     store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
+    store.register_late_pass(|| box manual_strip::ManualStrip);
+    store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1135,6 +1148,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
         LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
         LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC),
+        LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN),
         LintId::of(&panic_unimplemented::PANIC),
         LintId::of(&panic_unimplemented::TODO),
         LintId::of(&panic_unimplemented::UNIMPLEMENTED),
@@ -1152,6 +1166,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
         LintId::of(&attrs::INLINE_ALWAYS),
         LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
+        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
         LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
         LintId::of(&copies::MATCH_SAME_ARMS),
         LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
@@ -1180,6 +1195,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
         LintId::of(&loops::EXPLICIT_ITER_LOOP),
         LintId::of(&macro_use::MACRO_USE_IMPORTS),
+        LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
         LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
         LintId::of(&matches::MATCH_BOOL),
         LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
@@ -1230,6 +1246,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
         LintId::of(&utils::internal_lints::DEFAULT_LINT),
         LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
+        LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
         LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
         LintId::of(&utils::internal_lints::PRODUCE_ICE),
     ]);
@@ -1249,7 +1266,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&attrs::USELESS_ATTRIBUTE),
         LintId::of(&bit_mask::BAD_BIT_MASK),
         LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
-        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
         LintId::of(&blacklisted_name::BLACKLISTED_NAME),
         LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
         LintId::of(&booleans::LOGIC_BUG),
@@ -1330,6 +1346,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&main_recursion::MAIN_RECURSION),
         LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
         LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
+        LintId::of(&manual_strip::MANUAL_STRIP),
         LintId::of(&map_clone::MAP_CLONE),
         LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
@@ -1474,6 +1491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::CHAR_LIT_AS_U8),
         LintId::of(&types::FN_TO_NUMERIC_CAST),
         LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
+        LintId::of(&types::RC_BUFFER),
         LintId::of(&types::REDUNDANT_ALLOCATION),
         LintId::of(&types::TYPE_COMPLEXITY),
         LintId::of(&types::UNIT_ARG),
@@ -1507,7 +1525,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
         LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
         LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
-        LintId::of(&bit_mask::VERBOSE_BIT_MASK),
         LintId::of(&blacklisted_name::BLACKLISTED_NAME),
         LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
         LintId::of(&collapsible_if::COLLAPSIBLE_IF),
@@ -1622,6 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
         LintId::of(&loops::MUT_RANGE_BOUND),
         LintId::of(&loops::WHILE_LET_LOOP),
+        LintId::of(&manual_strip::MANUAL_STRIP),
         LintId::of(&map_identity::MAP_IDENTITY),
         LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
         LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
@@ -1775,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
         LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
         LintId::of(&types::BOX_VEC),
+        LintId::of(&types::RC_BUFFER),
         LintId::of(&types::REDUNDANT_ALLOCATION),
         LintId::of(&vec::USELESS_VEC),
     ]);
index 6c54c07869ad1a6a2b7f1814fef1ed6ee20718d6..3410341a1e3c5056aacfc16bffd2344bcf98e23b 100644 (file)
@@ -1114,7 +1114,7 @@ fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&
             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 is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym!(vec_type));
             if path.ident.name.as_str() == "push";
             then {
                 return Some((self_expr, pushed_item))
@@ -2601,11 +2601,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
                         span,
                         NEEDLESS_COLLECT_MSG,
                         |diag| {
-                            let (arg, pred) = if contains_arg.starts_with('&') {
-                                ("x", &contains_arg[1..])
-                            } else {
-                                ("&x", &*contains_arg)
-                            };
+                            let (arg, pred) = contains_arg
+                                    .strip_prefix('&')
+                                    .map_or(("&x", &*contains_arg), |s| ("x", s));
                             diag.span_suggestion(
                                 span,
                                 "replace with",
diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs
new file mode 100644 (file)
index 0000000..4afb0ab
--- /dev/null
@@ -0,0 +1,245 @@
+use crate::consts::{constant, Constant};
+use crate::utils::usage::mutated_variables;
+use crate::utils::{
+    eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
+};
+
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_hir::def::Res;
+use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::BinOpKind;
+use rustc_hir::{BorrowKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// **What it does:**
+    /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
+    /// the pattern's length.
+    ///
+    /// **Why is this bad?**
+    /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no
+    /// slicing which may panic and the compiler does not need to insert this panic code. It is
+    /// also sometimes more readable as it removes the need for duplicating or storing the pattern
+    /// used by `str::{starts,ends}_with` and in the slicing.
+    ///
+    /// **Known problems:**
+    /// None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let s = "hello, world!";
+    /// if s.starts_with("hello, ") {
+    ///     assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let s = "hello, world!";
+    /// if let Some(end) = s.strip_prefix("hello, ") {
+    ///     assert_eq!(end.to_uppercase(), "WORLD!");
+    /// }
+    /// ```
+    pub MANUAL_STRIP,
+    complexity,
+    "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
+}
+
+declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+enum StripKind {
+    Prefix,
+    Suffix,
+}
+
+impl<'tcx> LateLintPass<'tcx> for ManualStrip {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if_chain! {
+            if let Some((cond, then, _)) = higher::if_block(&expr);
+            if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
+            if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
+            if let ExprKind::Path(target_path) = &target_arg.kind;
+            then {
+                let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
+                    StripKind::Prefix
+                } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
+                    StripKind::Suffix
+                } else {
+                    return;
+                };
+                let target_res = qpath_res(cx, &target_path, target_arg.hir_id);
+                if target_res == Res::Err {
+                    return;
+                };
+
+                if_chain! {
+                    if let Res::Local(hir_id) = target_res;
+                    if let Some(used_mutably) = mutated_variables(then, cx);
+                    if used_mutably.contains(&hir_id);
+                    then {
+                        return;
+                    }
+                }
+
+                let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
+                if !strippings.is_empty() {
+
+                    let kind_word = match strip_kind {
+                        StripKind::Prefix => "prefix",
+                        StripKind::Suffix => "suffix",
+                    };
+
+                    let test_span = expr.span.until(then.span);
+                    span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| {
+                        diag.span_note(test_span, &format!("the {} was tested here", kind_word));
+                        multispan_sugg(
+                            diag,
+                            &format!("try using the `strip_{}` method", kind_word),
+                            vec![(test_span,
+                                  format!("if let Some(<stripped>) = {}.strip_{}({}) ",
+                                          snippet(cx, target_arg.span, ".."),
+                                          kind_word,
+                                          snippet(cx, pattern.span, "..")))]
+                            .into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
+                        )
+                    });
+                }
+            }
+        }
+    }
+}
+
+// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
+fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if_chain! {
+        if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind;
+        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+        if match_def_path(cx, method_def_id, &paths::STR_LEN);
+        then {
+            Some(arg)
+        } else {
+            None
+        }
+    }
+}
+
+// Returns the length of the `expr` if it's a constant string or char.
+fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
+    let (value, _) = constant(cx, cx.typeck_results(), expr)?;
+    match value {
+        Constant::Str(value) => Some(value.len() as u128),
+        Constant::Char(value) => Some(value.len_utf8() as u128),
+        _ => None,
+    }
+}
+
+// Tests if `expr` equals the length of the pattern.
+fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
+    if let ExprKind::Lit(Spanned {
+        node: LitKind::Int(n, _),
+        ..
+    }) = expr.kind
+    {
+        constant_length(cx, pattern).map_or(false, |length| length == n)
+    } else {
+        len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
+    }
+}
+
+// Tests if `expr` is a `&str`.
+fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match cx.typeck_results().expr_ty_adjusted(&expr).kind() {
+        ty::Ref(_, ty, _) => ty.is_str(),
+        _ => false,
+    }
+}
+
+// Removes the outer `AddrOf` expression if needed.
+fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
+    if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind {
+        unref
+    } else {
+        expr
+    }
+}
+
+// Find expressions where `target` is stripped using the length of `pattern`.
+// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
+// method.
+fn find_stripping<'tcx>(
+    cx: &LateContext<'tcx>,
+    strip_kind: StripKind,
+    target: Res,
+    pattern: &'tcx Expr<'_>,
+    expr: &'tcx Expr<'_>,
+) -> Vec<Span> {
+    struct StrippingFinder<'a, 'tcx> {
+        cx: &'a LateContext<'tcx>,
+        strip_kind: StripKind,
+        target: Res,
+        pattern: &'tcx Expr<'tcx>,
+        results: Vec<Span>,
+    }
+
+    impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
+        type Map = Map<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+
+        fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
+            if_chain! {
+                if is_ref_str(self.cx, ex);
+                let unref = peel_ref(ex);
+                if let ExprKind::Index(indexed, index) = &unref.kind;
+                if let Some(range) = higher::range(index);
+                if let higher::Range { start, end, .. } = range;
+                if let ExprKind::Path(path) = &indexed.kind;
+                if qpath_res(self.cx, path, ex.hir_id) == self.target;
+                then {
+                    match (self.strip_kind, start, end) {
+                        (StripKind::Prefix, Some(start), None) => {
+                            if eq_pattern_length(self.cx, self.pattern, start) {
+                                self.results.push(ex.span);
+                                return;
+                            }
+                        },
+                        (StripKind::Suffix, None, Some(end)) => {
+                            if_chain! {
+                                if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
+                                if let Some(left_arg) = len_arg(self.cx, left);
+                                if let ExprKind::Path(left_path) = &left_arg.kind;
+                                if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target;
+                                if eq_pattern_length(self.cx, self.pattern, right);
+                                then {
+                                    self.results.push(ex.span);
+                                    return;
+                                }
+                            }
+                        },
+                        _ => {}
+                    }
+                }
+            }
+
+            walk_expr(self, ex);
+        }
+    }
+
+    let mut finder = StrippingFinder {
+        cx,
+        strip_kind,
+        target,
+        pattern,
+        results: vec![],
+    };
+    walk_expr(&mut finder, expr);
+    finder.results
+}
diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs
new file mode 100644 (file)
index 0000000..5298e16
--- /dev/null
@@ -0,0 +1,147 @@
+use crate::utils::span_lint_and_help;
+
+use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)`
+    ///
+    /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// Before:
+    /// ```rust
+    /// use std::fmt;
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible,
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {}
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(|_| Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    ///  ```
+    ///
+    ///  After:
+    ///  ```rust
+    /// use std::{fmt, num::ParseIntError};
+    ///
+    /// #[derive(Debug)]
+    /// enum Error {
+    ///     Indivisible(ParseIntError),
+    ///     Remainder(u8),
+    /// }
+    ///
+    /// impl fmt::Display for Error {
+    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    ///         match self {
+    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
+    ///             Error::Remainder(remainder) => write!(
+    ///                 f,
+    ///                 "input is not divisible by three, remainder = {}",
+    ///                 remainder
+    ///             ),
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// impl std::error::Error for Error {
+    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+    ///         match self {
+    ///             Error::Indivisible(source) => Some(source),
+    ///             _ => None,
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+    ///     input
+    ///         .parse::<i32>()
+    ///         .map_err(Error::Indivisible)
+    ///         .map(|v| v % 3)
+    ///         .and_then(|remainder| {
+    ///             if remainder == 0 {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(Error::Remainder(remainder as u8))
+    ///             }
+    ///         })
+    /// }
+    /// ```
+    pub MAP_ERR_IGNORE,
+    pedantic,
+    "`map_err` should not ignore the original error"
+}
+
+declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
+
+impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
+    // do not try to lint if this is from a macro or desugaring
+    fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
+        if e.span.from_expansion() {
+            return;
+        }
+
+        // check if this is a method call (e.g. x.foo())
+        if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind {
+            // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
+            // Enum::Variant[2]))
+            if method.ident.as_str() == "map_err" && args.len() == 2 {
+                // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields
+                if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind {
+                    // check if this is by Reference (meaning there's no move statement)
+                    if capture == CaptureBy::Ref {
+                        // Get the closure body to check the parameters and values
+                        let closure_body = cx.tcx.hir().body(body_id);
+                        // make sure there's only one parameter (`|_|`)
+                        if closure_body.params.len() == 1 {
+                            // make sure that parameter is the wild token (`_`)
+                            if let PatKind::Wild = closure_body.params[0].pat.kind {
+                                // span the area of the closure capture and warn that the
+                                // original error will be thrown away
+                                span_lint_and_help(
+                                    cx,
+                                    MAP_ERR_IGNORE,
+                                    body_span,
+                                    "`map_err(|_|...` ignores the original error",
+                                    None,
+                                    "Consider wrapping the error in an enum variant",
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
index 57966452253d52e75e55367ff540000448324377..331b6c6c34a9447ab8d08c63f53fe51c239456fb 100644 (file)
@@ -1,4 +1,3 @@
-use crate::utils::walk_ptrs_ty;
 use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -90,12 +89,12 @@ fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti
 
 fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
-    let ty = walk_ptrs_ty(ty);
+    let ty = ty.peel_refs();
     is_type_diagnostic_item(cx, ty, sym!(vec_type))
 }
 
 fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
-    let ty = walk_ptrs_ty(ty);
+    let ty = ty.peel_refs();
     is_type_lang_item(cx, ty, LangItem::RangeFull)
 }
index 819846ebc793becfe6d8a999a3a5f419300e1e66..b1a4e06d4c32fdc918d0a5b2f3f1feb35f162ef4 100644 (file)
@@ -6,7 +6,7 @@
     expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
     is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
     snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
-    span_lint_and_then, walk_ptrs_ty,
+    span_lint_and_then,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
@@ -502,7 +502,7 @@ pub struct Matches {
 
 impl<'tcx> LateLintPass<'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if in_external_macro(cx.sess(), expr.span) {
+        if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
             return;
         }
 
@@ -794,7 +794,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms
 }
 
 fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
-    let ex_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(ex));
+    let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
     if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
         for arm in arms {
             if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
index 498f12518f8a33745325056f1264dcdb5f6565d2..ae37942e55a1bbf65cf77a47d1925cb4b141dc5e 100644 (file)
@@ -12,6 +12,7 @@
 use rustc_span::Span;
 
 pub(crate) struct OptionAndThenSome;
+
 impl BindInsteadOfMap for OptionAndThenSome {
     const TYPE_NAME: &'static str = "Option";
     const TYPE_QPATH: &'static [&'static str] = &paths::OPTION;
@@ -24,6 +25,7 @@ impl BindInsteadOfMap for OptionAndThenSome {
 }
 
 pub(crate) struct ResultAndThenOk;
+
 impl BindInsteadOfMap for ResultAndThenOk {
     const TYPE_NAME: &'static str = "Result";
     const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
@@ -36,6 +38,7 @@ impl BindInsteadOfMap for ResultAndThenOk {
 }
 
 pub(crate) struct ResultOrElseErrInfo;
+
 impl BindInsteadOfMap for ResultOrElseErrInfo {
     const TYPE_NAME: &'static str = "Result";
     const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
@@ -120,9 +123,9 @@ fn lint_closure_autofixable(
         }
     }
 
-    fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) {
+    fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool {
         let mut suggs = Vec::new();
-        let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
+        let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
             if_chain! {
                 if !in_macro(ret_expr.span);
                 if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind;
@@ -153,12 +156,13 @@ fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::
                 )
             });
         }
+        can_sugg
     }
 
     /// Lint use of `_.and_then(|x| Some(y))` for `Option`s
-    fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+    fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool {
         if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) {
-            return;
+            return false;
         }
 
         match args[1].kind {
@@ -166,8 +170,10 @@ fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
                 let closure_body = cx.tcx.hir().body(body_id);
                 let closure_expr = remove_blocks(&closure_body.value);
 
-                if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
-                    Self::lint_closure(cx, expr, closure_expr);
+                if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
+                    true
+                } else {
+                    Self::lint_closure(cx, expr, closure_expr)
                 }
             },
             // `_.and_then(Some)` case, which is no-op.
@@ -181,8 +187,9 @@ fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
                     snippet(cx, args[0].span, "..").into(),
                     Applicability::MachineApplicable,
                 );
+                true
             },
-            _ => {},
+            _ => false,
         }
     }
 }
index ba69c8266b1182be338a48dfb85571a9acb83ba8..dadd0f8ebb7c85992fde8064b4805f50201f7a77 100644 (file)
 use rustc_span::symbol::{sym, SymbolStr};
 
 use crate::consts::{constant, Constant};
+use crate::utils::eager_or_lazy::is_lazyness_candidate;
 use crate::utils::usage::mutated_variables;
 use crate::utils::{
     contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
-    is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats,
-    last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls,
-    method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
-    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
-    span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
+    is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
+    match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
+    single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
+    span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth,
+    SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -1454,18 +1455,21 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
             ["unwrap_or_else", "map"] => {
                 if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
-                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or");
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or");
                 }
             },
             ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
             ["and_then", ..] => {
-                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and");
-                bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
-                bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
+                let biom_option_linted = bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
+                let biom_result_linted = bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
+                if !biom_option_linted && !biom_result_linted {
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "and");
+                }
             },
             ["or_else", ..] => {
-                unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or");
-                bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]);
+                if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) {
+                    unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "or");
+                }
             },
             ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
             ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]),
@@ -1508,9 +1512,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
             ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
             ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
-            ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"),
-            ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"),
-            ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"),
+            ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
+            ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
+            ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
             _ => {},
         }
 
@@ -1714,37 +1718,6 @@ fn lint_or_fun_call<'tcx>(
     name: &str,
     args: &'tcx [hir::Expr<'_>],
 ) {
-    // Searches an expression for method calls or function calls that aren't ctors
-    struct FunCallFinder<'a, 'tcx> {
-        cx: &'a LateContext<'tcx>,
-        found: bool,
-    }
-
-    impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
-        type Map = Map<'tcx>;
-
-        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
-            let call_found = match &expr.kind {
-                // ignore enum and struct constructors
-                hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
-                hir::ExprKind::MethodCall(..) => true,
-                _ => false,
-            };
-
-            if call_found {
-                self.found |= true;
-            }
-
-            if !self.found {
-                intravisit::walk_expr(self, expr);
-            }
-        }
-
-        fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-            intravisit::NestedVisitorMap::None
-        }
-    }
-
     /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
     fn check_unwrap_or_default(
         cx: &LateContext<'_>,
@@ -1801,14 +1774,14 @@ fn check_general_case<'tcx>(
     ) {
         if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind {
             if path.ident.as_str() == "len" {
-                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
+                let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
 
                 match ty.kind() {
                     ty::Slice(_) | ty::Array(_, _) => return,
                     _ => (),
                 }
 
-                if match_type(cx, ty, &paths::VEC) {
+                if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
                     return;
                 }
             }
@@ -1825,8 +1798,7 @@ fn check_general_case<'tcx>(
         if_chain! {
             if know_types.iter().any(|k| k.2.contains(&name));
 
-            let mut finder = FunCallFinder { cx: &cx, found: false };
-            if { finder.visit_expr(&arg); finder.found };
+            if is_lazyness_candidate(cx, arg);
             if !contains_return(&arg);
 
             let self_ty = cx.typeck_results().expr_ty(self_expr);
@@ -1909,7 +1881,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex
                         && (method_name.ident.name == sym!(as_str) || method_name.ident.name == sym!(as_ref))
                         && {
                             let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
-                            let base_type = walk_ptrs_ty(arg_type);
+                            let base_type = arg_type.peel_refs();
                             *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym!(string_type))
                         }
                     {
@@ -2170,7 +2142,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
 }
 
 fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(arg));
+    let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
 
     if let ty::Adt(_, subst) = obj_ty.kind() {
         let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
@@ -2201,7 +2173,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
     let arg = &args[1];
     if let Some(arglists) = method_chain_args(arg, &["chars"]) {
         let target = &arglists[0][0];
-        let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(target));
+        let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
         let ref_str = if *self_ty.kind() == ty::Str {
             ""
         } else if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
@@ -2229,7 +2201,7 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
 }
 
 fn lint_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
+    let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
     if is_type_diagnostic_item(cx, obj_ty, sym!(string_type)) {
         lint_string_extend(cx, expr, args);
     }
@@ -2412,7 +2384,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
         }
     } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym!(vec_type))
         || matches!(
-            &walk_ptrs_ty(cx.typeck_results().expr_ty(caller_expr)).kind(),
+            &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
             ty::Array(_, _)
         )
     {
@@ -2615,7 +2587,7 @@ fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
 
 /// lint use of `unwrap()` for `Option`s and `Result`s
 fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&unwrap_args[0]));
+    let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs();
 
     let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
         Some((UNWRAP_USED, "an Option", "None"))
@@ -2643,7 +2615,7 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E
 
 /// lint use of `expect()` for `Option`s and `Result`s
 fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
-    let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&expect_args[0]));
+    let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs();
 
     let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
         Some((EXPECT_USED, "an Option", "None"))
@@ -3162,7 +3134,7 @@ fn lint_chars_cmp(
         if segment.ident.name == sym!(Some);
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty_adjusted(&args[0][0]));
+            let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
 
             if *self_ty.kind() != ty::Str {
                 return false;
@@ -3374,7 +3346,7 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_
             INTO_ITER_ON_REF,
             method_span,
             &format!(
-                "this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`",
+                "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
                 method_name, kind,
             ),
             "call directly",
index 31517659c34dcbc9efa957fa7ba3c6f06603d1af..08b3eab9b7cdfe3f631b2471c91968a4219184b6 100644 (file)
@@ -1,78 +1,17 @@
-use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg};
-use if_chain::if_chain;
+use crate::utils::{eager_or_lazy, usage};
+use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 
 use super::UNNECESSARY_LAZY_EVALUATIONS;
 
-// Return true if the expression is an accessor of any of the arguments
-fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
-    params.iter().any(|arg| {
-        if_chain! {
-            if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
-            if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
-            if let [p, ..] = path.segments;
-            then {
-                ident.name == p.ident.name
-            } else {
-                false
-            }
-        }
-    })
-}
-
-fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
-    paths.iter().any(|candidate| match_qpath(path, candidate))
-}
-
-fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
-    match expr.kind {
-        // Closures returning literals can be unconditionally simplified
-        hir::ExprKind::Lit(_) => true,
-
-        hir::ExprKind::Index(ref object, ref index) => {
-            // arguments are not being indexed into
-            if expr_uses_argument(object, params) {
-                false
-            } else {
-                // arguments are not used as index
-                !expr_uses_argument(index, params)
-            }
-        },
-
-        // Reading fields can be simplified if the object is not an argument of the closure
-        hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
-
-        // Paths can be simplified if the root is not the argument, this also covers None
-        hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
-
-        // Calls to Some, Ok, Err can be considered literals if they don't derive an argument
-        hir::ExprKind::Call(ref func, ref args) => if_chain! {
-            if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
-            if let hir::ExprKind::Path(ref path) = func.kind;
-            if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
-            then {
-                // Recursively check all arguments
-                args.iter().all(|arg| can_simplify(arg, params, variant_calls))
-            } else {
-                false
-            }
-        },
-
-        // For anything more complex than the above, a closure is probably the right solution,
-        // or the case is handled by an other lint
-        _ => false,
-    }
-}
-
 /// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
 /// replaced with `<fn>(return value of simple closure)`
 pub(super) fn lint<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'_>,
     args: &'tcx [hir::Expr<'_>],
-    allow_variant_calls: bool,
     simplify_using: &str,
 ) {
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
@@ -81,10 +20,13 @@ pub(super) fn lint<'tcx>(
     if is_option || is_result {
         if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
             let body = cx.tcx.hir().body(eid);
-            let ex = &body.value;
-            let params = &body.params;
+            let body_expr = &body.value;
+
+            if usage::BindingUsageFinder::are_params_used(cx, body) {
+                return;
+            }
 
-            if can_simplify(ex, params, allow_variant_calls) {
+            if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
                 let msg = if is_option {
                     "unnecessary closure used to substitute value for `Option::None`"
                 } else {
@@ -101,7 +43,7 @@ pub(super) fn lint<'tcx>(
                         "{0}.{1}({2})",
                         snippet(cx, args[0].span, ".."),
                         simplify_using,
-                        snippet(cx, ex.span, ".."),
+                        snippet(cx, body_expr.span, ".."),
                     ),
                     Applicability::MachineApplicable,
                 );
index d4a50dd9013f01a8e8862399fc57c67a8275d900..909e79f661a6d83959d765c57ebbe7a7a7caa514 100644 (file)
@@ -17,7 +17,7 @@
 use crate::utils::{
     get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
     last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
-    span_lint_and_then, span_lint_hir_and_then, walk_ptrs_ty, SpanlessEq,
+    span_lint_and_then, span_lint_hir_and_then, SpanlessEq,
 };
 
 declare_clippy_lint! {
     /// if y != x {} // where both are floats
     ///
     /// // Good
-    /// let error = f64::EPSILON; // Use an epsilon for comparison
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
     /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error = std::f64::EPSILON;
-    /// if (y - 1.23f64).abs() < error { }
-    /// if (y - x).abs() > error { }
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (y - 1.23f64).abs() < error_margin { }
+    /// if (y - x).abs() > error_margin { }
     /// ```
     pub FLOAT_CMP,
     correctness,
     /// if x == ONE { } // where both are floats
     ///
     /// // Good
-    /// let error = f64::EPSILON; // Use an epsilon for comparison
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
     /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error = std::f64::EPSILON;
-    /// if (x - ONE).abs() < error { }
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (x - ONE).abs() < error_margin { }
     /// ```
     pub FLOAT_CMP_CONST,
     restriction,
@@ -411,16 +411,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                         if !is_comparing_arrays {
                             diag.span_suggestion(
                                 expr.span,
-                                "consider comparing them within some error",
+                                "consider comparing them within some margin of error",
                                 format!(
-                                    "({}).abs() {} error",
+                                    "({}).abs() {} error_margin",
                                     lhs - rhs,
                                     if op == BinOpKind::Eq { '<' } else { '>' }
                                 ),
                                 Applicability::HasPlaceholders, // snippet
                             );
                         }
-                        diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`");
+                        diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
                     });
                 } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
                     span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
@@ -561,7 +561,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 }
 
 fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let value = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind();
+    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
 
     if let ty::Array(arr_ty, _) = value {
         return matches!(arr_ty.kind(), ty::Float(_));
@@ -571,7 +571,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 }
 
 fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    matches!(&walk_ptrs_ty(cx.typeck_results().expr_ty(expr)).kind(), ty::Array(_, _))
+    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
 }
 
 fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
index 02789735c17a313b1aa152aa01eadc3df3bf1692..9cb1cfb915d5796734b2f94b6167709a53a93ea3 100644 (file)
@@ -377,8 +377,8 @@ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: N
             if let PatKind::Ident(_, ident, None) = arg.pat.kind {
                 let arg_name = ident.to_string();
 
-                if arg_name.starts_with('_') {
-                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
+                if let Some(arg_name) = arg_name.strip_prefix('_') {
+                    if let Some(correspondence) = registered_names.get(arg_name) {
                         span_lint(
                             cx,
                             DUPLICATE_UNDERSCORE_ARGUMENT,
@@ -386,7 +386,7 @@ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: N
                             &format!(
                                 "`{}` already exists, having another argument having almost the same \
                                  name makes code comprehension and documentation more difficult",
-                                arg_name[1..].to_owned()
+                                arg_name
                             ),
                         );
                     }
index 7423107e8f9453fb48906f151988894411a58c83..8a2dbdc50eaea36af1fa272d37b8a098d2b88963 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method, walk_ptrs_ty};
+use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
     /// `BtreeSet` rely on either the hash or the order of keys be unchanging,
     /// so having types with interior mutability is a bad idea.
     ///
-    /// **Known problems:** We don't currently account for `Rc` or `Arc`, so
-    /// this may yield false positives.
+    /// **Known problems:** It's correct to use a struct, that contains interior mutability
+    /// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
+    /// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
+    /// The `bytes` crate is a great example of this.
     ///
     /// **Example:**
     /// ```rust
@@ -96,7 +98,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::
 // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
 // generics (because the compiler cannot ensure immutability for unknown types).
 fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
-    let ty = walk_ptrs_ty(ty);
+    let ty = ty.peel_refs();
     if let Adt(def, substs) = ty.kind() {
         if [&paths::HASHMAP, &paths::BTREEMAP, &paths::HASHSET, &paths::BTREESET]
             .iter()
index 73eabd4207e773cbc80a0c003b11e1cc3e204cf5..bb44eeb6adc51b30b056e80306b4448479161427 100644 (file)
@@ -6,14 +6,16 @@
 
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp};
+use rustc_infer::traits::specialization_graph;
 use rustc_lint::{LateContext, LateLintPass, Lint};
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_middle::ty::{Ty, TypeFlags};
+use rustc_middle::ty::{AssocKind, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{InnerSpan, Span, DUMMY_SP};
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then};
+use crate::utils::{in_constant, qpath_res, span_lint_and_then};
+use if_chain::if_chain;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for declaration of `const` items which is interior
     "referencing `const` with interior mutability"
 }
 
-#[allow(dead_code)]
 #[derive(Copy, Clone)]
 enum Source {
     Item { item: Span },
-    Assoc { item: Span, ty: Span },
+    Assoc { item: Span },
     Expr { expr: Span },
 }
 
@@ -110,10 +111,15 @@ fn lint(&self) -> (&'static Lint, &'static str, Span) {
 }
 
 fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
-    if ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) || is_copy(cx, ty) {
-        // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
-        // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
-        // as well.
+    // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
+    // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
+    // 'unfrozen'. However, this code causes a false negative in which
+    // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell<T>`.
+    // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
+    // since it works when a pointer indirection involves (`Cell<*const T>`).
+    // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
+    // but I'm not sure whether it's a decent way, if possible.
+    if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) {
         return;
     }
 
@@ -127,11 +133,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
                 let const_kw_span = span.from_inner(InnerSpan::new(0, 5));
                 diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)");
             },
-            Source::Assoc { ty: ty_span, .. } => {
-                if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) {
-                    diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty));
-                }
-            },
+            Source::Assoc { .. } => (),
             Source::Expr { .. } => {
                 diag.help("assign this const to a local or static variable, and use the variable here");
             },
@@ -152,14 +154,10 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
         if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind {
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
-            verify_ty_bound(
-                cx,
-                ty,
-                Source::Assoc {
-                    ty: hir_ty.span,
-                    item: trait_item.span,
-                },
-            );
+            // Normalize assoc types because ones originated from generic params
+            // bounded other traits could have their bound.
+            let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+            verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span });
         }
     }
 
@@ -167,17 +165,50 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
         if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind {
             let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id);
             let item = cx.tcx.hir().expect_item(item_hir_id);
-            // Ensure the impl is an inherent impl.
-            if let ItemKind::Impl { of_trait: None, .. } = item.kind {
-                let ty = hir_ty_to_ty(cx.tcx, hir_ty);
-                verify_ty_bound(
-                    cx,
-                    ty,
-                    Source::Assoc {
-                        ty: hir_ty.span,
-                        item: impl_item.span,
-                    },
-                );
+
+            match &item.kind {
+                ItemKind::Impl {
+                    of_trait: Some(of_trait_ref),
+                    ..
+                } => {
+                    if_chain! {
+                        // Lint a trait impl item only when the definition is a generic type,
+                        // assuming a assoc const is not meant to be a interior mutable type.
+                        if let Some(of_trait_def_id) = of_trait_ref.trait_def_id();
+                        if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id)
+                            .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id);
+                        if cx
+                            .tcx
+                            .layout_of(cx.tcx.param_env(of_trait_def_id).and(
+                                // Normalize assoc types because ones originated from generic params
+                                // bounded other traits could have their bound at the trait defs;
+                                // and, in that case, the definition is *not* generic.
+                                cx.tcx.normalize_erasing_regions(
+                                    cx.tcx.param_env(of_trait_def_id),
+                                    cx.tcx.type_of(of_assoc_item.def_id),
+                                ),
+                            ))
+                            .is_err();
+                        then {
+                            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
+                            let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+                            verify_ty_bound(
+                                cx,
+                                normalized,
+                                Source::Assoc {
+                                    item: impl_item.span,
+                                },
+                            );
+                        }
+                    }
+                },
+                ItemKind::Impl { of_trait: None, .. } => {
+                    let ty = hir_ty_to_ty(cx.tcx, hir_ty);
+                    // Normalize assoc types originated from generic params.
+                    let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+                    verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span });
+                },
+                _ => (),
             }
         }
     }
index e99d0317ba2e8ca1b6e3bcf5744ec4071931c1da..73a99a3a2f870a3f7351ee1a3da2cc6828ad121b 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty};
+use crate::utils::{match_type, paths, span_lint};
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -30,7 +30,7 @@
 impl<'tcx> LateLintPass<'tcx> for OpenOptions {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if let ExprKind::MethodCall(ref path, _, ref arguments, _) = e.kind {
-            let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0]));
+            let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
             if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
                 let mut options = Vec::new();
                 get_open_options(cx, &arguments[0], &mut options);
@@ -58,7 +58,7 @@ enum OpenOption {
 
 fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
     if let ExprKind::MethodCall(ref path, _, ref arguments, _) = argument.kind {
-        let obj_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&arguments[0]));
+        let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
 
         // Only proceed if this is a call on some object of type std::fs::OpenOptions
         if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
index 9494efe736cce2a37f7efafdf211b62a6e075214..4a3eb9c983a11617114d4231a2b58e74133b1279 100644 (file)
@@ -1,6 +1,7 @@
 use crate::utils;
+use crate::utils::eager_or_lazy;
 use crate::utils::sugg::Sugg;
-use crate::utils::{match_type, paths, span_lint_and_sugg};
+use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg};
 use if_chain::if_chain;
 
 use rustc_errors::Applicability;
 declare_clippy_lint! {
     /// **What it does:**
     /// Lints usage of  `if let Some(v) = ... { y } else { x }` which is more
-    /// idiomatically done with `Option::map_or` (if the else bit is a simple
-    /// expression) or `Option::map_or_else` (if the else bit is a longer
-    /// block).
+    /// idiomatically done with `Option::map_or` (if the else bit is a pure
+    /// expression) or `Option::map_or_else` (if the else bit is an impure
+    /// expresion).
     ///
     /// **Why is this bad?**
     /// Using the dedicated functions of the Option type is clearer and
     /// more concise than an if let expression.
     ///
     /// **Known problems:**
-    /// This lint uses whether the block is just an expression or if it has
-    /// more statements to decide whether to use `Option::map_or` or
-    /// `Option::map_or_else`. If you have a single expression which calls
-    /// an expensive function, then it would be more efficient to use
-    /// `Option::map_or_else`, but this lint would suggest `Option::map_or`.
-    ///
-    /// Also, this lint uses a deliberately conservative metric for checking
+    /// This lint uses a deliberately conservative metric for checking
     /// if the inside of either body contains breaks or continues which will
     /// cause it to not suggest a fix if either block contains a loop with
     /// continues or breaks contained within the loop.
@@ -73,7 +68,7 @@
 fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
     if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
         path.ident.name.to_ident_string() == "ok"
-            && match_type(cx, &cx.typeck_results().expr_ty(&receiver), &paths::RESULT)
+            && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym!(result_type))
     } else {
         false
     }
@@ -92,6 +87,7 @@ struct OptionIfLetElseOccurence {
 struct ReturnBreakContinueMacroVisitor {
     seen_return_break_continue: bool,
 }
+
 impl ReturnBreakContinueMacroVisitor {
     fn new() -> ReturnBreakContinueMacroVisitor {
         ReturnBreakContinueMacroVisitor {
@@ -99,6 +95,7 @@ fn new() -> ReturnBreakContinueMacroVisitor {
         }
     }
 }
+
 impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
     type Map = Map<'tcx>;
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -157,7 +154,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
 }
 
 /// If this is the else body of an if/else expression, then we need to wrap
-/// it in curcly braces. Otherwise, we don't.
+/// it in curly braces. Otherwise, we don't.
 fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
         if let Some(Expr {
@@ -199,7 +196,10 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
 /// If this expression is the option if let/else construct we're detecting, then
 /// this function returns an `OptionIfLetElseOccurence` struct with details if
 /// this construct is found, or None if this construct is not found.
-fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OptionIfLetElseOccurence> {
+fn detect_option_if_let_else<'tcx>(
+    cx: &'_ LateContext<'tcx>,
+    expr: &'_ Expr<'tcx>,
+) -> Option<OptionIfLetElseOccurence> {
     if_chain! {
         if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
         if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
@@ -214,10 +214,7 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Op
             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
             let some_body = extract_body_from_arm(&arms[0])?;
             let none_body = extract_body_from_arm(&arms[1])?;
-            let method_sugg = match &none_body.kind {
-                ExprKind::Block(..) => "map_or_else",
-                _ => "map_or",
-            };
+            let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
             let wrap_braces = should_wrap_in_braces(cx, expr);
             let (as_ref, as_mut) = match &cond_expr.kind {
@@ -243,8 +240,8 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Op
     }
 }
 
-impl<'a> LateLintPass<'a> for OptionIfLetElse {
-    fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) {
+impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         if let Some(detection) = detect_option_if_let_else(cx, expr) {
             span_lint_and_sugg(
                 cx,
diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs
new file mode 100644 (file)
index 0000000..4077aba
--- /dev/null
@@ -0,0 +1,90 @@
+use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::map::Map;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
+    ///
+    /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// fn result_with_panic() -> Result<bool, String>
+    /// {
+    ///     panic!("error");
+    /// }
+    /// ```
+    pub PANIC_IN_RESULT_FN,
+    restriction,
+    "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
+}
+
+declare_lint_pass!(PanicInResultFn  => [PANIC_IN_RESULT_FN]);
+
+impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        fn_kind: FnKind<'tcx>,
+        _: &'tcx hir::FnDecl<'tcx>,
+        body: &'tcx hir::Body<'tcx>,
+        span: Span,
+        hir_id: hir::HirId,
+    ) {
+        if !matches!(fn_kind, FnKind::Closure(_))
+            && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type))
+        {
+            lint_impl_body(cx, span, body);
+        }
+    }
+}
+
+struct FindPanicUnimplementedUnreachable {
+    result: Vec<Span>,
+}
+
+impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+        if ["unimplemented", "unreachable", "panic", "todo"]
+            .iter()
+            .any(|fun| is_expn_of(expr.span, fun).is_some())
+        {
+            self.result.push(expr.span);
+        }
+        // and check sub-expressions
+        intravisit::walk_expr(self, expr);
+    }
+
+    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+        NestedVisitorMap::None
+    }
+}
+
+fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
+    let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() };
+    panics.visit_expr(&body.value);
+    if !panics.result.is_empty() {
+        span_lint_and_then(
+            cx,
+            PANIC_IN_RESULT_FN,
+            impl_span,
+            "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
+            move |diag| {
+                diag.help(
+                    "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
+                );
+                diag.span_note(panics.result, "return Err() instead of panicking");
+            },
+        );
+    }
+}
index b8583402928b424013200de3d1680352c3f34b12..6eeb031d383c856e1e195e865415abe4fd367cb8 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{match_type, paths, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -46,7 +46,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
             if path.ident.name == sym!(push);
             if args.len() == 2;
-            if match_type(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])), &paths::PATH_BUF);
+            if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::PATH_BUF);
             if let Some(get_index_arg) = args.get(1);
             if let ExprKind::Lit(ref lit) = get_index_arg.kind;
             if let LitKind::Str(ref path_lit, _) = lit.node;
index 57a45e628db61497c3f605838e75f2e220a8a003..1a7f36fbdadbb65cb43fb2042e3c6e024901ed87 100644 (file)
@@ -239,10 +239,9 @@ fn check_fn(
                         );
                         let mut app = Applicability::MaybeIncorrect;
 
-                        let mut call_snip = &snip[dot + 1..];
+                        let call_snip = &snip[dot + 1..];
                         // Machine applicable when `call_snip` looks like `foobar()`
-                        if call_snip.ends_with("()") {
-                            call_snip = call_snip[..call_snip.len()-2].trim();
+                        if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
                             if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
                                 app = Applicability::MachineApplicable;
                             }
index c0890018d46aba3abe8c9843d8a98cdffadf63dc..ae6013530091e23716caadc26a3bc0df5144be44 100644 (file)
@@ -1,5 +1,5 @@
 use crate::consts::{constant_context, Constant};
-use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
@@ -44,7 +44,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
             if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count);
             if !in_macro(receiver.span);
             then {
-                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver));
+                let ty = cx.typeck_results().expr_ty(&receiver).peel_refs();
                 if ty.is_str() {
                     span_lint_and_sugg(
                         cx,
index 087d50c90e671236b090fa44266aeb3a0cda2e07..225fe58906f739f90419e0b88f2054018daafec4 100644 (file)
@@ -25,7 +25,6 @@
     /// **Example:**
     /// ```rust
     /// # let x = 1;
-    ///
     /// // Bad
     /// let x = &x;
     ///
@@ -75,7 +74,9 @@
     /// names to bindings or introducing more scopes to contain the bindings.
     ///
     /// **Known problems:** This lint, as the other shadowing related lints,
-    /// currently only catches very simple patterns.
+    /// currently only catches very simple patterns. Note that
+    /// `allow`/`warn`/`deny`/`forbid` attributes only work on the function level
+    /// for this lint.
     ///
     /// **Example:**
     /// ```rust
index 7a659bf779c0c0dd028a94bca82c9e684266d22f..15b66684eab702b4f1c74874fa9f52540cbf81bc 100644 (file)
@@ -8,7 +8,7 @@
 use if_chain::if_chain;
 
 use crate::utils::SpanlessEq;
-use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg, walk_ptrs_ty};
+use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for string appends of the form `x = x + y` (without
@@ -134,7 +134,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 }
 
 fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    is_type_diagnostic_item(cx, walk_ptrs_ty(cx.typeck_results().expr_ty(e)), sym!(string_type))
+    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym!(string_type))
 }
 
 fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
index 47a73ca9a24cf7eb5b46f6ee03d377700d48ec34..54b38d9f4ced2142e3201034c82fce78d54ed9f5 100644 (file)
@@ -1,7 +1,6 @@
 use crate::utils::sugg::Sugg;
 use crate::utils::{
     differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then,
-    walk_ptrs_ty,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -194,7 +193,7 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<
     if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind {
         if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind {
             if eq_expr_value(cx, lhs1, lhs2) {
-                let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1));
+                let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
 
                 if matches!(ty.kind(), ty::Slice(_))
                     || matches!(ty.kind(), ty::Array(_, _))
index 6c6188d61ad52fc6f492453ec95667fba4e67662..a29a199b8c3aa52f0cec527d2baeae3b5304da4f 100644 (file)
     "redundant allocation"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec
+    ///
+    /// **Why is this bad?** Expressions such as Rc<String> have no advantage over Rc<str>, since
+    /// it is larger and involves an extra level of indirection, and doesn't implement Borrow<str>.
+    ///
+    /// While mutating a buffer type would still be possible with Rc::get_mut(), it only
+    /// works if there are no additional references yet, which defeats the purpose of
+    /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
+    /// type with an interior mutable container (such as RefCell or Mutex) would normally
+    /// be used.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust,ignore
+    /// # use std::rc::Rc;
+    /// fn foo(interned: Rc<String>) { ... }
+    /// ```
+    ///
+    /// Better:
+    ///
+    /// ```rust,ignore
+    /// fn foo(interned: Rc<str>) { ... }
+    /// ```
+    pub RC_BUFFER,
+    perf,
+    "shared ownership of a buffer type"
+}
+
 pub struct Types {
     vec_box_size_threshold: u64,
 }
 
-impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]);
+impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]);
 
 impl<'tcx> LateLintPass<'tcx> for Types {
     fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
@@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str])
     None
 }
 
+fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
+    if match_type_parameter(cx, qpath, &paths::STRING).is_some() {
+        return Some("str");
+    }
+    if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() {
+        return Some("std::ffi::OsStr");
+    }
+    if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() {
+        return Some("std::path::Path");
+    }
+    None
+}
+
 fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> {
     let last = last_path_segment(qpath);
     if_chain! {
@@ -321,14 +364,15 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, is_local: boo
                 if let Some(def_id) = res.opt_def_id() {
                     if Some(def_id) == cx.tcx.lang_items().owned_box() {
                         if let Some(span) = match_borrows_parameter(cx, qpath) {
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Box<&T>`",
                                 "try",
-                                snippet(cx, span, "..").to_string(),
-                                Applicability::MachineApplicable,
+                                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                                applicability,
                             );
                             return; // don't recurse into the type
                         }
@@ -345,14 +389,15 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, is_local: boo
                         }
                     } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
                         if let Some(span) = match_type_parameter(cx, qpath, &paths::RC) {
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Rc<Rc<T>>`",
                                 "try",
-                                snippet(cx, span, "..").to_string(),
-                                Applicability::MachineApplicable,
+                                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                                applicability,
                             );
                             return; // don't recurse into the type
                         }
@@ -368,25 +413,109 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, is_local: boo
                                 GenericArg::Type(ty) => ty.span,
                                 _ => return,
                             };
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Rc<Box<T>>`",
                                 "try",
-                                format!("Rc<{}>", snippet(cx, inner_span, "..")),
+                                format!(
+                                    "Rc<{}>",
+                                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                                ),
+                                applicability,
+                            );
+                            return; // don't recurse into the type
+                        }
+                        if let Some(alternate) = match_buffer_type(cx, qpath) {
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Rc<T>` when T is a buffer type",
+                                "try",
+                                format!("Rc<{}>", alternate),
+                                Applicability::MachineApplicable,
+                            );
+                            return; // don't recurse into the type
+                        }
+                        if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
+                            let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => match &ty.kind {
+                                    TyKind::Path(qpath) => qpath,
+                                    _ => return,
+                                },
+                                _ => return,
+                            };
+                            let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => ty.span,
+                                _ => return,
+                            };
+                            let mut applicability = Applicability::MachineApplicable;
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Rc<T>` when T is a buffer type",
+                                "try",
+                                format!(
+                                    "Rc<[{}]>",
+                                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                                ),
                                 Applicability::MachineApplicable,
                             );
                             return; // don't recurse into the type
                         }
                         if let Some(span) = match_borrows_parameter(cx, qpath) {
+                            let mut applicability = Applicability::MachineApplicable;
                             span_lint_and_sugg(
                                 cx,
                                 REDUNDANT_ALLOCATION,
                                 hir_ty.span,
                                 "usage of `Rc<&T>`",
                                 "try",
-                                snippet(cx, span, "..").to_string(),
+                                snippet_with_applicability(cx, span, "..", &mut applicability).to_string(),
+                                applicability,
+                            );
+                            return; // don't recurse into the type
+                        }
+                    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
+                        if let Some(alternate) = match_buffer_type(cx, qpath) {
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Arc<T>` when T is a buffer type",
+                                "try",
+                                format!("Arc<{}>", alternate),
+                                Applicability::MachineApplicable,
+                            );
+                            return; // don't recurse into the type
+                        }
+                        if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
+                            let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => match &ty.kind {
+                                    TyKind::Path(qpath) => qpath,
+                                    _ => return,
+                                },
+                                _ => return,
+                            };
+                            let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] {
+                                GenericArg::Type(ty) => ty.span,
+                                _ => return,
+                            };
+                            let mut applicability = Applicability::MachineApplicable;
+                            span_lint_and_sugg(
+                                cx,
+                                RC_BUFFER,
+                                hir_ty.span,
+                                "usage of `Arc<T>` when T is a buffer type",
+                                "try",
+                                format!(
+                                    "Arc<[{}]>",
+                                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                                ),
                                 Applicability::MachineApplicable,
                             );
                             return; // don't recurse into the type
@@ -546,7 +675,6 @@ fn check_ty_rptr(
                             // details.
                             return;
                         }
-                        let mut applicability = Applicability::MachineApplicable;
                         span_lint_and_sugg(
                             cx,
                             BORROWED_BOX,
@@ -556,8 +684,12 @@ fn check_ty_rptr(
                             format!(
                                 "&{}{}",
                                 ltopt,
-                                &snippet_with_applicability(cx, inner.span, "..", &mut applicability)
+                                &snippet(cx, inner.span, "..")
                             ),
+                            // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
+                            // because the trait impls of it will break otherwise;
+                            // and there may be other cases that result in invalid code.
+                            // For example, type coercion doesn't work nicely.
                             Applicability::Unspecified,
                         );
                         return; // don't recurse into the type
index 1c7e62ecd3d2c0c1e7fe6e8c149ad8ebbed8d4ae..0f8797243eca316ae24c9512246239b8c376fccd 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty};
+use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -81,7 +81,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         // check for `expect`
         if let Some(arglists) = method_chain_args(expr, &["expect"]) {
-            let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+            let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
             if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
                 || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
             {
@@ -91,7 +91,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 
         // check for `unwrap`
         if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
-            let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
+            let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
             if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
                 || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
             {
index 615440e15f384bca82bff66cdda8796cf7e5266a..4e4a206a583a2d12c85c7ed55f9365fe25306b1e 100644 (file)
@@ -72,7 +72,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                             cx,
                             USELESS_CONVERSION,
                             e.span,
-                            "useless conversion to the same type",
+                            &format!("useless conversion to the same type: `{}`", b),
                             "consider removing `.into()`",
                             sugg,
                             Applicability::MachineApplicable, // snippet
@@ -95,7 +95,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                             cx,
                             USELESS_CONVERSION,
                             e.span,
-                            "useless conversion to the same type",
+                            &format!("useless conversion to the same type: `{}`", b),
                             "consider removing `.into_iter()`",
                             sugg,
                             Applicability::MachineApplicable, // snippet
@@ -116,7 +116,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                                 cx,
                                 USELESS_CONVERSION,
                                 e.span,
-                                "useless conversion to the same type",
+                                &format!("useless conversion to the same type: `{}`", b),
                                 None,
                                 "consider removing `.try_into()`",
                             );
@@ -147,7 +147,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                                     cx,
                                     USELESS_CONVERSION,
                                     e.span,
-                                    "useless conversion to the same type",
+                                    &format!("useless conversion to the same type: `{}`", b),
                                     None,
                                     &hint,
                                 );
@@ -166,7 +166,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                                     cx,
                                     USELESS_CONVERSION,
                                     e.span,
-                                    "useless conversion to the same type",
+                                    &format!("useless conversion to the same type: `{}`", b),
                                     &sugg_msg,
                                     sugg.to_string(),
                                     Applicability::MachineApplicable, // snippet
index e4e65b5f4d420b4937572ac2b7ee020dac9a4228..0a58231558ede21304bf1ecc9f4b7cd1e60d29b5 100644 (file)
@@ -51,6 +51,8 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
 ///
 /// The `help` message can be optionally attached to a `Span`.
 ///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
 /// # Example
 ///
 /// ```ignore
@@ -87,6 +89,8 @@ pub fn span_lint_and_help<'a, T: LintContext>(
 /// The `note` message is presented separately from the main lint message
 /// and is attached to a specific span:
 ///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
 /// # Example
 ///
 /// ```ignore
@@ -126,6 +130,7 @@ pub fn span_lint_and_note<'a, T: LintContext>(
 /// Like `span_lint` but allows to add notes, help and suggestions using a closure.
 ///
 /// If you need to customize your lint output a lot, use this function.
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
 where
     F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
@@ -168,6 +173,10 @@ pub fn span_lint_hir_and_then(
 /// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
 /// 2)"`.
 ///
+/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
+///
+/// # Example
+///
 /// ```ignore
 /// error: This `.fold` can be more succinctly expressed as `.any`
 /// --> $DIR/methods.rs:390:13
diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs
new file mode 100644 (file)
index 0000000..6938d99
--- /dev/null
@@ -0,0 +1,128 @@
+//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
+//!
+//! Things to consider:
+//!  - has the expression side-effects?
+//!  - is the expression computationally expensive?
+//!
+//! See lints:
+//!  - unnecessary-lazy-evaluations
+//!  - or-fun-call
+//!  - option-if-let-else
+
+use crate::utils::is_ctor_or_promotable_const_function;
+use rustc_hir::def::{DefKind, Res};
+
+use rustc_hir::intravisit;
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+
+use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::hir::map::Map;
+
+/// Is the expr pure (is it free from side-effects)?
+/// This function is named so to stress that it isn't exhaustive and returns FNs.
+fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
+    match expr.kind {
+        ExprKind::Lit(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
+        ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
+        ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
+        ExprKind::Struct(_, fields, expr) => {
+            fields.iter().all(|f| identify_some_pure_patterns(f.expr))
+                && expr.map_or(true, |e| identify_some_pure_patterns(e))
+        },
+        ExprKind::Call(
+            &Expr {
+                kind:
+                    ExprKind::Path(QPath::Resolved(
+                        _,
+                        Path {
+                            res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
+                            ..
+                        },
+                    )),
+                ..
+            },
+            args,
+        ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
+        ExprKind::Block(
+            &Block {
+                stmts,
+                expr: Some(expr),
+                ..
+            },
+            _,
+        ) => stmts.is_empty() && identify_some_pure_patterns(expr),
+        ExprKind::Box(..)
+        | ExprKind::Array(..)
+        | ExprKind::Call(..)
+        | ExprKind::MethodCall(..)
+        | ExprKind::Binary(..)
+        | ExprKind::Unary(..)
+        | ExprKind::Cast(..)
+        | ExprKind::Type(..)
+        | ExprKind::DropTemps(..)
+        | ExprKind::Loop(..)
+        | ExprKind::Match(..)
+        | ExprKind::Closure(..)
+        | ExprKind::Block(..)
+        | ExprKind::Assign(..)
+        | ExprKind::AssignOp(..)
+        | ExprKind::Index(..)
+        | ExprKind::Break(..)
+        | ExprKind::Continue(..)
+        | ExprKind::Ret(..)
+        | ExprKind::InlineAsm(..)
+        | ExprKind::LlvmInlineAsm(..)
+        | ExprKind::Repeat(..)
+        | ExprKind::Yield(..)
+        | ExprKind::Err => false,
+    }
+}
+
+/// Identify some potentially computationally expensive patterns.
+/// This function is named so to stress that its implementation is non-exhaustive.
+/// It returns FNs and FPs.
+fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    // Searches an expression for method calls or function calls that aren't ctors
+    struct FunCallFinder<'a, 'tcx> {
+        cx: &'a LateContext<'tcx>,
+        found: bool,
+    }
+
+    impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
+        type Map = Map<'tcx>;
+
+        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+            let call_found = match &expr.kind {
+                // ignore enum and struct constructors
+                ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
+                ExprKind::MethodCall(..) => true,
+                _ => false,
+            };
+
+            if call_found {
+                self.found |= true;
+            }
+
+            if !self.found {
+                intravisit::walk_expr(self, expr);
+            }
+        }
+
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::None
+        }
+    }
+
+    let mut finder = FunCallFinder { cx, found: false };
+    finder.visit_expr(expr);
+    finder.found
+}
+
+pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
+}
+
+pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    identify_some_potentially_expensive_patterns(cx, expr)
+}
index 8fa5d22210a3691f585b16256dc54e5464827d85..bfe426a25eb893e9520e28bb7b22d46bda17dd2c 100644 (file)
@@ -1,6 +1,6 @@
 use crate::utils::{
-    is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint,
-    span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq,
+    is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
+    snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
@@ -11,7 +11,7 @@
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::hir_id::CRATE_HIR_ID;
 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind};
+use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
     "found collapsible `span_lint_and_then` calls"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item
+    /// and suggests to use `utils::is_type_diagnostic_item()` instead.
+    ///
+    /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// Bad:
+    /// ```rust,ignore
+    /// utils::match_type(cx, ty, &paths::VEC)
+    /// ```
+    ///
+    /// Good:
+    /// ```rust,ignore
+    /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))
+    /// ```
+    pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
+    internal,
+    "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -404,7 +427,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
             let fn_name = path.ident;
             if let Some(sugg) = self.map.get(&*fn_name.as_str());
-            let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
+            let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
             if match_type(cx, ty, &paths::EARLY_CONTEXT)
                 || match_type(cx, ty, &paths::LATE_CONTEXT);
             then {
@@ -437,7 +460,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             let args = arg_lists[1];
             if args.len() == 1;
             let self_arg = &args[0];
-            let self_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(self_arg));
+            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
             if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
             then {
                 span_lint_and_sugg(
@@ -652,3 +675,89 @@ fn suggest_note(
         Applicability::MachineApplicable,
     );
 }
+
+declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
+
+impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) {
+            return;
+        }
+
+        if_chain! {
+            // Check if this is a call to utils::match_type()
+            if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
+            if let ExprKind::Path(fn_qpath) = &fn_path.kind;
+            if match_qpath(&fn_qpath, &["utils", "match_type"]);
+            // Extract the path to the matched type
+            if let Some(segments) = path_to_matched_type(cx, ty_path);
+            let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
+            if let Some(ty_did) = path_to_res(cx, &segments[..]).and_then(|res| res.opt_def_id());
+            // Check if the matched type is a diagnostic item
+            let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
+            if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
+            then {
+                let cx_snippet = snippet(cx, context.span, "_");
+                let ty_snippet = snippet(cx, ty.span, "_");
+
+                span_lint_and_sugg(
+                    cx,
+                    MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
+                    expr.span,
+                    "usage of `utils::match_type()` on a type diagnostic item",
+                    "try",
+                    format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+}
+
+fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
+    use rustc_hir::ItemKind;
+
+    match &expr.kind {
+        ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
+        ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) {
+            Res::Local(hir_id) => {
+                let parent_id = cx.tcx.hir().get_parent_node(hir_id);
+                if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
+                    if let Some(init) = local.init {
+                        return path_to_matched_type(cx, init);
+                    }
+                }
+            },
+            Res::Def(DefKind::Const | DefKind::Static, def_id) => {
+                if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
+                    if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
+                        let body = cx.tcx.hir().body(body_id);
+                        return path_to_matched_type(cx, &body.value);
+                    }
+                }
+            },
+            _ => {},
+        },
+        ExprKind::Array(exprs) => {
+            let segments: Vec<SymbolStr> = exprs
+                .iter()
+                .filter_map(|expr| {
+                    if let ExprKind::Lit(lit) = &expr.kind {
+                        if let LitKind::Str(sym, _) = lit.node {
+                            return Some(sym.as_str());
+                        }
+                    }
+
+                    None
+                })
+                .collect();
+
+            if segments.len() == exprs.len() {
+                return Some(segments);
+            }
+        },
+        _ => {},
+    }
+
+    None
+}
index 3ebbfed6456273638409bceffc8f6faa9c572d02..ea52741b7cc42ffc5197dfeb75a7fba08039936b 100644 (file)
@@ -10,6 +10,7 @@
 pub mod conf;
 pub mod constants;
 mod diagnostics;
+pub mod eager_or_lazy;
 pub mod higher;
 mod hir_utils;
 pub mod inspector;
@@ -130,6 +131,9 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
 }
 
 /// Checks if type is struct, enum or union type with the given def path.
+///
+/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
     match ty.kind() {
         ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
@@ -138,6 +142,8 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 }
 
 /// Checks if the type is equal to a diagnostic item
+///
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
     match ty.kind() {
         ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
@@ -748,14 +754,6 @@ pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
     }
 }
 
-/// Returns the base type for references and raw pointers.
-pub fn walk_ptrs_ty(ty: Ty<'_>) -> Ty<'_> {
-    match ty.kind() {
-        ty::Ref(_, ty, _) => walk_ptrs_ty(ty),
-        _ => ty,
-    }
-}
-
 /// Returns the base type for references and raw pointers, and count reference
 /// depth.
 pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
index 3b031a552e5cec205dfa97c18662d3bd0202a074..1583afad208ab2c19167e20d46a997e4f5512716 100644 (file)
 pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
 pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
+pub const STRING: [&str; 3] = ["alloc", "string", "String"];
 pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
+pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
+pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
+pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
 pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
index 811fde388d15adbaeffbd998ddcf91ea91089f52..ec8b7e59b597633cf3cb19a4c725e26a34e522f0 100644 (file)
@@ -49,7 +49,7 @@ pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
     /// Convenience function around `hir_opt` for suggestions with a default
     /// text.
     pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
-        Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
+        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
     }
 
     /// Same as `hir`, but it adapts the applicability level by following rules:
index 4a64b935ac9b4c4c4ef5ec09b9a89934e5c382e1..ea1dc3be29ba027a95695f5d7c039901e252343e 100644 (file)
@@ -1,6 +1,8 @@
 use crate::utils::match_var;
 use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
 use rustc_hir::def::Res;
+use rustc_hir::intravisit;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{Expr, HirId, Path};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -108,3 +110,67 @@ pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool {
     walk_expr(&mut visitor, body);
     !visitor.used
 }
+
+pub struct ParamBindingIdCollector {
+    binding_hir_ids: Vec<hir::HirId>,
+}
+impl<'tcx> ParamBindingIdCollector {
+    fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
+        let mut finder = ParamBindingIdCollector {
+            binding_hir_ids: Vec::new(),
+        };
+        finder.visit_body(body);
+        finder.binding_hir_ids
+    }
+}
+impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
+    type Map = Map<'tcx>;
+
+    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+        if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind {
+            self.binding_hir_ids.push(hir_id);
+        }
+    }
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::None
+    }
+}
+
+pub struct BindingUsageFinder<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    binding_ids: Vec<hir::HirId>,
+    usage_found: bool,
+}
+impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
+    pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
+        let mut finder = BindingUsageFinder {
+            cx,
+            binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
+            usage_found: false,
+        };
+        finder.visit_body(body);
+        finder.usage_found
+    }
+}
+impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
+    type Map = Map<'tcx>;
+
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        if !self.usage_found {
+            intravisit::walk_expr(self, expr);
+        }
+    }
+
+    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+        if let hir::def::Res::Local(id) = path.res {
+            if self.binding_ids.contains(&id) {
+                self.usage_found = true;
+            }
+        }
+    }
+
+    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
+        intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+    }
+}
index e653240d049170b61d4755cd964185195e2edce7..fac63bcb9937838cb2020409275ec7f889d44db5 100644 (file)
@@ -322,11 +322,15 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
 }
 
 /// Given a format string that ends in a newline and its span, calculates the span of the
-/// newline.
+/// newline, or the format string itself if the format string consists solely of a newline.
 fn newline_span(fmtstr: &StrLit) -> Span {
     let sp = fmtstr.span;
     let contents = &fmtstr.symbol.as_str();
 
+    if *contents == r"\n" {
+        return sp;
+    }
+
     let newline_sp_hi = sp.hi()
         - match fmtstr.style {
             StrStyle::Cooked => BytePos(1),
index 3c782e9b17ff15c0db0947b54b766aa52f8326ef..21e0f6f4fc76668547332cffb78bc4a1f1d93a2d 100644 (file)
@@ -488,7 +488,7 @@ For `LateLintPass` lints:
 While most of Clippy's lint utils are documented, most of rustc's internals lack
 documentation currently. This is unfortunate, but in most cases you can probably
 get away with copying things from existing similar lints. If you are stuck,
-don't hesitate to ask on [Discord] or in the issue/PR.
+don't hesitate to ask on [Zulip] or in the issue/PR.
 
 [utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/mod.rs
 [if_chain]: https://docs.rs/if_chain/*/if_chain/
@@ -500,4 +500,4 @@ don't hesitate to ask on [Discord] or in the issue/PR.
 [nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
 [ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
 [ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
-[Discord]: https://discord.gg/rust-lang
+[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
index 9dd4c8a5f7a703b935629d733e7b1cfa0735b8e7..53c3d084dbc98624bbe114172cf5f44ff7e82441 100644 (file)
@@ -60,7 +60,7 @@ impl LateLintPass<'_> for MyStructLint {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if_chain! {
             // Check our expr is calling a method
-            if let hir::ExprKind::MethodCall(path, _, _args) = &expr.kind;
+            if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
             // Check the name of this method is `some_method`
             if path.ident.name == sym!(some_method);
             then {
index 6697835e950d982d1767421d138411d921ee37ba..9603023ed067150bcebad70d50cb1435f583b308 100644 (file)
     Lint {
         name: "invalid_atomic_ordering",
         group: "correctness",
-        desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences",
+        desc: "usage of invalid atomic ordering in atomic operations and memory fences",
         deprecation: None,
         module: "atomic_ordering",
     },
         deprecation: None,
         module: "methods",
     },
+    Lint {
+        name: "manual_strip",
+        group: "complexity",
+        desc: "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing",
+        deprecation: None,
+        module: "manual_strip",
+    },
     Lint {
         name: "manual_swap",
         group: "complexity",
         deprecation: None,
         module: "entry",
     },
+    Lint {
+        name: "map_err_ignore",
+        group: "pedantic",
+        desc: "`map_err` should not ignore the original error",
+        deprecation: None,
+        module: "map_err_ignore",
+    },
     Lint {
         name: "map_flatten",
         group: "pedantic",
         deprecation: None,
         module: "panic_unimplemented",
     },
+    Lint {
+        name: "panic_in_result_fn",
+        group: "restriction",
+        desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ",
+        deprecation: None,
+        module: "panic_in_result_fn",
+    },
     Lint {
         name: "panic_params",
         group: "style",
         deprecation: None,
         module: "ranges",
     },
+    Lint {
+        name: "rc_buffer",
+        group: "perf",
+        desc: "shared ownership of a buffer type",
+        deprecation: None,
+        module: "types",
+    },
     Lint {
         name: "redundant_allocation",
         group: "perf",
     },
     Lint {
         name: "verbose_bit_mask",
-        group: "style",
+        group: "pedantic",
         desc: "expressions where a bit mask is less readable than the corresponding method call",
         deprecation: None,
         module: "bit_mask",
diff --git a/tests/ui/atomic_ordering_exchange.rs b/tests/ui/atomic_ordering_exchange.rs
new file mode 100644 (file)
index 0000000..1ddc12f
--- /dev/null
@@ -0,0 +1,45 @@
+#![warn(clippy::invalid_atomic_ordering)]
+
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+fn main() {
+    // `compare_exchange` (not weak) testing
+    let x = AtomicUsize::new(0);
+
+    // Allowed ordering combos
+    let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Relaxed);
+    let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Acquire);
+    let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Relaxed);
+    let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Relaxed);
+    let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Acquire);
+    let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Relaxed);
+    let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Relaxed);
+    let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Acquire);
+    let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::SeqCst);
+
+    // AcqRel is always forbidden as a failure ordering
+    let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
+    let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
+    let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
+    let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
+    let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
+
+    // Release is always forbidden as a failure ordering
+    let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
+    let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
+    let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
+    let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
+    let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
+
+    // Release success order forbids failure order of Acquire or SeqCst
+    let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
+    let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
+
+    // Relaxed success order also forbids failure order of Acquire or SeqCst
+    let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
+    let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
+
+    // Acquire/AcqRel forbids failure order of SeqCst
+    let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
+    let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
+}
diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr
new file mode 100644 (file)
index 0000000..4b9bfef
--- /dev/null
@@ -0,0 +1,131 @@
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:21:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
+   |                                                         ^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:22:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
+   |                                                         ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:23:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
+   |                                                         ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:24:56
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
+   |                                                        ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:25:56
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
+   |                                                        ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:28:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
+   |                                                         ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:29:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
+   |                                                         ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:30:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
+   |                                                         ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:31:56
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
+   |                                                        ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:32:56
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
+   |                                                        ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
+  --> $DIR/atomic_ordering_exchange.rs:35:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
+   |                                                         ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
+  --> $DIR/atomic_ordering_exchange.rs:36:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
+   |                                                         ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
+  --> $DIR/atomic_ordering_exchange.rs:39:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
+   |                                                         ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
+  --> $DIR/atomic_ordering_exchange.rs:40:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
+   |                                                         ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire`
+  --> $DIR/atomic_ordering_exchange.rs:43:57
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
+   |                                                         ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel`
+  --> $DIR/atomic_ordering_exchange.rs:44:56
+   |
+LL |     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
+   |                                                        ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: aborting due to 16 previous errors
+
diff --git a/tests/ui/atomic_ordering_exchange_weak.rs b/tests/ui/atomic_ordering_exchange_weak.rs
new file mode 100644 (file)
index 0000000..5906990
--- /dev/null
@@ -0,0 +1,47 @@
+#![warn(clippy::invalid_atomic_ordering)]
+
+use std::sync::atomic::{AtomicPtr, Ordering};
+
+fn main() {
+    let ptr = &mut 5;
+    let ptr2 = &mut 10;
+    // `compare_exchange_weak` testing
+    let x = AtomicPtr::new(ptr);
+
+    // Allowed ordering combos
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Relaxed);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Acquire);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Relaxed);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Relaxed);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Acquire);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Relaxed);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Relaxed);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Acquire);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::SeqCst);
+
+    // AcqRel is always forbidden as a failure ordering
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel);
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel);
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel);
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel);
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel);
+
+    // Release is always forbidden as a failure ordering
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release);
+
+    // Release success order forbids failure order of Acquire or SeqCst
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire);
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst);
+
+    // Relaxed success order also forbids failure order of Acquire or SeqCst
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst);
+    let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire);
+
+    // Acquire/AcqRel forbids failure order of SeqCst
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst);
+    let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst);
+}
diff --git a/tests/ui/atomic_ordering_exchange_weak.stderr b/tests/ui/atomic_ordering_exchange_weak.stderr
new file mode 100644 (file)
index 0000000..de7026f
--- /dev/null
@@ -0,0 +1,131 @@
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:23:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel);
+   |                                                                   ^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:24:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel);
+   |                                                                   ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:25:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel);
+   |                                                                   ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:26:66
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel);
+   |                                                                  ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:27:66
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel);
+   |                                                                  ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:30:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release);
+   |                                                                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:31:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release);
+   |                                                                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:32:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release);
+   |                                                                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:33:66
+   |
+LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release);
+   |                                                                  ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:34:66
+   |
+LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release);
+   |                                                                  ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release`
+  --> $DIR/atomic_ordering_exchange_weak.rs:37:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire);
+   |                                                                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release`
+  --> $DIR/atomic_ordering_exchange_weak.rs:38:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst);
+   |                                                                   ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed`
+  --> $DIR/atomic_ordering_exchange_weak.rs:41:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst);
+   |                                                                   ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed`
+  --> $DIR/atomic_ordering_exchange_weak.rs:42:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire);
+   |                                                                   ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Acquire`
+  --> $DIR/atomic_ordering_exchange_weak.rs:45:67
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst);
+   |                                                                   ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `AcqRel`
+  --> $DIR/atomic_ordering_exchange_weak.rs:46:66
+   |
+LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst);
+   |                                                                  ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: aborting due to 16 previous errors
+
diff --git a/tests/ui/atomic_ordering_fetch_update.rs b/tests/ui/atomic_ordering_fetch_update.rs
new file mode 100644 (file)
index 0000000..550bdb0
--- /dev/null
@@ -0,0 +1,45 @@
+#![warn(clippy::invalid_atomic_ordering)]
+
+use std::sync::atomic::{AtomicIsize, Ordering};
+
+fn main() {
+    // `fetch_update` testing
+    let x = AtomicIsize::new(0);
+
+    // Allowed ordering combos
+    let _ = x.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Acquire, Ordering::Acquire, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Acquire, Ordering::Relaxed, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Release, Ordering::Relaxed, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::AcqRel, Ordering::Acquire, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::SeqCst, Ordering::Acquire, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old + 1));
+
+    // AcqRel is always forbidden as a failure ordering
+    let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1));
+
+    // Release is always forbidden as a failure ordering
+    let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1));
+
+    // Release success order forbids failure order of Acquire or SeqCst
+    let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1));
+
+    // Relaxed success order also forbids failure order of Acquire or SeqCst
+    let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1));
+
+    // Acquire/AcqRel forbids failure order of SeqCst
+    let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1));
+    let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1));
+}
diff --git a/tests/ui/atomic_ordering_fetch_update.stderr b/tests/ui/atomic_ordering_fetch_update.stderr
new file mode 100644 (file)
index 0000000..694548e
--- /dev/null
@@ -0,0 +1,131 @@
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:21:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:22:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:23:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:24:46
+   |
+LL |     let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1));
+   |                                              ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:25:46
+   |
+LL |     let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1));
+   |                                              ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:28:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:29:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:30:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:31:46
+   |
+LL |     let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1));
+   |                                              ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:32:46
+   |
+LL |     let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1));
+   |                                              ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+
+error: fetch_update's failure ordering may not be stronger than the success ordering of `Release`
+  --> $DIR/atomic_ordering_fetch_update.rs:35:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be stronger than the success ordering of `Release`
+  --> $DIR/atomic_ordering_fetch_update.rs:36:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed`
+  --> $DIR/atomic_ordering_fetch_update.rs:39:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed`
+  --> $DIR/atomic_ordering_fetch_update.rs:40:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering mode `Relaxed` instead
+
+error: fetch_update's failure ordering may not be stronger than the success ordering of `Acquire`
+  --> $DIR/atomic_ordering_fetch_update.rs:43:47
+   |
+LL |     let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1));
+   |                                               ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: fetch_update's failure ordering may not be stronger than the success ordering of `AcqRel`
+  --> $DIR/atomic_ordering_fetch_update.rs:44:46
+   |
+LL |     let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1));
+   |                                              ^^^^^^^^^^^^^^^^
+   |
+   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+
+error: aborting due to 16 previous errors
+
index 39f87510548503654c7a610837fed55c9ebc64de..9fcc9ece49bb7e78840a34078d960e342e466329 100644 (file)
 static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
 const ONCE_INIT: Once = Once::new();
 
-trait Trait<T>: Copy {
-    type NonCopyType;
+trait Trait<T> {
+    type AssocType;
 
     const ATOMIC: AtomicUsize;
+    const INPUT: T;
+    const ASSOC: Self::AssocType;
+
+    fn function() {
+        let _ = &Self::INPUT;
+        let _ = &Self::ASSOC;
+    }
 }
 
 impl Trait<u32> for u64 {
-    type NonCopyType = u16;
+    type AssocType = AtomicUsize;
 
     const ATOMIC: AtomicUsize = AtomicUsize::new(9);
+    const INPUT: u32 = 10;
+    const ASSOC: Self::AssocType = AtomicUsize::new(11);
+
+    fn function() {
+        let _ = &Self::INPUT;
+        let _ = &Self::ASSOC; //~ ERROR interior mutability
+    }
 }
 
 // This is just a pointer that can be safely dereferended,
index 5800af7e960f4da7247f652d0685bcaf3af32093..ed726a6b46e64b9e4b62cab9bc18f1c93baf93ba 100644 (file)
@@ -1,14 +1,22 @@
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:66:5
+  --> $DIR/borrow_interior_mutable_const.rs:44:18
+   |
+LL |         let _ = &Self::ASSOC; //~ ERROR interior mutability
+   |                  ^^^^^^^^^^^
+   |
+   = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
+   = help: assign this const to a local or static variable, and use the variable here
+
+error: a `const` item with interior mutability should not be borrowed
+  --> $DIR/borrow_interior_mutable_const.rs:80:5
    |
 LL |     ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
    |     ^^^^^^
    |
-   = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings`
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:67:16
+  --> $DIR/borrow_interior_mutable_const.rs:81:16
    |
 LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
    |                ^^^^^^
@@ -16,7 +24,7 @@ LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:70:22
+  --> $DIR/borrow_interior_mutable_const.rs:84:22
    |
 LL |     let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
    |                      ^^^^^^^^^
@@ -24,7 +32,7 @@ LL |     let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:71:25
+  --> $DIR/borrow_interior_mutable_const.rs:85:25
    |
 LL |     let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
    |                         ^^^^^^^^^
@@ -32,7 +40,7 @@ LL |     let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:72:27
+  --> $DIR/borrow_interior_mutable_const.rs:86:27
    |
 LL |     let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
    |                           ^^^^^^^^^
@@ -40,7 +48,7 @@ LL |     let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:73:26
+  --> $DIR/borrow_interior_mutable_const.rs:87:26
    |
 LL |     let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
    |                          ^^^^^^^^^
@@ -48,7 +56,7 @@ LL |     let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:84:14
+  --> $DIR/borrow_interior_mutable_const.rs:98:14
    |
 LL |     let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
    |              ^^^^^^^^^^^^
@@ -56,7 +64,7 @@ LL |     let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:85:14
+  --> $DIR/borrow_interior_mutable_const.rs:99:14
    |
 LL |     let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
    |              ^^^^^^^^^^^^
@@ -64,7 +72,7 @@ LL |     let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:86:19
+  --> $DIR/borrow_interior_mutable_const.rs:100:19
    |
 LL |     let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
    |                   ^^^^^^^^^^^^
@@ -72,7 +80,7 @@ LL |     let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:87:14
+  --> $DIR/borrow_interior_mutable_const.rs:101:14
    |
 LL |     let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    |              ^^^^^^^^^^^^
@@ -80,7 +88,7 @@ LL |     let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:88:13
+  --> $DIR/borrow_interior_mutable_const.rs:102:13
    |
 LL |     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
    |             ^^^^^^^^^^^^
@@ -88,7 +96,7 @@ LL |     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:94:13
+  --> $DIR/borrow_interior_mutable_const.rs:108:13
    |
 LL |     let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    |             ^^^^^^^^^^^^
@@ -96,7 +104,7 @@ LL |     let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:99:5
+  --> $DIR/borrow_interior_mutable_const.rs:113:5
    |
 LL |     CELL.set(2); //~ ERROR interior mutability
    |     ^^^^
@@ -104,7 +112,7 @@ LL |     CELL.set(2); //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:100:16
+  --> $DIR/borrow_interior_mutable_const.rs:114:16
    |
 LL |     assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
    |                ^^^^
@@ -112,7 +120,7 @@ LL |     assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:113:5
+  --> $DIR/borrow_interior_mutable_const.rs:127:5
    |
 LL |     u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
    |     ^^^^^^^^^^^
@@ -120,12 +128,12 @@ LL |     u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> $DIR/borrow_interior_mutable_const.rs:114:16
+  --> $DIR/borrow_interior_mutable_const.rs:128:16
    |
 LL |     assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
    |                ^^^^^^^^^^^
    |
    = help: assign this const to a local or static variable, and use the variable here
 
-error: aborting due to 16 previous errors
+error: aborting due to 17 previous errors
 
diff --git a/tests/ui/crashes/ice-2636.rs b/tests/ui/crashes/ice-2636.rs
deleted file mode 100644 (file)
index e0b5815..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#![allow(dead_code)]
-
-enum Foo {
-    A,
-    B,
-    C,
-}
-
-macro_rules! test_hash {
-    ($foo:expr, $($t:ident => $ord:expr),+ ) => {
-        use self::Foo::*;
-        match $foo {
-            $ ( & $t => $ord,
-            )*
-        };
-    };
-}
-
-fn main() {
-    let a = Foo::A;
-    test_hash!(&a, A => 0, B => 1, C => 2);
-}
diff --git a/tests/ui/crashes/ice-2636.stderr b/tests/ui/crashes/ice-2636.stderr
deleted file mode 100644 (file)
index 53799b4..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-error: you don't need to add `&` to both the expression and the patterns
-  --> $DIR/ice-2636.rs:12:9
-   |
-LL | /         match $foo {
-LL | |             $ ( & $t => $ord,
-LL | |             )*
-LL | |         };
-   | |_________^
-...
-LL |       test_hash!(&a, A => 0, B => 1, C => 2);
-   |       --------------------------------------- in this macro invocation
-   |
-   = note: `-D clippy::match-ref-pats` implied by `-D warnings`
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to previous error
-
index b4003ed8932d37bd214e7160db8757fc7cfbedd4..3afcdca2f04dd165b03f7fb7fcdf0e12d0867073 100644 (file)
@@ -34,60 +34,141 @@ macro_rules! declare_const {
 #[allow(clippy::declare_interior_mutable_const)]
 const ONCE_INIT: Once = Once::new();
 
-trait Trait<T>: Copy {
-    type NonCopyType;
-
+// a constant whose type is a concrete type should be linted at the definition site.
+trait ConcreteTypes {
     const ATOMIC: AtomicUsize; //~ ERROR interior mutable
     const INTEGER: u64;
     const STRING: String;
-    const SELF: Self; // (no error)
-    const INPUT: T;
-    //~^ ERROR interior mutable
-    //~| HELP consider requiring `T` to be `Copy`
-    const ASSOC: Self::NonCopyType;
-    //~^ ERROR interior mutable
-    //~| HELP consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
+    declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+}
 
-    const AN_INPUT: T = Self::INPUT;
-    //~^ ERROR interior mutable
-    //~| ERROR consider requiring `T` to be `Copy`
-    declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
+impl ConcreteTypes for u64 {
+    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
+    const INTEGER: u64 = 10;
+    const STRING: String = String::new();
 }
 
-trait Trait2 {
-    type CopyType: Copy;
+// a helper trait used below
+trait ConstDefault {
+    const DEFAULT: Self;
+}
+
+// a constant whose type is a generic type should be linted at the implementation site.
+trait GenericTypes<T, U> {
+    const TO_REMAIN_GENERIC: T;
+    const TO_BE_CONCRETE: U;
 
-    const SELF_2: Self;
-    //~^ ERROR interior mutable
-    //~| HELP consider requiring `Self` to be `Copy`
-    const ASSOC_2: Self::CopyType; // (no error)
+    const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC;
+    declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC);
 }
 
-// we don't lint impl of traits, because an impl has no power to change the interface.
-impl Trait<u32> for u64 {
-    type NonCopyType = u16;
+impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for u64 {
+    const TO_REMAIN_GENERIC: T = T::DEFAULT;
+    const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
+}
 
-    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
-    const INTEGER: u64 = 10;
-    const STRING: String = String::new();
-    const SELF: Self = 11;
-    const INPUT: u32 = 12;
-    const ASSOC: Self::NonCopyType = 13;
+// a helper type used below
+struct Wrapper<T>(T);
+
+// a constant whose type is an associated type should be linted at the implementation site, too.
+trait AssocTypes {
+    type ToBeFrozen;
+    type ToBeUnfrozen;
+    type ToBeGenericParam;
+
+    const TO_BE_FROZEN: Self::ToBeFrozen;
+    const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
+    const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
+    // to ensure it can handle things when a generic type remains after normalization.
+    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
 }
 
-struct Local<T, U>(T, U);
+impl<T: ConstDefault> AssocTypes for Vec<T> {
+    type ToBeFrozen = u16;
+    type ToBeUnfrozen = AtomicUsize;
+    type ToBeGenericParam = T;
+
+    const TO_BE_FROZEN: Self::ToBeFrozen = 12;
+    const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
+    const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
+    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
+}
+
+// a helper trait used below
+trait AssocTypesHelper {
+    type NotToBeBounded;
+    type ToBeBounded;
+
+    const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
+}
 
-impl<T: Trait2 + Trait<u32>, U: Trait2> Local<T, U> {
-    const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
+// a constant whose type is an assoc type originated from a generic param bounded at the definition
+// site should be linted at there.
+trait AssocTypesFromGenericParam<T>
+where
+    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+    const NOT_BOUNDED: T::NotToBeBounded;
+    const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
+}
+
+impl<T> AssocTypesFromGenericParam<T> for u64
+where
+    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+    // an associated type could remain unknown in a trait impl.
+    const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
+    const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
+}
+
+// a constant whose type is `Self` should be linted at the implementation site as well.
+// (`Option` requires `Sized` bound.)
+trait SelfType: Sized {
+    const SELF: Self;
+    // this was the one in the original issue (#5050).
+    const WRAPPED_SELF: Option<Self>;
+}
+
+impl SelfType for u64 {
+    const SELF: Self = 16;
+    const WRAPPED_SELF: Option<Self> = Some(20);
+}
+
+impl SelfType for AtomicUsize {
+    // this (interior mutable `Self` const) exists in `parking_lot`.
+    // `const_trait_impl` will replace it in the future, hopefully.
+    const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
+    const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
+}
+
+// Even though a constant contains a generic type, if it also have a interior mutable type,
+// it should be linted at the definition site.
+trait BothOfCellAndGeneric<T> {
+    // this is a false negative in the current implementation.
+    const DIRECT: Cell<T>;
+    const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
+}
+
+impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 {
+    const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
+    const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
+}
+
+struct Local<T>(T);
+
+// a constant in an inherent impl are essentially the same as a normal const item
+// except there can be a generic or associated type.
+impl<T> Local<T>
+where
+    T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+    const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
     const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
-    const T_SELF: T = T::SELF_2;
-    const U_SELF: U = U::SELF_2;
-    //~^ ERROR interior mutable
-    //~| HELP consider requiring `U` to be `Copy`
-    const T_ASSOC: T::NonCopyType = T::ASSOC;
-    //~^ ERROR interior mutable
-    //~| HELP consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
-    const U_ASSOC: U::CopyType = U::ASSOC_2;
+
+    const GENERIC_TYPE: T = T::DEFAULT;
+
+    const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
+    const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
 }
 
 fn main() {}
index 6a9a57361f9f39fe1c762988bfdc4e6b632898a5..5cb10be88d89cde900cfaa62c2861852dd7bde8a 100644 (file)
@@ -36,75 +36,75 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:40:5
+  --> $DIR/declare_interior_mutable_const.rs:39:5
    |
 LL |     const ATOMIC: AtomicUsize; //~ ERROR interior mutable
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:44:5
+  --> $DIR/declare_interior_mutable_const.rs:16:9
+   |
+LL |         const $name: $ty = $e;
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+   |     ----------------------------------------------------------- in this macro invocation
    |
-LL |     const INPUT: T;
-   |     ^^^^^^^^^^^^^-^
-   |                  |
-   |                  consider requiring `T` to be `Copy`
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:47:5
+  --> $DIR/declare_interior_mutable_const.rs:67:5
    |
-LL |     const ASSOC: Self::NonCopyType;
-   |     ^^^^^^^^^^^^^-----------------^
-   |                  |
-   |                  consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy`
+LL |     const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:51:5
+  --> $DIR/declare_interior_mutable_const.rs:92:5
    |
-LL |     const AN_INPUT: T = Self::INPUT;
-   |     ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^
-   |                     |
-   |                     consider requiring `T` to be `Copy`
+LL |     const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:16:9
+  --> $DIR/declare_interior_mutable_const.rs:93:5
    |
-LL |         const $name: $ty = $e;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
-...
-LL |     declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable
-   |     ----------------------------------------------- in this macro invocation
+LL |     const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/declare_interior_mutable_const.rs:112:5
    |
-   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+LL |     const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/declare_interior_mutable_const.rs:140:5
+   |
+LL |     const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:60:5
+  --> $DIR/declare_interior_mutable_const.rs:141:5
    |
-LL |     const SELF_2: Self;
-   |     ^^^^^^^^^^^^^^----^
-   |                   |
-   |                   consider requiring `Self` to be `Copy`
+LL |     const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:81:5
+  --> $DIR/declare_interior_mutable_const.rs:149:5
    |
-LL |     const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:84:5
+  --> $DIR/declare_interior_mutable_const.rs:165:5
    |
-LL |     const U_SELF: U = U::SELF_2;
-   |     ^^^^^^^^^^^^^^-^^^^^^^^^^^^^
-   |                   |
-   |                   consider requiring `U` to be `Copy`
+LL |     const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:87:5
+  --> $DIR/declare_interior_mutable_const.rs:171:5
    |
-LL |     const T_ASSOC: T::NonCopyType = T::ASSOC;
-   |     ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^
-   |                    |
-   |                    consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy`
+LL |     const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 13 previous errors
+error: aborting due to 15 previous errors
 
index 9181d789d4fb1a95f6b7f01505b6014fad9660c0..6b5bcdaa78e2762cd82fc68130cdb4405d217e03 100644 (file)
@@ -1,5 +1,6 @@
 #![warn(clippy::drop_ref)]
 #![allow(clippy::toplevel_ref_arg)]
+#![allow(clippy::map_err_ignore)]
 
 use std::mem::drop;
 
index 35ae88b78a4c5d28b9cfe56de106c8bf863e5c54..7974bf56d44cf582e1b80e98ecf4c31c4aeb226e 100644 (file)
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:9:5
+  --> $DIR/drop_ref.rs:10:5
    |
 LL |     drop(&SomeStruct);
    |     ^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::drop-ref` implied by `-D warnings`
 note: argument has type `&SomeStruct`
-  --> $DIR/drop_ref.rs:9:10
+  --> $DIR/drop_ref.rs:10:10
    |
 LL |     drop(&SomeStruct);
    |          ^^^^^^^^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:12:5
+  --> $DIR/drop_ref.rs:13:5
    |
 LL |     drop(&owned1);
    |     ^^^^^^^^^^^^^
    |
 note: argument has type `&SomeStruct`
-  --> $DIR/drop_ref.rs:12:10
+  --> $DIR/drop_ref.rs:13:10
    |
 LL |     drop(&owned1);
    |          ^^^^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:13:5
+  --> $DIR/drop_ref.rs:14:5
    |
 LL |     drop(&&owned1);
    |     ^^^^^^^^^^^^^^
    |
 note: argument has type `&&SomeStruct`
-  --> $DIR/drop_ref.rs:13:10
+  --> $DIR/drop_ref.rs:14:10
    |
 LL |     drop(&&owned1);
    |          ^^^^^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:14:5
+  --> $DIR/drop_ref.rs:15:5
    |
 LL |     drop(&mut owned1);
    |     ^^^^^^^^^^^^^^^^^
    |
 note: argument has type `&mut SomeStruct`
-  --> $DIR/drop_ref.rs:14:10
+  --> $DIR/drop_ref.rs:15:10
    |
 LL |     drop(&mut owned1);
    |          ^^^^^^^^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:18:5
+  --> $DIR/drop_ref.rs:19:5
    |
 LL |     drop(reference1);
    |     ^^^^^^^^^^^^^^^^
    |
 note: argument has type `&SomeStruct`
-  --> $DIR/drop_ref.rs:18:10
+  --> $DIR/drop_ref.rs:19:10
    |
 LL |     drop(reference1);
    |          ^^^^^^^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:21:5
+  --> $DIR/drop_ref.rs:22:5
    |
 LL |     drop(reference2);
    |     ^^^^^^^^^^^^^^^^
    |
 note: argument has type `&mut SomeStruct`
-  --> $DIR/drop_ref.rs:21:10
+  --> $DIR/drop_ref.rs:22:10
    |
 LL |     drop(reference2);
    |          ^^^^^^^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:24:5
+  --> $DIR/drop_ref.rs:25:5
    |
 LL |     drop(reference3);
    |     ^^^^^^^^^^^^^^^^
    |
 note: argument has type `&SomeStruct`
-  --> $DIR/drop_ref.rs:24:10
+  --> $DIR/drop_ref.rs:25:10
    |
 LL |     drop(reference3);
    |          ^^^^^^^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:29:5
+  --> $DIR/drop_ref.rs:30:5
    |
 LL |     drop(&val);
    |     ^^^^^^^^^^
    |
 note: argument has type `&T`
-  --> $DIR/drop_ref.rs:29:10
+  --> $DIR/drop_ref.rs:30:10
    |
 LL |     drop(&val);
    |          ^^^^
 
 error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing.
-  --> $DIR/drop_ref.rs:37:5
+  --> $DIR/drop_ref.rs:38:5
    |
 LL |     std::mem::drop(&SomeStruct);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: argument has type `&SomeStruct`
-  --> $DIR/drop_ref.rs:37:20
+  --> $DIR/drop_ref.rs:38:20
    |
 LL |     std::mem::drop(&SomeStruct);
    |                    ^^^^^^^^^^^
index 2d454e8e70de5ab173e7a0d511f1872f896cdac8..f7c380fc915c06a14f595c6b3a9ce66c16382ce5 100644 (file)
@@ -2,34 +2,34 @@ error: strict comparison of `f32` or `f64`
   --> $DIR/float_cmp.rs:65:5
    |
 LL |     ONE as f64 != 2.0;
-   |     ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error`
+   |     ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
    |
    = note: `-D clippy::float-cmp` implied by `-D warnings`
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
   --> $DIR/float_cmp.rs:70:5
    |
 LL |     x == 1.0;
-   |     ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error`
+   |     ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
   --> $DIR/float_cmp.rs:73:5
    |
 LL |     twice(x) != twice(ONE as f64);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
   --> $DIR/float_cmp.rs:93:5
    |
 LL |     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` arrays
   --> $DIR/float_cmp.rs:98:5
@@ -37,15 +37,15 @@ error: strict comparison of `f32` or `f64` arrays
 LL |     a1 == a2;
    |     ^^^^^^^^
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
   --> $DIR/float_cmp.rs:99:5
    |
 LL |     a1[0] == a2[0];
-   |     ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error`
+   |     ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: aborting due to 6 previous errors
 
index 19dc4a284b726744b219bd4aa9f2d55e40fa130a..5d0455363e8e02dc2167dfe6833902751267876f 100644 (file)
@@ -2,58 +2,58 @@ error: strict comparison of `f32` or `f64` constant
   --> $DIR/float_cmp_const.rs:20:5
    |
 LL |     1f32 == ONE;
-   |     ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error`
+   |     ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin`
    |
    = note: `-D clippy::float-cmp-const` implied by `-D warnings`
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
   --> $DIR/float_cmp_const.rs:21:5
    |
 LL |     TWO == ONE;
-   |     ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error`
+   |     ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
   --> $DIR/float_cmp_const.rs:22:5
    |
 LL |     TWO != ONE;
-   |     ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error`
+   |     ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
   --> $DIR/float_cmp_const.rs:23:5
    |
 LL |     ONE + ONE == TWO;
-   |     ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error`
+   |     ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
   --> $DIR/float_cmp_const.rs:25:5
    |
 LL |     x as f32 == ONE;
-   |     ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error`
+   |     ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
   --> $DIR/float_cmp_const.rs:28:5
    |
 LL |     v == ONE;
-   |     ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error`
+   |     ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
   --> $DIR/float_cmp_const.rs:29:5
    |
 LL |     v != ONE;
-   |     ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error`
+   |     ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant arrays
   --> $DIR/float_cmp_const.rs:61:5
@@ -61,7 +61,7 @@ error: strict comparison of `f32` or `f64` constant arrays
 LL |     NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error`
+   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: aborting due to 8 previous errors
 
index 000d5269930ba8f29a69a8c496c7cded649a14dc..ca8ca53c80c3f436956fef359df23a0778ebd111 100644 (file)
@@ -15,7 +15,8 @@ fn main() {
     x[3]; // Ok, should not produce stderr.
 
     let y = &x;
-    y[0];
+    y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
+    y[4]; // Ok, rustc will handle references too.
 
     let v = vec![0; 5];
     v[0];
index 2b3f9be2dfb9b030da728450f37fc3fac103268d..2f6c9e2f4e5a09c4f4d38913515f255c19850d08 100644 (file)
@@ -8,15 +8,7 @@ LL |     x[index];
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic.
-  --> $DIR/indexing_slicing_index.rs:18:5
-   |
-LL |     y[0];
-   |     ^^^^
-   |
-   = help: Consider using `.get(n)` or `.get_mut(n)` instead
-
-error: indexing may panic.
-  --> $DIR/indexing_slicing_index.rs:21:5
+  --> $DIR/indexing_slicing_index.rs:22:5
    |
 LL |     v[0];
    |     ^^^^
@@ -24,7 +16,7 @@ LL |     v[0];
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic.
-  --> $DIR/indexing_slicing_index.rs:22:5
+  --> $DIR/indexing_slicing_index.rs:23:5
    |
 LL |     v[10];
    |     ^^^^^
@@ -32,7 +24,7 @@ LL |     v[10];
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic.
-  --> $DIR/indexing_slicing_index.rs:23:5
+  --> $DIR/indexing_slicing_index.rs:24:5
    |
 LL |     v[1 << 3];
    |     ^^^^^^^^^
@@ -40,7 +32,7 @@ LL |     v[1 << 3];
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic.
-  --> $DIR/indexing_slicing_index.rs:29:5
+  --> $DIR/indexing_slicing_index.rs:30:5
    |
 LL |     v[N];
    |     ^^^^
@@ -48,12 +40,12 @@ LL |     v[N];
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic.
-  --> $DIR/indexing_slicing_index.rs:30:5
+  --> $DIR/indexing_slicing_index.rs:31:5
    |
 LL |     v[M];
    |     ^^^^
    |
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
-error: aborting due to 7 previous errors
+error: aborting due to 6 previous errors
 
index ec6c157ac1a2659510a80d6f36355d5a41b90041..2231deee833ee362be885eaaf0a7ed3d321209a2 100644 (file)
@@ -71,29 +71,17 @@ LL |     &x[1..][..5];
    |
    = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
 
-error: slicing may panic.
-  --> $DIR/indexing_slicing_slice.rs:24:6
-   |
-LL |     &y[1..2];
-   |      ^^^^^^^
-   |
-   = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
-
-error: slicing may panic.
-  --> $DIR/indexing_slicing_slice.rs:25:6
+error: range is out of bounds
+  --> $DIR/indexing_slicing_slice.rs:25:12
    |
 LL |     &y[0..=4];
-   |      ^^^^^^^^
-   |
-   = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead
+   |            ^
 
-error: slicing may panic.
-  --> $DIR/indexing_slicing_slice.rs:26:6
+error: range is out of bounds
+  --> $DIR/indexing_slicing_slice.rs:26:11
    |
 LL |     &y[..=4];
-   |      ^^^^^^^
-   |
-   = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
+   |           ^
 
 error: slicing may panic.
   --> $DIR/indexing_slicing_slice.rs:31:6
@@ -133,5 +121,5 @@ LL |     &v[..100];
    |
    = help: Consider using `.get(..n)`or `.get_mut(..n)` instead
 
-error: aborting due to 17 previous errors
+error: aborting due to 16 previous errors
 
index 1cd6400b0195baedc7a1e09d8849859d2361f1a1..28003b365bbd50f675dd7fb11bf4759a3badb243 100644 (file)
@@ -1,4 +1,4 @@
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
   --> $DIR/into_iter_on_ref.rs:14:30
    |
 LL |     let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
@@ -6,157 +6,157 @@ LL |     let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter()
    |
    = note: `-D clippy::into-iter-on-ref` implied by `-D warnings`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
   --> $DIR/into_iter_on_ref.rs:15:46
    |
 LL |     let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter()
    |                                              ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
   --> $DIR/into_iter_on_ref.rs:16:41
    |
 LL |     let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
    |                                         ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice`
   --> $DIR/into_iter_on_ref.rs:17:44
    |
 LL |     let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter()
    |                                            ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
   --> $DIR/into_iter_on_ref.rs:19:32
    |
 LL |     let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
    |                                ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
   --> $DIR/into_iter_on_ref.rs:20:36
    |
 LL |     let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter()
    |                                    ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array`
   --> $DIR/into_iter_on_ref.rs:21:40
    |
 LL |     let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut()
    |                                        ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option`
   --> $DIR/into_iter_on_ref.rs:23:24
    |
 LL |     let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter()
    |                        ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option`
   --> $DIR/into_iter_on_ref.rs:24:28
    |
 LL |     let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut()
    |                            ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result`
   --> $DIR/into_iter_on_ref.rs:25:32
    |
 LL |     let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter()
    |                                ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result`
   --> $DIR/into_iter_on_ref.rs:26:37
    |
 LL |     let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut()
    |                                     ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec`
   --> $DIR/into_iter_on_ref.rs:27:34
    |
 LL |     let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                  ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec`
   --> $DIR/into_iter_on_ref.rs:28:38
    |
 LL |     let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
    |                                      ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap`
   --> $DIR/into_iter_on_ref.rs:29:44
    |
 LL |     let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                            ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap`
   --> $DIR/into_iter_on_ref.rs:30:48
    |
 LL |     let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
    |                                                ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque`
   --> $DIR/into_iter_on_ref.rs:31:39
    |
 LL |     let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                       ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque`
   --> $DIR/into_iter_on_ref.rs:32:43
    |
 LL |     let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
    |                                           ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList`
   --> $DIR/into_iter_on_ref.rs:33:41
    |
 LL |     let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                         ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList`
   --> $DIR/into_iter_on_ref.rs:34:45
    |
 LL |     let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
    |                                             ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap`
   --> $DIR/into_iter_on_ref.rs:35:43
    |
 LL |     let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                           ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap`
+error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap`
   --> $DIR/into_iter_on_ref.rs:36:47
    |
 LL |     let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut()
    |                                               ^^^^^^^^^ help: call directly: `iter_mut`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet`
   --> $DIR/into_iter_on_ref.rs:38:39
    |
 LL |     let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                       ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap`
   --> $DIR/into_iter_on_ref.rs:39:41
    |
 LL |     let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                         ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet`
   --> $DIR/into_iter_on_ref.rs:40:38
    |
 LL |     let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter()
    |                                      ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path`
   --> $DIR/into_iter_on_ref.rs:41:43
    |
 LL |     let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter()
    |                                           ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf`
   --> $DIR/into_iter_on_ref.rs:42:47
    |
 LL |     let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter()
    |                                               ^^^^^^^^^ help: call directly: `iter`
 
-error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array`
+error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array`
   --> $DIR/into_iter_on_ref.rs:44:26
    |
 LL |     let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter()
index 802beeb4be6b18360bd5f6e59defababa1319c74..32a67f181df435ed1157c5d00f6196495509668a 100644 (file)
@@ -33,6 +33,7 @@ fn issue985_alt() -> i32 {
     x
 }
 
+#[allow(clippy::manual_strip)]
 fn issue975() -> String {
     let mut udn = "dummy".to_string();
     if udn.starts_with("uuid:") {
index c53a63a541bc9d9045f62a540620d2df68077606..7de560c73486bf7e622523d4af6036b8ac4b5163 100644 (file)
@@ -1,5 +1,5 @@
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:63:5
+  --> $DIR/let_if_seq.rs:64:5
    |
 LL | /     let mut foo = 0;
 LL | |     if f() {
@@ -11,7 +11,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:68:5
+  --> $DIR/let_if_seq.rs:69:5
    |
 LL | /     let mut bar = 0;
 LL | |     if f() {
@@ -25,7 +25,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:76:5
+  --> $DIR/let_if_seq.rs:77:5
    |
 LL | /     let quz;
 LL | |     if f() {
@@ -36,7 +36,7 @@ LL | |     }
    | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:105:5
+  --> $DIR/let_if_seq.rs:106:5
    |
 LL | /     let mut baz = 0;
 LL | |     if f() {
diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs
new file mode 100644 (file)
index 0000000..cbb84eb
--- /dev/null
@@ -0,0 +1,66 @@
+#![warn(clippy::manual_strip)]
+
+fn main() {
+    let s = "abc";
+
+    if s.starts_with("ab") {
+        str::to_string(&s["ab".len()..]);
+        s["ab".len()..].to_string();
+
+        str::to_string(&s[2..]);
+        s[2..].to_string();
+    }
+
+    if s.ends_with("bc") {
+        str::to_string(&s[..s.len() - "bc".len()]);
+        s[..s.len() - "bc".len()].to_string();
+
+        str::to_string(&s[..s.len() - 2]);
+        s[..s.len() - 2].to_string();
+    }
+
+    // Character patterns
+    if s.starts_with('a') {
+        str::to_string(&s[1..]);
+        s[1..].to_string();
+    }
+
+    // Variable prefix
+    let prefix = "ab";
+    if s.starts_with(prefix) {
+        str::to_string(&s[prefix.len()..]);
+    }
+
+    // Constant prefix
+    const PREFIX: &str = "ab";
+    if s.starts_with(PREFIX) {
+        str::to_string(&s[PREFIX.len()..]);
+        str::to_string(&s[2..]);
+    }
+
+    // Constant target
+    const TARGET: &str = "abc";
+    if TARGET.starts_with(prefix) {
+        str::to_string(&TARGET[prefix.len()..]);
+    }
+
+    // String target - not mutated.
+    let s1: String = "abc".into();
+    if s1.starts_with("ab") {
+        s1[2..].to_uppercase();
+    }
+
+    // String target - mutated. (Don't lint.)
+    let mut s2: String = "abc".into();
+    if s2.starts_with("ab") {
+        s2.push('d');
+        s2[2..].to_uppercase();
+    }
+
+    // Target not stripped. (Don't lint.)
+    let s3 = String::from("abcd");
+    let s4 = String::from("efgh");
+    if s3.starts_with("ab") {
+        s4[2..].to_string();
+    }
+}
diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr
new file mode 100644 (file)
index 0000000..1352a87
--- /dev/null
@@ -0,0 +1,132 @@
+error: stripping a prefix manually
+  --> $DIR/manual_strip.rs:7:24
+   |
+LL |         str::to_string(&s["ab".len()..]);
+   |                        ^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::manual-strip` implied by `-D warnings`
+note: the prefix was tested here
+  --> $DIR/manual_strip.rs:6:5
+   |
+LL |     if s.starts_with("ab") {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+   |
+LL |     if let Some(<stripped>) = s.strip_prefix("ab") {
+LL |         str::to_string(<stripped>);
+LL |         <stripped>.to_string();
+LL | 
+LL |         str::to_string(<stripped>);
+LL |         <stripped>.to_string();
+   |
+
+error: stripping a suffix manually
+  --> $DIR/manual_strip.rs:15:24
+   |
+LL |         str::to_string(&s[..s.len() - "bc".len()]);
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the suffix was tested here
+  --> $DIR/manual_strip.rs:14:5
+   |
+LL |     if s.ends_with("bc") {
+   |     ^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_suffix` method
+   |
+LL |     if let Some(<stripped>) = s.strip_suffix("bc") {
+LL |         str::to_string(<stripped>);
+LL |         <stripped>.to_string();
+LL | 
+LL |         str::to_string(<stripped>);
+LL |         <stripped>.to_string();
+   |
+
+error: stripping a prefix manually
+  --> $DIR/manual_strip.rs:24:24
+   |
+LL |         str::to_string(&s[1..]);
+   |                        ^^^^^^^
+   |
+note: the prefix was tested here
+  --> $DIR/manual_strip.rs:23:5
+   |
+LL |     if s.starts_with('a') {
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+   |
+LL |     if let Some(<stripped>) = s.strip_prefix('a') {
+LL |         str::to_string(<stripped>);
+LL |         <stripped>.to_string();
+   |
+
+error: stripping a prefix manually
+  --> $DIR/manual_strip.rs:31:24
+   |
+LL |         str::to_string(&s[prefix.len()..]);
+   |                        ^^^^^^^^^^^^^^^^^^
+   |
+note: the prefix was tested here
+  --> $DIR/manual_strip.rs:30:5
+   |
+LL |     if s.starts_with(prefix) {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+   |
+LL |     if let Some(<stripped>) = s.strip_prefix(prefix) {
+LL |         str::to_string(<stripped>);
+   |
+
+error: stripping a prefix manually
+  --> $DIR/manual_strip.rs:37:24
+   |
+LL |         str::to_string(&s[PREFIX.len()..]);
+   |                        ^^^^^^^^^^^^^^^^^^
+   |
+note: the prefix was tested here
+  --> $DIR/manual_strip.rs:36:5
+   |
+LL |     if s.starts_with(PREFIX) {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+   |
+LL |     if let Some(<stripped>) = s.strip_prefix(PREFIX) {
+LL |         str::to_string(<stripped>);
+LL |         str::to_string(<stripped>);
+   |
+
+error: stripping a prefix manually
+  --> $DIR/manual_strip.rs:44:24
+   |
+LL |         str::to_string(&TARGET[prefix.len()..]);
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the prefix was tested here
+  --> $DIR/manual_strip.rs:43:5
+   |
+LL |     if TARGET.starts_with(prefix) {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+   |
+LL |     if let Some(<stripped>) = TARGET.strip_prefix(prefix) {
+LL |         str::to_string(<stripped>);
+   |
+
+error: stripping a prefix manually
+  --> $DIR/manual_strip.rs:50:9
+   |
+LL |         s1[2..].to_uppercase();
+   |         ^^^^^^^
+   |
+note: the prefix was tested here
+  --> $DIR/manual_strip.rs:49:5
+   |
+LL |     if s1.starts_with("ab") {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+   |
+LL |     if let Some(<stripped>) = s1.strip_prefix("ab") {
+LL |         <stripped>.to_uppercase();
+   |
+
+error: aborting due to 7 previous errors
+
diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs
new file mode 100644 (file)
index 0000000..617b642
--- /dev/null
@@ -0,0 +1,25 @@
+#![warn(clippy::map_err_ignore)]
+use std::convert::TryFrom;
+use std::error::Error;
+use std::fmt;
+
+#[derive(Debug)]
+enum Errors {
+    Ignored,
+}
+
+impl Error for Errors {}
+
+impl fmt::Display for Errors {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Error")
+    }
+}
+
+fn main() -> Result<(), Errors> {
+    let x = u32::try_from(-123_i32);
+
+    println!("{:?}", x.map_err(|_| Errors::Ignored));
+
+    Ok(())
+}
diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr
new file mode 100644 (file)
index 0000000..7273f46
--- /dev/null
@@ -0,0 +1,11 @@
+error: `map_err(|_|...` ignores the original error
+  --> $DIR/map_err.rs:22:32
+   |
+LL |     println!("{:?}", x.map_err(|_| Errors::Ignored));
+   |                                ^^^
+   |
+   = note: `-D clippy::map-err-ignore` implied by `-D warnings`
+   = help: Consider wrapping the error in an enum variant
+
+error: aborting due to previous error
+
diff --git a/tests/ui/match_type_on_diag_item.rs b/tests/ui/match_type_on_diag_item.rs
new file mode 100644 (file)
index 0000000..fe950b0
--- /dev/null
@@ -0,0 +1,50 @@
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+extern crate rustc_hir;
+extern crate rustc_lint;
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Ty;
+
+mod paths {
+    pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
+}
+
+mod utils {
+    use super::*;
+
+    pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool {
+        false
+    }
+}
+
+use utils::match_type;
+
+declare_lint! {
+    pub TEST_LINT,
+    Warn,
+    ""
+}
+
+declare_lint_pass!(Pass => [TEST_LINT]);
+
+static OPTION: [&str; 3] = ["core", "option", "Option"];
+
+impl<'tcx> LateLintPass<'tcx> for Pass {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) {
+        let ty = cx.typeck_results().expr_ty(expr);
+
+        let _ = match_type(cx, ty, &paths::VEC);
+        let _ = match_type(cx, ty, &OPTION);
+        let _ = match_type(cx, ty, &["core", "result", "Result"]);
+
+        let rc_path = &["alloc", "rc", "Rc"];
+        let _ = utils::match_type(cx, ty, rc_path);
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr
new file mode 100644 (file)
index 0000000..5e5fe9e
--- /dev/null
@@ -0,0 +1,33 @@
+error: usage of `utils::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:41:17
+   |
+LL |         let _ = match_type(cx, ty, &paths::VEC);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))`
+   |
+note: the lint level is defined here
+  --> $DIR/match_type_on_diag_item.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
+
+error: usage of `utils::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:42:17
+   |
+LL |         let _ = match_type(cx, ty, &OPTION);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))`
+
+error: usage of `utils::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:43:17
+   |
+LL |         let _ = match_type(cx, ty, &["core", "result", "Result"]);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))`
+
+error: usage of `utils::match_type()` on a type diagnostic item
+  --> $DIR/match_type_on_diag_item.rs:46:17
+   |
+LL |         let _ = utils::match_type(cx, ty, rc_path);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))`
+
+error: aborting due to 4 previous errors
+
index 695a460cc4edfda0a16871c1aa0b3f1498359271..a7fb00a270577d64f9e3c9fa24cb2b96f6ffba38 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![warn(clippy::option_if_let_else)]
+#![allow(clippy::redundant_closure)]
 
 fn bad1(string: Option<&str>) -> (bool, &str) {
     string.map_or((false, "hello"), |x| (true, x))
@@ -36,6 +37,14 @@ fn longer_body(arg: Option<u32>) -> u32 {
     })
 }
 
+fn impure_else(arg: Option<i32>) {
+    let side_effect = || {
+        println!("return 1");
+        1
+    };
+    let _ = arg.map_or_else(|| side_effect(), |x| x);
+}
+
 fn test_map_or_else(arg: Option<u32>) {
     let _ = arg.map_or_else(|| {
         let mut y = 1;
@@ -71,4 +80,5 @@ fn main() {
     let _ = longer_body(None);
     test_map_or_else(None);
     let _ = negative_tests(None);
+    let _ = impure_else(None);
 }
index dee80d26bd976d93fda3f72fd0ce3a4cd65c3307..895fd86321faf01fe60fa80213d1c6a62df59bca 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![warn(clippy::option_if_let_else)]
+#![allow(clippy::redundant_closure)]
 
 fn bad1(string: Option<&str>) -> (bool, &str) {
     if let Some(x) = string {
@@ -52,6 +53,19 @@ fn longer_body(arg: Option<u32>) -> u32 {
     }
 }
 
+fn impure_else(arg: Option<i32>) {
+    let side_effect = || {
+        println!("return 1");
+        1
+    };
+    let _ = if let Some(x) = arg {
+        x
+    } else {
+        // map_or_else must be suggested
+        side_effect()
+    };
+}
+
 fn test_map_or_else(arg: Option<u32>) {
     let _ = if let Some(x) = arg {
         x * x * x * x
@@ -89,4 +103,5 @@ fn main() {
     let _ = longer_body(None);
     test_map_or_else(None);
     let _ = negative_tests(None);
+    let _ = impure_else(None);
 }
index 7005850efaf833b47f693ae04a7e9cd37722e476..b69fe7676827058f6cbda555238c035ca29e71a3 100644 (file)
@@ -1,5 +1,5 @@
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:5:5
+  --> $DIR/option_if_let_else.rs:6:5
    |
 LL | /     if let Some(x) = string {
 LL | |         (true, x)
@@ -11,7 +11,7 @@ LL | |     }
    = note: `-D clippy::option-if-let-else` implied by `-D warnings`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:15:12
+  --> $DIR/option_if_let_else.rs:16:12
    |
 LL |       } else if let Some(x) = string {
    |  ____________^
@@ -22,19 +22,19 @@ LL | |     }
    | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:23:13
+  --> $DIR/option_if_let_else.rs:24:13
    |
 LL |     let _ = if let Some(s) = *string { s.len() } else { 0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:24:13
+  --> $DIR/option_if_let_else.rs:25:13
    |
 LL |     let _ = if let Some(s) = &num { s } else { &0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:25:13
+  --> $DIR/option_if_let_else.rs:26:13
    |
 LL |       let _ = if let Some(s) = &mut num {
    |  _____________^
@@ -54,13 +54,13 @@ LL |     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:31:13
+  --> $DIR/option_if_let_else.rs:32:13
    |
 LL |     let _ = if let Some(ref s) = num { s } else { &0 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:32:13
+  --> $DIR/option_if_let_else.rs:33:13
    |
 LL |       let _ = if let Some(mut s) = num {
    |  _____________^
@@ -80,7 +80,7 @@ LL |     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:38:13
+  --> $DIR/option_if_let_else.rs:39:13
    |
 LL |       let _ = if let Some(ref mut s) = num {
    |  _____________^
@@ -100,7 +100,7 @@ LL |     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:47:5
+  --> $DIR/option_if_let_else.rs:48:5
    |
 LL | /     if let Some(x) = arg {
 LL | |         let y = x * x;
@@ -119,7 +119,19 @@ LL |     })
    |
 
 error: use Option::map_or_else instead of an if let/else
-  --> $DIR/option_if_let_else.rs:56:13
+  --> $DIR/option_if_let_else.rs:61:13
+   |
+LL |       let _ = if let Some(x) = arg {
+   |  _____________^
+LL | |         x
+LL | |     } else {
+LL | |         // map_or_else must be suggested
+LL | |         side_effect()
+LL | |     };
+   | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
+
+error: use Option::map_or_else instead of an if let/else
+  --> $DIR/option_if_let_else.rs:70:13
    |
 LL |       let _ = if let Some(x) = arg {
    |  _____________^
@@ -142,10 +154,10 @@ LL |     }, |x| x * x * x * x);
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:85:13
+  --> $DIR/option_if_let_else.rs:99:13
    |
 LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 
-error: aborting due to 11 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs
new file mode 100644 (file)
index 0000000..287726f
--- /dev/null
@@ -0,0 +1,70 @@
+#![warn(clippy::panic_in_result_fn)]
+
+struct A;
+
+impl A {
+    fn result_with_panic() -> Result<bool, String> // should emit lint
+    {
+        panic!("error");
+    }
+
+    fn result_with_unimplemented() -> Result<bool, String> // should emit lint
+    {
+        unimplemented!();
+    }
+
+    fn result_with_unreachable() -> Result<bool, String> // should emit lint
+    {
+        unreachable!();
+    }
+
+    fn result_with_todo() -> Result<bool, String> // should emit lint
+    {
+        todo!("Finish this");
+    }
+
+    fn other_with_panic() // should not emit lint
+    {
+        panic!("");
+    }
+
+    fn other_with_unreachable() // should not emit lint
+    {
+        unreachable!();
+    }
+
+    fn other_with_unimplemented() // should not emit lint
+    {
+        unimplemented!();
+    }
+
+    fn other_with_todo() // should not emit lint
+    {
+        todo!("finish this")
+    }
+
+    fn result_without_banned_functions() -> Result<bool, String> // should not emit lint
+    {
+        Ok(true)
+    }
+}
+
+fn function_result_with_panic() -> Result<bool, String> // should emit lint
+{
+    panic!("error");
+}
+
+fn todo() {
+    println!("something");
+}
+
+fn function_result_with_custom_todo() -> Result<bool, String> // should not emit lint
+{
+    todo();
+    Ok(true)
+}
+
+fn main() -> Result<(), String> {
+    todo!("finish main method");
+    Ok(())
+}
diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr
new file mode 100644 (file)
index 0000000..c6936fd
--- /dev/null
@@ -0,0 +1,105 @@
+error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+  --> $DIR/panic_in_result_fn.rs:6:5
+   |
+LL | /     fn result_with_panic() -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         panic!("error");
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::panic-in-result-fn` implied by `-D warnings`
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn.rs:8:9
+   |
+LL |         panic!("error");
+   |         ^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+  --> $DIR/panic_in_result_fn.rs:11:5
+   |
+LL | /     fn result_with_unimplemented() -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         unimplemented!();
+LL | |     }
+   | |_____^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn.rs:13:9
+   |
+LL |         unimplemented!();
+   |         ^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+  --> $DIR/panic_in_result_fn.rs:16:5
+   |
+LL | /     fn result_with_unreachable() -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         unreachable!();
+LL | |     }
+   | |_____^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn.rs:18:9
+   |
+LL |         unreachable!();
+   |         ^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+  --> $DIR/panic_in_result_fn.rs:21:5
+   |
+LL | /     fn result_with_todo() -> Result<bool, String> // should emit lint
+LL | |     {
+LL | |         todo!("Finish this");
+LL | |     }
+   | |_____^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn.rs:23:9
+   |
+LL |         todo!("Finish this");
+   |         ^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+  --> $DIR/panic_in_result_fn.rs:52:1
+   |
+LL | / fn function_result_with_panic() -> Result<bool, String> // should emit lint
+LL | | {
+LL | |     panic!("error");
+LL | | }
+   | |_^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn.rs:54:5
+   |
+LL |     panic!("error");
+   |     ^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`
+  --> $DIR/panic_in_result_fn.rs:67:1
+   |
+LL | / fn main() -> Result<(), String> {
+LL | |     todo!("finish main method");
+LL | |     Ok(())
+LL | | }
+   | |_^
+   |
+   = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing
+note: return Err() instead of panicking
+  --> $DIR/panic_in_result_fn.rs:68:5
+   |
+LL |     todo!("finish main method");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
+
index 3f710540e9038f1347da79b04555773d239219bd..a43a1fc4f5241c33a5ebe6416196793ad2b057cf 100644 (file)
@@ -9,6 +9,7 @@ fn main() {
     print!("Hello {}\n", "world");
     print!("Hello {} {}\n", "world", "#2");
     print!("{}\n", 1265);
+    print!("\n");
 
     // these are all fine
     print!("");
index 05fe88915d6efc40bcc5921efb894d83e0642820..54b3ad75b31e83bb9a893ee910257a29de946fcd 100644 (file)
@@ -44,7 +44,18 @@ LL |     println!("{}", 1265);
    |     ^^^^^^^    --
 
 error: using `print!()` with a format string that ends in a single newline
-  --> $DIR/print_with_newline.rs:30:5
+  --> $DIR/print_with_newline.rs:12:5
+   |
+LL |     print!("/n");
+   |     ^^^^^^^^^^^^
+   |
+help: use `println!` instead
+   |
+LL |     println!();
+   |     ^^^^^^^ --
+
+error: using `print!()` with a format string that ends in a single newline
+  --> $DIR/print_with_newline.rs:31:5
    |
 LL |     print!("//n"); // should fail
    |     ^^^^^^^^^^^^^^
@@ -55,7 +66,7 @@ LL |     println!("/"); // should fail
    |     ^^^^^^^    --
 
 error: using `print!()` with a format string that ends in a single newline
-  --> $DIR/print_with_newline.rs:37:5
+  --> $DIR/print_with_newline.rs:38:5
    |
 LL | /     print!(
 LL | |         "
@@ -70,7 +81,7 @@ LL |         ""
    |
 
 error: using `print!()` with a format string that ends in a single newline
-  --> $DIR/print_with_newline.rs:41:5
+  --> $DIR/print_with_newline.rs:42:5
    |
 LL | /     print!(
 LL | |         r"
@@ -85,7 +96,7 @@ LL |         r""
    |
 
 error: using `print!()` with a format string that ends in a single newline
-  --> $DIR/print_with_newline.rs:49:5
+  --> $DIR/print_with_newline.rs:50:5
    |
 LL |     print!("/r/n"); //~ ERROR
    |     ^^^^^^^^^^^^^^^
@@ -96,7 +107,7 @@ LL |     println!("/r"); //~ ERROR
    |     ^^^^^^^     --
 
 error: using `print!()` with a format string that ends in a single newline
-  --> $DIR/print_with_newline.rs:50:5
+  --> $DIR/print_with_newline.rs:51:5
    |
 LL |     print!("foo/rbar/n") // ~ ERROR
    |     ^^^^^^^^^^^^^^^^^^^^
@@ -106,5 +117,5 @@ help: use `println!` instead
 LL |     println!("foo/rbar") // ~ ERROR
    |     ^^^^^^^          --
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs
new file mode 100644 (file)
index 0000000..1fa9864
--- /dev/null
@@ -0,0 +1,26 @@
+#![warn(clippy::rc_buffer)]
+
+use std::cell::RefCell;
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+struct S {
+    // triggers lint
+    bad1: Rc<String>,
+    bad2: Rc<PathBuf>,
+    bad3: Rc<Vec<u8>>,
+    bad4: Rc<OsString>,
+    // does not trigger lint
+    good1: Rc<RefCell<String>>,
+}
+
+// triggers lint
+fn func_bad1(_: Rc<String>) {}
+fn func_bad2(_: Rc<PathBuf>) {}
+fn func_bad3(_: Rc<Vec<u8>>) {}
+fn func_bad4(_: Rc<OsString>) {}
+// does not trigger lint
+fn func_good1(_: Rc<RefCell<String>>) {}
+
+fn main() {}
diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr
new file mode 100644 (file)
index 0000000..e4cc169
--- /dev/null
@@ -0,0 +1,52 @@
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:10:11
+   |
+LL |     bad1: Rc<String>,
+   |           ^^^^^^^^^^ help: try: `Rc<str>`
+   |
+   = note: `-D clippy::rc-buffer` implied by `-D warnings`
+
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:11:11
+   |
+LL |     bad2: Rc<PathBuf>,
+   |           ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
+
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:12:11
+   |
+LL |     bad3: Rc<Vec<u8>>,
+   |           ^^^^^^^^^^^ help: try: `Rc<[u8]>`
+
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:13:11
+   |
+LL |     bad4: Rc<OsString>,
+   |           ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
+
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:19:17
+   |
+LL | fn func_bad1(_: Rc<String>) {}
+   |                 ^^^^^^^^^^ help: try: `Rc<str>`
+
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:20:17
+   |
+LL | fn func_bad2(_: Rc<PathBuf>) {}
+   |                 ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
+
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:21:17
+   |
+LL | fn func_bad3(_: Rc<Vec<u8>>) {}
+   |                 ^^^^^^^^^^^ help: try: `Rc<[u8]>`
+
+error: usage of `Rc<T>` when T is a buffer type
+  --> $DIR/rc_buffer.rs:22:17
+   |
+LL | fn func_bad4(_: Rc<OsString>) {}
+   |                 ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs
new file mode 100644 (file)
index 0000000..5d58658
--- /dev/null
@@ -0,0 +1,25 @@
+#![warn(clippy::rc_buffer)]
+
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::sync::{Arc, Mutex};
+
+struct S {
+    // triggers lint
+    bad1: Arc<String>,
+    bad2: Arc<PathBuf>,
+    bad3: Arc<Vec<u8>>,
+    bad4: Arc<OsString>,
+    // does not trigger lint
+    good1: Arc<Mutex<String>>,
+}
+
+// triggers lint
+fn func_bad1(_: Arc<String>) {}
+fn func_bad2(_: Arc<PathBuf>) {}
+fn func_bad3(_: Arc<Vec<u8>>) {}
+fn func_bad4(_: Arc<OsString>) {}
+// does not trigger lint
+fn func_good1(_: Arc<Mutex<String>>) {}
+
+fn main() {}
diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr
new file mode 100644 (file)
index 0000000..8252270
--- /dev/null
@@ -0,0 +1,52 @@
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:9:11
+   |
+LL |     bad1: Arc<String>,
+   |           ^^^^^^^^^^^ help: try: `Arc<str>`
+   |
+   = note: `-D clippy::rc-buffer` implied by `-D warnings`
+
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:10:11
+   |
+LL |     bad2: Arc<PathBuf>,
+   |           ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
+
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:11:11
+   |
+LL |     bad3: Arc<Vec<u8>>,
+   |           ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
+
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:12:11
+   |
+LL |     bad4: Arc<OsString>,
+   |           ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
+
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:18:17
+   |
+LL | fn func_bad1(_: Arc<String>) {}
+   |                 ^^^^^^^^^^^ help: try: `Arc<str>`
+
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:19:17
+   |
+LL | fn func_bad2(_: Arc<PathBuf>) {}
+   |                 ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
+
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:20:17
+   |
+LL | fn func_bad3(_: Arc<Vec<u8>>) {}
+   |                 ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
+
+error: usage of `Arc<T>` when T is a buffer type
+  --> $DIR/rc_buffer_arc.rs:21:17
+   |
+LL | fn func_bad4(_: Arc<OsString>) {}
+   |                 ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/rc_buffer_redefined_string.rs b/tests/ui/rc_buffer_redefined_string.rs
new file mode 100644 (file)
index 0000000..5d31a84
--- /dev/null
@@ -0,0 +1,12 @@
+#![warn(clippy::rc_buffer)]
+
+use std::rc::Rc;
+
+struct String;
+
+struct S {
+    // does not trigger lint
+    good1: Rc<String>,
+}
+
+fn main() {}
diff --git a/tests/ui/rc_buffer_redefined_string.stderr b/tests/ui/rc_buffer_redefined_string.stderr
new file mode 100644 (file)
index 0000000..e69de29
index 8084fdefdc23eced3f6b2a54643b16b637f667d8..fe8f62503b76748685eb4df7ca893a038ac65e2f 100644 (file)
@@ -18,39 +18,14 @@ fn main() {
 
     if Err::<i32, i32>(42).is_err() {}
 
-    if None::<()>.is_none() {}
-
-    if Some(42).is_some() {}
-
-    if Some(42).is_some() {
-        foo();
-    } else {
-        bar();
-    }
-
-    while Some(42).is_some() {}
-
-    while Some(42).is_none() {}
-
-    while None::<()>.is_none() {}
-
     while Ok::<i32, i32>(10).is_ok() {}
 
     while Ok::<i32, i32>(10).is_err() {}
 
-    let mut v = vec![1, 2, 3];
-    while v.pop().is_some() {
-        foo();
-    }
-
     if Ok::<i32, i32>(42).is_ok() {}
 
     if Err::<i32, i32>(42).is_err() {}
 
-    if None::<i32>.is_none() {}
-
-    if Some(42).is_some() {}
-
     if let Ok(x) = Ok::<i32, i32>(42) {
         println!("{}", x);
     }
@@ -63,48 +38,25 @@ fn main() {
 
     Err::<i32, i32>(42).is_ok();
 
-    Some(42).is_some();
-
-    None::<()>.is_none();
-
-    let _ = None::<()>.is_none();
-
     let _ = if Ok::<usize, ()>(4).is_ok() { true } else { false };
 
-    let opt = Some(false);
-    let x = if opt.is_some() { true } else { false };
-    takes_bool(x);
-
     issue5504();
     issue6067();
+    issue6065();
 
-    let _ = if gen_opt().is_some() {
+    let _ = if gen_res().is_ok() {
         1
-    } else if gen_opt().is_none() {
-        2
-    } else if gen_res().is_ok() {
-        3
     } else if gen_res().is_err() {
-        4
+        2
     } else {
-        5
+        3
     };
 }
 
-fn gen_opt() -> Option<()> {
-    None
-}
-
 fn gen_res() -> Result<(), ()> {
     Ok(())
 }
 
-fn takes_bool(_: bool) {}
-
-fn foo() {}
-
-fn bar() {}
-
 macro_rules! m {
     () => {
         Some(42u32)
@@ -128,31 +80,30 @@ fn issue5504() {
     while m!().is_some() {}
 }
 
+fn issue6065() {
+    macro_rules! if_let_in_macro {
+        ($pat:pat, $x:expr) => {
+            if let Some($pat) = $x {}
+        };
+    }
+
+    // shouldn't be linted
+    if_let_in_macro!(_, Some(42));
+}
+
 // Methods that are unstable const should not be suggested within a const context, see issue #5697.
-// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none`
-// of `Option` were stabilized as const, so the following should be linted.
+// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const,
+// so the following should be linted.
 const fn issue6067() {
     if Ok::<i32, i32>(42).is_ok() {}
 
     if Err::<i32, i32>(42).is_err() {}
 
-    if Some(42).is_some() {}
-
-    if None::<()>.is_none() {}
-
     while Ok::<i32, i32>(10).is_ok() {}
 
     while Ok::<i32, i32>(10).is_err() {}
 
-    while Some(42).is_some() {}
-
-    while None::<()>.is_none() {}
-
     Ok::<i32, i32>(42).is_ok();
 
     Err::<i32, i32>(42).is_err();
-
-    Some(42).is_some();
-
-    None::<()>.is_none();
 }
index 48a32cb1c7b7d9452908407aaa345a592fa2baf6..09426a6e59082275ac3c35124585476add74f551 100644 (file)
@@ -18,39 +18,14 @@ fn main() {
 
     if let Err(_) = Err::<i32, i32>(42) {}
 
-    if let None = None::<()> {}
-
-    if let Some(_) = Some(42) {}
-
-    if let Some(_) = Some(42) {
-        foo();
-    } else {
-        bar();
-    }
-
-    while let Some(_) = Some(42) {}
-
-    while let None = Some(42) {}
-
-    while let None = None::<()> {}
-
     while let Ok(_) = Ok::<i32, i32>(10) {}
 
     while let Err(_) = Ok::<i32, i32>(10) {}
 
-    let mut v = vec![1, 2, 3];
-    while let Some(_) = v.pop() {
-        foo();
-    }
-
     if Ok::<i32, i32>(42).is_ok() {}
 
     if Err::<i32, i32>(42).is_err() {}
 
-    if None::<i32>.is_none() {}
-
-    if Some(42).is_some() {}
-
     if let Ok(x) = Ok::<i32, i32>(42) {
         println!("{}", x);
     }
@@ -75,57 +50,25 @@ fn main() {
         Err(_) => false,
     };
 
-    match Some(42) {
-        Some(_) => true,
-        None => false,
-    };
-
-    match None::<()> {
-        Some(_) => false,
-        None => true,
-    };
-
-    let _ = match None::<()> {
-        Some(_) => false,
-        None => true,
-    };
-
     let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
 
-    let opt = Some(false);
-    let x = if let Some(_) = opt { true } else { false };
-    takes_bool(x);
-
     issue5504();
     issue6067();
+    issue6065();
 
-    let _ = if let Some(_) = gen_opt() {
+    let _ = if let Ok(_) = gen_res() {
         1
-    } else if let None = gen_opt() {
-        2
-    } else if let Ok(_) = gen_res() {
-        3
     } else if let Err(_) = gen_res() {
-        4
+        2
     } else {
-        5
+        3
     };
 }
 
-fn gen_opt() -> Option<()> {
-    None
-}
-
 fn gen_res() -> Result<(), ()> {
     Ok(())
 }
 
-fn takes_bool(_: bool) {}
-
-fn foo() {}
-
-fn bar() {}
-
 macro_rules! m {
     () => {
         Some(42u32)
@@ -149,26 +92,29 @@ fn try_result_opt() -> Result<i32, i32> {
     while let Some(_) = m!() {}
 }
 
+fn issue6065() {
+    macro_rules! if_let_in_macro {
+        ($pat:pat, $x:expr) => {
+            if let Some($pat) = $x {}
+        };
+    }
+
+    // shouldn't be linted
+    if_let_in_macro!(_, Some(42));
+}
+
 // Methods that are unstable const should not be suggested within a const context, see issue #5697.
-// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none`
-// of `Option` were stabilized as const, so the following should be linted.
+// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const,
+// so the following should be linted.
 const fn issue6067() {
     if let Ok(_) = Ok::<i32, i32>(42) {}
 
     if let Err(_) = Err::<i32, i32>(42) {}
 
-    if let Some(_) = Some(42) {}
-
-    if let None = None::<()> {}
-
     while let Ok(_) = Ok::<i32, i32>(10) {}
 
     while let Err(_) = Ok::<i32, i32>(10) {}
 
-    while let Some(_) = Some(42) {}
-
-    while let None = None::<()> {}
-
     match Ok::<i32, i32>(42) {
         Ok(_) => true,
         Err(_) => false,
@@ -178,14 +124,4 @@ const fn issue6067() {
         Ok(_) => false,
         Err(_) => true,
     };
-
-    match Some(42) {
-        Some(_) => true,
-        None => false,
-    };
-
-    match None::<()> {
-        Some(_) => false,
-        None => true,
-    };
 }
index 17185217e8950de05cf517424230ee39bd7ca400..3473ceea00e22ecec0fbdc59fa6c42291a63d3b3 100644 (file)
@@ -18,62 +18,20 @@ error: redundant pattern matching, consider using `is_err()`
 LL |     if let Err(_) = Err::<i32, i32>(42) {}
    |     -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
 
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:21:12
-   |
-LL |     if let None = None::<()> {}
-   |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
-
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:23:12
-   |
-LL |     if let Some(_) = Some(42) {}
-   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
-
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:25:12
-   |
-LL |     if let Some(_) = Some(42) {
-   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
-
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:31:15
-   |
-LL |     while let Some(_) = Some(42) {}
-   |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:33:15
-   |
-LL |     while let None = Some(42) {}
-   |     ----------^^^^----------- help: try this: `while Some(42).is_none()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:35:15
-   |
-LL |     while let None = None::<()> {}
-   |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
-
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:37:15
+  --> $DIR/redundant_pattern_matching.rs:21:15
    |
 LL |     while let Ok(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:39:15
+  --> $DIR/redundant_pattern_matching.rs:23:15
    |
 LL |     while let Err(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
 
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:42:15
-   |
-LL |     while let Some(_) = v.pop() {
-   |     ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
-
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:58:5
+  --> $DIR/redundant_pattern_matching.rs:33:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -82,7 +40,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:63:5
+  --> $DIR/redundant_pattern_matching.rs:38:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -91,7 +49,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:68:5
+  --> $DIR/redundant_pattern_matching.rs:43:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -100,7 +58,7 @@ LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:73:5
+  --> $DIR/redundant_pattern_matching.rs:48:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -108,144 +66,74 @@ LL | |         Err(_) => false,
 LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()`
 
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:78:5
-   |
-LL | /     match Some(42) {
-LL | |         Some(_) => true,
-LL | |         None => false,
-LL | |     };
-   | |_____^ help: try this: `Some(42).is_some()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:83:5
-   |
-LL | /     match None::<()> {
-LL | |         Some(_) => false,
-LL | |         None => true,
-LL | |     };
-   | |_____^ help: try this: `None::<()>.is_none()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:88:13
-   |
-LL |       let _ = match None::<()> {
-   |  _____________^
-LL | |         Some(_) => false,
-LL | |         None => true,
-LL | |     };
-   | |_____^ help: try this: `None::<()>.is_none()`
-
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:93:20
+  --> $DIR/redundant_pattern_matching.rs:53:20
    |
 LL |     let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false };
    |             -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()`
 
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:96:20
-   |
-LL |     let x = if let Some(_) = opt { true } else { false };
-   |             -------^^^^^^^------ help: try this: `if opt.is_some()`
-
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:102:20
-   |
-LL |     let _ = if let Some(_) = gen_opt() {
-   |             -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:104:19
-   |
-LL |     } else if let None = gen_opt() {
-   |            -------^^^^------------ help: try this: `if gen_opt().is_none()`
-
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:106:19
+  --> $DIR/redundant_pattern_matching.rs:59:20
    |
-LL |     } else if let Ok(_) = gen_res() {
-   |            -------^^^^^------------ help: try this: `if gen_res().is_ok()`
+LL |     let _ = if let Ok(_) = gen_res() {
+   |             -------^^^^^------------ help: try this: `if gen_res().is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:108:19
+  --> $DIR/redundant_pattern_matching.rs:61:19
    |
 LL |     } else if let Err(_) = gen_res() {
    |            -------^^^^^^------------ help: try this: `if gen_res().is_err()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:141:19
+  --> $DIR/redundant_pattern_matching.rs:84:19
    |
 LL |         while let Some(_) = r#try!(result_opt()) {}
    |         ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:142:16
+  --> $DIR/redundant_pattern_matching.rs:85:16
    |
 LL |         if let Some(_) = r#try!(result_opt()) {}
    |         -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:148:12
+  --> $DIR/redundant_pattern_matching.rs:91:12
    |
 LL |     if let Some(_) = m!() {}
    |     -------^^^^^^^------- help: try this: `if m!().is_some()`
 
 error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:149:15
+  --> $DIR/redundant_pattern_matching.rs:92:15
    |
 LL |     while let Some(_) = m!() {}
    |     ----------^^^^^^^------- help: try this: `while m!().is_some()`
 
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:156:12
+  --> $DIR/redundant_pattern_matching.rs:110:12
    |
 LL |     if let Ok(_) = Ok::<i32, i32>(42) {}
    |     -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:158:12
+  --> $DIR/redundant_pattern_matching.rs:112:12
    |
 LL |     if let Err(_) = Err::<i32, i32>(42) {}
    |     -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()`
 
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:160:12
-   |
-LL |     if let Some(_) = Some(42) {}
-   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:162:12
-   |
-LL |     if let None = None::<()> {}
-   |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
-
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:164:15
+  --> $DIR/redundant_pattern_matching.rs:114:15
    |
 LL |     while let Ok(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:166:15
+  --> $DIR/redundant_pattern_matching.rs:116:15
    |
 LL |     while let Err(_) = Ok::<i32, i32>(10) {}
    |     ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()`
 
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:168:15
-   |
-LL |     while let Some(_) = Some(42) {}
-   |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:170:15
-   |
-LL |     while let None = None::<()> {}
-   |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
-
 error: redundant pattern matching, consider using `is_ok()`
-  --> $DIR/redundant_pattern_matching.rs:172:5
+  --> $DIR/redundant_pattern_matching.rs:118:5
    |
 LL | /     match Ok::<i32, i32>(42) {
 LL | |         Ok(_) => true,
@@ -254,7 +142,7 @@ LL | |     };
    | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()`
 
 error: redundant pattern matching, consider using `is_err()`
-  --> $DIR/redundant_pattern_matching.rs:177:5
+  --> $DIR/redundant_pattern_matching.rs:123:5
    |
 LL | /     match Err::<i32, i32>(42) {
 LL | |         Ok(_) => false,
@@ -262,23 +150,5 @@ LL | |         Err(_) => true,
 LL | |     };
    | |_____^ help: try this: `Err::<i32, i32>(42).is_err()`
 
-error: redundant pattern matching, consider using `is_some()`
-  --> $DIR/redundant_pattern_matching.rs:182:5
-   |
-LL | /     match Some(42) {
-LL | |         Some(_) => true,
-LL | |         None => false,
-LL | |     };
-   | |_____^ help: try this: `Some(42).is_some()`
-
-error: redundant pattern matching, consider using `is_none()`
-  --> $DIR/redundant_pattern_matching.rs:187:5
-   |
-LL | /     match None::<()> {
-LL | |         Some(_) => false,
-LL | |         None => true,
-LL | |     };
-   | |_____^ help: try this: `None::<()>.is_none()`
-
-error: aborting due to 41 previous errors
+error: aborting due to 22 previous errors
 
diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed
new file mode 100644 (file)
index 0000000..499b975
--- /dev/null
@@ -0,0 +1,85 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+    clippy::unit_arg,
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    deprecated
+)]
+
+fn main() {
+    if None::<()>.is_none() {}
+
+    if Some(42).is_some() {}
+
+    if Some(42).is_some() {
+        foo();
+    } else {
+        bar();
+    }
+
+    while Some(42).is_some() {}
+
+    while Some(42).is_none() {}
+
+    while None::<()>.is_none() {}
+
+    let mut v = vec![1, 2, 3];
+    while v.pop().is_some() {
+        foo();
+    }
+
+    if None::<i32>.is_none() {}
+
+    if Some(42).is_some() {}
+
+    Some(42).is_some();
+
+    None::<()>.is_none();
+
+    let _ = None::<()>.is_none();
+
+    let opt = Some(false);
+    let x = if opt.is_some() { true } else { false };
+    takes_bool(x);
+
+    issue6067();
+
+    let _ = if gen_opt().is_some() {
+        1
+    } else if gen_opt().is_none() {
+        2
+    } else {
+        3
+    };
+}
+
+fn gen_opt() -> Option<()> {
+    None
+}
+
+fn takes_bool(_: bool) {}
+
+fn foo() {}
+
+fn bar() {}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+    if Some(42).is_some() {}
+
+    if None::<()>.is_none() {}
+
+    while Some(42).is_some() {}
+
+    while None::<()>.is_none() {}
+
+    Some(42).is_some();
+
+    None::<()>.is_none();
+}
diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs
new file mode 100644 (file)
index 0000000..2a98435
--- /dev/null
@@ -0,0 +1,100 @@
+// run-rustfix
+
+#![warn(clippy::all)]
+#![warn(clippy::redundant_pattern_matching)]
+#![allow(
+    clippy::unit_arg,
+    unused_must_use,
+    clippy::needless_bool,
+    clippy::match_like_matches_macro,
+    deprecated
+)]
+
+fn main() {
+    if let None = None::<()> {}
+
+    if let Some(_) = Some(42) {}
+
+    if let Some(_) = Some(42) {
+        foo();
+    } else {
+        bar();
+    }
+
+    while let Some(_) = Some(42) {}
+
+    while let None = Some(42) {}
+
+    while let None = None::<()> {}
+
+    let mut v = vec![1, 2, 3];
+    while let Some(_) = v.pop() {
+        foo();
+    }
+
+    if None::<i32>.is_none() {}
+
+    if Some(42).is_some() {}
+
+    match Some(42) {
+        Some(_) => true,
+        None => false,
+    };
+
+    match None::<()> {
+        Some(_) => false,
+        None => true,
+    };
+
+    let _ = match None::<()> {
+        Some(_) => false,
+        None => true,
+    };
+
+    let opt = Some(false);
+    let x = if let Some(_) = opt { true } else { false };
+    takes_bool(x);
+
+    issue6067();
+
+    let _ = if let Some(_) = gen_opt() {
+        1
+    } else if let None = gen_opt() {
+        2
+    } else {
+        3
+    };
+}
+
+fn gen_opt() -> Option<()> {
+    None
+}
+
+fn takes_bool(_: bool) {}
+
+fn foo() {}
+
+fn bar() {}
+
+// Methods that are unstable const should not be suggested within a const context, see issue #5697.
+// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const,
+// so the following should be linted.
+const fn issue6067() {
+    if let Some(_) = Some(42) {}
+
+    if let None = None::<()> {}
+
+    while let Some(_) = Some(42) {}
+
+    while let None = None::<()> {}
+
+    match Some(42) {
+        Some(_) => true,
+        None => false,
+    };
+
+    match None::<()> {
+        Some(_) => false,
+        None => true,
+    };
+}
diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr
new file mode 100644 (file)
index 0000000..eebb344
--- /dev/null
@@ -0,0 +1,134 @@
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:14:12
+   |
+LL |     if let None = None::<()> {}
+   |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
+   |
+   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:16:12
+   |
+LL |     if let Some(_) = Some(42) {}
+   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:18:12
+   |
+LL |     if let Some(_) = Some(42) {
+   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:24:15
+   |
+LL |     while let Some(_) = Some(42) {}
+   |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:26:15
+   |
+LL |     while let None = Some(42) {}
+   |     ----------^^^^----------- help: try this: `while Some(42).is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:28:15
+   |
+LL |     while let None = None::<()> {}
+   |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:31:15
+   |
+LL |     while let Some(_) = v.pop() {
+   |     ----------^^^^^^^---------- help: try this: `while v.pop().is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:39:5
+   |
+LL | /     match Some(42) {
+LL | |         Some(_) => true,
+LL | |         None => false,
+LL | |     };
+   | |_____^ help: try this: `Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:44:5
+   |
+LL | /     match None::<()> {
+LL | |         Some(_) => false,
+LL | |         None => true,
+LL | |     };
+   | |_____^ help: try this: `None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:49:13
+   |
+LL |       let _ = match None::<()> {
+   |  _____________^
+LL | |         Some(_) => false,
+LL | |         None => true,
+LL | |     };
+   | |_____^ help: try this: `None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:55:20
+   |
+LL |     let x = if let Some(_) = opt { true } else { false };
+   |             -------^^^^^^^------ help: try this: `if opt.is_some()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:60:20
+   |
+LL |     let _ = if let Some(_) = gen_opt() {
+   |             -------^^^^^^^------------ help: try this: `if gen_opt().is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:62:19
+   |
+LL |     } else if let None = gen_opt() {
+   |            -------^^^^------------ help: try this: `if gen_opt().is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:83:12
+   |
+LL |     if let Some(_) = Some(42) {}
+   |     -------^^^^^^^----------- help: try this: `if Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:85:12
+   |
+LL |     if let None = None::<()> {}
+   |     -------^^^^------------- help: try this: `if None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:87:15
+   |
+LL |     while let Some(_) = Some(42) {}
+   |     ----------^^^^^^^----------- help: try this: `while Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:89:15
+   |
+LL |     while let None = None::<()> {}
+   |     ----------^^^^------------- help: try this: `while None::<()>.is_none()`
+
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/redundant_pattern_matching_option.rs:91:5
+   |
+LL | /     match Some(42) {
+LL | |         Some(_) => true,
+LL | |         None => false,
+LL | |     };
+   | |_____^ help: try this: `Some(42).is_some()`
+
+error: redundant pattern matching, consider using `is_none()`
+  --> $DIR/redundant_pattern_matching_option.rs:96:5
+   |
+LL | /     match None::<()> {
+LL | |         Some(_) => false,
+LL | |         None => true,
+LL | |     };
+   | |_____^ help: try this: `None::<()>.is_none()`
+
+error: aborting due to 19 previous errors
+
index 1cef8c2cfc997dc0a7937dbae6fcdcc7b872a8f7..fbdc977b769a413587a80c87dd8393c1116fe8fd 100644 (file)
@@ -1,4 +1,5 @@
 #![allow(unused_parens)]
+#![warn(clippy::verbose_bit_mask)]
 
 fn main() {
     let x: i32 = 42;
index 320d9cc3f643412bad951fad6547a7ea07a8d2a3..798551118309e01650118c0beb29ca398b2bb5ae 100644 (file)
@@ -1,5 +1,5 @@
 error: bit mask could be simplified with a call to `trailing_zeros`
-  --> $DIR/trailing_zeros.rs:5:13
+  --> $DIR/trailing_zeros.rs:6:13
    |
 LL |     let _ = (x & 0b1111 == 0); // suggest trailing_zeros
    |             ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4`
@@ -7,7 +7,7 @@ LL |     let _ = (x & 0b1111 == 0); // suggest trailing_zeros
    = note: `-D clippy::verbose-bit-mask` implied by `-D warnings`
 
 error: bit mask could be simplified with a call to `trailing_zeros`
-  --> $DIR/trailing_zeros.rs:6:13
+  --> $DIR/trailing_zeros.rs:7:13
    |
 LL |     let _ = x & 0b1_1111 == 0; // suggest trailing_zeros
    |             ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5`
index fa66e68794e4be0d6b1ac082c2c9b2044b0e36bb..4ba2a0a5dbcc1d5dd07beca432d67f708e11e4b8 100644 (file)
@@ -2,6 +2,7 @@
 #![warn(clippy::unnecessary_lazy_evaluations)]
 #![allow(clippy::redundant_closure)]
 #![allow(clippy::bind_instead_of_map)]
+#![allow(clippy::map_identity)]
 
 struct Deep(Option<usize>);
 
@@ -34,13 +35,13 @@ fn main() {
     let _ = opt.unwrap_or(2);
     let _ = opt.unwrap_or(astronomers_pi);
     let _ = opt.unwrap_or(ext_str.some_field);
-    let _ = opt.unwrap_or(ext_arr[0]);
+    let _ = opt.unwrap_or_else(|| ext_arr[0]);
     let _ = opt.and(ext_opt);
     let _ = opt.or(ext_opt);
     let _ = opt.or(None);
     let _ = opt.get_or_insert(2);
     let _ = opt.ok_or(2);
-    let _ = opt.ok_or(ext_arr[0]);
+    let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
 
     // Cases when unwrap is not called on a simple variable
     let _ = Some(10).unwrap_or(2);
@@ -60,7 +61,6 @@ fn main() {
     // Should not lint - Option
     let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
     let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
-    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
     let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
     let _ = opt.or_else(some_call);
     let _ = opt.or_else(|| some_call());
@@ -69,13 +69,16 @@ fn main() {
     let _ = deep.0.get_or_insert_with(|| some_call());
     let _ = deep.0.or_else(some_call);
     let _ = deep.0.or_else(|| some_call());
+    let _ = opt.ok_or_else(|| ext_arr[0]);
 
-    // These are handled by bind_instead_of_map
+    // should not lint, bind_instead_of_map takes priority
     let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
     let _ = Some(10).and_then(|idx| Some(idx));
-    let _: Option<usize> = None.or_else(|| Some(3));
-    let _ = deep.0.or_else(|| Some(3));
-    let _ = opt.or_else(|| Some(3));
+
+    // should lint, bind_instead_of_map doesn't apply
+    let _: Option<usize> = None.or(Some(3));
+    let _ = deep.0.or(Some(3));
+    let _ = opt.or(Some(3));
 
     // Should lint - Result
     let res: Result<usize, usize> = Err(5);
@@ -92,26 +95,28 @@ fn main() {
     let _ = res2.unwrap_or_else(|err| err.return_some_field());
     let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
 
+    // should not lint, bind_instead_of_map takes priority
     let _: Result<usize, usize> = res.and_then(|x| Ok(x));
-    let _: Result<usize, usize> = res.and_then(|x| Err(x));
-
-    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
     let _: Result<usize, usize> = res.or_else(|err| Err(err));
 
-    // These are handled by bind_instead_of_map
     let _: Result<usize, usize> = res.and_then(|_| Ok(2));
     let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
     let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
 
-    let _: Result<usize, usize> = res.and_then(|_| Err(2));
-    let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
-    let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
-
-    let _: Result<usize, usize> = res.or_else(|_| Ok(2));
-    let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
-    let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
-
     let _: Result<usize, usize> = res.or_else(|_| Err(2));
     let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
     let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+
+    // should lint, bind_instead_of_map doesn't apply
+    let _: Result<usize, usize> = res.and(Err(2));
+    let _: Result<usize, usize> = res.and(Err(astronomers_pi));
+    let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
+
+    let _: Result<usize, usize> = res.or(Ok(2));
+    let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
+    let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
+
+    // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
+    let _: Result<usize, usize> = res.and_then(|x| Err(x));
+    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
 }
index 04f47d1aa2978f8afb54126b6d2cd97532f20521..466915217e42e9dc94cf983efe4d2b70b29fed0c 100644 (file)
@@ -2,6 +2,7 @@
 #![warn(clippy::unnecessary_lazy_evaluations)]
 #![allow(clippy::redundant_closure)]
 #![allow(clippy::bind_instead_of_map)]
+#![allow(clippy::map_identity)]
 
 struct Deep(Option<usize>);
 
@@ -40,7 +41,7 @@ fn main() {
     let _ = opt.or_else(|| None);
     let _ = opt.get_or_insert_with(|| 2);
     let _ = opt.ok_or_else(|| 2);
-    let _ = opt.ok_or_else(|| ext_arr[0]);
+    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
 
     // Cases when unwrap is not called on a simple variable
     let _ = Some(10).unwrap_or_else(|| 2);
@@ -60,7 +61,6 @@ fn main() {
     // Should not lint - Option
     let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
     let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
-    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
     let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
     let _ = opt.or_else(some_call);
     let _ = opt.or_else(|| some_call());
@@ -69,10 +69,13 @@ fn main() {
     let _ = deep.0.get_or_insert_with(|| some_call());
     let _ = deep.0.or_else(some_call);
     let _ = deep.0.or_else(|| some_call());
+    let _ = opt.ok_or_else(|| ext_arr[0]);
 
-    // These are handled by bind_instead_of_map
+    // should not lint, bind_instead_of_map takes priority
     let _ = Some(10).and_then(|idx| Some(ext_arr[idx]));
     let _ = Some(10).and_then(|idx| Some(idx));
+
+    // should lint, bind_instead_of_map doesn't apply
     let _: Option<usize> = None.or_else(|| Some(3));
     let _ = deep.0.or_else(|| Some(3));
     let _ = opt.or_else(|| Some(3));
@@ -92,17 +95,19 @@ fn main() {
     let _ = res2.unwrap_or_else(|err| err.return_some_field());
     let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
 
+    // should not lint, bind_instead_of_map takes priority
     let _: Result<usize, usize> = res.and_then(|x| Ok(x));
-    let _: Result<usize, usize> = res.and_then(|x| Err(x));
-
-    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
     let _: Result<usize, usize> = res.or_else(|err| Err(err));
 
-    // These are handled by bind_instead_of_map
     let _: Result<usize, usize> = res.and_then(|_| Ok(2));
     let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
     let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
 
+    let _: Result<usize, usize> = res.or_else(|_| Err(2));
+    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
+    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+
+    // should lint, bind_instead_of_map doesn't apply
     let _: Result<usize, usize> = res.and_then(|_| Err(2));
     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
@@ -111,7 +116,7 @@ fn main() {
     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
 
-    let _: Result<usize, usize> = res.or_else(|_| Err(2));
-    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
-    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
+    // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
+    let _: Result<usize, usize> = res.and_then(|x| Err(x));
+    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
 }
index 5c1b2eb1f14e833bd7aecb01e58c78a7699641da..44dcd0cafbb6e7614579eff586b9f87b83a444ed 100644 (file)
@@ -1,5 +1,5 @@
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:34:13
+  --> $DIR/unnecessary_lazy_eval.rs:35:13
    |
 LL |     let _ = opt.unwrap_or_else(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)`
@@ -7,142 +7,190 @@ LL |     let _ = opt.unwrap_or_else(|| 2);
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:35:13
+  --> $DIR/unnecessary_lazy_eval.rs:36:13
    |
 LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:36:13
+  --> $DIR/unnecessary_lazy_eval.rs:37:13
    |
 LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:37:13
-   |
-LL |     let _ = opt.unwrap_or_else(|| ext_arr[0]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])`
-
-error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:38:13
+  --> $DIR/unnecessary_lazy_eval.rs:39:13
    |
 LL |     let _ = opt.and_then(|_| ext_opt);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:39:13
+  --> $DIR/unnecessary_lazy_eval.rs:40:13
    |
 LL |     let _ = opt.or_else(|| ext_opt);
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:40:13
+  --> $DIR/unnecessary_lazy_eval.rs:41:13
    |
 LL |     let _ = opt.or_else(|| None);
    |             ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:41:13
+  --> $DIR/unnecessary_lazy_eval.rs:42:13
    |
 LL |     let _ = opt.get_or_insert_with(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:42:13
+  --> $DIR/unnecessary_lazy_eval.rs:43:13
    |
 LL |     let _ = opt.ok_or_else(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:43:13
+  --> $DIR/unnecessary_lazy_eval.rs:44:13
    |
-LL |     let _ = opt.ok_or_else(|| ext_arr[0]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])`
+LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:46:13
+  --> $DIR/unnecessary_lazy_eval.rs:47:13
    |
 LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:47:13
+  --> $DIR/unnecessary_lazy_eval.rs:48:13
    |
 LL |     let _ = Some(10).and_then(|_| ext_opt);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:48:28
+  --> $DIR/unnecessary_lazy_eval.rs:49:28
    |
 LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:49:13
+  --> $DIR/unnecessary_lazy_eval.rs:50:13
    |
 LL |     let _ = None.get_or_insert_with(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:50:35
+  --> $DIR/unnecessary_lazy_eval.rs:51:35
    |
 LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |                                   ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:51:28
+  --> $DIR/unnecessary_lazy_eval.rs:52:28
    |
 LL |     let _: Option<usize> = None.or_else(|| None);
    |                            ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:54:13
+  --> $DIR/unnecessary_lazy_eval.rs:55:13
    |
 LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:55:13
+  --> $DIR/unnecessary_lazy_eval.rs:56:13
    |
 LL |     let _ = deep.0.and_then(|_| ext_opt);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:56:13
+  --> $DIR/unnecessary_lazy_eval.rs:57:13
    |
 LL |     let _ = deep.0.or_else(|| None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:57:13
+  --> $DIR/unnecessary_lazy_eval.rs:58:13
    |
 LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:58:13
+  --> $DIR/unnecessary_lazy_eval.rs:59:13
    |
 LL |     let _ = deep.0.ok_or_else(|| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)`
 
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:79:28
+   |
+LL |     let _: Option<usize> = None.or_else(|| Some(3));
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(Some(3))`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:80:13
+   |
+LL |     let _ = deep.0.or_else(|| Some(3));
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(Some(3))`
+
+error: unnecessary closure used to substitute value for `Option::None`
+  --> $DIR/unnecessary_lazy_eval.rs:81:13
+   |
+LL |     let _ = opt.or_else(|| Some(3));
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(Some(3))`
+
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:84:13
+  --> $DIR/unnecessary_lazy_eval.rs:87:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| 2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:85:13
+  --> $DIR/unnecessary_lazy_eval.rs:88:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:86:13
+  --> $DIR/unnecessary_lazy_eval.rs:89:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)`
 
-error: aborting due to 24 previous errors
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:111:35
+   |
+LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:112:35
+   |
+LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:113:35
+   |
+LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:115:35
+   |
+LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:116:35
+   |
+LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval.rs:117:35
+   |
+LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))`
+
+error: aborting due to 32 previous errors
 
index f1e880d2696c40c430e23e11f2a60aec52e5dc7f..11c6efb25cceace746742c0bf5677c2c21b5043d 100644 (file)
@@ -1,4 +1,4 @@
-error: useless conversion to the same type
+error: useless conversion to the same type: `T`
   --> $DIR/useless_conversion.rs:6:13
    |
 LL |     let _ = T::from(val);
@@ -10,61 +10,61 @@ note: the lint level is defined here
 LL | #![deny(clippy::useless_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `T`
   --> $DIR/useless_conversion.rs:7:5
    |
 LL |     val.into()
    |     ^^^^^^^^^^ help: consider removing `.into()`: `val`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `i32`
   --> $DIR/useless_conversion.rs:19:22
    |
 LL |         let _: i32 = 0i32.into();
    |                      ^^^^^^^^^^^ help: consider removing `.into()`: `0i32`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion.rs:60:21
    |
 LL |     let _: String = "foo".to_string().into();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion.rs:61:21
    |
 LL |     let _: String = From::from("foo".to_string());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion.rs:62:13
    |
 LL |     let _ = String::from("foo".to_string());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion.rs:63:13
    |
 LL |     let _ = String::from(format!("A: {:04}", 123));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::str::Lines`
   --> $DIR/useless_conversion.rs:64:13
    |
 LL |     let _ = "".lines().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::vec::IntoIter<i32>`
   --> $DIR/useless_conversion.rs:65:13
    |
 LL |     let _ = vec![1, 2, 3].into_iter().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion.rs:66:21
    |
 LL |     let _: String = format!("Hello {}", "world").into();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `i32`
   --> $DIR/useless_conversion.rs:71:13
    |
 LL |     let _ = i32::from(a + b) * 3;
index b765727c168f5a5273cf55890b05ceebcf26a8a5..2e0d9129bfb3064735077e7ff5a6c23ede5390c6 100644 (file)
@@ -1,4 +1,4 @@
-error: useless conversion to the same type
+error: useless conversion to the same type: `T`
   --> $DIR/useless_conversion_try.rs:6:13
    |
 LL |     let _ = T::try_from(val).unwrap();
@@ -11,7 +11,7 @@ LL | #![deny(clippy::useless_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: consider removing `T::try_from()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `T`
   --> $DIR/useless_conversion_try.rs:7:5
    |
 LL |     val.try_into().unwrap()
@@ -19,7 +19,7 @@ LL |     val.try_into().unwrap()
    |
    = help: consider removing `.try_into()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:29:21
    |
 LL |     let _: String = "foo".to_string().try_into().unwrap();
@@ -27,7 +27,7 @@ LL |     let _: String = "foo".to_string().try_into().unwrap();
    |
    = help: consider removing `.try_into()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:30:21
    |
 LL |     let _: String = TryFrom::try_from("foo".to_string()).unwrap();
@@ -35,7 +35,7 @@ LL |     let _: String = TryFrom::try_from("foo".to_string()).unwrap();
    |
    = help: consider removing `TryFrom::try_from()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:31:13
    |
 LL |     let _ = String::try_from("foo".to_string()).unwrap();
@@ -43,7 +43,7 @@ LL |     let _ = String::try_from("foo".to_string()).unwrap();
    |
    = help: consider removing `String::try_from()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:32:13
    |
 LL |     let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
@@ -51,7 +51,7 @@ LL |     let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
    |
    = help: consider removing `String::try_from()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:33:21
    |
 LL |     let _: String = format!("Hello {}", "world").try_into().unwrap();
@@ -59,7 +59,7 @@ LL |     let _: String = format!("Hello {}", "world").try_into().unwrap();
    |
    = help: consider removing `.try_into()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:34:21
    |
 LL |     let _: String = "".to_owned().try_into().unwrap();
@@ -67,7 +67,7 @@ LL |     let _: String = "".to_owned().try_into().unwrap();
    |
    = help: consider removing `.try_into()`
 
-error: useless conversion to the same type
+error: useless conversion to the same type: `std::string::String`
   --> $DIR/useless_conversion_try.rs:35:27
    |
 LL |     let _: String = match String::from("_").try_into() {
index 93afd73d1114d257998089f1c683bfa090c06458..1c1b1b58402e830393a9f2e57a43918d414f6f85 100644 (file)
@@ -14,6 +14,7 @@ fn main() {
     write!(&mut v, "Hello {}\n", "world");
     write!(&mut v, "Hello {} {}\n", "world", "#2");
     write!(&mut v, "{}\n", 1265);
+    write!(&mut v, "\n");
 
     // These should be fine
     write!(&mut v, "");
index 2473329ca7276e149eb6702d32ea76c87de995c6..a14e86122ee5d0b54c64d4d0aa48b7a0e0389da6 100644 (file)
@@ -44,7 +44,18 @@ LL |     writeln!(&mut v, "{}", 1265);
    |     ^^^^^^^            --
 
 error: using `write!()` with a format string that ends in a single newline
-  --> $DIR/write_with_newline.rs:35:5
+  --> $DIR/write_with_newline.rs:17:5
+   |
+LL |     write!(&mut v, "/n");
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `writeln!()` instead
+   |
+LL |     writeln!(&mut v, );
+   |     ^^^^^^^         --
+
+error: using `write!()` with a format string that ends in a single newline
+  --> $DIR/write_with_newline.rs:36:5
    |
 LL |     write!(&mut v, "//n"); // should fail
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -55,7 +66,7 @@ LL |     writeln!(&mut v, "/"); // should fail
    |     ^^^^^^^            --
 
 error: using `write!()` with a format string that ends in a single newline
-  --> $DIR/write_with_newline.rs:42:5
+  --> $DIR/write_with_newline.rs:43:5
    |
 LL | /     write!(
 LL | |         &mut v,
@@ -72,7 +83,7 @@ LL |         ""
    |
 
 error: using `write!()` with a format string that ends in a single newline
-  --> $DIR/write_with_newline.rs:47:5
+  --> $DIR/write_with_newline.rs:48:5
    |
 LL | /     write!(
 LL | |         &mut v,
@@ -89,7 +100,7 @@ LL |         r""
    |
 
 error: using `write!()` with a format string that ends in a single newline
-  --> $DIR/write_with_newline.rs:56:5
+  --> $DIR/write_with_newline.rs:57:5
    |
 LL |     write!(&mut v, "/r/n"); //~ ERROR
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -100,7 +111,7 @@ LL |     writeln!(&mut v, "/r"); //~ ERROR
    |     ^^^^^^^             --
 
 error: using `write!()` with a format string that ends in a single newline
-  --> $DIR/write_with_newline.rs:57:5
+  --> $DIR/write_with_newline.rs:58:5
    |
 LL |     write!(&mut v, "foo/rbar/n");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,5 +121,5 @@ help: use `writeln!()` instead
 LL |     writeln!(&mut v, "foo/rbar");
    |     ^^^^^^^                  --
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
index 411229935c36a4729a69823a2594395b0e3bb4e8..ed3c83af616d7da8e8de1ca6dd103477a9da656b 100644 (file)
@@ -1,6 +1,6 @@
 [relabel]
 allow-unauthenticated = [
-    "C-*", "A-*", "E-*", "L-*", "M-*", "O-*",
+    "C-*", "A-*", "E-*", "L-*", "M-*", "O-*", "S-*",
     "good first issue", "needs test"
 ]