about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--Cargo.toml2
-rw-r--r--book/src/SUMMARY.md3
-rw-r--r--book/src/development/emitting_lints.md217
-rw-r--r--book/src/development/trait_checking.md105
-rw-r--r--book/src/development/writing_tests.md223
-rw-r--r--book/src/lint_configuration.md24
-rw-r--r--clippy_lints/src/declared_lints.rs2
-rw-r--r--clippy_lints/src/default_union_representation.rs3
-rw-r--r--clippy_lints/src/dereference.rs8
-rw-r--r--clippy_lints/src/lib.rs5
-rw-r--r--clippy_lints/src/loops/explicit_iter_loop.rs17
-rw-r--r--clippy_lints/src/loops/mod.rs10
-rw-r--r--clippy_lints/src/loops/never_loop.rs256
-rw-r--r--clippy_lints/src/methods/iter_out_of_bounds.rs106
-rw-r--r--clippy_lints/src/methods/mod.rs36
-rw-r--r--clippy_lints/src/missing_asserts_for_indexing.rs391
-rw-r--r--clippy_lints/src/raw_strings.rs80
-rw-r--r--clippy_lints/src/unit_return_expecting_ord.rs2
-rw-r--r--clippy_lints/src/utils/conf.rs20
-rw-r--r--clippy_utils/src/paths.rs2
-rw-r--r--clippy_utils/src/source.rs2
-rw-r--r--clippy_utils/src/sugg.rs2
-rw-r--r--clippy_utils/src/ty/type_certainty/mod.rs2
-rw-r--r--tests/compile-test.rs38
-rw-r--r--tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr2
-rw-r--r--tests/ui/crashes/ice-360.rs3
-rw-r--r--tests/ui/crashes/ice-360.stderr20
-rw-r--r--tests/ui/empty_loop.rs3
-rw-r--r--tests/ui/empty_loop.stderr4
-rw-r--r--tests/ui/explicit_auto_deref.fixed8
-rw-r--r--tests/ui/explicit_auto_deref.rs6
-rw-r--r--tests/ui/explicit_auto_deref.stderr12
-rw-r--r--tests/ui/explicit_iter_loop.fixed7
-rw-r--r--tests/ui/explicit_iter_loop.rs1
-rw-r--r--tests/ui/explicit_iter_loop.stderr64
-rw-r--r--tests/ui/iter_out_of_bounds.rs71
-rw-r--r--tests/ui/iter_out_of_bounds.stderr119
-rw-r--r--tests/ui/iter_skip_next.fixed1
-rw-r--r--tests/ui/iter_skip_next.rs1
-rw-r--r--tests/ui/iter_skip_next.stderr14
-rw-r--r--tests/ui/iter_skip_next_unfixable.rs2
-rw-r--r--tests/ui/iter_skip_zero.fixed2
-rw-r--r--tests/ui/iter_skip_zero.rs2
-rw-r--r--tests/ui/missing_asserts_for_indexing.fixed121
-rw-r--r--tests/ui/missing_asserts_for_indexing.rs121
-rw-r--r--tests/ui/missing_asserts_for_indexing.stderr252
-rw-r--r--tests/ui/missing_asserts_for_indexing_unfixable.rs49
-rw-r--r--tests/ui/missing_asserts_for_indexing_unfixable.stderr163
-rw-r--r--tests/ui/needless_collect_indirect.rs6
-rw-r--r--tests/ui/needless_collect_indirect.stderr7
-rw-r--r--tests/ui/needless_raw_string.fixed13
-rw-r--r--tests/ui/needless_raw_string.rs13
-rw-r--r--tests/ui/needless_raw_string.stderr48
-rw-r--r--tests/ui/needless_raw_string_hashes.fixed19
-rw-r--r--tests/ui/needless_raw_string_hashes.rs19
-rw-r--r--tests/ui/needless_raw_string_hashes.stderr142
-rw-r--r--tests/ui/never_loop.rs47
-rw-r--r--tests/ui/never_loop.stderr18
-rw-r--r--tests/ui/similar_names.rs1
-rw-r--r--tests/ui/similar_names.stderr28
-rw-r--r--tests/ui/vec.fixed7
-rw-r--r--tests/ui/vec.rs7
-rw-r--r--tests/ui/vec.stderr38
-rw-r--r--tests/ui/write_literal_2.stderr7
65 files changed, 2689 insertions, 338 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92cc19edb05..fd7232e280f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5031,6 +5031,7 @@ Released 2018-09-13
 [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 [`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
 [`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
+[`iter_out_of_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_out_of_bounds
 [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero
@@ -5131,6 +5132,7 @@ Released 2018-09-13
 [`misnamed_getters`]: https://rust-lang.github.io/rust-clippy/master/index.html#misnamed_getters
 [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 [`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
+[`missing_asserts_for_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_asserts_for_indexing
 [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
@@ -5570,4 +5572,5 @@ Released 2018-09-13
 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
 [`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
 [`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
+[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
 <!-- end autogenerated links to configuration documentation -->
diff --git a/Cargo.toml b/Cargo.toml
index cd04c78ef9e..2d8b590dbe3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,7 +27,7 @@ tempfile = { version = "3.2", optional = true }
 termize = "0.1"
 
 [dev-dependencies]
-ui_test = "0.17.0"
+ui_test = "0.20"
 tester = "0.9"
 regex = "1.5"
 toml = "0.7.3"
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index daaefd06a97..b02457307d7 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -14,8 +14,11 @@
     - [Basics](development/basics.md)
     - [Adding Lints](development/adding_lints.md)
     - [Defining Lints](development/defining_lints.md)
+    - [Writing tests](development/writing_tests.md)
     - [Lint Passes](development/lint_passes.md)
+    - [Emitting lints](development/emitting_lints.md)
     - [Type Checking](development/type_checking.md)
+    - [Trait Checking](development/trait_checking.md)
     - [Method Checking](development/method_checking.md)
     - [Macro Expansions](development/macro_expansions.md)
     - [Common Tools](development/common_tools_writing_lints.md)
diff --git a/book/src/development/emitting_lints.md b/book/src/development/emitting_lints.md
new file mode 100644
index 00000000000..a12f6aa91b3
--- /dev/null
+++ b/book/src/development/emitting_lints.md
@@ -0,0 +1,217 @@
+# Emitting a lint
+
+Once we have [defined a lint](defining_lints.md), written [UI
+tests](writing_tests.md) and chosen [the lint pass](lint_passes.md) for the lint,
+we can begin the implementation of the lint logic so that we can emit it and
+gradually work towards a lint that behaves as expected.
+
+Note that we will not go into concrete implementation of a lint logic in this
+chapter. We will go into details in later chapters as well as in two examples of
+real Clippy lints.
+
+To emit a lint, we must implement a pass (see [Lint Passes](lint_passes.md)) for
+the lint that we have declared. In this example we'll implement a "late" lint,
+so take a look at the [LateLintPass][late_lint_pass] documentation, which
+provides an abundance of methods that we can implement for our lint.
+
+```rust
+pub trait LateLintPass<'tcx>: LintPass {
+    // Trait methods
+}
+```
+
+By far the most common method used for Clippy lints is [`check_expr`
+method][late_check_expr], this is because Rust is an expression language and,
+more often than not, the lint we want to work on must examine expressions.
+
+> _Note:_ If you don't fully understand what expressions are in Rust, take a
+> look at the official documentation on [expressions][rust_expressions]
+
+Other common ones include the [`check_fn` method][late_check_fn] and the
+[`check_item` method][late_check_item].
+
+### Emitting a lint
+
+Inside the trait method that we implement, we can write down the lint logic and
+emit the lint with suggestions.
+
+Clippy's [diagnostics] provides quite a few diagnostic functions that we can use
+to emit lints. Take a look at the documentation to pick one that suits your
+lint's needs the best. Some common ones you will encounter in the Clippy
+repository includes:
+
+- [`span_lint`]: Emits a lint without providing any other information
+- [`span_lint_and_note`]: Emits a lint and adds a note
+- [`span_lint_and_help`]: Emits a lint and provides a helpful message
+- [`span_lint_and_sugg`]: Emits a lint and provides a suggestion to fix the code
+- [`span_lint_and_then`]: Like `span_lint`, but allows for a lot of output
+  customization.
+
+```rust
+impl<'tcx> LateLintPass<'tcx> for LintName {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)  {
+        // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
+        if some_lint_expr_logic(expr) {
+            span_lint_and_help(
+                cx, // < The context
+                LINT_NAME, // < The name of the lint in ALL CAPS
+                expr.span, // < The span to lint
+                "message on why the lint is emitted",
+                None, // < An optional help span (to highlight something in the lint)
+                "message that provides a helpful suggestion",
+            );
+        }
+    }
+}
+```
+
+> Note: The message should be matter of fact and avoid capitalization and
+> punctuation. If multiple sentences are needed, the messages should probably be
+> split up into an error + a help / note / suggestion message.
+
+## Suggestions: Automatic fixes
+
+Some lints know what to change in order to fix the code. For example, the lint
+[`range_plus_one`][range_plus_one] warns for ranges where the user wrote `x..y +
+1` instead of using an [inclusive range][inclusive_range] (`x..=y`). The fix to
+this code would be changing the `x..y + 1` expression to `x..=y`. **This is
+where suggestions come in**.
+
+A suggestion is a change that the lint provides to fix the issue it is linting.
+The output looks something like this (from the example earlier):
+
+```text
+error: an inclusive range would be more readable
+  --> $DIR/range_plus_minus_one.rs:37:14
+   |
+LL |     for _ in 1..1 + 1 {}
+   |              ^^^^^^^^ help: use: `1..=1`
+```
+
+**Not all suggestions are always right**, some of them require human
+supervision, that's why we have [Applicability][applicability].
+
+Applicability indicates confidence in the correctness of the suggestion, some
+are always right (`Applicability::MachineApplicable`), but we use
+`Applicability::MaybeIncorrect` and others when talking about a suggestion that
+may be incorrect.
+
+### Example
+
+The same lint `LINT_NAME` but that emits a suggestion would look something like this:
+
+```rust
+impl<'tcx> LateLintPass<'tcx> for LintName {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)  {
+        // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint
+        if some_lint_expr_logic(expr) {
+            span_lint_and_sugg( // < Note this change
+                cx,
+                LINT_NAME,
+                span,
+                "message on why the lint is emitted",
+                "use",
+                format!("foo + {} * bar", snippet(cx, expr.span, "<default>")), // < Suggestion
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+```
+
+Suggestions generally use the [`format!`][format_macro] macro to interpolate the
+old values with the new ones. To get code snippets, use one of the `snippet*`
+functions from `clippy_utils::source`.
+
+## How to choose between notes, help messages and suggestions
+
+Notes are presented separately from the main lint message, they provide useful
+information that the user needs to understand why the lint was activated. They
+are the most helpful when attached to a span.
+
+Examples:
+
+### Notes
+
+```text
+error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
+  --> $DIR/drop_forget_ref.rs:10:5
+   |
+10 |     forget(&SomeStruct);
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::forget-ref` implied by `-D warnings`
+note: argument has type &SomeStruct
+  --> $DIR/drop_forget_ref.rs:10:12
+   |
+10 |     forget(&SomeStruct);
+   |            ^^^^^^^^^^^
+```
+
+### Help Messages
+
+Help messages are specifically to help the user. These are used in situation
+where you can't provide a specific machine applicable suggestion. They can also
+be attached to a span.
+
+Example:
+
+```text
+error: constant division of 0.0 with 0.0 will always result in NaN
+  --> $DIR/zero_div_zero.rs:6:25
+   |
+6  |     let other_f64_nan = 0.0f64 / 0.0;
+   |                         ^^^^^^^^^^^^
+   |
+   = help: consider using `f64::NAN` if you would like a constant representing NaN
+```
+
+### Suggestions
+
+Suggestions are the most helpful, they are changes to the source code to fix the
+error. The magic in suggestions is that tools like `rustfix` can detect them and
+automatically fix your code.
+
+Example:
+
+```text
+error: This `.fold` can be more succinctly expressed as `.any`
+--> $DIR/methods.rs:390:13
+    |
+390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
+    |
+```
+
+### Snippets
+
+Snippets are pieces of the source code (as a string), they are extracted
+generally using the [`snippet`][snippet_fn] function.
+
+For example, if you want to know how an item looks (and you know the item's
+span), you could use `snippet(cx, span, "..")`.
+
+## Final: Run UI Tests to Emit the Lint
+
+Now, if we run our [UI test](writing_tests.md), we should see that Clippy now
+produces output that contains the lint message we designed.
+
+The next step is to implement the logic properly, which is a detail that we will
+cover in the next chapters.
+
+[diagnostics]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/index.html
+[late_check_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_expr
+[late_check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_fn
+[late_check_item]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_item
+[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+[rust_expressions]: https://doc.rust-lang.org/reference/expressions.html
+[`span_lint`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint.html
+[`span_lint_and_note`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_note.html
+[`span_lint_and_help`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_help.html
+[`span_lint_and_sugg`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html
+[`span_lint_and_then`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_then.html
+[range_plus_one]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
+[inclusive_range]: https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html
+[applicability]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_errors/enum.Applicability.html
+[snippet_fn]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/source/fn.snippet.html
+[format_macro]: https://doc.rust-lang.org/std/macro.format.html
diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md
new file mode 100644
index 00000000000..fb263922cf8
--- /dev/null
+++ b/book/src/development/trait_checking.md
@@ -0,0 +1,105 @@
+# Trait Checking
+
+Besides [type checking](type_checking.md), we might want to examine if
+a specific type `Ty` implements certain trait when implementing a lint.
+There are three approaches to achieve this, depending on if the target trait
+that we want to examine has a [diagnostic item][diagnostic_items],
+[lang item][lang_items], or neither.
+
+## Using Diagnostic Items
+
+As explained in the [Rust Compiler Development Guide][rustc_dev_guide], diagnostic items
+are introduced for identifying types via [Symbols][symbol].
+
+For instance, if we want to examine whether an expression implements
+the `Iterator` trait, we could simply write the following code,
+providing the `LateContext` (`cx`), our expression at hand, and
+the symbol of the trait in question:
+
+```rust
+use clippy_utils::is_trait_method;
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_span::symbol::sym;
+
+impl LateLintPass<'_> for CheckIteratorTraitLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+		let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
+    		implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
+		});
+		if implements_iterator {
+			// [...]
+		}
+
+    }
+}
+```
+
+> **Note**: Refer to [this index][symbol_index] for all the defined `Symbol`s.
+
+## Using Lang Items
+
+Besides diagnostic items, we can also use [`lang_items`][lang_items].
+Take a look at the documentation to find that `LanguageItems` contains
+all language items defined in the compiler.
+
+Using one of its `*_trait` method, we could obtain the [DefId] of any
+specific item, such as `Clone`, `Copy`, `Drop`, `Eq`, which are familiar
+to many Rustaceans.
+
+For instance, if we want to examine whether an expression `expr` implements
+`Drop` trait, we could access `LanguageItems` via our `LateContext`'s
+[TyCtxt], which provides a `lang_items` method that will return the id of
+`Drop` trait to us. Then, by calling Clippy utils function `implements_trait`
+we can check that the `Ty` of the `expr` implements the trait:
+
+```rust
+use clippy_utils::implements_trait;
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+
+impl LateLintPass<'_> for CheckDropTraitLint {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        let ty = cx.typeck_results().expr_ty(expr);
+        if cx.tcx.lang_items()
+            .drop_trait()
+            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
+                println!("`expr` implements `Drop` trait!");
+            }
+    }
+}
+```
+
+## Using Type Path
+
+If neither diagnostic item nor a language item is available, we can use
+[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait
+implementation.
+
+> **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item.
+
+Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` :
+
+```rust
+use clippy_utils::{match_trait_method, paths};
+use rustc_hir::Expr;
+use rustc_lint::{LateContext, LateLintPass};
+
+impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) {
+            println!("`expr` implements `CORE_ITER_CLONED` trait!");
+        }
+    }
+}
+```
+
+[DefId]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
+[diagnostic_items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
+[lang_items]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/lang_items/struct.LanguageItems.html
+[paths]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/paths.rs
+[rustc_dev_guide]: https://rustc-dev-guide.rust-lang.org/
+[symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html
+[symbol_index]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/sym/index.html
+[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
+[rust]: https://github.com/rust-lang/rust
diff --git a/book/src/development/writing_tests.md b/book/src/development/writing_tests.md
new file mode 100644
index 00000000000..7cb530a3bb9
--- /dev/null
+++ b/book/src/development/writing_tests.md
@@ -0,0 +1,223 @@
+# Testing
+
+Developing lints for Clippy is a Test-Driven Development (TDD) process because
+our first task before implementing any logic for a new lint is to write some test cases.
+
+## Develop Lints with Tests
+
+When we develop Clippy, we enter a complex and chaotic realm full of
+programmatic issues, stylistic errors, illogical code and non-adherence to convention.
+Tests are the first layer of order we can leverage to define when and where
+we want a new lint to trigger or not.
+
+Moreover, writing tests first help Clippy developers to find a balance for
+the first iteration of and further enhancements for a lint.
+With test cases on our side, we will not have to worry about over-engineering
+a lint on its first version nor missing out some obvious edge cases of the lint.
+This approach empowers us to iteratively enhance each lint.
+
+## Clippy UI Tests
+
+We use **UI tests** for testing in Clippy. These UI tests check that the output
+of Clippy is exactly as we expect it to be. Each test is just a plain Rust file
+that contains the code we want to check.
+
+The output of Clippy is compared against a `.stderr` file. Note that you don't
+have to create this file yourself. We'll get to generating the `.stderr` files
+with the command [`cargo bless`](#cargo-bless) (seen later on).
+
+### Write Test Cases
+
+Let us now think about some tests for our imaginary `foo_functions` lint. We
+start by opening the test file `tests/ui/foo_functions.rs` that was created by
+`cargo dev new_lint`.
+
+Update the file with some examples to get started:
+
+```rust
+#![warn(clippy::foo_functions)] // < Add this, so the lint is guaranteed to be enabled in this file
+
+// Impl methods
+struct A;
+impl A {
+    pub fn fo(&self) {}
+    pub fn foo(&self) {} //~ ERROR: function called "foo"
+    pub fn food(&self) {}
+}
+
+// Default trait methods
+trait B {
+    fn fo(&self) {}
+    fn foo(&self) {} //~ ERROR: function called "foo"
+    fn food(&self) {}
+}
+
+// Plain functions
+fn fo() {}
+fn foo() {} //~ ERROR: function called "foo"
+fn food() {}
+
+fn main() {
+    // We also don't want to lint method calls
+    foo();
+    let a = A;
+    a.foo();
+}
+```
+
+Without actual lint logic to emit the lint when we see a `foo` function name,
+this test will just pass, because no lint will be emitted. However, we can now
+run the test with the following command:
+
+```sh
+$ TESTNAME=foo_functions cargo uitest
+```
+
+Clippy will compile and it will conclude with an `ok` for the tests:
+
+```
+...Clippy warnings and test outputs...
+test compile_test ... ok
+test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s
+```
+
+This is normal. After all, we wrote a bunch of Rust code but we haven't really
+implemented any logic for Clippy to detect `foo` functions and emit a lint.
+
+As we gradually implement our lint logic, we will keep running this UI test command.
+Clippy will begin outputting information that allows us to check if the output is
+turning into what we want it to be.
+
+### Example output
+
+As our `foo_functions` lint is tested, the output would look something like this:
+
+```
+failures:
+
+---- compile_test stdout ----
+normalized stderr:
+error: function called "foo"
+  --> $DIR/foo_functions.rs:6:12
+   |
+LL |     pub fn foo(&self) {}
+   |            ^^^
+   |
+   = note: `-D clippy::foo-functions` implied by `-D warnings`
+
+error: function called "foo"
+  --> $DIR/foo_functions.rs:13:8
+   |
+LL |     fn foo(&self) {}
+   |        ^^^
+
+error: function called "foo"
+  --> $DIR/foo_functions.rs:19:4
+   |
+LL | fn foo() {}
+   |    ^^^
+
+error: aborting due to 3 previous errors
+```
+
+Note the *failures* label at the top of the fragment, we'll get rid of it
+(saving this output) in the next section.
+
+> _Note:_ You can run multiple test files by specifying a comma separated list:
+> `TESTNAME=foo_functions,bar_methods,baz_structs`.
+
+### `cargo bless`
+
+Once we are satisfied with the output, we need to run this command to
+generate or update the `.stderr` file for our lint:
+
+```sh
+$ TESTNAME=foo_functions cargo uibless
+```
+
+This writes the emitted lint suggestions and fixes to the `.stderr` file, with
+the reason for the lint, suggested fixes, and line numbers, etc.
+
+Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
+our lint, we need to commit the generated `.stderr` files, too.
+
+In general, you should only commit files changed by `cargo bless` for the
+specific lint you are creating/editing.
+
+> _Note:_ If the generated `.stderr`, and `.fixed` files are empty,
+> they should be removed.
+
+## `toml` Tests
+
+Some lints can be configured through a `clippy.toml` file. Those configuration
+values are tested in `tests/ui-toml`.
+
+To add a new test there, create a new directory and add the files:
+
+- `clippy.toml`: Put here the configuration value you want to test.
+- `lint_name.rs`: A test file where you put the testing code, that should see a
+  different lint behavior according to the configuration set in the
+  `clippy.toml` file.
+
+The potential `.stderr` and `.fixed` files can again be generated with `cargo
+bless`.
+
+## Cargo Lints
+
+The process of testing is different for Cargo lints in that now we are
+interested in the `Cargo.toml` manifest file. In this case, we also need a
+minimal crate associated with that manifest. Those tests are generated in
+`tests/ui-cargo`.
+
+Imagine we have a new example lint that is named `foo_categories`, we can run:
+
+```sh
+$ cargo dev new_lint --name=foo_categories --pass=late --category=cargo
+```
+
+After running `cargo dev new_lint` we will find by default two new crates,
+each with its manifest file:
+
+* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the
+  new lint to raise an error.
+* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger
+  the lint.
+
+If you need more cases, you can copy one of those crates (under
+`foo_categories`) and rename it.
+
+The process of generating the `.stderr` file is the same as for other lints
+and prepending the `TESTNAME` variable to `cargo uitest` works for Cargo lints too.
+
+## Rustfix Tests
+
+If the lint you are working on is making use of structured suggestions,
+[`rustfix`] will apply the suggestions from the lint to the test file code and
+compare that to the contents of a `.fixed` file.
+
+Structured suggestions tell a user how to fix or re-write certain code that has
+been linted with [`span_lint_and_sugg`].
+
+Should `span_lint_and_sugg` be used to generate a suggestion, but not all
+suggestions lead to valid code, you can use the `//@no-rustfix` comment on top
+of the test file, to not run `rustfix` on that file.
+
+We'll talk about suggestions more in depth in a later chapter.
+<!-- FIXME: (blyxyas) Link to "Emitting lints" when that gets merged -->
+
+Use `cargo bless` to automatically generate the `.fixed` file after running
+the tests.
+
+[`rustfix`]: https://github.com/rust-lang/rustfix
+[`span_lint_and_sugg`]: https://doc.rust-lang.org/beta/nightly-rustc/clippy_utils/diagnostics/fn.span_lint_and_sugg.html
+
+## Testing Manually
+
+Manually testing against an example file can be useful if you have added some
+`println!`s and the test suite output becomes unreadable.
+
+To try Clippy with your local modifications, run from the working copy root.
+
+```sh
+$ cargo dev lint input.rs
+```
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index caaad6d1173..52c795e04fe 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -751,3 +751,27 @@ Which crates to allow absolute paths from
 * [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths)
 
 
+## `enforce-iter-loop-reborrow`
+#### Example
+```
+let mut vec = vec![1, 2, 3];
+let rmvec = &mut vec;
+for _ in rmvec.iter() {}
+for _ in rmvec.iter_mut() {}
+```
+
+Use instead:
+```
+let mut vec = vec![1, 2, 3];
+let rmvec = &mut vec;
+for _ in &*rmvec {}
+for _ in &mut *rmvec {}
+```
+
+**Default Value:** `false` (`bool`)
+
+---
+**Affected lints:**
+* [`explicit_iter_loop`](https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop)
+
+
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index f73f1ed18f8..150d883985c 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -365,6 +365,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::ITER_NTH_ZERO_INFO,
     crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO,
     crate::methods::ITER_ON_SINGLE_ITEMS_INFO,
+    crate::methods::ITER_OUT_OF_BOUNDS_INFO,
     crate::methods::ITER_OVEREAGER_CLONED_INFO,
     crate::methods::ITER_SKIP_NEXT_INFO,
     crate::methods::ITER_SKIP_ZERO_INFO,
@@ -456,6 +457,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
     crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
     crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
+    crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO,
     crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
     crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
     crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs
index 03b5a2d6d08..bbce6e1dd8f 100644
--- a/clippy_lints/src/default_union_representation.rs
+++ b/clippy_lints/src/default_union_representation.rs
@@ -69,6 +69,9 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
 }
 
 /// Returns true if the given item is a union with at least two non-ZST fields.
+/// (ZST fields having an arbitrary offset is completely inconsequential, and
+/// if there is only one field left after ignoring ZST fields then the offset
+/// of that field does not matter either.)
 fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Union(data, _) = &item.kind {
         data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index 58c27855000..4bff55b2ecb 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -609,12 +609,14 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             adjusted_ty,
                         },
                     ));
-                } else if stability.is_deref_stable() {
+                } else if stability.is_deref_stable()
+                    && let Some(parent) = get_parent_expr(cx, expr)
+                {
                     self.state = Some((
                         State::ExplicitDeref { mutability: None },
                         StateData {
-                            span: expr.span,
-                            hir_id: expr.hir_id,
+                            span: parent.span,
+                            hir_id: parent.hir_id,
                             adjusted_ty,
                         },
                     ));
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ab71bfbdc58..4ff97e03a14 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -210,6 +210,7 @@ mod misc;
 mod misc_early;
 mod mismatching_type_param_order;
 mod missing_assert_message;
+mod missing_asserts_for_indexing;
 mod missing_const_for_fn;
 mod missing_doc;
 mod missing_enforced_import_rename;
@@ -695,7 +696,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     });
     store.register_late_pass(|_| Box::<shadow::Shadow>::default());
     store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
-    store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv())));
+    let enforce_iter_loop_reborrow = conf.enforce_iter_loop_reborrow;
+    store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow)));
     store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
     store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
     store.register_late_pass(|_| Box::new(entry::HashMapPass));
@@ -1099,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
     store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
     store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
+    store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs
index 7b8c88235a9..6ab256ef001 100644
--- a/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -13,8 +13,14 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMut
 use rustc_middle::ty::{self, EarlyBinder, Ty, TypeAndMut};
 use rustc_span::sym;
 
-pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, msrv: &Msrv) {
-    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr) else {
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    self_arg: &Expr<'_>,
+    call_expr: &Expr<'_>,
+    msrv: &Msrv,
+    enforce_iter_loop_reborrow: bool,
+) {
+    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else {
         return;
     };
     if let ty::Array(_, count) = *ty.peel_refs().kind() {
@@ -102,6 +108,7 @@ fn is_ref_iterable<'tcx>(
     cx: &LateContext<'tcx>,
     self_arg: &Expr<'_>,
     call_expr: &Expr<'_>,
+    enforce_iter_loop_reborrow: bool,
 ) -> Option<(AdjustKind, Ty<'tcx>)> {
     let typeck = cx.typeck_results();
     if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
@@ -142,7 +149,8 @@ fn is_ref_iterable<'tcx>(
                 {
                     return Some((AdjustKind::None, self_ty));
                 }
-            } else if let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind()
+            } else if enforce_iter_loop_reborrow
+                && let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind()
                 && let Some(mutbl) = mutbl
             {
                 // Attempt to reborrow the mutable reference
@@ -186,7 +194,8 @@ fn is_ref_iterable<'tcx>(
                 },
                 ..
             ] => {
-                if target != self_ty
+                if enforce_iter_loop_reborrow
+                    && target != self_ty
                     && implements_trait(cx, target, trait_id, &[])
                     && let Some(ty) =
                         make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs
index ffd29ab7630..1fb16adad7a 100644
--- a/clippy_lints/src/loops/mod.rs
+++ b/clippy_lints/src/loops/mod.rs
@@ -609,10 +609,14 @@ declare_clippy_lint! {
 
 pub struct Loops {
     msrv: Msrv,
+    enforce_iter_loop_reborrow: bool,
 }
 impl Loops {
-    pub fn new(msrv: Msrv) -> Self {
-        Self { msrv }
+    pub fn new(msrv: Msrv, enforce_iter_loop_reborrow: bool) -> Self {
+        Self {
+            msrv,
+            enforce_iter_loop_reborrow,
+        }
     }
 }
 impl_lint_pass!(Loops => [
@@ -719,7 +723,7 @@ impl Loops {
         if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
             match method.ident.as_str() {
                 "iter" | "iter_mut" => {
-                    explicit_iter_loop::check(cx, self_arg, arg, &self.msrv);
+                    explicit_iter_loop::check(cx, self_arg, arg, &self.msrv, self.enforce_iter_loop_reborrow);
                 },
                 "into_iter" => {
                     explicit_into_iter_loop::check(cx, self_arg, arg);
diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs
index cc19ac55e5e..5cdf0bd891f 100644
--- a/clippy_lints/src/loops/never_loop.rs
+++ b/clippy_lints/src/loops/never_loop.rs
@@ -1,6 +1,5 @@
 use super::utils::make_iterator_snippet;
 use super::NEVER_LOOP;
-use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::ForLoop;
 use clippy_utils::source::snippet;
@@ -18,7 +17,7 @@ pub(super) fn check<'tcx>(
     for_loop: Option<&ForLoop<'_>>,
 ) {
     match never_loop_block(cx, block, &mut Vec::new(), loop_id) {
-        NeverLoopResult::AlwaysBreak => {
+        NeverLoopResult::Diverging => {
             span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
                 if let Some(ForLoop {
                     arg: iterator,
@@ -39,67 +38,76 @@ pub(super) fn check<'tcx>(
                 }
             });
         },
-        NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
-        NeverLoopResult::IgnoreUntilEnd(_) => unreachable!(),
+        NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Normal => (),
     }
 }
 
+/// The `never_loop` analysis keeps track of three things:
+///
+/// * Has any (reachable) code path hit a `continue` of the main loop?
+/// * Is the current code path diverging (that is, the next expression is not reachable)
+/// * For each block label `'a` inside the main loop, has any (reachable) code path encountered a
+///   `break 'a`?
+///
+/// The first two bits of information are in this enum, and the last part is in the
+/// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by
+/// scope.
 #[derive(Copy, Clone)]
 enum NeverLoopResult {
-    // A break/return always get triggered but not necessarily for the main loop.
-    AlwaysBreak,
-    // A continue may occur for the main loop.
+    /// A continue may occur for the main loop.
     MayContinueMainLoop,
-    // Ignore everything until the end of the block with this id
-    IgnoreUntilEnd(HirId),
-    Otherwise,
+    /// We have not encountered any main loop continue,
+    /// but we are diverging (subsequent control flow is not reachable)
+    Diverging,
+    /// We have not encountered any main loop continue,
+    /// and subsequent control flow is (possibly) reachable
+    Normal,
 }
 
 #[must_use]
 fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
     match arg {
-        NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
+        NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal,
         NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
-        NeverLoopResult::IgnoreUntilEnd(id) => NeverLoopResult::IgnoreUntilEnd(id),
     }
 }
 
 // Combine two results for parts that are called in order.
 #[must_use]
-fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
+fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult) -> NeverLoopResult {
     match first {
-        NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop | NeverLoopResult::IgnoreUntilEnd(_) => {
-            first
-        },
-        NeverLoopResult::Otherwise => second,
+        NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop => first,
+        NeverLoopResult::Normal => second(),
+    }
+}
+
+// Combine an iterator of results for parts that are called in order.
+#[must_use]
+fn combine_seq_many(iter: impl IntoIterator<Item = NeverLoopResult>) -> NeverLoopResult {
+    for e in iter {
+        if let NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop = e {
+            return e;
+        }
     }
+    NeverLoopResult::Normal
 }
 
 // Combine two results where only one of the part may have been executed.
 #[must_use]
-fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirId]) -> NeverLoopResult {
+fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
     match (b1, b2) {
-        (NeverLoopResult::IgnoreUntilEnd(a), NeverLoopResult::IgnoreUntilEnd(b)) => {
-            if ignore_ids.iter().find(|&e| e == &a || e == &b).unwrap() == &a {
-                NeverLoopResult::IgnoreUntilEnd(b)
-            } else {
-                NeverLoopResult::IgnoreUntilEnd(a)
-            }
-        },
-        (i @ NeverLoopResult::IgnoreUntilEnd(_), NeverLoopResult::AlwaysBreak)
-        | (NeverLoopResult::AlwaysBreak, i @ NeverLoopResult::IgnoreUntilEnd(_)) => i,
-        (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
         (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
             NeverLoopResult::MayContinueMainLoop
         },
-        (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
+        (NeverLoopResult::Normal, _) | (_, NeverLoopResult::Normal) => NeverLoopResult::Normal,
+        (NeverLoopResult::Diverging, NeverLoopResult::Diverging) => NeverLoopResult::Diverging,
     }
 }
 
 fn never_loop_block<'tcx>(
     cx: &LateContext<'tcx>,
     block: &Block<'tcx>,
-    ignore_ids: &mut Vec<HirId>,
+    local_labels: &mut Vec<(HirId, bool)>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
     let iter = block
@@ -107,15 +115,21 @@ fn never_loop_block<'tcx>(
         .iter()
         .filter_map(stmt_to_expr)
         .chain(block.expr.map(|expr| (expr, None)));
-
-    iter.map(|(e, els)| {
-        let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
+    combine_seq_many(iter.map(|(e, els)| {
+        let e = never_loop_expr(cx, e, local_labels, main_loop_id);
         // els is an else block in a let...else binding
         els.map_or(e, |els| {
-            combine_branches(e, never_loop_block(cx, els, ignore_ids, main_loop_id), ignore_ids)
+            combine_seq(e, || match never_loop_block(cx, els, local_labels, main_loop_id) {
+                // Returning MayContinueMainLoop here means that
+                // we will not evaluate the rest of the body
+                NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
+                // An else block always diverges, so the Normal case should not happen,
+                // but the analysis is approximate so it might return Normal anyway.
+                // Returning Normal here says that nothing more happens on the main path
+                NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal,
+            })
         })
-    })
-    .fold(NeverLoopResult::Otherwise, combine_seq)
+    }))
 }
 
 fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> {
@@ -131,76 +145,69 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
 fn never_loop_expr<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &Expr<'tcx>,
-    ignore_ids: &mut Vec<HirId>,
+    local_labels: &mut Vec<(HirId, bool)>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
-    match expr.kind {
+    let result = match expr.kind {
         ExprKind::Unary(_, e)
         | ExprKind::Cast(e, _)
         | ExprKind::Type(e, _)
         | ExprKind::Field(e, _)
         | ExprKind::AddrOf(_, _, e)
         | ExprKind::Repeat(e, _)
-        | ExprKind::DropTemps(e) => never_loop_expr(cx, e, ignore_ids, main_loop_id),
-        ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, ignore_ids, main_loop_id),
-        ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, &mut es.iter(), ignore_ids, main_loop_id),
+        | ExprKind::DropTemps(e) => never_loop_expr(cx, e, local_labels, main_loop_id),
+        ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, local_labels, main_loop_id),
+        ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, es.iter(), local_labels, main_loop_id),
         ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
             cx,
-            &mut std::iter::once(receiver).chain(es.iter()),
-            ignore_ids,
+            std::iter::once(receiver).chain(es.iter()),
+            local_labels,
             main_loop_id,
         ),
         ExprKind::Struct(_, fields, base) => {
-            let fields = never_loop_expr_all(cx, &mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
+            let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id);
             if let Some(base) = base {
-                combine_seq(fields, never_loop_expr(cx, base, ignore_ids, main_loop_id))
+                combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id))
             } else {
                 fields
             }
         },
-        ExprKind::Call(e, es) => never_loop_expr_all(cx, &mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
+        ExprKind::Call(e, es) => never_loop_expr_all(cx, once(e).chain(es.iter()), local_labels, main_loop_id),
         ExprKind::Binary(_, e1, e2)
         | ExprKind::Assign(e1, e2, _)
         | ExprKind::AssignOp(_, e1, e2)
-        | ExprKind::Index(e1, e2, _) => {
-            never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id)
-        },
+        | ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, [e1, e2].iter().copied(), local_labels, main_loop_id),
         ExprKind::Loop(b, _, _, _) => {
-            // Break can come from the inner loop so remove them.
-            absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))
+            // We don't attempt to track reachability after a loop,
+            // just assume there may have been a break somewhere
+            absorb_break(never_loop_block(cx, b, local_labels, main_loop_id))
         },
         ExprKind::If(e, e2, e3) => {
-            let e1 = never_loop_expr(cx, e, ignore_ids, main_loop_id);
-            let e2 = never_loop_expr(cx, e2, ignore_ids, main_loop_id);
-            // If we know the `if` condition evaluates to `true`, don't check everything past it; it
-            // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable
-            if let Some(Constant::Bool(true)) = constant(cx, cx.typeck_results(), e) {
-                return combine_seq(e1, e2);
-            }
-            let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
-                never_loop_expr(cx, e, ignore_ids, main_loop_id)
-            });
-            combine_seq(e1, combine_branches(e2, e3, ignore_ids))
+            let e1 = never_loop_expr(cx, e, local_labels, main_loop_id);
+            combine_seq(e1, || {
+                let e2 = never_loop_expr(cx, e2, local_labels, main_loop_id);
+                let e3 = e3.as_ref().map_or(NeverLoopResult::Normal, |e| {
+                    never_loop_expr(cx, e, local_labels, main_loop_id)
+                });
+                combine_branches(e2, e3)
+            })
         },
         ExprKind::Match(e, arms, _) => {
-            let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
-            if arms.is_empty() {
-                e
-            } else {
-                let arms = never_loop_expr_branch(cx, &mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
-                combine_seq(e, arms)
-            }
+            let e = never_loop_expr(cx, e, local_labels, main_loop_id);
+            combine_seq(e, || {
+                arms.iter().fold(NeverLoopResult::Diverging, |a, b| {
+                    combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id))
+                })
+            })
         },
         ExprKind::Block(b, l) => {
             if l.is_some() {
-                ignore_ids.push(b.hir_id);
-            }
-            let ret = never_loop_block(cx, b, ignore_ids, main_loop_id);
-            if l.is_some() {
-                ignore_ids.pop();
+                local_labels.push((b.hir_id, false));
             }
+            let ret = never_loop_block(cx, b, local_labels, main_loop_id);
+            let jumped_to = l.is_some() && local_labels.pop().unwrap().1;
             match ret {
-                NeverLoopResult::IgnoreUntilEnd(a) if a == b.hir_id => NeverLoopResult::Otherwise,
+                NeverLoopResult::Diverging if jumped_to => NeverLoopResult::Normal,
                 _ => ret,
             }
         },
@@ -211,74 +218,67 @@ fn never_loop_expr<'tcx>(
             if id == main_loop_id {
                 NeverLoopResult::MayContinueMainLoop
             } else {
-                NeverLoopResult::AlwaysBreak
+                NeverLoopResult::Diverging
             }
         },
-        // checks if break targets a block instead of a loop
-        ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
-            .map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| {
-                never_loop_expr(cx, e, ignore_ids, main_loop_id)
-            }),
-        ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
-            combine_seq(
-                never_loop_expr(cx, e, ignore_ids, main_loop_id),
-                NeverLoopResult::AlwaysBreak,
-            )
-        }),
-        ExprKind::Become(e) => combine_seq(
-            never_loop_expr(cx, e, ignore_ids, main_loop_id),
-            NeverLoopResult::AlwaysBreak,
-        ),
-        ExprKind::InlineAsm(asm) => asm
-            .operands
-            .iter()
-            .map(|(o, _)| match o {
-                InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
-                    never_loop_expr(cx, expr, ignore_ids, main_loop_id)
-                },
-                InlineAsmOperand::Out { expr, .. } => {
-                    never_loop_expr_all(cx, &mut expr.iter().copied(), ignore_ids, main_loop_id)
-                },
-                InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
-                    cx,
-                    &mut once(*in_expr).chain(out_expr.iter().copied()),
-                    ignore_ids,
-                    main_loop_id,
-                ),
-                InlineAsmOperand::Const { .. }
-                | InlineAsmOperand::SymFn { .. }
-                | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
+        ExprKind::Break(_, e) | ExprKind::Ret(e) => {
+            let first = e.as_ref().map_or(NeverLoopResult::Normal, |e| {
+                never_loop_expr(cx, e, local_labels, main_loop_id)
+            });
+            combine_seq(first, || {
+                // checks if break targets a block instead of a loop
+                if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind {
+                    if let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) {
+                        *reachable = true;
+                    }
+                }
+                NeverLoopResult::Diverging
             })
-            .fold(NeverLoopResult::Otherwise, combine_seq),
+        },
+        ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || {
+            NeverLoopResult::Diverging
+        }),
+        ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o {
+            InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
+                never_loop_expr(cx, expr, local_labels, main_loop_id)
+            },
+            InlineAsmOperand::Out { expr, .. } => {
+                never_loop_expr_all(cx, expr.iter().copied(), local_labels, main_loop_id)
+            },
+            InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
+                cx,
+                once(*in_expr).chain(out_expr.iter().copied()),
+                local_labels,
+                main_loop_id,
+            ),
+            InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => {
+                NeverLoopResult::Normal
+            },
+        })),
         ExprKind::OffsetOf(_, _)
         | ExprKind::Yield(_, _)
         | ExprKind::Closure { .. }
         | ExprKind::Path(_)
         | ExprKind::ConstBlock(_)
         | ExprKind::Lit(_)
-        | ExprKind::Err(_) => NeverLoopResult::Otherwise,
-    }
+        | ExprKind::Err(_) => NeverLoopResult::Normal,
+    };
+    combine_seq(result, || {
+        if cx.typeck_results().expr_ty(expr).is_never() {
+            NeverLoopResult::Diverging
+        } else {
+            NeverLoopResult::Normal
+        }
+    })
 }
 
 fn never_loop_expr_all<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
     cx: &LateContext<'tcx>,
-    es: &mut T,
-    ignore_ids: &mut Vec<HirId>,
-    main_loop_id: HirId,
-) -> NeverLoopResult {
-    es.map(|e| never_loop_expr(cx, e, ignore_ids, main_loop_id))
-        .fold(NeverLoopResult::Otherwise, combine_seq)
-}
-
-fn never_loop_expr_branch<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
-    cx: &LateContext<'tcx>,
-    e: &mut T,
-    ignore_ids: &mut Vec<HirId>,
+    es: T,
+    local_labels: &mut Vec<(HirId, bool)>,
     main_loop_id: HirId,
 ) -> NeverLoopResult {
-    e.fold(NeverLoopResult::AlwaysBreak, |a, b| {
-        combine_branches(a, never_loop_expr(cx, b, ignore_ids, main_loop_id), ignore_ids)
-    })
+    combine_seq_many(es.map(|e| never_loop_expr(cx, e, local_labels, main_loop_id)))
 }
 
 fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs
new file mode 100644
index 00000000000..79c6d63254b
--- /dev/null
+++ b/clippy_lints/src/methods/iter_out_of_bounds.rs
@@ -0,0 +1,106 @@
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::higher::VecArgs;
+use clippy_utils::{expr_or_init, is_trait_method, match_def_path, paths};
+use rustc_ast::LitKind;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self};
+use rustc_span::sym;
+
+use super::ITER_OUT_OF_BOUNDS;
+
+fn expr_as_u128(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<u128> {
+    if let ExprKind::Lit(lit) = expr_or_init(cx, e).kind
+        && let LitKind::Int(n, _) = lit.node
+    {
+        Some(n)
+    } else {
+        None
+    }
+}
+
+/// Attempts to extract the length out of an iterator expression.
+fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> Option<u128> {
+    let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else {
+        return None;
+    };
+    let did = adt.did();
+
+    if match_def_path(cx, did, &paths::ARRAY_INTO_ITER) {
+        // For array::IntoIter<T, const N: usize>, the length is the second generic
+        // parameter.
+        substs
+            .const_at(1)
+            .try_eval_target_usize(cx.tcx, cx.param_env)
+            .map(u128::from)
+    } else if match_def_path(cx, did, &paths::SLICE_ITER)
+        && let ExprKind::MethodCall(_, recv, ..) = iter.kind
+    {
+        if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
+            // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
+            len.try_eval_target_usize(cx.tcx, cx.param_env).map(u128::from)
+        } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
+            match args {
+                VecArgs::Vec(vec) => vec.len().try_into().ok(),
+                VecArgs::Repeat(_, len) => expr_as_u128(cx, len),
+            }
+        } else {
+            None
+        }
+    } else if match_def_path(cx, did, &paths::ITER_EMPTY) {
+        Some(0)
+    } else if match_def_path(cx, did, &paths::ITER_ONCE) {
+        Some(1)
+    }  else {
+        None
+    }
+}
+
+fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'tcx>,
+    arg: &'tcx Expr<'tcx>,
+    message: &'static str,
+    note: &'static str,
+) {
+    if is_trait_method(cx, expr, sym::Iterator)
+        && let Some(len) = get_iterator_length(cx, recv)
+        && let Some(skipped) = expr_as_u128(cx, arg)
+        && skipped > len
+    {
+        span_lint_and_note(cx, ITER_OUT_OF_BOUNDS, expr.span, message, None, note);
+    }
+}
+
+pub(super) fn check_skip<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'tcx>,
+    arg: &'tcx Expr<'tcx>,
+) {
+    check(
+        cx,
+        expr,
+        recv,
+        arg,
+        "this `.skip()` call skips more items than the iterator will produce",
+        "this operation is useless and will create an empty iterator",
+    );
+}
+
+pub(super) fn check_take<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    recv: &'tcx Expr<'tcx>,
+    arg: &'tcx Expr<'tcx>,
+) {
+    check(
+        cx,
+        expr,
+        recv,
+        arg,
+        "this `.take()` call takes more items than the iterator will produce",
+        "this operation is useless and the returned iterator will simply yield the same items",
+    );
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 5fb0d9f0a57..81223fa8d95 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -43,6 +43,7 @@ mod iter_next_slice;
 mod iter_nth;
 mod iter_nth_zero;
 mod iter_on_single_or_empty_collections;
+mod iter_out_of_bounds;
 mod iter_overeager_cloned;
 mod iter_skip_next;
 mod iter_skip_zero;
@@ -3054,12 +3055,12 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
+    /// vec![1, 2, 3, 4, 5].resize(0, 5)
     /// ```
     ///
     /// Use instead:
     /// ```rust
-    /// vec!(1, 2, 3, 4, 5).clear()
+    /// vec![1, 2, 3, 4, 5].clear()
     /// ```
     #[clippy::version = "1.46.0"]
     pub VEC_RESIZE_TO_ZERO,
@@ -3538,6 +3539,30 @@ declare_clippy_lint! {
     "acquiring a write lock when a read lock would work"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for iterator combinator calls such as `.take(x)` or `.skip(x)`
+    /// where `x` is greater than the amount of items that an iterator will produce.
+    ///
+    /// ### Why is this bad?
+    /// Taking or skipping more items than there are in an iterator either creates an iterator
+    /// with all items from the original iterator or an iterator with no items at all.
+    /// This is most likely not what the user intended to do.
+    ///
+    /// ### Example
+    /// ```rust
+    /// for _ in [1, 2, 3].iter().take(4) {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// for _ in [1, 2, 3].iter() {}
+    /// ```
+    #[clippy::version = "1.74.0"]
+    pub ITER_OUT_OF_BOUNDS,
+    suspicious,
+    "calls to `.take()` or `.skip()` that are out of bounds"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -3676,7 +3701,8 @@ impl_lint_pass!(Methods => [
     STRING_LIT_CHARS_ANY,
     ITER_SKIP_ZERO,
     FILTER_MAP_BOOL_THEN,
-    READONLY_WRITE_LOCK
+    READONLY_WRITE_LOCK,
+    ITER_OUT_OF_BOUNDS,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4146,6 +4172,7 @@ impl Methods {
                 },
                 ("skip", [arg]) => {
                     iter_skip_zero::check(cx, expr, arg);
+                    iter_out_of_bounds::check_skip(cx, expr, recv, arg);
 
                     if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
                         iter_overeager_cloned::check(cx, expr, recv, recv2,
@@ -4173,7 +4200,8 @@ impl Methods {
                     }
                 },
                 ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
-                ("take", [_arg]) => {
+                ("take", [arg]) => {
+                    iter_out_of_bounds::check_take(cx, expr, recv, arg);
                     if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
                         iter_overeager_cloned::check(cx, expr, recv, recv2,
                                 iter_overeager_cloned::Op::LaterCloned, false);
diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs
new file mode 100644
index 00000000000..08fec2b8ec8
--- /dev/null
+++ b/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -0,0 +1,391 @@
+use std::mem;
+use std::ops::ControlFlow;
+
+use clippy_utils::comparisons::{normalize_comparison, Rel};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{eq_expr_value, hash_expr, higher};
+use rustc_ast::{LitKind, RangeLimits};
+use rustc_data_structures::unhash::UnhashMap;
+use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir::{BinOp, Block, Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for repeated slice indexing without asserting beforehand that the length
+    /// is greater than the largest index used to index into the slice.
+    ///
+    /// ### Why is this bad?
+    /// In the general case where the compiler does not have a lot of information
+    /// about the length of a slice, indexing it repeatedly will generate a bounds check
+    /// for every single index.
+    ///
+    /// Asserting that the length of the slice is at least as large as the largest value
+    /// to index beforehand gives the compiler enough information to elide the bounds checks,
+    /// effectively reducing the number of bounds checks from however many times
+    /// the slice was indexed to just one (the assert).
+    ///
+    /// ### Drawbacks
+    /// False positives. It is, in general, very difficult to predict how well
+    /// the optimizer will be able to elide bounds checks and it very much depends on
+    /// the surrounding code. For example, indexing into the slice yielded by the
+    /// [`slice::chunks_exact`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunks_exact)
+    /// iterator will likely have all of the bounds checks elided even without an assert
+    /// if the `chunk_size` is a constant.
+    ///
+    /// Asserts are not tracked across function calls. Asserting the length of a slice
+    /// in a different function likely gives the optimizer enough information
+    /// about the length of a slice, but this lint will not detect that.
+    ///
+    /// ### Example
+    /// ```rust
+    /// fn sum(v: &[u8]) -> u8 {
+    ///     // 4 bounds checks
+    ///     v[0] + v[1] + v[2] + v[3]
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn sum(v: &[u8]) -> u8 {
+    ///     assert!(v.len() > 4);
+    ///     // no bounds checks
+    ///     v[0] + v[1] + v[2] + v[3]
+    /// }
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub MISSING_ASSERTS_FOR_INDEXING,
+    restriction,
+    "indexing into a slice multiple times without an `assert`"
+}
+declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]);
+
+fn report_lint<F>(cx: &LateContext<'_>, full_span: Span, msg: &str, indexes: &[Span], f: F)
+where
+    F: FnOnce(&mut Diagnostic),
+{
+    span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| {
+        f(diag);
+        for span in indexes {
+            diag.span_note(*span, "slice indexed here");
+        }
+        diag.note("asserting the length before indexing will elide bounds checks");
+    });
+}
+
+#[derive(Copy, Clone, Debug)]
+enum LengthComparison {
+    /// `v.len() < 5`
+    LengthLessThanInt,
+    /// `5 < v.len()`
+    IntLessThanLength,
+    /// `v.len() <= 5`
+    LengthLessThanOrEqualInt,
+    /// `5 <= v.len()`
+    IntLessThanOrEqualLength,
+}
+
+/// Extracts parts out of a length comparison expression.
+///
+/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, `v.len()`))`
+fn len_comparison<'hir>(
+    bin_op: BinOp,
+    left: &'hir Expr<'hir>,
+    right: &'hir Expr<'hir>,
+) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
+    macro_rules! int_lit_pat {
+        ($id:ident) => {
+            ExprKind::Lit(Spanned {
+                node: LitKind::Int($id, _),
+                ..
+            })
+        };
+    }
+
+    // normalize comparison, `v.len() > 4` becomes `4 < v.len()`
+    // this simplifies the logic a bit
+    let (op, left, right) = normalize_comparison(bin_op.node, left, right)?;
+    match (op, &left.kind, &right.kind) {
+        (Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, *left as usize, right)),
+        (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, *right as usize, left)),
+        (Rel::Le, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanOrEqualLength, *left as usize, right)),
+        (Rel::Le, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanOrEqualInt, *right as usize, left)),
+        _ => None,
+    }
+}
+
+/// Attempts to extract parts out of an `assert!`-like expression
+/// in the form `assert!(some_slice.len() > 5)`.
+///
+/// `assert!` has expanded to an if expression at the HIR, so this
+/// actually works not just with `assert!` specifically, but anything
+/// that has a never type expression in the `then` block (e.g. `panic!`).
+fn assert_len_expr<'hir>(
+    cx: &LateContext<'_>,
+    expr: &'hir Expr<'hir>,
+) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
+    if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
+        && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
+        && let ExprKind::Binary(bin_op, left, right) = &condition.kind
+
+        && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right)
+        && let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind
+        && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
+        && method.ident.name == sym::len
+
+        // check if `then` block has a never type expression
+        && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind
+        && cx.typeck_results().expr_ty(then_expr).is_never()
+    {
+        Some((cmp, asserted_len, recv))
+    } else {
+        None
+    }
+}
+
+#[derive(Debug)]
+enum IndexEntry<'hir> {
+    /// `assert!` without any indexing (so far)
+    StrayAssert {
+        asserted_len: usize,
+        comparison: LengthComparison,
+        assert_span: Span,
+        slice: &'hir Expr<'hir>,
+    },
+    /// `assert!` with indexing
+    ///
+    /// We also store the highest index to be able to check
+    /// if the `assert!` asserts the right length.
+    AssertWithIndex {
+        highest_index: usize,
+        asserted_len: usize,
+        assert_span: Span,
+        slice: &'hir Expr<'hir>,
+        indexes: Vec<Span>,
+        comparison: LengthComparison,
+    },
+    /// Indexing without an `assert!`
+    IndexWithoutAssert {
+        highest_index: usize,
+        indexes: Vec<Span>,
+        slice: &'hir Expr<'hir>,
+    },
+}
+
+impl<'hir> IndexEntry<'hir> {
+    pub fn slice(&self) -> &'hir Expr<'hir> {
+        match self {
+            IndexEntry::StrayAssert { slice, .. }
+            | IndexEntry::AssertWithIndex { slice, .. }
+            | IndexEntry::IndexWithoutAssert { slice, .. } => slice,
+        }
+    }
+
+    pub fn index_spans(&self) -> Option<&[Span]> {
+        match self {
+            IndexEntry::StrayAssert { .. } => None,
+            IndexEntry::AssertWithIndex { indexes, .. } | IndexEntry::IndexWithoutAssert { indexes, .. } => {
+                Some(indexes)
+            },
+        }
+    }
+}
+
+/// Extracts the upper index of a slice indexing expression.
+///
+/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`,
+/// for `..=5` this returns `Some(5)`
+fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
+    if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(index, _) = lit.node {
+        Some(index as usize)
+    } else if let Some(higher::Range { end: Some(end), limits, .. }) = higher::Range::hir(expr)
+        && let ExprKind::Lit(lit) = &end.kind
+        && let LitKind::Int(index @ 1.., _) = lit.node
+    {
+        match limits {
+            RangeLimits::HalfOpen => Some(index as usize - 1),
+            RangeLimits::Closed => Some(index as usize),
+        }
+    } else {
+        None
+    }
+}
+
+/// Checks if the expression is an index into a slice and adds it to `indexes`
+fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
+    if let ExprKind::Index(slice, index_lit, _) = expr.kind
+        && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice()
+        && let Some(index) = upper_index_expr(index_lit)
+    {
+        let hash = hash_expr(cx, slice);
+
+        let indexes = map.entry(hash).or_default();
+        let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
+
+        if let Some(entry) = entry {
+            match entry {
+                IndexEntry::StrayAssert { asserted_len, comparison, assert_span, slice } => {
+                    *entry = IndexEntry::AssertWithIndex {
+                        highest_index: index,
+                        asserted_len: *asserted_len,
+                        assert_span: *assert_span,
+                        slice,
+                        indexes: vec![expr.span],
+                        comparison: *comparison,
+                    };
+                },
+                IndexEntry::IndexWithoutAssert { highest_index, indexes, .. }
+                | IndexEntry::AssertWithIndex { highest_index, indexes, .. } => {
+                    indexes.push(expr.span);
+                    *highest_index = (*highest_index).max(index);
+                },
+            }
+        } else {
+            indexes.push(IndexEntry::IndexWithoutAssert {
+                highest_index: index,
+                indexes: vec![expr.span],
+                slice,
+            });
+        }
+    }
+}
+
+/// Checks if the expression is an `assert!` expression and adds it to `asserts`
+fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
+    if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) {
+        let hash = hash_expr(cx, slice);
+        let indexes = map.entry(hash).or_default();
+
+        let entry = indexes.iter_mut().find(|entry| eq_expr_value(cx, entry.slice(), slice));
+
+        if let Some(entry) = entry {
+            if let IndexEntry::IndexWithoutAssert {
+                highest_index,
+                indexes,
+                slice,
+            } = entry
+            {
+                *entry = IndexEntry::AssertWithIndex {
+                    highest_index: *highest_index,
+                    indexes: mem::take(indexes),
+                    slice,
+                    assert_span: expr.span,
+                    comparison,
+                    asserted_len,
+                };
+            }
+        } else {
+            indexes.push(IndexEntry::StrayAssert {
+                asserted_len,
+                comparison,
+                assert_span: expr.span,
+                slice,
+            });
+        }
+    }
+}
+
+/// Inspects indexes and reports lints.
+///
+/// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
+fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>>) {
+    for bucket in map.values() {
+        for entry in bucket {
+            let Some(full_span) = entry
+                .index_spans()
+                .and_then(|spans| spans.first().zip(spans.last()))
+                .map(|(low, &high)| low.to(high))
+            else {
+                continue;
+            };
+
+            match entry {
+                IndexEntry::AssertWithIndex {
+                    highest_index,
+                    asserted_len,
+                    indexes,
+                    comparison,
+                    assert_span,
+                    slice,
+                } if indexes.len() > 1 => {
+                    // if we have found an `assert!`, let's also check that it's actually right
+                    // and if it convers the highest index and if not, suggest the correct length
+                    let sugg = match comparison {
+                        // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks.
+                        // The user probably meant `v.len() > 5`
+                        LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => Some(
+                            format!("assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..")),
+                        ),
+                        // `5 < v.len()` == `v.len() > 5`
+                        LengthComparison::IntLessThanLength if asserted_len < highest_index => Some(format!(
+                            "assert!({}.len() > {highest_index})",
+                            snippet(cx, slice.span, "..")
+                        )),
+                        // `5 <= v.len() == `v.len() >= 5`
+                        LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => Some(format!(
+                            "assert!({}.len() > {highest_index})",
+                            snippet(cx, slice.span, "..")
+                        )),
+                        _ => None,
+                    };
+
+                    if let Some(sugg) = sugg {
+                        report_lint(
+                            cx,
+                            full_span,
+                            "indexing into a slice multiple times with an `assert` that does not cover the highest index",
+                            indexes,
+                            |diag| {
+                                diag.span_suggestion(
+                                    *assert_span,
+                                    "provide the highest index that is indexed with",
+                                    sugg,
+                                    Applicability::MachineApplicable,
+                                );
+                            },
+                        );
+                    }
+                },
+                IndexEntry::IndexWithoutAssert {
+                    indexes,
+                    highest_index,
+                    slice,
+                } if indexes.len() > 1 => {
+                    // if there was no `assert!` but more than one index, suggest
+                    // adding an `assert!` that covers the highest index
+                    report_lint(
+                        cx,
+                        full_span,
+                        "indexing into a slice multiple times without an `assert`",
+                        indexes,
+                        |diag| {
+                            diag.help(format!(
+                                "consider asserting the length before indexing: `assert!({}.len() > {highest_index});`",
+                                snippet(cx, slice.span, "..")
+                            ));
+                        },
+                    );
+                },
+                _ => {},
+            }
+        }
+    }
+}
+
+impl LateLintPass<'_> for MissingAssertsForIndexing {
+    fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
+        let mut map = UnhashMap::default();
+
+        for_each_expr(block, |expr| {
+            check_index(cx, expr, &mut map);
+            check_assert(cx, expr, &mut map);
+            ControlFlow::<!, ()>::Continue(())
+        });
+
+        report_indexes(cx, &map);
+    }
+}
diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs
index ccabb577cb7..a4fe501f79c 100644
--- a/clippy_lints/src/raw_strings.rs
+++ b/clippy_lints/src/raw_strings.rs
@@ -1,7 +1,7 @@
 use std::iter::once;
 use std::ops::ControlFlow;
 
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_ast::token::LitKind;
@@ -9,6 +9,7 @@ use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{BytePos, Pos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -76,14 +77,24 @@ impl EarlyLintPass for RawStrings {
             }
 
             if !str.contains(['\\', '"']) {
-                span_lint_and_sugg(
+                span_lint_and_then(
                     cx,
                     NEEDLESS_RAW_STRINGS,
                     expr.span,
                     "unnecessary raw string literal",
-                    "try",
-                    format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        let (start, end) = hash_spans(expr.span, prefix, 0, max);
+
+                        // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text
+                        let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1);
+                        let start = start.with_lo(r_pos);
+
+                        diag.multipart_suggestion(
+                            "try",
+                            vec![(start, String::new()), (end, String::new())],
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
 
                 return;
@@ -96,13 +107,6 @@ impl EarlyLintPass for RawStrings {
                 let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
                     match b {
                         b'"' if !following_quote => (following_quote, req) = (true, 1),
-                        // I'm a bit surprised the compiler didn't optimize this out, there's no
-                        // branch but it still ends up doing an unnecessary comparison, it's:
-                        // - cmp r9b,1h
-                        // - sbb cl,-1h
-                        // which will add 1 if it's true. With this change, it becomes:
-                        // - add cl,r9b
-                        // isn't that so much nicer?
                         b'#' => req += u8::from(following_quote),
                         _ => {
                             if following_quote {
@@ -126,18 +130,58 @@ impl EarlyLintPass for RawStrings {
             };
 
             if req < max {
-                let hashes = "#".repeat(req as usize);
-
-                span_lint_and_sugg(
+                span_lint_and_then(
                     cx,
                     NEEDLESS_RAW_STRING_HASHES,
                     expr.span,
                     "unnecessary hashes around raw string literal",
-                    "try",
-                    format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        let (start, end) = hash_spans(expr.span, prefix, req, max);
+
+                        let message = match max - req {
+                            _ if req == 0 => "remove all the hashes around the literal".to_string(),
+                            1 => "remove one hash from both sides of the literal".to_string(),
+                            n => format!("remove {n} hashes from both sides of the literal"),
+                        };
+
+                        diag.multipart_suggestion(
+                            message,
+                            vec![(start, String::new()), (end, String::new())],
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             }
         }
     }
 }
+
+/// Returns spans pointing at the unneeded hashes, e.g. for a `req` of `1` and `max` of `3`:
+///
+/// ```ignore
+/// r###".."###
+///   ^^    ^^
+/// ```
+fn hash_spans(literal_span: Span, prefix: &str, req: u8, max: u8) -> (Span, Span) {
+    let literal_span = literal_span.data();
+
+    // BytePos: we checked prefix appears literally in the source text
+    let hash_start = literal_span.lo + BytePos::from_usize(prefix.len());
+    let hash_end = literal_span.hi;
+
+    // BytePos: req/max are counts of the ASCII character #
+    let start = Span::new(
+        hash_start + BytePos(req.into()),
+        hash_start + BytePos(max.into()),
+        literal_span.ctxt,
+        None,
+    );
+    let end = Span::new(
+        hash_end - BytePos(req.into()),
+        hash_end - BytePos(max.into()),
+        literal_span.ctxt,
+        None,
+    );
+
+    (start, end)
+}
diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs
index dd829ded0d0..de4b8738e35 100644
--- a/clippy_lints/src/unit_return_expecting_ord.rs
+++ b/clippy_lints/src/unit_return_expecting_ord.rs
@@ -26,7 +26,7 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
-    /// let mut twins = vec!((1, 1), (2, 2));
+    /// let mut twins = vec![(1, 1), (2, 2)];
     /// twins.sort_by_key(|x| { x.1; });
     /// ```
     #[clippy::version = "1.47.0"]
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 58ae0656db7..26889475522 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -561,6 +561,26 @@ define_Conf! {
     /// Which crates to allow absolute paths from
     (absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet<String> =
         rustc_data_structures::fx::FxHashSet::default()),
+    /// Lint: EXPLICIT_ITER_LOOP
+    ///
+    /// Whether to recommend using implicit into iter for reborrowed values.
+    ///
+    /// #### Example
+    /// ```
+    /// let mut vec = vec![1, 2, 3];
+    /// let rmvec = &mut vec;
+    /// for _ in rmvec.iter() {}
+    /// for _ in rmvec.iter_mut() {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```
+    /// let mut vec = vec![1, 2, 3];
+    /// let rmvec = &mut vec;
+    /// for _ in &*rmvec {}
+    /// for _ in &mut *rmvec {}
+    /// ```
+    (enforce_iter_loop_reborrow: bool = false),
 }
 
 /// Search for the configuration file.
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index a567c5cbdc9..2fb24b5c7ed 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -49,6 +49,7 @@ pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
+pub const ITER_ONCE: [&str; 5] = ["core", "iter", "sources", "once", "Once"];
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 #[cfg(feature = "internal")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
@@ -163,3 +164,4 @@ pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"];
 pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"];
 #[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so
 pub const BOOL_THEN: [&str; 4] = ["core", "bool", "<impl bool>", "then"];
+pub const ARRAY_INTO_ITER: [&str; 4] = ["core", "array", "iter", "IntoIter"];
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index dc4ee725681..0b2d1e2b089 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -362,7 +362,7 @@ pub fn snippet_block_with_context<'a>(
 }
 
 /// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
-/// will result in the macro call, rather then the expansion, if the span is from a child context.
+/// will result in the macro call, rather than the expansion, if the span is from a child context.
 /// If the span is not from a child context, it will be used directly instead.
 ///
 /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs
index ee5a49a2073..ae8ee371ffa 100644
--- a/clippy_utils/src/sugg.rs
+++ b/clippy_utils/src/sugg.rs
@@ -88,7 +88,7 @@ impl<'a> Sugg<'a> {
     }
 
     /// Same as `hir`, but first walks the span up to the given context. This will result in the
-    /// macro call, rather then the expansion, if the span is from a child context. If the span is
+    /// macro call, rather than the expansion, if the span is from a child context. If the span is
     /// not from a child context, it will be used directly instead.
     ///
     /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR
diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs
index 06fd9529044..dc7756533ac 100644
--- a/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/clippy_utils/src/ty/type_certainty/mod.rs
@@ -150,7 +150,7 @@ fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certa
 }
 
 /// Tries to tell whether a `QPath` resolves to something certain, e.g., whether all of its path
-/// segments generic arguments are are instantiated.
+/// segments generic arguments are instantiated.
 ///
 /// `qpath` could refer to either a type or a value. The heuristic never needs the `DefId` of a
 /// value. So `DefId`s are retained only when `resolves_to_type` is true.
diff --git a/tests/compile-test.rs b/tests/compile-test.rs
index 844e66728f2..171dea49d19 100644
--- a/tests/compile-test.rs
+++ b/tests/compile-test.rs
@@ -114,34 +114,26 @@ fn canonicalize(path: impl AsRef<Path>) -> PathBuf {
 }
 
 fn base_config(test_dir: &str) -> (Config, Args) {
-    let bless = var_os("RUSTC_BLESS").is_some_and(|v| v != "0") || env::args().any(|arg| arg == "--bless");
-
-    let args = Args {
-        filters: env::var("TESTNAME")
-            .map(|filters| filters.split(',').map(str::to_string).collect())
-            .unwrap_or_default(),
-        quiet: false,
-        check: !bless,
-        threads: match std::env::var_os("RUST_TEST_THREADS") {
-            Some(n) => n.to_str().unwrap().parse().unwrap(),
-            None => std::thread::available_parallelism().unwrap(),
-        },
-        skip: Vec::new(),
-    };
+    let mut args = Args::test().unwrap();
+    args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
 
     let mut config = Config {
-        mode: Mode::Yolo { rustfix: true },
+        mode: Mode::Yolo {
+            rustfix: ui_test::RustfixMode::Everything,
+        },
         stderr_filters: vec![(Match::PathBackslash, b"/")],
         stdout_filters: vec![],
-        output_conflict_handling: if bless {
-            OutputConflictHandling::Bless
-        } else {
-            OutputConflictHandling::Error("cargo uibless".into())
-        },
+        filter_files: env::var("TESTNAME")
+            .map(|filters| filters.split(',').map(str::to_string).collect())
+            .unwrap_or_default(),
         target: None,
         out_dir: canonicalize(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into())).join("ui_test"),
         ..Config::rustc(Path::new("tests").join(test_dir))
     };
+    config.with_args(&args, /* bless by default */ false);
+    if let OutputConflictHandling::Error(err) = &mut config.output_conflict_handling {
+        *err = "cargo uibless".into();
+    }
     let current_exe_path = env::current_exe().unwrap();
     let deps_path = current_exe_path.parent().unwrap();
     let profile_path = deps_path.parent().unwrap();
@@ -184,7 +176,6 @@ fn run_ui() {
 
     ui_test::run_tests_generic(
         vec![config],
-        args,
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
         if quiet {
@@ -209,7 +200,6 @@ fn run_internal_tests() {
 
     ui_test::run_tests_generic(
         vec![config],
-        args,
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
         if quiet {
@@ -243,7 +233,6 @@ fn run_ui_toml() {
 
     ui_test::run_tests_generic(
         vec![config],
-        args,
         ui_test::default_file_filter,
         |config, path, _file_contents| {
             config
@@ -302,8 +291,7 @@ fn run_ui_cargo() {
 
     ui_test::run_tests_generic(
         vec![config],
-        args,
-        |path, args, _config| path.ends_with("Cargo.toml") && ui_test::default_filter_by_arg(path, args),
+        |path, config| path.ends_with("Cargo.toml") && ui_test::default_any_file_filter(path, config),
         |config, path, _file_contents| {
             config.out_dir = canonicalize(
                 std::env::current_dir()
diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index cdabe6460cd..b97bb144468 100644
--- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -28,6 +28,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            disallowed-types
            doc-valid-idents
            enable-raw-pointer-heuristic-for-send
+           enforce-iter-loop-reborrow
            enforced-import-renames
            enum-variant-name-threshold
            enum-variant-size-threshold
@@ -99,6 +100,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            disallowed-types
            doc-valid-idents
            enable-raw-pointer-heuristic-for-send
+           enforce-iter-loop-reborrow
            enforced-import-renames
            enum-variant-name-threshold
            enum-variant-size-threshold
diff --git a/tests/ui/crashes/ice-360.rs b/tests/ui/crashes/ice-360.rs
index 28589e1efed..0d10932b098 100644
--- a/tests/ui/crashes/ice-360.rs
+++ b/tests/ui/crashes/ice-360.rs
@@ -3,7 +3,8 @@ fn main() {}
 fn no_panic<T>(slice: &[T]) {
     let mut iter = slice.iter();
     loop {
-        //~^ ERROR: this loop could be written as a `while let` loop
+        //~^ ERROR: this loop never actually loops
+        //~| ERROR: this loop could be written as a `while let` loop
         //~| NOTE: `-D clippy::while-let-loop` implied by `-D warnings`
         let _ = match iter.next() {
             Some(ele) => ele,
diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr
index 292b27dd934..73287d231de 100644
--- a/tests/ui/crashes/ice-360.stderr
+++ b/tests/ui/crashes/ice-360.stderr
@@ -1,10 +1,24 @@
+error: this loop never actually loops
+  --> $DIR/ice-360.rs:5:5
+   |
+LL | /     loop {
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     }
+   | |_____^
+   |
+   = note: `#[deny(clippy::never_loop)]` on by default
+
 error: this loop could be written as a `while let` loop
   --> $DIR/ice-360.rs:5:5
    |
 LL | /     loop {
 LL | |
 LL | |
-LL | |         let _ = match iter.next() {
+LL | |
 ...  |
 LL | |
 LL | |     }
@@ -13,7 +27,7 @@ LL | |     }
    = note: `-D clippy::while-let-loop` implied by `-D warnings`
 
 error: empty `loop {}` wastes CPU cycles
-  --> $DIR/ice-360.rs:12:9
+  --> $DIR/ice-360.rs:13:9
    |
 LL |         loop {}
    |         ^^^^^^^
@@ -21,5 +35,5 @@ LL |         loop {}
    = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
    = note: `-D clippy::empty-loop` implied by `-D warnings`
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/empty_loop.rs b/tests/ui/empty_loop.rs
index 54e8fb4907c..be347563135 100644
--- a/tests/ui/empty_loop.rs
+++ b/tests/ui/empty_loop.rs
@@ -7,10 +7,12 @@ use proc_macros::{external, inline_macros};
 
 fn should_trigger() {
     loop {}
+    #[allow(clippy::never_loop)]
     loop {
         loop {}
     }
 
+    #[allow(clippy::never_loop)]
     'outer: loop {
         'inner: loop {}
     }
@@ -18,6 +20,7 @@ fn should_trigger() {
 
 #[inline_macros]
 fn should_not_trigger() {
+    #[allow(clippy::never_loop)]
     loop {
         panic!("This is fine")
     }
diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr
index 7602412334b..8594f26036e 100644
--- a/tests/ui/empty_loop.stderr
+++ b/tests/ui/empty_loop.stderr
@@ -8,7 +8,7 @@ LL |     loop {}
    = note: `-D clippy::empty-loop` implied by `-D warnings`
 
 error: empty `loop {}` wastes CPU cycles
-  --> $DIR/empty_loop.rs:11:9
+  --> $DIR/empty_loop.rs:12:9
    |
 LL |         loop {}
    |         ^^^^^^^
@@ -16,7 +16,7 @@ LL |         loop {}
    = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
 
 error: empty `loop {}` wastes CPU cycles
-  --> $DIR/empty_loop.rs:15:9
+  --> $DIR/empty_loop.rs:17:9
    |
 LL |         'inner: loop {}
    |         ^^^^^^^^^^^^^^^
diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed
index 86bee11eb87..d046a926935 100644
--- a/tests/ui/explicit_auto_deref.fixed
+++ b/tests/ui/explicit_auto_deref.fixed
@@ -205,7 +205,7 @@ fn main() {
         }
     }
 
-    f_str(&&ref_str); // `needless_borrow` will suggest removing both references
+    f_str(&ref_str); // `needless_borrow` will suggest removing both references
     f_str(&ref_str); // `needless_borrow` will suggest removing only one reference
 
     let x = &&40;
@@ -293,4 +293,10 @@ fn main() {
     fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
         *x
     }
+
+    // Issue #11366
+    let _: &mut u32 = match &mut Some(&mut 0u32) {
+        Some(x) => x,
+        None => panic!(),
+    };
 }
diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs
index 7a505bdf558..3dd08e487f8 100644
--- a/tests/ui/explicit_auto_deref.rs
+++ b/tests/ui/explicit_auto_deref.rs
@@ -293,4 +293,10 @@ fn main() {
     fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
         *x
     }
+
+    // Issue #11366
+    let _: &mut u32 = match &mut Some(&mut 0u32) {
+        Some(x) => &mut *x,
+        None => panic!(),
+    };
 }
diff --git a/tests/ui/explicit_auto_deref.stderr b/tests/ui/explicit_auto_deref.stderr
index 34ce188530c..e98f1c40b27 100644
--- a/tests/ui/explicit_auto_deref.stderr
+++ b/tests/ui/explicit_auto_deref.stderr
@@ -193,10 +193,10 @@ LL |     let _ = f_str(**ref_ref_str);
    |                   ^^^^^^^^^^^^^ help: try: `ref_ref_str`
 
 error: deref which would be done by auto-deref
-  --> $DIR/explicit_auto_deref.rs:208:13
+  --> $DIR/explicit_auto_deref.rs:208:12
    |
 LL |     f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
-   |             ^^^^^^^^ help: try: `ref_str`
+   |            ^^^^^^^^^ help: try: `ref_str`
 
 error: deref which would be done by auto-deref
   --> $DIR/explicit_auto_deref.rs:209:12
@@ -234,5 +234,11 @@ error: deref which would be done by auto-deref
 LL |         *x
    |         ^^ help: try: `x`
 
-error: aborting due to 39 previous errors
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:299:20
+   |
+LL |         Some(x) => &mut *x,
+   |                    ^^^^^^^ help: try: `x`
+
+error: aborting due to 40 previous errors
 
diff --git a/tests/ui/explicit_iter_loop.fixed b/tests/ui/explicit_iter_loop.fixed
index 57292114e38..f08397defa5 100644
--- a/tests/ui/explicit_iter_loop.fixed
+++ b/tests/ui/explicit_iter_loop.fixed
@@ -4,6 +4,7 @@
     clippy::similar_names,
     clippy::needless_borrow,
     clippy::deref_addrof,
+    clippy::unnecessary_mut_passed,
     dead_code
 )]
 
@@ -20,15 +21,15 @@ fn main() {
     for _ in rvec {}
 
     let rmvec = &mut vec;
-    for _ in &*rmvec {}
-    for _ in &mut *rmvec {}
+    for _ in rmvec.iter() {}
+    for _ in rmvec.iter_mut() {}
 
     for _ in &vec {} // these are fine
     for _ in &mut vec {} // these are fine
 
     for _ in &[1, 2, 3] {}
 
-    for _ in &*(&mut [1, 2, 3]) {}
+    for _ in (&mut [1, 2, 3]).iter() {}
 
     for _ in &[0; 32] {}
     for _ in &[0; 33] {}
diff --git a/tests/ui/explicit_iter_loop.rs b/tests/ui/explicit_iter_loop.rs
index 66280c23843..2ee6825d445 100644
--- a/tests/ui/explicit_iter_loop.rs
+++ b/tests/ui/explicit_iter_loop.rs
@@ -4,6 +4,7 @@
     clippy::similar_names,
     clippy::needless_borrow,
     clippy::deref_addrof,
+    clippy::unnecessary_mut_passed,
     dead_code
 )]
 
diff --git a/tests/ui/explicit_iter_loop.stderr b/tests/ui/explicit_iter_loop.stderr
index c311096117f..725d9b63cf8 100644
--- a/tests/ui/explicit_iter_loop.stderr
+++ b/tests/ui/explicit_iter_loop.stderr
@@ -1,5 +1,5 @@
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:16:14
+  --> $DIR/explicit_iter_loop.rs:17:14
    |
 LL |     for _ in vec.iter() {}
    |              ^^^^^^^^^^ help: to write this more concisely, try: `&vec`
@@ -11,132 +11,106 @@ LL | #![deny(clippy::explicit_iter_loop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:17:14
+  --> $DIR/explicit_iter_loop.rs:18:14
    |
 LL |     for _ in vec.iter_mut() {}
    |              ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:20:14
+  --> $DIR/explicit_iter_loop.rs:21:14
    |
 LL |     for _ in rvec.iter() {}
    |              ^^^^^^^^^^^ help: to write this more concisely, try: `rvec`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:23:14
-   |
-LL |     for _ in rmvec.iter() {}
-   |              ^^^^^^^^^^^^ help: to write this more concisely, try: `&*rmvec`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:24:14
-   |
-LL |     for _ in rmvec.iter_mut() {}
-   |              ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *rmvec`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:29:14
+  --> $DIR/explicit_iter_loop.rs:30:14
    |
 LL |     for _ in [1, 2, 3].iter() {}
    |              ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:31:14
-   |
-LL |     for _ in (&mut [1, 2, 3]).iter() {}
-   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&*(&mut [1, 2, 3])`
-
-error: the method `iter` doesn't need a mutable reference
-  --> $DIR/explicit_iter_loop.rs:31:14
-   |
-LL |     for _ in (&mut [1, 2, 3]).iter() {}
-   |              ^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
-
-error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:33:14
+  --> $DIR/explicit_iter_loop.rs:34:14
    |
 LL |     for _ in [0; 32].iter() {}
    |              ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:34:14
+  --> $DIR/explicit_iter_loop.rs:35:14
    |
 LL |     for _ in [0; 33].iter() {}
    |              ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 33]`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:37:14
+  --> $DIR/explicit_iter_loop.rs:38:14
    |
 LL |     for _ in ll.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&ll`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:39:14
+  --> $DIR/explicit_iter_loop.rs:40:14
    |
 LL |     for _ in rll.iter() {}
    |              ^^^^^^^^^^ help: to write this more concisely, try: `rll`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:42:14
+  --> $DIR/explicit_iter_loop.rs:43:14
    |
 LL |     for _ in vd.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&vd`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:44:14
+  --> $DIR/explicit_iter_loop.rs:45:14
    |
 LL |     for _ in rvd.iter() {}
    |              ^^^^^^^^^^ help: to write this more concisely, try: `rvd`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:47:14
+  --> $DIR/explicit_iter_loop.rs:48:14
    |
 LL |     for _ in bh.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&bh`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:50:14
+  --> $DIR/explicit_iter_loop.rs:51:14
    |
 LL |     for _ in hm.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&hm`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:53:14
+  --> $DIR/explicit_iter_loop.rs:54:14
    |
 LL |     for _ in bt.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&bt`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:56:14
+  --> $DIR/explicit_iter_loop.rs:57:14
    |
 LL |     for _ in hs.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&hs`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:59:14
+  --> $DIR/explicit_iter_loop.rs:60:14
    |
 LL |     for _ in bs.iter() {}
    |              ^^^^^^^^^ help: to write this more concisely, try: `&bs`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:148:14
+  --> $DIR/explicit_iter_loop.rs:149:14
    |
 LL |     for _ in x.iter() {}
    |              ^^^^^^^^ help: to write this more concisely, try: `&x`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:149:14
+  --> $DIR/explicit_iter_loop.rs:150:14
    |
 LL |     for _ in x.iter_mut() {}
    |              ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut x`
 
 error: it is more concise to loop over references to containers instead of using explicit iteration methods
-  --> $DIR/explicit_iter_loop.rs:152:14
+  --> $DIR/explicit_iter_loop.rs:153:14
    |
 LL |     for _ in r.iter() {}
    |              ^^^^^^^^ help: to write this more concisely, try: `r`
 
-error: aborting due to 22 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/tests/ui/iter_out_of_bounds.rs b/tests/ui/iter_out_of_bounds.rs
new file mode 100644
index 00000000000..3cfe6e82fc1
--- /dev/null
+++ b/tests/ui/iter_out_of_bounds.rs
@@ -0,0 +1,71 @@
+//@no-rustfix
+
+#![deny(clippy::iter_out_of_bounds)]
+#![allow(clippy::useless_vec)]
+
+fn opaque_empty_iter() -> impl Iterator<Item = ()> {
+    std::iter::empty()
+}
+
+fn main() {
+    #[allow(clippy::never_loop)]
+    for _ in [1, 2, 3].iter().skip(4) {
+        //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+        unreachable!();
+    }
+    for (i, _) in [1, 2, 3].iter().take(4).enumerate() {
+        //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+        assert!(i <= 2);
+    }
+
+    #[allow(clippy::needless_borrow)]
+    for _ in (&&&&&&[1, 2, 3]).iter().take(4) {}
+    //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+
+    for _ in [1, 2, 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in [1; 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    // Should not lint
+    for _ in opaque_empty_iter().skip(1) {}
+
+    for _ in vec![1, 2, 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in vec![1; 3].iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    let x = [1, 2, 3];
+    for _ in x.iter().skip(4) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    let n = 4;
+    for _ in x.iter().skip(n) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    let empty = std::iter::empty::<i8>;
+
+    for _ in empty().skip(1) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in empty().take(1) {}
+    //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+
+    for _ in std::iter::once(1).skip(2) {}
+    //~^ ERROR: this `.skip()` call skips more items than the iterator will produce
+
+    for _ in std::iter::once(1).take(2) {}
+    //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+
+    for x in [].iter().take(1) {
+        //~^ ERROR: this `.take()` call takes more items than the iterator will produce
+        let _: &i32 = x;
+    }
+
+    // ok, not out of bounds
+    for _ in [1].iter().take(1) {}
+    for _ in [1, 2, 3].iter().take(2) {}
+    for _ in [1, 2, 3].iter().skip(2) {}
+}
diff --git a/tests/ui/iter_out_of_bounds.stderr b/tests/ui/iter_out_of_bounds.stderr
new file mode 100644
index 00000000000..f235faec8e5
--- /dev/null
+++ b/tests/ui/iter_out_of_bounds.stderr
@@ -0,0 +1,119 @@
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:12:14
+   |
+LL |     for _ in [1, 2, 3].iter().skip(4) {
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+note: the lint level is defined here
+  --> $DIR/iter_out_of_bounds.rs:3:9
+   |
+LL | #![deny(clippy::iter_out_of_bounds)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:16:19
+   |
+LL |     for (i, _) in [1, 2, 3].iter().take(4).enumerate() {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:22:14
+   |
+LL |     for _ in (&&&&&&[1, 2, 3]).iter().take(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:25:14
+   |
+LL |     for _ in [1, 2, 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:28:14
+   |
+LL |     for _ in [1; 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:34:14
+   |
+LL |     for _ in vec![1, 2, 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:37:14
+   |
+LL |     for _ in vec![1; 3].iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:41:14
+   |
+LL |     for _ in x.iter().skip(4) {}
+   |              ^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:45:14
+   |
+LL |     for _ in x.iter().skip(n) {}
+   |              ^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:50:14
+   |
+LL |     for _ in empty().skip(1) {}
+   |              ^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:53:14
+   |
+LL |     for _ in empty().take(1) {}
+   |              ^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.skip()` call skips more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:56:14
+   |
+LL |     for _ in std::iter::once(1).skip(2) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and will create an empty iterator
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:59:14
+   |
+LL |     for _ in std::iter::once(1).take(2) {}
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: this `.take()` call takes more items than the iterator will produce
+  --> $DIR/iter_out_of_bounds.rs:62:14
+   |
+LL |     for x in [].iter().take(1) {
+   |              ^^^^^^^^^^^^^^^^^
+   |
+   = note: this operation is useless and the returned iterator will simply yield the same items
+
+error: aborting due to 14 previous errors
+
diff --git a/tests/ui/iter_skip_next.fixed b/tests/ui/iter_skip_next.fixed
index 310b24a9cde..3e41b363249 100644
--- a/tests/ui/iter_skip_next.fixed
+++ b/tests/ui/iter_skip_next.fixed
@@ -4,6 +4,7 @@
 #![allow(clippy::disallowed_names)]
 #![allow(clippy::iter_nth)]
 #![allow(clippy::useless_vec)]
+#![allow(clippy::iter_out_of_bounds)]
 #![allow(unused_mut, dead_code)]
 
 extern crate option_helpers;
diff --git a/tests/ui/iter_skip_next.rs b/tests/ui/iter_skip_next.rs
index 222d6a2a184..6d96441ca96 100644
--- a/tests/ui/iter_skip_next.rs
+++ b/tests/ui/iter_skip_next.rs
@@ -4,6 +4,7 @@
 #![allow(clippy::disallowed_names)]
 #![allow(clippy::iter_nth)]
 #![allow(clippy::useless_vec)]
+#![allow(clippy::iter_out_of_bounds)]
 #![allow(unused_mut, dead_code)]
 
 extern crate option_helpers;
diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr
index ca6970b27f1..4ee26e088ce 100644
--- a/tests/ui/iter_skip_next.stderr
+++ b/tests/ui/iter_skip_next.stderr
@@ -1,5 +1,5 @@
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:16:28
+  --> $DIR/iter_skip_next.rs:17:28
    |
 LL |     let _ = some_vec.iter().skip(42).next();
    |                            ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
@@ -7,37 +7,37 @@ LL |     let _ = some_vec.iter().skip(42).next();
    = note: `-D clippy::iter-skip-next` implied by `-D warnings`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:17:36
+  --> $DIR/iter_skip_next.rs:18:36
    |
 LL |     let _ = some_vec.iter().cycle().skip(42).next();
    |                                    ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:18:20
+  --> $DIR/iter_skip_next.rs:19:20
    |
 LL |     let _ = (1..10).skip(10).next();
    |                    ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:19:33
+  --> $DIR/iter_skip_next.rs:20:33
    |
 LL |     let _ = &some_vec[..].iter().skip(3).next();
    |                                 ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:27:26
+  --> $DIR/iter_skip_next.rs:28:26
    |
 LL |     let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
    |                          ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:29:29
+  --> $DIR/iter_skip_next.rs:30:29
    |
 LL |         let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
    |                             ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
 
 error: called `skip(..).next()` on an iterator
-  --> $DIR/iter_skip_next.rs:35:29
+  --> $DIR/iter_skip_next.rs:36:29
    |
 LL |         let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
    |                             ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
diff --git a/tests/ui/iter_skip_next_unfixable.rs b/tests/ui/iter_skip_next_unfixable.rs
index 9c224f4117d..6c98bdc8c88 100644
--- a/tests/ui/iter_skip_next_unfixable.rs
+++ b/tests/ui/iter_skip_next_unfixable.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::iter_skip_next)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::iter_out_of_bounds)]
 //@no-rustfix
 /// Checks implementation of `ITER_SKIP_NEXT` lint
 fn main() {
diff --git a/tests/ui/iter_skip_zero.fixed b/tests/ui/iter_skip_zero.fixed
index 62a83d5905b..447d07100e9 100644
--- a/tests/ui/iter_skip_zero.fixed
+++ b/tests/ui/iter_skip_zero.fixed
@@ -1,5 +1,5 @@
 //@aux-build:proc_macros.rs
-#![allow(clippy::useless_vec, unused)]
+#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)]
 #![warn(clippy::iter_skip_zero)]
 
 #[macro_use]
diff --git a/tests/ui/iter_skip_zero.rs b/tests/ui/iter_skip_zero.rs
index c96696dde65..ba63c398180 100644
--- a/tests/ui/iter_skip_zero.rs
+++ b/tests/ui/iter_skip_zero.rs
@@ -1,5 +1,5 @@
 //@aux-build:proc_macros.rs
-#![allow(clippy::useless_vec, unused)]
+#![allow(clippy::useless_vec, clippy::iter_out_of_bounds, unused)]
 #![warn(clippy::iter_skip_zero)]
 
 #[macro_use]
diff --git a/tests/ui/missing_asserts_for_indexing.fixed b/tests/ui/missing_asserts_for_indexing.fixed
new file mode 100644
index 00000000000..a96827259f5
--- /dev/null
+++ b/tests/ui/missing_asserts_for_indexing.fixed
@@ -0,0 +1,121 @@
+#![allow(unused)]
+#![warn(clippy::missing_asserts_for_indexing)]
+
+// ok
+fn sum_with_assert(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_other_way(v: &[u8]) -> u8 {
+    assert!(5 <= v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge(v: &[u8]) -> u8 {
+    assert!(v.len() >= 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 {
+    assert!(4 < v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+fn sum_with_assert_lt(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_assert_le(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+// ok, don't lint for single array access
+fn single_access(v: &[u8]) -> u8 {
+    v[0]
+}
+
+// ok
+fn subslice_ok(v: &[u8]) {
+    assert!(v.len() > 3);
+    let _ = v[0];
+    let _ = v[1..4];
+}
+
+fn subslice_bad(v: &[u8]) {
+    assert!(v.len() > 3);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..4];
+}
+
+// ok
+fn subslice_inclusive_ok(v: &[u8]) {
+    assert!(v.len() > 4);
+    let _ = v[0];
+    let _ = v[1..=4];
+}
+
+fn subslice_inclusive_bad(v: &[u8]) {
+    assert!(v.len() > 4);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..=4];
+}
+
+fn index_different_slices_ok(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+}
+
+fn side_effect() -> &'static [u8] {
+    &[]
+}
+
+fn index_side_effect_expr() {
+    let _ = side_effect()[0] + side_effect()[1];
+}
+
+// ok, single access for different slices
+fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
+    let _ = v1[0] + v2[1];
+}
+
+fn main() {}
diff --git a/tests/ui/missing_asserts_for_indexing.rs b/tests/ui/missing_asserts_for_indexing.rs
new file mode 100644
index 00000000000..0b4b883acf8
--- /dev/null
+++ b/tests/ui/missing_asserts_for_indexing.rs
@@ -0,0 +1,121 @@
+#![allow(unused)]
+#![warn(clippy::missing_asserts_for_indexing)]
+
+// ok
+fn sum_with_assert(v: &[u8]) -> u8 {
+    assert!(v.len() > 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_other_way(v: &[u8]) -> u8 {
+    assert!(5 <= v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge(v: &[u8]) -> u8 {
+    assert!(v.len() >= 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+// ok
+fn sum_with_assert_ge_other_way(v: &[u8]) -> u8 {
+    assert!(4 < v.len());
+    v[0] + v[1] + v[2] + v[3] + v[4]
+}
+
+fn sum_with_assert_lt(v: &[u8]) -> u8 {
+    assert!(v.len() < 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_assert_le(v: &[u8]) -> u8 {
+    assert!(v.len() <= 5);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len(v: &[u8]) -> u8 {
+    assert!(v.len() > 3);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+fn sum_with_incorrect_assert_len2(v: &[u8]) -> u8 {
+    assert!(v.len() >= 4);
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+
+// ok, don't lint for single array access
+fn single_access(v: &[u8]) -> u8 {
+    v[0]
+}
+
+// ok
+fn subslice_ok(v: &[u8]) {
+    assert!(v.len() > 3);
+    let _ = v[0];
+    let _ = v[1..4];
+}
+
+fn subslice_bad(v: &[u8]) {
+    assert!(v.len() >= 3);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..4];
+}
+
+// ok
+fn subslice_inclusive_ok(v: &[u8]) {
+    assert!(v.len() > 4);
+    let _ = v[0];
+    let _ = v[1..=4];
+}
+
+fn subslice_inclusive_bad(v: &[u8]) {
+    assert!(v.len() >= 4);
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v[1..=4];
+}
+
+fn index_different_slices_ok(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+fn index_different_slices_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() >= 12);
+    assert!(v2.len() >= 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+}
+fn index_different_slices_one_wrong_len(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() >= 12);
+    assert!(v2.len() > 15);
+    let _ = v1[0] + v1[12];
+    //~^ ERROR: indexing into a slice multiple times with an `assert` that does not cover the
+    let _ = v2[5] + v2[15];
+}
+
+fn side_effect() -> &'static [u8] {
+    &[]
+}
+
+fn index_side_effect_expr() {
+    let _ = side_effect()[0] + side_effect()[1];
+}
+
+// ok, single access for different slices
+fn index_different_slice_in_same_expr(v1: &[u8], v2: &[u8]) {
+    let _ = v1[0] + v2[1];
+}
+
+fn main() {}
diff --git a/tests/ui/missing_asserts_for_indexing.stderr b/tests/ui/missing_asserts_for_indexing.stderr
new file mode 100644
index 00000000000..75eb4a35cd4
--- /dev/null
+++ b/tests/ui/missing_asserts_for_indexing.stderr
@@ -0,0 +1,252 @@
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:30:5
+   |
+LL |     assert!(v.len() < 5);
+   |     -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:30:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+   = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:36:5
+   |
+LL |     assert!(v.len() <= 5);
+   |     --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:36:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:42:5
+   |
+LL |     assert!(v.len() > 3);
+   |     -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:42:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:48:5
+   |
+LL |     assert!(v.len() >= 4);
+   |     --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:48:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:66:13
+   |
+LL |       assert!(v.len() >= 3);
+   |       --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)`
+LL |       let _ = v[0];
+   |  _____________^
+LL | |
+LL | |     let _ = v[1..4];
+   | |___________________^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:66:13
+   |
+LL |     let _ = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:68:13
+   |
+LL |     let _ = v[1..4];
+   |             ^^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:80:13
+   |
+LL |       assert!(v.len() >= 4);
+   |       --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
+LL |       let _ = v[0];
+   |  _____________^
+LL | |
+LL | |     let _ = v[1..=4];
+   | |____________________^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:80:13
+   |
+LL |     let _ = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:82:13
+   |
+LL |     let _ = v[1..=4];
+   |             ^^^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:95:13
+   |
+LL |     assert!(v1.len() >= 12);
+   |     ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
+LL |     assert!(v2.len() >= 15);
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:95:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:95:21
+   |
+LL |     let _ = v1[0] + v1[12];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:97:13
+   |
+LL |     assert!(v2.len() >= 15);
+   |     ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)`
+...
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:97:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:97:21
+   |
+LL |     let _ = v2[5] + v2[15];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times with an `assert` that does not cover the highest index
+  --> $DIR/missing_asserts_for_indexing.rs:103:13
+   |
+LL |     assert!(v1.len() >= 12);
+   |     ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
+LL |     assert!(v2.len() > 15);
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^^^^^^^^^^
+   |
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:103:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing.rs:103:21
+   |
+LL |     let _ = v1[0] + v1[12];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.rs b/tests/ui/missing_asserts_for_indexing_unfixable.rs
new file mode 100644
index 00000000000..4346ed892f2
--- /dev/null
+++ b/tests/ui/missing_asserts_for_indexing_unfixable.rs
@@ -0,0 +1,49 @@
+#![allow(unused)]
+#![warn(clippy::missing_asserts_for_indexing)]
+
+fn sum(v: &[u8]) -> u8 {
+    v[0] + v[1] + v[2] + v[3] + v[4]
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+}
+
+fn subslice(v: &[u8]) {
+    let _ = v[0];
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+    let _ = v[1..4];
+}
+
+fn variables(v: &[u8]) -> u8 {
+    let a = v[0];
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+    let b = v[1];
+    let c = v[2];
+    a + b + c
+}
+
+fn index_different_slices(v1: &[u8], v2: &[u8]) {
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+fn index_different_slices2(v1: &[u8], v2: &[u8]) {
+    assert!(v1.len() > 12);
+    let _ = v1[0] + v1[12];
+    let _ = v2[5] + v2[15];
+}
+
+struct Foo<'a> {
+    v: &'a [u8],
+    v2: &'a [u8],
+}
+
+fn index_struct_field(f: &Foo<'_>) {
+    let _ = f.v[0] + f.v[1];
+    //~^ ERROR: indexing into a slice multiple times without an `assert`
+}
+
+fn index_struct_different_fields(f: &Foo<'_>) {
+    // ok, different fields
+    let _ = f.v[0] + f.v2[1];
+}
+
+fn main() {}
diff --git a/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/tests/ui/missing_asserts_for_indexing_unfixable.stderr
new file mode 100644
index 00000000000..a26524d9a11
--- /dev/null
+++ b/tests/ui/missing_asserts_for_indexing_unfixable.stderr
@@ -0,0 +1,163 @@
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v.len() > 4);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:5
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |     ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:12
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |            ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:19
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                   ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:26
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                          ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:5:33
+   |
+LL |     v[0] + v[1] + v[2] + v[3] + v[4]
+   |                                 ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+   = note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13
+   |
+LL |       let _ = v[0];
+   |  _____________^
+LL | |
+LL | |     let _ = v[1..4];
+   | |___________________^
+   |
+   = help: consider asserting the length before indexing: `assert!(v.len() > 3);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:10:13
+   |
+LL |     let _ = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:12:13
+   |
+LL |     let _ = v[1..4];
+   |             ^^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13
+   |
+LL |       let a = v[0];
+   |  _____________^
+LL | |
+LL | |     let b = v[1];
+LL | |     let c = v[2];
+   | |________________^
+   |
+   = help: consider asserting the length before indexing: `assert!(v.len() > 2);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:16:13
+   |
+LL |     let a = v[0];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:18:13
+   |
+LL |     let b = v[1];
+   |             ^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:19:13
+   |
+LL |     let c = v[2];
+   |             ^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v1.len() > 12);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:13
+   |
+LL |     let _ = v1[0] + v1[12];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:24:21
+   |
+LL |     let _ = v1[0] + v1[12];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:25:21
+   |
+LL |     let _ = v2[5] + v2[15];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:13
+   |
+LL |     let _ = v2[5] + v2[15];
+   |             ^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:31:21
+   |
+LL |     let _ = v2[5] + v2[15];
+   |                     ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: indexing into a slice multiple times without an `assert`
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13
+   |
+LL |     let _ = f.v[0] + f.v[1];
+   |             ^^^^^^^^^^^^^^^
+   |
+   = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);`
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:13
+   |
+LL |     let _ = f.v[0] + f.v[1];
+   |             ^^^^^^
+note: slice indexed here
+  --> $DIR/missing_asserts_for_indexing_unfixable.rs:40:22
+   |
+LL |     let _ = f.v[0] + f.v[1];
+   |                      ^^^^^^
+   = note: asserting the length before indexing will elide bounds checks
+
+error: aborting due to 7 previous errors
+
diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs
index 9d66c5f255f..b4a536cb159 100644
--- a/tests/ui/needless_collect_indirect.rs
+++ b/tests/ui/needless_collect_indirect.rs
@@ -260,6 +260,7 @@ mod issue_8553 {
         let w = v.iter().collect::<Vec<_>>();
         //~^ ERROR: avoid using `collect()` when not needed
         // Do lint
+        #[allow(clippy::never_loop)]
         for _ in 0..w.len() {
             todo!();
         }
@@ -270,6 +271,7 @@ mod issue_8553 {
         let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
         let w = v.iter().collect::<Vec<_>>();
         // Do not lint, because w is used.
+        #[allow(clippy::never_loop)]
         for _ in 0..w.len() {
             todo!();
         }
@@ -283,6 +285,7 @@ mod issue_8553 {
         let mut w = v.iter().collect::<Vec<_>>();
         //~^ ERROR: avoid using `collect()` when not needed
         // Do lint
+        #[allow(clippy::never_loop)]
         while 1 == w.len() {
             todo!();
         }
@@ -293,6 +296,7 @@ mod issue_8553 {
         let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
         let mut w = v.iter().collect::<Vec<_>>();
         // Do not lint, because w is used.
+        #[allow(clippy::never_loop)]
         while 1 == w.len() {
             todo!();
         }
@@ -306,6 +310,7 @@ mod issue_8553 {
         let mut w = v.iter().collect::<Vec<_>>();
         //~^ ERROR: avoid using `collect()` when not needed
         // Do lint
+        #[allow(clippy::never_loop)]
         while let Some(i) = Some(w.len()) {
             todo!();
         }
@@ -316,6 +321,7 @@ mod issue_8553 {
         let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
         let mut w = v.iter().collect::<Vec<_>>();
         // Do not lint, because w is used.
+        #[allow(clippy::never_loop)]
         while let Some(i) = Some(w.len()) {
             todo!();
         }
diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr
index 9337a741242..47048b74125 100644
--- a/tests/ui/needless_collect_indirect.stderr
+++ b/tests/ui/needless_collect_indirect.stderr
@@ -230,11 +230,12 @@ help: take the original Iterator's count instead of collecting it and finding th
 LL ~         
 LL |
 LL |         // Do lint
+LL |         #[allow(clippy::never_loop)]
 LL ~         for _ in 0..v.iter().count() {
    |
 
 error: avoid using `collect()` when not needed
-  --> $DIR/needless_collect_indirect.rs:283:30
+  --> $DIR/needless_collect_indirect.rs:285:30
    |
 LL |         let mut w = v.iter().collect::<Vec<_>>();
    |                              ^^^^^^^
@@ -247,11 +248,12 @@ help: take the original Iterator's count instead of collecting it and finding th
 LL ~         
 LL |
 LL |         // Do lint
+LL |         #[allow(clippy::never_loop)]
 LL ~         while 1 == v.iter().count() {
    |
 
 error: avoid using `collect()` when not needed
-  --> $DIR/needless_collect_indirect.rs:306:30
+  --> $DIR/needless_collect_indirect.rs:310:30
    |
 LL |         let mut w = v.iter().collect::<Vec<_>>();
    |                              ^^^^^^^
@@ -264,6 +266,7 @@ help: take the original Iterator's count instead of collecting it and finding th
 LL ~         
 LL |
 LL |         // Do lint
+LL |         #[allow(clippy::never_loop)]
 LL ~         while let Some(i) = Some(v.iter().count()) {
    |
 
diff --git a/tests/ui/needless_raw_string.fixed b/tests/ui/needless_raw_string.fixed
index 4db375178b4..85549810513 100644
--- a/tests/ui/needless_raw_string.fixed
+++ b/tests/ui/needless_raw_string.fixed
@@ -9,8 +9,13 @@ fn main() {
     b"aaa";
     br#""aaa""#;
     br#"\s"#;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr#""aaa""#;
-    // cr#"\s"#;
+    c"aaa";
+    cr#""aaa""#;
+    cr#"\s"#;
+
+    "
+        a
+        multiline
+        string
+    ";
 }
diff --git a/tests/ui/needless_raw_string.rs b/tests/ui/needless_raw_string.rs
index 59c75fda41b..06d49730387 100644
--- a/tests/ui/needless_raw_string.rs
+++ b/tests/ui/needless_raw_string.rs
@@ -9,8 +9,13 @@ fn main() {
     br#"aaa"#;
     br#""aaa""#;
     br#"\s"#;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr#""aaa""#;
-    // cr#"\s"#;
+    cr#"aaa"#;
+    cr#""aaa""#;
+    cr#"\s"#;
+
+    r#"
+        a
+        multiline
+        string
+    "#;
 }
diff --git a/tests/ui/needless_raw_string.stderr b/tests/ui/needless_raw_string.stderr
index ddc36af2e72..9c417fe1699 100644
--- a/tests/ui/needless_raw_string.stderr
+++ b/tests/ui/needless_raw_string.stderr
@@ -2,15 +2,57 @@ error: unnecessary raw string literal
   --> $DIR/needless_raw_string.rs:6:5
    |
 LL |     r#"aaa"#;
-   |     ^^^^^^^^ help: try: `"aaa"`
+   |     ^^^^^^^^
    |
    = note: `-D clippy::needless-raw-strings` implied by `-D warnings`
+help: try
+   |
+LL -     r#"aaa"#;
+LL +     "aaa";
+   |
 
 error: unnecessary raw string literal
   --> $DIR/needless_raw_string.rs:9:5
    |
 LL |     br#"aaa"#;
-   |     ^^^^^^^^^ help: try: `b"aaa"`
+   |     ^^^^^^^^^
+   |
+help: try
+   |
+LL -     br#"aaa"#;
+LL +     b"aaa";
+   |
+
+error: unnecessary raw string literal
+  --> $DIR/needless_raw_string.rs:12:5
+   |
+LL |     cr#"aaa"#;
+   |     ^^^^^^^^^
+   |
+help: try
+   |
+LL -     cr#"aaa"#;
+LL +     c"aaa";
+   |
+
+error: unnecessary raw string literal
+  --> $DIR/needless_raw_string.rs:16:5
+   |
+LL | /     r#"
+LL | |         a
+LL | |         multiline
+LL | |         string
+LL | |     "#;
+   | |______^
+   |
+help: try
+   |
+LL ~     "
+LL |         a
+LL |         multiline
+LL |         string
+LL ~     ";
+   |
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/needless_raw_string_hashes.fixed b/tests/ui/needless_raw_string_hashes.fixed
index 84902157ab4..e980adeeff4 100644
--- a/tests/ui/needless_raw_string_hashes.fixed
+++ b/tests/ui/needless_raw_string_hashes.fixed
@@ -3,17 +3,22 @@
 #![feature(c_str_literals)]
 
 fn main() {
-    r#"aaa"#;
+    r"\aaa";
     r#"Hello "world"!"#;
     r####" "### "## "# "####;
     r###" "aa" "# "## "###;
-    br#"aaa"#;
+    br"\aaa";
     br#"Hello "world"!"#;
     br####" "### "## "# "####;
     br###" "aa" "# "## "###;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr##"Hello "world"!"##;
-    // cr######" "### "## "# "######;
-    // cr######" "aa" "# "## "######;
+    cr"\aaa";
+    cr#"Hello "world"!"#;
+    cr####" "### "## "# "####;
+    cr###" "aa" "# "## "###;
+
+    r"
+        \a
+        multiline
+        string
+    ";
 }
diff --git a/tests/ui/needless_raw_string_hashes.rs b/tests/ui/needless_raw_string_hashes.rs
index 62abae83859..6113c5f25ae 100644
--- a/tests/ui/needless_raw_string_hashes.rs
+++ b/tests/ui/needless_raw_string_hashes.rs
@@ -3,17 +3,22 @@
 #![feature(c_str_literals)]
 
 fn main() {
-    r#"aaa"#;
+    r#"\aaa"#;
     r##"Hello "world"!"##;
     r######" "### "## "# "######;
     r######" "aa" "# "## "######;
-    br#"aaa"#;
+    br#"\aaa"#;
     br##"Hello "world"!"##;
     br######" "### "## "# "######;
     br######" "aa" "# "## "######;
-    // currently disabled: https://github.com/rust-lang/rust/issues/113333
-    // cr#"aaa"#;
-    // cr##"Hello "world"!"##;
-    // cr######" "### "## "# "######;
-    // cr######" "aa" "# "## "######;
+    cr#"\aaa"#;
+    cr##"Hello "world"!"##;
+    cr######" "### "## "# "######;
+    cr######" "aa" "# "## "######;
+
+    r#"
+        \a
+        multiline
+        string
+    "#;
 }
diff --git a/tests/ui/needless_raw_string_hashes.stderr b/tests/ui/needless_raw_string_hashes.stderr
index 9649d59a71f..ab6a87c5e8d 100644
--- a/tests/ui/needless_raw_string_hashes.stderr
+++ b/tests/ui/needless_raw_string_hashes.stderr
@@ -1,40 +1,166 @@
 error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:6:5
+   |
+LL |     r#"\aaa"#;
+   |     ^^^^^^^^^
+   |
+   = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings`
+help: remove all the hashes around the literal
+   |
+LL -     r#"\aaa"#;
+LL +     r"\aaa";
+   |
+
+error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:7:5
    |
 LL |     r##"Hello "world"!"##;
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: try: `r#"Hello "world"!"#`
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the literal
+   |
+LL -     r##"Hello "world"!"##;
+LL +     r#"Hello "world"!"#;
    |
-   = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings`
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:8:5
    |
 LL |     r######" "### "## "# "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r####" "### "## "# "####`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 2 hashes from both sides of the literal
+   |
+LL -     r######" "### "## "# "######;
+LL +     r####" "### "## "# "####;
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:9:5
    |
 LL |     r######" "aa" "# "## "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r###" "aa" "# "## "###`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 3 hashes from both sides of the literal
+   |
+LL -     r######" "aa" "# "## "######;
+LL +     r###" "aa" "# "## "###;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:10:5
+   |
+LL |     br#"\aaa"#;
+   |     ^^^^^^^^^^
+   |
+help: remove all the hashes around the literal
+   |
+LL -     br#"\aaa"#;
+LL +     br"\aaa";
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:11:5
    |
 LL |     br##"Hello "world"!"##;
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `br#"Hello "world"!"#`
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the literal
+   |
+LL -     br##"Hello "world"!"##;
+LL +     br#"Hello "world"!"#;
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:12:5
    |
 LL |     br######" "### "## "# "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br####" "### "## "# "####`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 2 hashes from both sides of the literal
+   |
+LL -     br######" "### "## "# "######;
+LL +     br####" "### "## "# "####;
+   |
 
 error: unnecessary hashes around raw string literal
   --> $DIR/needless_raw_string_hashes.rs:13:5
    |
 LL |     br######" "aa" "# "## "######;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br###" "aa" "# "## "###`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 3 hashes from both sides of the literal
+   |
+LL -     br######" "aa" "# "## "######;
+LL +     br###" "aa" "# "## "###;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:14:5
+   |
+LL |     cr#"\aaa"#;
+   |     ^^^^^^^^^^
+   |
+help: remove all the hashes around the literal
+   |
+LL -     cr#"\aaa"#;
+LL +     cr"\aaa";
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:15:5
+   |
+LL |     cr##"Hello "world"!"##;
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove one hash from both sides of the literal
+   |
+LL -     cr##"Hello "world"!"##;
+LL +     cr#"Hello "world"!"#;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:16:5
+   |
+LL |     cr######" "### "## "# "######;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 2 hashes from both sides of the literal
+   |
+LL -     cr######" "### "## "# "######;
+LL +     cr####" "### "## "# "####;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:17:5
+   |
+LL |     cr######" "aa" "# "## "######;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove 3 hashes from both sides of the literal
+   |
+LL -     cr######" "aa" "# "## "######;
+LL +     cr###" "aa" "# "## "###;
+   |
+
+error: unnecessary hashes around raw string literal
+  --> $DIR/needless_raw_string_hashes.rs:19:5
+   |
+LL | /     r#"
+LL | |         \a
+LL | |         multiline
+LL | |         string
+LL | |     "#;
+   | |______^
+   |
+help: remove all the hashes around the literal
+   |
+LL ~     r"
+LL |         \a
+LL |         multiline
+LL |         string
+LL ~     ";
+   |
 
-error: aborting due to 6 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs
index 001b94391ca..33208364f0e 100644
--- a/tests/ui/never_loop.rs
+++ b/tests/ui/never_loop.rs
@@ -337,10 +337,8 @@ pub fn test26() {
 
 pub fn test27() {
     loop {
-        //~^ ERROR: this loop never actually loops
         'label: {
             let x = true;
-            // Lints because we cannot prove it's always `true`
             if x {
                 break 'label;
             }
@@ -349,6 +347,51 @@ pub fn test27() {
     }
 }
 
+// issue 11004
+pub fn test29() {
+    loop {
+        'label: {
+            if true {
+                break 'label;
+            }
+            return;
+        }
+    }
+}
+
+pub fn test30() {
+    'a: loop {
+        'b: {
+            for j in 0..2 {
+                if j == 1 {
+                    break 'b;
+                }
+            }
+            break 'a;
+        }
+    }
+}
+
+pub fn test31(b: bool) {
+    'a: loop {
+        'b: {
+            'c: loop {
+                //~^ ERROR: this loop never actually loops
+                if b { break 'c } else { break 'b }
+            }
+            continue 'a;
+        }
+        break 'a;
+    }
+}
+
+pub fn test32(b: bool) {
+    loop {
+        //~^ ERROR: this loop never actually loops
+        panic!("oh no");
+    }
+}
+
 fn main() {
     test1();
     test2();
diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr
index 6d6d2c8ac52..37ccd63d27c 100644
--- a/tests/ui/never_loop.stderr
+++ b/tests/ui/never_loop.stderr
@@ -153,16 +153,22 @@ LL |             if let Some(_) = (0..20).next() {
    |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: this loop never actually loops
-  --> $DIR/never_loop.rs:339:5
+  --> $DIR/never_loop.rs:378:13
+   |
+LL | /             'c: loop {
+LL | |
+LL | |                 if b { break 'c } else { break 'b }
+LL | |             }
+   | |_____________^
+
+error: this loop never actually loops
+  --> $DIR/never_loop.rs:389:5
    |
 LL | /     loop {
 LL | |
-LL | |         'label: {
-LL | |             let x = true;
-...  |
-LL | |         }
+LL | |         panic!("oh no");
 LL | |     }
    | |_____^
 
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs
index c5a941316da..f46af56c6e2 100644
--- a/tests/ui/similar_names.rs
+++ b/tests/ui/similar_names.rs
@@ -3,6 +3,7 @@
     unused,
     clippy::println_empty_string,
     clippy::empty_loop,
+    clippy::never_loop,
     clippy::diverging_sub_expression,
     clippy::let_unit_value
 )]
diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr
index 059e26d5891..00735617bc2 100644
--- a/tests/ui/similar_names.stderr
+++ b/tests/ui/similar_names.stderr
@@ -1,84 +1,84 @@
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:21:9
+  --> $DIR/similar_names.rs:22:9
    |
 LL |     let bpple: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:19:9
+  --> $DIR/similar_names.rs:20:9
    |
 LL |     let apple: i32;
    |         ^^^^^
    = note: `-D clippy::similar-names` implied by `-D warnings`
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:24:9
+  --> $DIR/similar_names.rs:25:9
    |
 LL |     let cpple: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:19:9
+  --> $DIR/similar_names.rs:20:9
    |
 LL |     let apple: i32;
    |         ^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:49:9
+  --> $DIR/similar_names.rs:50:9
    |
 LL |     let bluby: i32;
    |         ^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:48:9
+  --> $DIR/similar_names.rs:49:9
    |
 LL |     let blubx: i32;
    |         ^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:54:9
+  --> $DIR/similar_names.rs:55:9
    |
 LL |     let coke: i32;
    |         ^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:52:9
+  --> $DIR/similar_names.rs:53:9
    |
 LL |     let cake: i32;
    |         ^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:73:9
+  --> $DIR/similar_names.rs:74:9
    |
 LL |     let xyzeabc: i32;
    |         ^^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:71:9
+  --> $DIR/similar_names.rs:72:9
    |
 LL |     let xyz1abc: i32;
    |         ^^^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:78:9
+  --> $DIR/similar_names.rs:79:9
    |
 LL |     let parsee: i32;
    |         ^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:76:9
+  --> $DIR/similar_names.rs:77:9
    |
 LL |     let parser: i32;
    |         ^^^^^^
 
 error: binding's name is too similar to existing binding
-  --> $DIR/similar_names.rs:100:16
+  --> $DIR/similar_names.rs:101:16
    |
 LL |         bpple: sprang,
    |                ^^^^^^
    |
 note: existing binding defined here
-  --> $DIR/similar_names.rs:99:16
+  --> $DIR/similar_names.rs:100:16
    |
 LL |         apple: spring,
    |                ^^^^^^
diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed
index 3ff2acbe28f..de1eb613ce0 100644
--- a/tests/ui/vec.fixed
+++ b/tests/ui/vec.fixed
@@ -1,5 +1,10 @@
 #![warn(clippy::useless_vec)]
-#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args, unused)]
+#![allow(
+    clippy::nonstandard_macro_braces,
+    clippy::never_loop,
+    clippy::uninlined_format_args,
+    unused
+)]
 
 use std::rc::Rc;
 
diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs
index 2ab025f424a..1da10c28880 100644
--- a/tests/ui/vec.rs
+++ b/tests/ui/vec.rs
@@ -1,5 +1,10 @@
 #![warn(clippy::useless_vec)]
-#![allow(clippy::nonstandard_macro_braces, clippy::uninlined_format_args, unused)]
+#![allow(
+    clippy::nonstandard_macro_braces,
+    clippy::never_loop,
+    clippy::uninlined_format_args,
+    unused
+)]
 
 use std::rc::Rc;
 
diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr
index 5cd6d9fa8c7..a4c2b8984e0 100644
--- a/tests/ui/vec.stderr
+++ b/tests/ui/vec.stderr
@@ -1,5 +1,5 @@
 error: useless use of `vec!`
-  --> $DIR/vec.rs:30:14
+  --> $DIR/vec.rs:35:14
    |
 LL |     on_slice(&vec![]);
    |              ^^^^^^^ help: you can use a slice directly: `&[]`
@@ -7,109 +7,109 @@ LL |     on_slice(&vec![]);
    = note: `-D clippy::useless-vec` implied by `-D warnings`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:32:18
+  --> $DIR/vec.rs:37:18
    |
 LL |     on_mut_slice(&mut vec![]);
    |                  ^^^^^^^^^^^ help: you can use a slice directly: `&mut []`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:34:14
+  --> $DIR/vec.rs:39:14
    |
 LL |     on_slice(&vec![1, 2]);
    |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:36:18
+  --> $DIR/vec.rs:41:18
    |
 LL |     on_mut_slice(&mut vec![1, 2]);
    |                  ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:38:14
+  --> $DIR/vec.rs:43:14
    |
 LL |     on_slice(&vec![1, 2]);
    |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:40:18
+  --> $DIR/vec.rs:45:18
    |
 LL |     on_mut_slice(&mut vec![1, 2]);
    |                  ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:42:14
+  --> $DIR/vec.rs:47:14
    |
 LL |     on_slice(&vec!(1, 2));
    |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:44:18
+  --> $DIR/vec.rs:49:18
    |
 LL |     on_mut_slice(&mut vec![1, 2]);
    |                  ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:46:14
+  --> $DIR/vec.rs:51:14
    |
 LL |     on_slice(&vec![1; 2]);
    |              ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:48:18
+  --> $DIR/vec.rs:53:18
    |
 LL |     on_mut_slice(&mut vec![1; 2]);
    |                  ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:74:19
+  --> $DIR/vec.rs:79:19
    |
 LL |     let _x: i32 = vec![1, 2, 3].iter().sum();
    |                   ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:77:17
+  --> $DIR/vec.rs:82:17
    |
 LL |     let mut x = vec![1, 2, 3];
    |                 ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:83:22
+  --> $DIR/vec.rs:88:22
    |
 LL |     let _x: &[i32] = &vec![1, 2, 3];
    |                      ^^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:85:14
+  --> $DIR/vec.rs:90:14
    |
 LL |     for _ in vec![1, 2, 3] {}
    |              ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:123:20
+  --> $DIR/vec.rs:128:20
    |
 LL |     for _string in vec![repro!(true), repro!(null)] {
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[repro!(true), repro!(null)]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:140:18
+  --> $DIR/vec.rs:145:18
    |
 LL |     in_macro!(1, vec![1, 2], vec![1; 2]);
    |                  ^^^^^^^^^^ help: you can use an array directly: `[1, 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:140:30
+  --> $DIR/vec.rs:145:30
    |
 LL |     in_macro!(1, vec![1, 2], vec![1; 2]);
    |                              ^^^^^^^^^^ help: you can use an array directly: `[1; 2]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:159:14
+  --> $DIR/vec.rs:164:14
    |
 LL |     for a in vec![1, 2, 3] {
    |              ^^^^^^^^^^^^^ help: you can use an array directly: `[1, 2, 3]`
 
 error: useless use of `vec!`
-  --> $DIR/vec.rs:163:14
+  --> $DIR/vec.rs:168:14
    |
 LL |     for a in vec![String::new(), String::new()] {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]`
diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr
index 84b302d8d3b..20b1f0e2a45 100644
--- a/tests/ui/write_literal_2.stderr
+++ b/tests/ui/write_literal_2.stderr
@@ -2,9 +2,14 @@ error: unnecessary raw string literal
   --> $DIR/write_literal_2.rs:13:24
    |
 LL |     writeln!(v, r"{}", r"{hello}");
-   |                        ^^^^^^^^^^ help: try: `"{hello}"`
+   |                        ^^^^^^^^^^
    |
    = note: `-D clippy::needless-raw-strings` implied by `-D warnings`
+help: try
+   |
+LL -     writeln!(v, r"{}", r"{hello}");
+LL +     writeln!(v, r"{}", "{hello}");
+   |
 
 error: literal with an empty format string
   --> $DIR/write_literal_2.rs:10:23