]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #6304 - matthiaskrgr:crash_6302, r=llogiq
authorbors <bors@rust-lang.org>
Sat, 7 Nov 2020 18:10:35 +0000 (18:10 +0000)
committerbors <bors@rust-lang.org>
Sat, 7 Nov 2020 18:10:35 +0000 (18:10 +0000)
FROM_ITER_INSTEAD_OF_COLLECT: avoid unwrapping unconditionally

Fixes #6302

changelog: fix unwrap of None when checking libcore with clippy

37 files changed:
CHANGELOG.md
README.md
clippy_lints/src/await_holding_invalid.rs
clippy_lints/src/lib.rs
clippy_lints/src/manual_async_fn.rs
clippy_lints/src/methods/unnecessary_lazy_eval.rs
clippy_lints/src/non_copy_const.rs
clippy_lints/src/strings.rs
clippy_lints/src/unused_unit.rs
clippy_lints/src/utils/mod.rs
clippy_lints/src/utils/paths.rs
src/lintlist/mod.rs
tests/ui/borrow_interior_mutable_const.rs [deleted file]
tests/ui/borrow_interior_mutable_const.stderr [deleted file]
tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs [new file with mode: 0644]
tests/ui/borrow_interior_mutable_const/enums.rs [new file with mode: 0644]
tests/ui/borrow_interior_mutable_const/enums.stderr [new file with mode: 0644]
tests/ui/borrow_interior_mutable_const/others.rs [new file with mode: 0644]
tests/ui/borrow_interior_mutable_const/others.stderr [new file with mode: 0644]
tests/ui/borrow_interior_mutable_const/traits.rs [new file with mode: 0644]
tests/ui/borrow_interior_mutable_const/traits.stderr [new file with mode: 0644]
tests/ui/declare_interior_mutable_const.rs [deleted file]
tests/ui/declare_interior_mutable_const.stderr [deleted file]
tests/ui/declare_interior_mutable_const/enums.rs [new file with mode: 0644]
tests/ui/declare_interior_mutable_const/enums.stderr [new file with mode: 0644]
tests/ui/declare_interior_mutable_const/others.rs [new file with mode: 0644]
tests/ui/declare_interior_mutable_const/others.stderr [new file with mode: 0644]
tests/ui/declare_interior_mutable_const/traits.rs [new file with mode: 0644]
tests/ui/declare_interior_mutable_const/traits.stderr [new file with mode: 0644]
tests/ui/manual_async_fn.fixed
tests/ui/manual_async_fn.rs
tests/ui/manual_async_fn.stderr
tests/ui/string_from_utf8_as_bytes.fixed [new file with mode: 0644]
tests/ui/string_from_utf8_as_bytes.rs [new file with mode: 0644]
tests/ui/string_from_utf8_as_bytes.stderr [new file with mode: 0644]
tests/ui/unnecessary_lazy_eval_unfixable.rs [new file with mode: 0644]
tests/ui/unnecessary_lazy_eval_unfixable.stderr [new file with mode: 0644]

index 1b9b33803de26c29bc093fb6bb18274127b439b7..e0770a45c5334d244eeac402970763850c1c9518 100644 (file)
@@ -1956,6 +1956,7 @@ Released 2018-09-13
 [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 [`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
+[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 [`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
index 8a5975e1f9711b25878cc04d41d7215f3562b3be..1da626b505dfb9e1da2978c909a392acba24dcc7 100644 (file)
--- a/README.md
+++ b/README.md
@@ -7,28 +7,22 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g
 
 [There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
-We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
-
-* `clippy::all` (everything that is on by default: all the categories below except for `nursery`, `pedantic`, and `cargo`)
-* `clippy::correctness` (code that is just **outright wrong** or **very very useless**, causes hard errors by default)
-* `clippy::style` (code that should be written in a more idiomatic way)
-* `clippy::complexity` (code that does something simple but in a complex way)
-* `clippy::perf` (code that can be written in a faster way)
-* `clippy::pedantic` (lints which are rather strict, off by default)
-* `clippy::nursery` (new lints that aren't quite ready yet, off by default)
-* `clippy::cargo` (checks against the cargo manifest, off by default)
+Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
+You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
+
+Category | Description | Default level
+-- | -- | --
+`clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny**
+`clippy::correctness` | code that is outright wrong or very useless | **deny**
+`clippy::style` | code that should be written in a more idiomatic way | **warn**
+`clippy::complexity` | code that does something simple but in a complex way | **warn**
+`clippy::perf` | code that can be written to run faster | **warn**
+`clippy::pedantic` | lints which are rather strict or might have false positives | allow
+`clippy::nursery` | new lints that are still under development | allow
+`clippy::cargo` | lints for the cargo manifest | allow
 
 More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
 
-Only the following of those categories are enabled by default:
-
-* `clippy::style`
-* `clippy::correctness`
-* `clippy::complexity`
-* `clippy::perf`
-
-Other categories need to be enabled in order for their lints to be executed.
-
 The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
 for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
 very selectively, if at all.
index fcebb54c6c217840d3b5a2b7faa8f1249dc72a29..ca819663fded8f172236bf8eaf05aa43953856c1 100644 (file)
@@ -65,8 +65,8 @@
     /// use std::cell::RefCell;
     ///
     /// async fn foo(x: &RefCell<u32>) {
-    ///   let b = x.borrow_mut()();
-    ///   *ref += 1;
+    ///   let mut y = x.borrow_mut();
+    ///   *y += 1;
     ///   bar.await;
     /// }
     /// ```
@@ -77,8 +77,8 @@
     ///
     /// async fn foo(x: &RefCell<u32>) {
     ///   {
-    ///     let b = x.borrow_mut();
-    ///     *ref += 1;
+    ///      let mut y = x.borrow_mut();
+    ///      *y += 1;
     ///   }
     ///   bar.await;
     /// }
index 126852df502eb38465750a851771675fedb648ee..1f4bed92e695d62690d8b49d128b7258b7247a64 100644 (file)
@@ -832,6 +832,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &stable_sort_primitive::STABLE_SORT_PRIMITIVE,
         &strings::STRING_ADD,
         &strings::STRING_ADD_ASSIGN,
+        &strings::STRING_FROM_UTF8_AS_BYTES,
         &strings::STRING_LIT_AS_BYTES,
         &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
         &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
@@ -1527,6 +1528,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
         LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
         LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
+        LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES),
         LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
         LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
         LintId::of(&swap::ALMOST_SWAPPED),
@@ -1752,6 +1754,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&reference::DEREF_ADDROF),
         LintId::of(&reference::REF_IN_DEREF),
         LintId::of(&repeat_once::REPEAT_ONCE),
+        LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES),
         LintId::of(&swap::MANUAL_SWAP),
         LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
         LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
index 864d1ea87f57572d45fa9e8f7dfcc5063fd5b1d8..7b3b450ef93e943fad6882126463f32a42617ebd 100644 (file)
@@ -1,5 +1,5 @@
 use crate::utils::paths::FUTURE_FROM_GENERATOR;
-use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then};
+use crate::utils::{match_function_call, position_before_rarrow, snippet_block, snippet_opt, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
@@ -69,7 +69,7 @@ fn check_fn(
                     |diag| {
                         if_chain! {
                             if let Some(header_snip) = snippet_opt(cx, header_span);
-                            if let Some(ret_pos) = header_snip.rfind("->");
+                            if let Some(ret_pos) = position_before_rarrow(header_snip.clone());
                             if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
                             then {
                                 let help = format!("make the function `async` and {}", ret_sugg);
@@ -194,7 +194,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
         },
         _ => {
             let sugg = "return the output of the future directly";
-            snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip)))
+            snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip)))
         },
     }
 }
index cde89983a2656aff2d0a50eb646b690699a79c6b..a867bdb326d722dfc0c089fda2fa6f8b6adc71d2 100644 (file)
@@ -33,6 +33,17 @@ pub(super) fn lint<'tcx>(
                 } else {
                     "unnecessary closure used to substitute value for `Result::Err`"
                 };
+                let applicability = if body
+                    .params
+                    .iter()
+                    // bindings are checked to be unused above
+                    .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
+                {
+                    Applicability::MachineApplicable
+                } else {
+                    // replacing the lambda may break type inference
+                    Applicability::MaybeIncorrect
+                };
 
                 span_lint_and_sugg(
                     cx,
@@ -46,7 +57,7 @@ pub(super) fn lint<'tcx>(
                         simplify_using,
                         snippet(cx, body_expr.span, ".."),
                     ),
-                    Applicability::MachineApplicable,
+                    applicability,
                 );
             }
         }
index 7b662eae7753eef1571c09880064486ec15f2cd5..6b0d198edcffa647112af10cceccea3a3304f2d2 100644 (file)
@@ -5,11 +5,15 @@
 use std::ptr;
 
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp};
+use rustc_hir::def_id::DefId;
+use rustc_hir::{
+    BodyId, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
+};
 use rustc_infer::traits::specialization_graph;
 use rustc_lint::{LateContext, LateLintPass, Lint};
+use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_middle::ty::{AssocKind, Ty};
+use rustc_middle::ty::{self, AssocKind, Const, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{InnerSpan, Span, DUMMY_SP};
 use rustc_typeck::hir_ty_to_ty;
     /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
     /// and this lint should be suppressed.
     ///
-    /// When an enum has variants with interior mutability, use of its non interior mutable
-    /// variants can generate false positives. See issue
-    /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
+    /// Even though the lint avoids triggering on a constant whose type has enums that have variants
+    /// with interior mutability, and its value uses non interior mutable variants (see
+    /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
+    /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
+    /// it complains about associated constants without default values only based on its types;
+    /// which might not be preferable.
+    /// There're other enums plus associated constants cases that the lint cannot handle.
     ///
     /// Types that have underlying or potential interior mutability trigger the lint whether
     /// the interior mutable field is used or not. See issues
     /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
-    /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
     ///
     /// **Example:**
     /// ```rust
     "referencing `const` with interior mutability"
 }
 
+fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    // 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.
+    cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env)
+}
+
+fn is_value_unfrozen_raw<'tcx>(
+    cx: &LateContext<'tcx>,
+    result: Result<ConstValue<'tcx>, ErrorHandled>,
+    ty: Ty<'tcx>,
+) -> bool {
+    fn inner<'tcx>(cx: &LateContext<'tcx>, val: &'tcx Const<'tcx>) -> bool {
+        match val.ty.kind() {
+            // the fact that we have to dig into every structs to search enums
+            // leads us to the point checking `UnsafeCell` directly is the only option.
+            ty::Adt(ty_def, ..) if Some(ty_def.did) == cx.tcx.lang_items().unsafe_cell_type() => true,
+            ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
+                let val = cx.tcx.destructure_const(cx.param_env.and(val));
+                val.fields.iter().any(|field| inner(cx, field))
+            },
+            _ => false,
+        }
+    }
+
+    result.map_or_else(
+        |err| {
+            // Consider `TooGeneric` cases as being unfrozen.
+            // This causes a false positive where an assoc const whose type is unfrozen
+            // have a value that is a frozen variant with a generic param (an example is
+            // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`).
+            // However, it prevents a number of false negatives that is, I think, important:
+            // 1. assoc consts in trait defs referring to consts of themselves
+            //    (an example is `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
+            // 2. a path expr referring to assoc consts whose type is doesn't have
+            //    any frozen variants in trait defs (i.e. without substitute for `Self`).
+            //    (e.g. borrowing `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
+            // 3. similar to the false positive above;
+            //    but the value is an unfrozen variant, or the type has no enums. (An example is
+            //    `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT`
+            //    and `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
+            // One might be able to prevent these FNs correctly, and replace this with `false`;
+            // e.g. implementing `has_frozen_variant` described above, and not running this function
+            // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd
+            // case (that actually removes another suboptimal behavior (I won't say 'false positive') where,
+            // similar to 2., but with the a frozen variant) (e.g. borrowing
+            // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`).
+            // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
+            err == ErrorHandled::TooGeneric
+        },
+        |val| inner(cx, Const::from_value(cx.tcx, val, ty)),
+    )
+}
+
+fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
+    let result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id());
+    is_value_unfrozen_raw(cx, result, ty)
+}
+
+fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
+    let substs = cx.typeck_results().node_substs(hir_id);
+
+    let result = cx
+        .tcx
+        .const_eval_resolve(cx.param_env, ty::WithOptConstParam::unknown(def_id), substs, None, None);
+    is_value_unfrozen_raw(cx, result, ty)
+}
+
 #[derive(Copy, Clone)]
 enum Source {
     Item { item: Span },
@@ -130,19 +210,7 @@ fn lint(&self) -> (&'static Lint, &'static str, Span) {
     }
 }
 
-fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
-    // 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;
-    }
-
+fn lint(cx: &LateContext<'_>, source: Source) {
     let (lint, msg, span) = source.lint();
     span_lint_and_then(cx, lint, span, msg, |diag| {
         if span.from_expansion() {
@@ -165,24 +233,44 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) {
 
 impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
-        if let ItemKind::Const(hir_ty, ..) = &it.kind {
+        if let ItemKind::Const(hir_ty, body_id) = it.kind {
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
-            verify_ty_bound(cx, ty, Source::Item { item: it.span });
+
+            if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
+                lint(cx, Source::Item { item: it.span });
+            }
         }
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
-        if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind {
+        if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind {
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
+
             // 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 });
+            if is_unfrozen(cx, normalized)
+                // When there's no default value, lint it only according to its type;
+                // in other words, lint consts whose value *could* be unfrozen, not definitely is.
+                // This feels inconsistent with how the lint treats generic types,
+                // which avoids linting types which potentially become unfrozen.
+                // One could check whether a unfrozen type have a *frozen variant*
+                // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`),
+                // and do the same as the case of generic types at impl items.
+                // Note that it isn't sufficient to check if it has an enum
+                // since all of that enum's variants can be unfrozen:
+                // i.e. having an enum doesn't necessary mean a type has a frozen variant.
+                // And, implementing it isn't a trivial task; it'll probably end up
+                // re-implementing the trait predicate evaluation specific to `Freeze`.
+                && body_id_opt.map_or(true, |body_id| is_value_unfrozen_poly(cx, body_id, normalized))
+            {
+                lint(cx, Source::Assoc { item: trait_item.span });
+            }
         }
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
-        if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind {
+        if let ImplItemKind::Const(hir_ty, body_id) = &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);
 
@@ -209,16 +297,23 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
                                 ),
                             ))
                             .is_err();
+                            // If there were a function like `has_frozen_variant` described above,
+                            // we should use here as a frozen variant is a potential to be frozen
+                            // similar to unknown layouts.
+                            // e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
                         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,
-                                },
-                            );
+                            if is_unfrozen(cx, normalized)
+                                && is_value_unfrozen_poly(cx, *body_id, normalized)
+                            {
+                                lint(
+                                   cx,
+                                   Source::Assoc {
+                                       item: impl_item.span,
+                                    },
+                                );
+                            }
                         }
                     }
                 },
@@ -226,7 +321,10 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
                     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 });
+
+                    if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, *body_id, normalized) {
+                        lint(cx, Source::Assoc { item: impl_item.span });
+                    }
                 },
                 _ => (),
             }
@@ -241,8 +339,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             }
 
             // Make sure it is a const item.
-            match qpath_res(cx, qpath, expr.hir_id) {
-                Res::Def(DefKind::Const | DefKind::AssocConst, _) => {},
+            let item_def_id = match qpath_res(cx, qpath, expr.hir_id) {
+                Res::Def(DefKind::Const | DefKind::AssocConst, did) => did,
                 _ => return,
             };
 
@@ -319,7 +417,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 cx.typeck_results().expr_ty(dereferenced_expr)
             };
 
-            verify_ty_bound(cx, ty, Source::Expr { expr: expr.span });
+            if is_unfrozen(cx, ty) && is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) {
+                lint(cx, Source::Expr { expr: expr.span });
+            }
         }
     }
 }
index 0dd2da949c4c33e5d8105148abda3f98430465b0..ede37624f71a4f1e7364cf4ae70af461c74dacf3 100644 (file)
@@ -1,5 +1,5 @@
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -9,7 +9,10 @@
 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};
+use crate::utils::{
+    get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint,
+    span_lint_and_sugg,
+};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for string appends of the form `x = x + y` (without
@@ -174,16 +177,75 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
     }
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Check if the string is transformed to byte array and casted back to string.
+    ///
+    /// **Why is this bad?** It's unnecessary, the string can be used directly.
+    ///
+    /// **Known problems:** None
+    ///
+    /// **Example:**
+    /// ```rust
+    /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
+    /// ```
+    /// could be written as
+    /// ```rust
+    /// let _ = &"Hello World!"[6..11];
+    /// ```
+    pub STRING_FROM_UTF8_AS_BYTES,
+    complexity,
+    "casting string slices to byte slices and back"
+}
+
 // Max length a b"foo" string can take
 const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
 
-declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]);
+declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]);
 
 impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         use crate::utils::{snippet, snippet_with_applicability};
         use rustc_ast::LitKind;
 
+        if_chain! {
+            // Find std::str::converts::from_utf8
+            if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8);
+
+            // Find string::as_bytes
+            if let ExprKind::AddrOf(BorrowKind::Ref, _, ref args) = args[0].kind;
+            if let ExprKind::Index(ref left, ref right) = args.kind;
+            let (method_names, expressions, _) = method_calls(left, 1);
+            if method_names.len() == 1;
+            if expressions.len() == 1;
+            if expressions[0].len() == 1;
+            if method_names[0] == sym!(as_bytes);
+
+            // Check for slicer
+            if let ExprKind::Struct(ref path, _, _) = right.kind;
+            if let QPath::LangItem(LangItem::Range, _) = path;
+
+            then {
+                let mut applicability = Applicability::MachineApplicable;
+                let string_expression = &expressions[0][0];
+
+                let snippet_app = snippet_with_applicability(
+                    cx,
+                    string_expression.span, "..",
+                    &mut applicability,
+                );
+
+                span_lint_and_sugg(
+                    cx,
+                    STRING_FROM_UTF8_AS_BYTES,
+                    e.span,
+                    "calling a slice of `as_bytes()` with `from_utf8` should be not necessary",
+                    "try",
+                    format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")),
+                    applicability
+                )
+            }
+        }
+
         if_chain! {
             if let ExprKind::MethodCall(path, _, args, _) = &e.kind;
             if path.ident.name == sym!(as_bytes);
index b1339c3d6395de31cb5c56573debb95fdca5806c..f61fd2ecd735d915964c5b3ce80bfaa6da56e29e 100644 (file)
@@ -7,7 +7,7 @@
 use rustc_span::source_map::Span;
 use rustc_span::BytePos;
 
-use crate::utils::span_lint_and_sugg;
+use crate::utils::{position_before_rarrow, span_lint_and_sugg};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for unit (`()`) expressions that can be removed.
@@ -120,26 +120,13 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
 
 fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
     let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
-        fn_source
-            .rfind("->")
-            .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
-                let mut rpos = rpos;
-                let chars: Vec<char> = fn_source.chars().collect();
-                while rpos > 1 {
-                    if let Some(c) = chars.get(rpos - 1) {
-                        if c.is_whitespace() {
-                            rpos -= 1;
-                            continue;
-                        }
-                    }
-                    break;
-                }
-                (
-                    #[allow(clippy::cast_possible_truncation)]
-                    ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
-                    Applicability::MachineApplicable,
-                )
-            })
+        position_before_rarrow(fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
+            (
+                #[allow(clippy::cast_possible_truncation)]
+                ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
+                Applicability::MachineApplicable,
+            )
+        })
     } else {
         (ty.span, Applicability::MaybeIncorrect)
     };
index 85e7f055e79de4810ef1322764c9d3dd0e77006b..8e4149df0328b18cb5663978d456b8e2c90c3582 100644 (file)
@@ -659,6 +659,35 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
     snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 }
 
+/// Returns the positon just before rarrow
+///
+/// ```rust,ignore
+/// fn into(self) -> () {}
+///              ^
+/// // in case of unformatted code
+/// fn into2(self)-> () {}
+///               ^
+/// fn into3(self)   -> () {}
+///               ^
+/// ```
+#[allow(clippy::needless_pass_by_value)]
+pub fn position_before_rarrow(s: String) -> Option<usize> {
+    s.rfind("->").map(|rpos| {
+        let mut rpos = rpos;
+        let chars: Vec<char> = s.chars().collect();
+        while rpos > 1 {
+            if let Some(c) = chars.get(rpos - 1) {
+                if c.is_whitespace() {
+                    rpos -= 1;
+                    continue;
+                }
+            }
+            break;
+        }
+        rpos
+    })
+}
+
 /// Extends the span to the beginning of the spans line, incl. whitespaces.
 ///
 /// ```rust,ignore
index 1ad8c6029860b7a0d33037355a261f0915466012..2be5ff93f86959efd637996f0805417359260170 100644 (file)
 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_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 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"];
index 702f9d86de62deae4d0650d75df79d07715cc988..bc0a0ad2b179deb226b975b288bdc6560633fe62 100644 (file)
         deprecation: None,
         module: "methods",
     },
+    Lint {
+        name: "string_from_utf8_as_bytes",
+        group: "complexity",
+        desc: "casting string slices to byte slices and back",
+        deprecation: None,
+        module: "strings",
+    },
     Lint {
         name: "string_lit_as_bytes",
         group: "nursery",
diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs
deleted file mode 100644 (file)
index 9fcc9ec..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-#![warn(clippy::borrow_interior_mutable_const)]
-#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
-#![allow(const_item_mutation)]
-
-use std::borrow::Cow;
-use std::cell::{Cell, UnsafeCell};
-use std::fmt::Display;
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::Once;
-
-const ATOMIC: AtomicUsize = AtomicUsize::new(5);
-const CELL: Cell<usize> = Cell::new(6);
-const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
-const INTEGER: u8 = 8;
-const STRING: String = String::new();
-const STR: &str = "012345";
-const COW: Cow<str> = Cow::Borrowed("abcdef");
-const NO_ANN: &dyn Display = &70;
-static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
-const ONCE_INIT: Once = Once::new();
-
-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 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,
-// it's semantically the same as `&'static T`;
-// but it isn't allowed to make a static reference from an arbitrary integer value at the moment.
-// For more information, please see the issue #5918.
-pub struct StaticRef<T> {
-    ptr: *const T,
-}
-
-impl<T> StaticRef<T> {
-    /// Create a new `StaticRef` from a raw pointer
-    ///
-    /// ## Safety
-    ///
-    /// Callers must pass in a reference to statically allocated memory which
-    /// does not overlap with other values.
-    pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
-        StaticRef { ptr }
-    }
-}
-
-impl<T> std::ops::Deref for StaticRef<T> {
-    type Target = T;
-
-    fn deref(&self) -> &'static T {
-        unsafe { &*self.ptr }
-    }
-}
-
-// use a tuple to make sure referencing a field behind a pointer isn't linted.
-const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
-
-fn main() {
-    ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
-    assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
-
-    let _once = ONCE_INIT;
-    let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
-    let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
-    let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
-    let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
-    let _atomic_into_inner = ATOMIC.into_inner();
-    // these should be all fine.
-    let _twice = (ONCE_INIT, ONCE_INIT);
-    let _ref_twice = &(ONCE_INIT, ONCE_INIT);
-    let _ref_once = &(ONCE_INIT, ONCE_INIT).0;
-    let _array_twice = [ONCE_INIT, ONCE_INIT];
-    let _ref_array_twice = &[ONCE_INIT, ONCE_INIT];
-    let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
-
-    // referencing projection is still bad.
-    let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
-    let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
-    let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
-    let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
-    let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
-    let _ = &*ATOMIC_TUPLE.1; //~ ERROR interior mutability
-    let _ = &ATOMIC_TUPLE.2;
-    let _ = (&&&&ATOMIC_TUPLE).0;
-    let _ = (&&&&ATOMIC_TUPLE).2;
-    let _ = ATOMIC_TUPLE.0;
-    let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
-    let _ = ATOMIC_TUPLE.1.into_iter();
-    let _ = ATOMIC_TUPLE.2;
-    let _ = &{ ATOMIC_TUPLE };
-
-    CELL.set(2); //~ ERROR interior mutability
-    assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
-
-    assert_eq!(INTEGER, 8);
-    assert!(STRING.is_empty());
-
-    let a = ATOMIC;
-    a.store(4, Ordering::SeqCst);
-    assert_eq!(a.load(Ordering::SeqCst), 4);
-
-    STATIC_TUPLE.0.store(3, Ordering::SeqCst);
-    assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3);
-    assert!(STATIC_TUPLE.1.is_empty());
-
-    u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
-    assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
-
-    assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
-
-    let _ = &CELL_REF.0;
-}
diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr
deleted file mode 100644 (file)
index ed726a6..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-error: a `const` item with interior mutability should not be borrowed
-  --> $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
-   |     ^^^^^^
-   |
-   = 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:81:16
-   |
-LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ 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:22
-   |
-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:85:25
-   |
-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:86:27
-   |
-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:87:26
-   |
-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:98:14
-   |
-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:99:14
-   |
-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:100:19
-   |
-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:101:14
-   |
-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:102:13
-   |
-LL |     let _ = ATOMIC_TUPLE.0[0].load(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:108:13
-   |
-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:113:5
-   |
-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:114:16
-   |
-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:127:5
-   |
-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: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 17 previous errors
-
diff --git a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs
new file mode 100644 (file)
index 0000000..2289f78
--- /dev/null
@@ -0,0 +1,16 @@
+// this file solely exists to test constants defined in foreign crates.
+// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure.
+
+#![allow(clippy::declare_interior_mutable_const)]
+
+use std::sync::atomic::AtomicUsize;
+
+enum Private<T> {
+    ToBeUnfrozen(T),
+    Frozen(usize),
+}
+
+pub struct Wrapper(Private<AtomicUsize>);
+
+pub const WRAPPED_PRIVATE_UNFROZEN_VARIANT: Wrapper = Wrapper(Private::ToBeUnfrozen(AtomicUsize::new(6)));
+pub const WRAPPED_PRIVATE_FROZEN_VARIANT: Wrapper = Wrapper(Private::Frozen(7));
diff --git a/tests/ui/borrow_interior_mutable_const/enums.rs b/tests/ui/borrow_interior_mutable_const/enums.rs
new file mode 100644 (file)
index 0000000..5027db4
--- /dev/null
@@ -0,0 +1,101 @@
+// aux-build:helper.rs
+
+#![warn(clippy::borrow_interior_mutable_const)]
+#![allow(clippy::declare_interior_mutable_const)]
+
+// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions.
+
+extern crate helper;
+
+use std::cell::Cell;
+use std::sync::atomic::AtomicUsize;
+
+enum OptionalCell {
+    Unfrozen(Cell<bool>),
+    Frozen,
+}
+
+const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
+const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+fn borrow_optional_cell() {
+    let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability
+    let _ = &FROZEN_VARIANT;
+}
+
+trait AssocConsts {
+    const TO_BE_UNFROZEN_VARIANT: OptionalCell;
+    const TO_BE_FROZEN_VARIANT: OptionalCell;
+
+    const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+    const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+    fn function() {
+        // This is the "suboptimal behavior" mentioned in `is_value_unfrozen`
+        // caused by a similar reason to unfrozen types without any default values
+        // get linted even if it has frozen variants'.
+        let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable
+
+        // The lint ignores default values because an impl of this trait can set
+        // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`.
+        let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable
+    }
+}
+
+impl AssocConsts for u64 {
+    const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+    const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+    fn function() {
+        let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+        let _ = &<Self as AssocConsts>::TO_BE_FROZEN_VARIANT;
+        let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable
+        let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
+    }
+}
+
+trait AssocTypes {
+    type ToBeUnfrozen;
+
+    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+
+    // there's no need to test here because it's the exactly same as `trait::AssocTypes`
+    fn function();
+}
+
+impl AssocTypes for u64 {
+    type ToBeUnfrozen = AtomicUsize;
+
+    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
+
+    fn function() {
+        let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+        let _ = &<Self as AssocTypes>::TO_BE_FROZEN_VARIANT;
+    }
+}
+
+enum BothOfCellAndGeneric<T> {
+    Unfrozen(Cell<*const T>),
+    Generic(*const T),
+    Frozen(usize),
+}
+
+impl<T> BothOfCellAndGeneric<T> {
+    const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+    const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+    const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
+
+    fn function() {
+        let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability
+        let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability
+        let _ = &Self::FROZEN_VARIANT;
+    }
+}
+
+fn main() {
+    // constants defined in foreign crates
+    let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability
+    let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT;
+}
diff --git a/tests/ui/borrow_interior_mutable_const/enums.stderr b/tests/ui/borrow_interior_mutable_const/enums.stderr
new file mode 100644 (file)
index 0000000..654a1ee
--- /dev/null
@@ -0,0 +1,75 @@
+error: a `const` item with interior mutability should not be borrowed
+  --> $DIR/enums.rs:22:14
+   |
+LL |     let _ = &UNFROZEN_VARIANT; //~ 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/enums.rs:37:18
+   |
+LL |         let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/enums.rs:41:18
+   |
+LL |         let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/enums.rs:50:18
+   |
+LL |         let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/enums.rs:52:18
+   |
+LL |         let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/enums.rs:74:18
+   |
+LL |         let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/enums.rs:91:18
+   |
+LL |         let _ = &Self::UNFROZEN_VARIANT; //~ 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/enums.rs:92:18
+   |
+LL |         let _ = &Self::GENERIC_VARIANT; //~ 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/enums.rs:99:14
+   |
+LL |     let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: assign this const to a local or static variable, and use the variable here
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs
new file mode 100644 (file)
index 0000000..ea25729
--- /dev/null
@@ -0,0 +1,104 @@
+#![warn(clippy::borrow_interior_mutable_const)]
+#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
+#![allow(const_item_mutation)]
+
+use std::borrow::Cow;
+use std::cell::{Cell, UnsafeCell};
+use std::fmt::Display;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Once;
+
+const ATOMIC: AtomicUsize = AtomicUsize::new(5);
+const CELL: Cell<usize> = Cell::new(6);
+const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
+const INTEGER: u8 = 8;
+const STRING: String = String::new();
+const STR: &str = "012345";
+const COW: Cow<str> = Cow::Borrowed("abcdef");
+const NO_ANN: &dyn Display = &70;
+static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
+const ONCE_INIT: Once = Once::new();
+
+// This is just a pointer that can be safely dereferenced,
+// it's semantically the same as `&'static T`;
+// but it isn't allowed to make a static reference from an arbitrary integer value at the moment.
+// For more information, please see the issue #5918.
+pub struct StaticRef<T> {
+    ptr: *const T,
+}
+
+impl<T> StaticRef<T> {
+    /// Create a new `StaticRef` from a raw pointer
+    ///
+    /// ## Safety
+    ///
+    /// Callers must pass in a reference to statically allocated memory which
+    /// does not overlap with other values.
+    pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
+        StaticRef { ptr }
+    }
+}
+
+impl<T> std::ops::Deref for StaticRef<T> {
+    type Target = T;
+
+    fn deref(&self) -> &'static T {
+        unsafe { &*self.ptr }
+    }
+}
+
+// use a tuple to make sure referencing a field behind a pointer isn't linted.
+const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
+
+fn main() {
+    ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
+    assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
+
+    let _once = ONCE_INIT;
+    let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
+    let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
+    let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
+    let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
+    let _atomic_into_inner = ATOMIC.into_inner();
+    // these should be all fine.
+    let _twice = (ONCE_INIT, ONCE_INIT);
+    let _ref_twice = &(ONCE_INIT, ONCE_INIT);
+    let _ref_once = &(ONCE_INIT, ONCE_INIT).0;
+    let _array_twice = [ONCE_INIT, ONCE_INIT];
+    let _ref_array_twice = &[ONCE_INIT, ONCE_INIT];
+    let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
+
+    // referencing projection is still bad.
+    let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
+    let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
+    let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
+    let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+    let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
+    let _ = &*ATOMIC_TUPLE.1;
+    let _ = &ATOMIC_TUPLE.2;
+    let _ = (&&&&ATOMIC_TUPLE).0;
+    let _ = (&&&&ATOMIC_TUPLE).2;
+    let _ = ATOMIC_TUPLE.0;
+    let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
+    let _ = ATOMIC_TUPLE.1.into_iter();
+    let _ = ATOMIC_TUPLE.2;
+    let _ = &{ ATOMIC_TUPLE };
+
+    CELL.set(2); //~ ERROR interior mutability
+    assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
+
+    assert_eq!(INTEGER, 8);
+    assert!(STRING.is_empty());
+
+    let a = ATOMIC;
+    a.store(4, Ordering::SeqCst);
+    assert_eq!(a.load(Ordering::SeqCst), 4);
+
+    STATIC_TUPLE.0.store(3, Ordering::SeqCst);
+    assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3);
+    assert!(STATIC_TUPLE.1.is_empty());
+
+    assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
+
+    let _ = &CELL_REF.0;
+}
diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr
new file mode 100644 (file)
index 0000000..9a908cf
--- /dev/null
@@ -0,0 +1,115 @@
+error: a `const` item with interior mutability should not be borrowed
+  --> $DIR/others.rs:54: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/others.rs:55:16
+   |
+LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ 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/others.rs:58:22
+   |
+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/others.rs:59:25
+   |
+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/others.rs:60:27
+   |
+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/others.rs:61:26
+   |
+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/others.rs:72:14
+   |
+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/others.rs:73:14
+   |
+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/others.rs:74:19
+   |
+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/others.rs:75:14
+   |
+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/others.rs:76:13
+   |
+LL |     let _ = ATOMIC_TUPLE.0[0].load(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/others.rs:82:13
+   |
+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/others.rs:87:5
+   |
+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/others.rs:88:16
+   |
+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: aborting due to 14 previous errors
+
diff --git a/tests/ui/borrow_interior_mutable_const/traits.rs b/tests/ui/borrow_interior_mutable_const/traits.rs
new file mode 100644 (file)
index 0000000..06b5d62
--- /dev/null
@@ -0,0 +1,202 @@
+#![warn(clippy::borrow_interior_mutable_const)]
+#![allow(clippy::declare_interior_mutable_const)]
+
+// this file replicates its `declare` counterpart. Please see it for more discussions.
+
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+trait ConcreteTypes {
+    const ATOMIC: AtomicUsize;
+    const STRING: String;
+
+    fn function() {
+        let _ = &Self::ATOMIC; //~ ERROR interior mutable
+        let _ = &Self::STRING;
+    }
+}
+
+impl ConcreteTypes for u64 {
+    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
+    const STRING: String = String::new();
+
+    fn function() {
+        // Lint this again since implementers can choose not to borrow it.
+        let _ = &Self::ATOMIC; //~ ERROR interior mutable
+        let _ = &Self::STRING;
+    }
+}
+
+// a helper trait used below
+trait ConstDefault {
+    const DEFAULT: Self;
+}
+
+trait GenericTypes<T, U> {
+    const TO_REMAIN_GENERIC: T;
+    const TO_BE_CONCRETE: U;
+
+    fn function() {
+        let _ = &Self::TO_REMAIN_GENERIC;
+    }
+}
+
+impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for Vec<T> {
+    const TO_REMAIN_GENERIC: T = T::DEFAULT;
+    const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
+
+    fn function() {
+        let _ = &Self::TO_REMAIN_GENERIC;
+        let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable
+    }
+}
+
+// a helper type used below
+pub struct Wrapper<T>(T);
+
+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>;
+    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
+
+    fn function() {
+        let _ = &Self::TO_BE_FROZEN;
+        let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
+    }
+}
+
+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);
+    const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
+    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
+
+    fn function() {
+        let _ = &Self::TO_BE_FROZEN;
+        let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable
+        let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable
+        let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM;
+    }
+}
+
+// a helper trait used below
+trait AssocTypesHelper {
+    type NotToBeBounded;
+    type ToBeBounded;
+
+    const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
+}
+
+trait AssocTypesFromGenericParam<T>
+where
+    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+    const NOT_BOUNDED: T::NotToBeBounded;
+    const BOUNDED: T::ToBeBounded;
+
+    fn function() {
+        let _ = &Self::NOT_BOUNDED;
+        let _ = &Self::BOUNDED; //~ ERROR interior mutable
+    }
+}
+
+impl<T> AssocTypesFromGenericParam<T> for Vec<T>
+where
+    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+    const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
+    const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
+
+    fn function() {
+        let _ = &Self::NOT_BOUNDED;
+        let _ = &Self::BOUNDED; //~ ERROR interior mutable
+    }
+}
+
+trait SelfType: Sized {
+    const SELF: Self;
+    const WRAPPED_SELF: Option<Self>;
+
+    fn function() {
+        let _ = &Self::SELF;
+        let _ = &Self::WRAPPED_SELF;
+    }
+}
+
+impl SelfType for u64 {
+    const SELF: Self = 16;
+    const WRAPPED_SELF: Option<Self> = Some(20);
+
+    fn function() {
+        let _ = &Self::SELF;
+        let _ = &Self::WRAPPED_SELF;
+    }
+}
+
+impl SelfType for AtomicUsize {
+    const SELF: Self = AtomicUsize::new(17);
+    const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
+
+    fn function() {
+        let _ = &Self::SELF; //~ ERROR interior mutable
+        let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable
+    }
+}
+
+trait BothOfCellAndGeneric<T> {
+    const DIRECT: Cell<T>;
+    const INDIRECT: Cell<*const T>;
+
+    fn function() {
+        let _ = &Self::DIRECT;
+        let _ = &Self::INDIRECT; //~ ERROR interior mutable
+    }
+}
+
+impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> {
+    const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
+    const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
+
+    fn function() {
+        let _ = &Self::DIRECT;
+        let _ = &Self::INDIRECT; //~ ERROR interior mutable
+    }
+}
+
+struct Local<T>(T);
+
+impl<T> Local<T>
+where
+    T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
+{
+    const ATOMIC: AtomicUsize = AtomicUsize::new(18);
+    const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
+
+    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);
+
+    fn function() {
+        let _ = &Self::ATOMIC; //~ ERROR interior mutable
+        let _ = &Self::COW;
+        let _ = &Self::GENERIC_TYPE;
+        let _ = &Self::ASSOC_TYPE;
+        let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable
+    }
+}
+
+fn main() {
+    u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
+    assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
+}
diff --git a/tests/ui/borrow_interior_mutable_const/traits.stderr b/tests/ui/borrow_interior_mutable_const/traits.stderr
new file mode 100644 (file)
index 0000000..8f26403
--- /dev/null
@@ -0,0 +1,123 @@
+error: a `const` item with interior mutability should not be borrowed
+  --> $DIR/traits.rs:15:18
+   |
+LL |         let _ = &Self::ATOMIC; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^
+   |
+   = 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/traits.rs:26:18
+   |
+LL |         let _ = &Self::ATOMIC; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^
+   |
+   = 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/traits.rs:51:18
+   |
+LL |         let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:86:18
+   |
+LL |         let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:87:18
+   |
+LL |         let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:109:18
+   |
+LL |         let _ = &Self::BOUNDED; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:122:18
+   |
+LL |         let _ = &Self::BOUNDED; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:151:18
+   |
+LL |         let _ = &Self::SELF; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^
+   |
+   = 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/traits.rs:152:18
+   |
+LL |         let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:162:18
+   |
+LL |         let _ = &Self::INDIRECT; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:172:18
+   |
+LL |         let _ = &Self::INDIRECT; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:191:18
+   |
+LL |         let _ = &Self::ATOMIC; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^
+   |
+   = 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/traits.rs:195:18
+   |
+LL |         let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/traits.rs:200:5
+   |
+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/traits.rs:201: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 15 previous errors
+
diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs
deleted file mode 100644 (file)
index 3afcdca..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-#![warn(clippy::declare_interior_mutable_const)]
-
-use std::borrow::Cow;
-use std::cell::Cell;
-use std::fmt::Display;
-use std::sync::atomic::AtomicUsize;
-use std::sync::Once;
-
-const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
-const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
-const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
-//~^ ERROR interior mutable
-
-macro_rules! declare_const {
-    ($name:ident: $ty:ty = $e:expr) => {
-        const $name: $ty = $e;
-    };
-}
-declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
-
-// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
-
-const INTEGER: u8 = 8;
-const STRING: String = String::new();
-const STR: &str = "012345";
-const COW: Cow<str> = Cow::Borrowed("abcdef");
-//^ note: a const item of Cow is used in the `postgres` package.
-
-const NO_ANN: &dyn Display = &70;
-
-static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
-//^ there should be no lints on this line
-
-#[allow(clippy::declare_interior_mutable_const)]
-const ONCE_INIT: Once = Once::new();
-
-// 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;
-    declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
-}
-
-impl ConcreteTypes for u64 {
-    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
-    const INTEGER: u64 = 10;
-    const STRING: String = String::new();
-}
-
-// 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 HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC;
-    declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC);
-}
-
-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
-}
-
-// 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>;
-}
-
-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;
-}
-
-// 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 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() {}
diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr
deleted file mode 100644 (file)
index 5cb10be..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:9:1
-   |
-LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
-   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   | |
-   | make this a static item (maybe with lazy_static)
-   |
-   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
-
-error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:10:1
-   |
-LL | const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
-   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   | |
-   | make this a static item (maybe with lazy_static)
-
-error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:11:1
-   |
-LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
-   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   | |
-   | make this a static item (maybe with lazy_static)
-
-error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:16:9
-   |
-LL |         const $name: $ty = $e;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
-   | ------------------------------------------ in this macro invocation
-   |
-   = 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:39:5
-   |
-LL |     const ATOMIC: AtomicUsize; //~ ERROR interior mutable
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should never be interior mutable
-  --> $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
-   |
-   = 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:67:5
-   |
-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:92:5
-   |
-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:93:5
-   |
-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
-   |
-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:141:5
-   |
-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:149:5
-   |
-LL |     const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should never be interior mutable
-  --> $DIR/declare_interior_mutable_const.rs:165:5
-   |
-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:171:5
-   |
-LL |     const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 15 previous errors
-
diff --git a/tests/ui/declare_interior_mutable_const/enums.rs b/tests/ui/declare_interior_mutable_const/enums.rs
new file mode 100644 (file)
index 0000000..f445186
--- /dev/null
@@ -0,0 +1,123 @@
+#![warn(clippy::declare_interior_mutable_const)]
+
+use std::cell::Cell;
+use std::sync::atomic::AtomicUsize;
+
+enum OptionalCell {
+    Unfrozen(Cell<bool>),
+    Frozen,
+}
+
+// a constant with enums should be linted only when the used variant is unfrozen (#3962).
+const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable
+const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+const fn unfrozen_variant() -> OptionalCell {
+    OptionalCell::Unfrozen(Cell::new(false))
+}
+
+const fn frozen_variant() -> OptionalCell {
+    OptionalCell::Frozen
+}
+
+const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable
+const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant();
+
+enum NestedInnermost {
+    Unfrozen(AtomicUsize),
+    Frozen,
+}
+
+struct NestedInner {
+    inner: NestedInnermost,
+}
+
+enum NestedOuter {
+    NestedInner(NestedInner),
+    NotNested(usize),
+}
+
+struct NestedOutermost {
+    outer: NestedOuter,
+}
+
+// a constant with enums should be linted according to its value, no matter how structs involve.
+const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
+    outer: NestedOuter::NestedInner(NestedInner {
+        inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
+    }),
+}; //~ ERROR interior mutable
+const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost {
+    outer: NestedOuter::NestedInner(NestedInner {
+        inner: NestedInnermost::Frozen,
+    }),
+};
+
+trait AssocConsts {
+    // When there's no default value, lint it only according to its type.
+    // Further details are on the corresponding code (`NonCopyConst::check_trait_item`).
+    const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+    const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+
+    // Lint default values accordingly.
+    const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable
+    const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+}
+
+// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it
+// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'.
+impl AssocConsts for u64 {
+    const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+    const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
+
+    // even if this sets an unfrozen variant, the lint ignores it.
+    const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
+}
+
+// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters
+// here are values; and I think substituted generics at definitions won't appear in MIR.
+trait AssocTypes {
+    type ToBeUnfrozen;
+
+    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
+}
+
+impl AssocTypes for u64 {
+    type ToBeUnfrozen = AtomicUsize;
+
+    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
+}
+
+// Use raw pointers since direct generics have a false negative at the type level.
+enum BothOfCellAndGeneric<T> {
+    Unfrozen(Cell<*const T>),
+    Generic(*const T),
+    Frozen(usize),
+}
+
+impl<T> BothOfCellAndGeneric<T> {
+    const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+
+    // This is a false positive. The argument about this is on `is_value_unfrozen_raw`
+    const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+
+    const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
+
+    // This is what is likely to be a false negative when one tries to fix
+    // the `GENERIC_VARIANT` false positive.
+    const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable
+}
+
+// associated types here is basically the same as the one above.
+trait BothOfCellAndGenericWithAssocType {
+    type AssocType;
+
+    const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
+        BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+    const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+    const FROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Frozen(5);
+}
+
+fn main() {}
diff --git a/tests/ui/declare_interior_mutable_const/enums.stderr b/tests/ui/declare_interior_mutable_const/enums.stderr
new file mode 100644 (file)
index 0000000..84198d5
--- /dev/null
@@ -0,0 +1,89 @@
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:12:1
+   |
+LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable
+   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | make this a static item (maybe with lazy_static)
+   |
+   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:23:1
+   |
+LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable
+   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | make this a static item (maybe with lazy_static)
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:45:1
+   |
+LL |   const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
+   |   ^----
+   |   |
+   |  _make this a static item (maybe with lazy_static)
+   | |
+LL | |     outer: NestedOuter::NestedInner(NestedInner {
+LL | |         inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
+LL | |     }),
+LL | | }; //~ ERROR interior mutable
+   | |__^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:59:5
+   |
+LL |     const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:60:5
+   |
+LL |     const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:63:5
+   |
+LL |     const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:89:5
+   |
+LL |     const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:101:5
+   |
+LL |     const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mut...
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:104:5
+   |
+LL |     const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:110:5
+   |
+LL |     const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:117:5
+   |
+LL | /     const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
+LL | |         BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable
+   | |____________________________________________________________________^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/enums.rs:119:5
+   |
+LL |     const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mu...
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs
new file mode 100644 (file)
index 0000000..48c5e95
--- /dev/null
@@ -0,0 +1,34 @@
+#![warn(clippy::declare_interior_mutable_const)]
+
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::fmt::Display;
+use std::sync::atomic::AtomicUsize;
+use std::sync::Once;
+
+const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
+const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
+const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
+//~^ ERROR interior mutable
+
+macro_rules! declare_const {
+    ($name:ident: $ty:ty = $e:expr) => {
+        const $name: $ty = $e;
+    };
+}
+declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
+
+// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
+
+const INTEGER: u8 = 8;
+const STRING: String = String::new();
+const STR: &str = "012345";
+const COW: Cow<str> = Cow::Borrowed("abcdef");
+//^ note: a const item of Cow is used in the `postgres` package.
+
+const NO_ANN: &dyn Display = &70;
+
+static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
+//^ there should be no lints on this line
+
+fn main() {}
diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr
new file mode 100644 (file)
index 0000000..6153c96
--- /dev/null
@@ -0,0 +1,39 @@
+error: a `const` item should never be interior mutable
+  --> $DIR/others.rs:9:1
+   |
+LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable
+   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | make this a static item (maybe with lazy_static)
+   |
+   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
+
+error: a `const` item should never be interior mutable
+  --> $DIR/others.rs:10:1
+   |
+LL | const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable
+   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | make this a static item (maybe with lazy_static)
+
+error: a `const` item should never be interior mutable
+  --> $DIR/others.rs:11:1
+   |
+LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
+   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | make this a static item (maybe with lazy_static)
+
+error: a `const` item should never be interior mutable
+  --> $DIR/others.rs:16:9
+   |
+LL |         const $name: $ty = $e;
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable
+   | ------------------------------------------ in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/declare_interior_mutable_const/traits.rs b/tests/ui/declare_interior_mutable_const/traits.rs
new file mode 100644 (file)
index 0000000..535147c
--- /dev/null
@@ -0,0 +1,150 @@
+#![warn(clippy::declare_interior_mutable_const)]
+
+use std::borrow::Cow;
+use std::cell::Cell;
+use std::sync::atomic::AtomicUsize;
+
+macro_rules! declare_const {
+    ($name:ident: $ty:ty = $e:expr) => {
+        const $name: $ty = $e;
+    };
+}
+
+// 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;
+    declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+}
+
+impl ConcreteTypes for u64 {
+    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
+    const INTEGER: u64 = 10;
+    const STRING: String = String::new();
+}
+
+// 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 HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC;
+    declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC);
+}
+
+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
+}
+
+// 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>;
+}
+
+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;
+}
+
+// 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 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() {}
diff --git a/tests/ui/declare_interior_mutable_const/traits.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr
new file mode 100644 (file)
index 0000000..bb77f39
--- /dev/null
@@ -0,0 +1,75 @@
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:15:5
+   |
+LL |     const ATOMIC: AtomicUsize; //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:9:9
+   |
+LL |         const $name: $ty = $e;
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable
+   |     ----------------------------------------------------------- in this macro invocation
+   |
+   = 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/traits.rs:43:5
+   |
+LL |     const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:68:5
+   |
+LL |     const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:69:5
+   |
+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/traits.rs:88:5
+   |
+LL |     const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:116:5
+   |
+LL |     const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:117:5
+   |
+LL |     const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21)); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:125:5
+   |
+LL |     const INDIRECT: Cell<*const T>; //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:141:5
+   |
+LL |     const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: a `const` item should never be interior mutable
+  --> $DIR/traits.rs:147:5
+   |
+LL |     const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
index 4f551690c4370ab3b7f2428237f3a0d5e3548180..5184f6fdb88b3991850aead57bf02ea0a75b46cf 100644 (file)
@@ -7,7 +7,19 @@ use std::future::Future;
 
 async fn fut() -> i32 { 42 }
 
-async fn empty_fut()  {}
+#[rustfmt::skip]
+async fn fut2() -> i32 { 42 }
+
+#[rustfmt::skip]
+async fn fut3() -> i32 { 42 }
+
+async fn empty_fut() {}
+
+#[rustfmt::skip]
+async fn empty_fut2() {}
+
+#[rustfmt::skip]
+async fn empty_fut3() {}
 
 async fn core_fut() -> i32 { 42 }
 
index 6ed60309947a81b27f5e2a2b7a559cdbbacf9b7e..68c0e591f0b6eed6b4ffd5eeb1b8bd0fde47bae1 100644 (file)
@@ -9,10 +9,30 @@ fn fut() -> impl Future<Output = i32> {
     async { 42 }
 }
 
+#[rustfmt::skip]
+fn fut2() ->impl Future<Output = i32> {
+    async { 42 }
+}
+
+#[rustfmt::skip]
+fn fut3()-> impl Future<Output = i32> {
+    async { 42 }
+}
+
 fn empty_fut() -> impl Future<Output = ()> {
     async {}
 }
 
+#[rustfmt::skip]
+fn empty_fut2() ->impl Future<Output = ()> {
+    async {}
+}
+
+#[rustfmt::skip]
+fn empty_fut3()-> impl Future<Output = ()> {
+    async {}
+}
+
 fn core_fut() -> impl core::future::Future<Output = i32> {
     async move { 42 }
 }
index ccd828674276ba48cf9c6ba2a0f357edaecc5331..fdd43db3255ee5991fe49597ded05698fbae3f64 100644 (file)
@@ -15,14 +15,44 @@ LL | fn fut() -> impl Future<Output = i32> { 42 }
    |                                       ^^^^^^
 
 error: this function can be simplified using the `async fn` syntax
-  --> $DIR/manual_async_fn.rs:12:1
+  --> $DIR/manual_async_fn.rs:13:1
+   |
+LL | fn fut2() ->impl Future<Output = i32> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `async` and return the output of the future directly
+   |
+LL | async fn fut2() -> i32 {
+   | ^^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+   |
+LL | fn fut2() ->impl Future<Output = i32> { 42 }
+   |                                       ^^^^^^
+
+error: this function can be simplified using the `async fn` syntax
+  --> $DIR/manual_async_fn.rs:18:1
+   |
+LL | fn fut3()-> impl Future<Output = i32> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `async` and return the output of the future directly
+   |
+LL | async fn fut3() -> i32 {
+   | ^^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+   |
+LL | fn fut3()-> impl Future<Output = i32> { 42 }
+   |                                       ^^^^^^
+
+error: this function can be simplified using the `async fn` syntax
+  --> $DIR/manual_async_fn.rs:22:1
    |
 LL | fn empty_fut() -> impl Future<Output = ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 help: make the function `async` and remove the return type
    |
-LL | async fn empty_fut()  {
+LL | async fn empty_fut() {
    | ^^^^^^^^^^^^^^^^^^^^
 help: move the body of the async block to the enclosing function
    |
@@ -30,7 +60,37 @@ LL | fn empty_fut() -> impl Future<Output = ()> {}
    |                                            ^^
 
 error: this function can be simplified using the `async fn` syntax
-  --> $DIR/manual_async_fn.rs:16:1
+  --> $DIR/manual_async_fn.rs:27:1
+   |
+LL | fn empty_fut2() ->impl Future<Output = ()> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `async` and remove the return type
+   |
+LL | async fn empty_fut2() {
+   | ^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+   |
+LL | fn empty_fut2() ->impl Future<Output = ()> {}
+   |                                            ^^
+
+error: this function can be simplified using the `async fn` syntax
+  --> $DIR/manual_async_fn.rs:32:1
+   |
+LL | fn empty_fut3()-> impl Future<Output = ()> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `async` and remove the return type
+   |
+LL | async fn empty_fut3() {
+   | ^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+   |
+LL | fn empty_fut3()-> impl Future<Output = ()> {}
+   |                                            ^^
+
+error: this function can be simplified using the `async fn` syntax
+  --> $DIR/manual_async_fn.rs:36:1
    |
 LL | fn core_fut() -> impl core::future::Future<Output = i32> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,7 +105,7 @@ LL | fn core_fut() -> impl core::future::Future<Output = i32> { 42 }
    |                                                          ^^^^^^
 
 error: this function can be simplified using the `async fn` syntax
-  --> $DIR/manual_async_fn.rs:38:5
+  --> $DIR/manual_async_fn.rs:58:5
    |
 LL |     fn inh_fut() -> impl Future<Output = i32> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -65,7 +125,7 @@ LL |             let c = 21;
  ...
 
 error: this function can be simplified using the `async fn` syntax
-  --> $DIR/manual_async_fn.rs:73:1
+  --> $DIR/manual_async_fn.rs:93:1
    |
 LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -80,7 +140,7 @@ LL | fn elided(_: &i32) -> impl Future<Output = i32> + '_ { 42 }
    |                                                      ^^^^^^
 
 error: this function can be simplified using the `async fn` syntax
-  --> $DIR/manual_async_fn.rs:82:1
+  --> $DIR/manual_async_fn.rs:102:1
    |
 LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -94,5 +154,5 @@ help: move the body of the async block to the enclosing function
 LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future<Output = i32> + 'a + 'b { 42 }
    |                                                                                    ^^^^^^
 
-error: aborting due to 6 previous errors
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/string_from_utf8_as_bytes.fixed b/tests/ui/string_from_utf8_as_bytes.fixed
new file mode 100644 (file)
index 0000000..6e665cd
--- /dev/null
@@ -0,0 +1,6 @@
+// run-rustfix
+#![warn(clippy::string_from_utf8_as_bytes)]
+
+fn main() {
+    let _ = Some(&"Hello World!"[6..11]);
+}
diff --git a/tests/ui/string_from_utf8_as_bytes.rs b/tests/ui/string_from_utf8_as_bytes.rs
new file mode 100644 (file)
index 0000000..670d206
--- /dev/null
@@ -0,0 +1,6 @@
+// run-rustfix
+#![warn(clippy::string_from_utf8_as_bytes)]
+
+fn main() {
+    let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]);
+}
diff --git a/tests/ui/string_from_utf8_as_bytes.stderr b/tests/ui/string_from_utf8_as_bytes.stderr
new file mode 100644 (file)
index 0000000..bf5e5d3
--- /dev/null
@@ -0,0 +1,10 @@
+error: calling a slice of `as_bytes()` with `from_utf8` should be not necessary
+  --> $DIR/string_from_utf8_as_bytes.rs:5:13
+   |
+LL |     let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&"Hello World!"[6..11])`
+   |
+   = note: `-D clippy::string-from-utf8-as-bytes` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs
new file mode 100644 (file)
index 0000000..2e923bc
--- /dev/null
@@ -0,0 +1,18 @@
+#![warn(clippy::unnecessary_lazy_evaluations)]
+
+struct Deep(Option<usize>);
+
+#[derive(Copy, Clone)]
+struct SomeStruct {
+    some_field: usize,
+}
+
+fn main() {
+    // fix will break type inference
+    let _ = Ok(1).unwrap_or_else(|()| 2);
+    mod e {
+        pub struct E;
+    }
+    let _ = Ok(1).unwrap_or_else(|e::E| 2);
+    let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
+}
diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr
new file mode 100644 (file)
index 0000000..581d641
--- /dev/null
@@ -0,0 +1,22 @@
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13
+   |
+LL |     let _ = Ok(1).unwrap_or_else(|()| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
+   |
+   = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13
+   |
+LL |     let _ = Ok(1).unwrap_or_else(|e::E| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
+
+error: unnecessary closure used to substitute value for `Result::Err`
+  --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13
+   |
+LL |     let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)`
+
+error: aborting due to 3 previous errors
+