diff --git a/Cargo.lock b/Cargo.lock index 295a5b43a6f32..386b57e6a44e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -531,7 +531,7 @@ dependencies = [ "tester", "tokio", "toml 0.7.5", - "ui_test 0.18.1", + "ui_test 0.20.0", "walkdir", ] @@ -5596,9 +5596,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.18.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640159421816683e558867ffc0e60ed3a3ed97ec6ccb22c03adb41bf87c5cfa4" +checksum = "bfd8fb9b15c8332cf51bfc2dc4830063b2446a9c9d732421b56f2478024a3971" dependencies = [ "annotate-snippets", "anyhow", diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh index 798782340ee7d..c05c6ecc1151c 100644 --- a/src/tools/clippy/.github/driver.sh +++ b/src/tools/clippy/.github/driver.sh @@ -30,7 +30,7 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? ./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1 -sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr +sed -e "s,tests/ui,\$DIR," -e "/= help: for/d" double_neg.stderr > normalized.stderr diff -u normalized.stderr tests/ui/double_neg.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 5c69714bc1e99..9b96f8dc25338 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -52,24 +52,14 @@ jobs: needs: changelog strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc] - exclude: + include: - os: ubuntu-latest - host: x86_64-apple-darwin - - os: ubuntu-latest - host: x86_64-pc-windows-msvc - - os: macos-latest - host: x86_64-unknown-linux-gnu - - os: macos-latest - host: i686-unknown-linux-gnu - - os: macos-latest - host: x86_64-pc-windows-msvc - - os: windows-latest host: x86_64-unknown-linux-gnu - - os: windows-latest + - os: ubuntu-latest host: i686-unknown-linux-gnu - os: windows-latest + host: x86_64-pc-windows-msvc + - os: macos-latest host: x86_64-apple-darwin runs-on: ${{ matrix.os }} @@ -84,8 +74,17 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Install i686 dependencies + if: matrix.host == 'i686-unknown-linux-gnu' + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install gcc-multilib zlib1g-dev:i386 + - name: Install toolchain - run: rustup show active-toolchain + run: | + rustup set default-host ${{ matrix.host }} + rustup show active-toolchain # Run - name: Set LD_LIBRARY_PATH (Linux) @@ -109,11 +108,11 @@ jobs: run: cargo build --tests --features deny-warnings,internal - name: Test - if: runner.os == 'Linux' + if: matrix.host == 'x86_64-unknown-linux-gnu' run: cargo test --features deny-warnings,internal - name: Test - if: runner.os != 'Linux' + if: matrix.host != 'x86_64-unknown-linux-gnu' run: cargo test --features deny-warnings,internal -- --skip dogfood - name: Test clippy_lints diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 92cc19edb0505..a7ae4a0ee2c09 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/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 @@ -5204,6 +5206,8 @@ Released 2018-09-13 [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal +[`non_canonical_clone_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_clone_impl +[`non_canonical_partial_ord_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_canonical_partial_ord_impl [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty @@ -5570,4 +5574,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 diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 7c78beb5ddef9..2d8b590dbe311 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -27,7 +27,7 @@ tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -ui_test = "0.18.1" +ui_test = "0.20" tester = "0.9" regex = "1.5" toml = "0.7.3" diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md index daaefd06a97f3..b02457307d743 100644 --- a/src/tools/clippy/book/src/SUMMARY.md +++ b/src/tools/clippy/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/src/tools/clippy/book/src/development/emitting_lints.md b/src/tools/clippy/book/src/development/emitting_lints.md new file mode 100644 index 0000000000000..a12f6aa91b307 --- /dev/null +++ b/src/tools/clippy/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, "")), // < 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/src/tools/clippy/book/src/development/trait_checking.md b/src/tools/clippy/book/src/development/trait_checking.md new file mode 100644 index 0000000000000..fb263922cf88e --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/book/src/development/writing_tests.md b/src/tools/clippy/book/src/development/writing_tests.md new file mode 100644 index 0000000000000..8937e0d8e9469 --- /dev/null +++ b/src/tools/clippy/book/src/development/writing_tests.md @@ -0,0 +1,218 @@ +# 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](emitting_lints.md). + +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/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index caaad6d11736f..52c795e04fe95 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/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/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index f73f1ed18f8b6..a4d40df52e7bc 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -211,8 +211,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, - crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO, - crate::incorrect_impls::INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE_INFO, crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, crate::indexing_slicing::INDEXING_SLICING_INFO, crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, @@ -365,6 +363,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 +455,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, @@ -496,6 +496,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, crate::no_effect::UNNECESSARY_OPERATION_INFO, crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO, + crate::non_canonical_impls::NON_CANONICAL_CLONE_IMPL_INFO, + crate::non_canonical_impls::NON_CANONICAL_PARTIAL_ORD_IMPL_INFO, crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO, crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO, crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index 03b5a2d6d0829..bbce6e1dd8f2b 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/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/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 58c2785500013..8090f821d1ff2 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -3,7 +3,7 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, }; @@ -33,7 +33,6 @@ use rustc_middle::ty::{ use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::collections::VecDeque; @@ -452,13 +451,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // Trait methods taking `self` arg_ty } && impl_ty.is_ref() - && cx.tcx.infer_ctxt().build() - .type_implements_trait( - trait_id, - [impl_ty.into()].into_iter().chain(args.iter().copied()), - cx.param_env, - ) - .must_apply_modulo_regions() + && implements_trait( + cx, + impl_ty, + trait_id, + &args[..cx.tcx.generics_of(trait_id).params.len() - 1], + ) { false } else { @@ -609,12 +607,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, }, )); @@ -1399,6 +1399,13 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data return; } + if let ExprKind::Field(parent_expr, _) = expr.kind + && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind() + && adt.is_union() + { + // Auto deref does not apply on union field + return; + } span_lint_hir_and_then( cx, EXPLICIT_AUTO_DEREF, diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 9a85cc4ce2dc0..d2bfc4f8e273d 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -217,8 +217,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind(); if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); - if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); - if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); + if cx.tcx.hir().attrs(impl_item_hir).is_empty(); then { if adt_def.is_struct() { diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 1971cab64ef38..7469f813ef87f 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; +use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; +use rustc_hir::{Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{ExpnId, Span}; @@ -111,6 +112,10 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { self.check(cx, expr.span); + // `$t + $t` can have the context of $t, check also the span of the binary operator + if let ExprKind::Binary(op, ..) = expr.kind { + self.check(cx, op.span); + } } fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { @@ -147,4 +152,8 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { self.check(cx, path.span); } + + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { + self.check(cx, attr.span); + } } diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index d8f2fbcea0027..4498e9ccdec25 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -472,7 +472,7 @@ struct DocHeaders { fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { /// We don't want the parser to choke on intra doc links. Since we don't - /// actually care about rendering them, just pretend that all broken links are + /// actually care about rendering them, just pretend that all broken links /// point to a fake address. #[expect(clippy::unnecessary_wraps)] // we're following a type signature fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> { diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index ab6ad3f3b3ab0..e2d19e2455700 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -6,7 +6,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && then_expr.span.ctxt() == ctxt && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) - && !stmts_contains_early_return(then_block.stmts) + && !contains_return(then_block.stmts) { let mut app = Applicability::Unspecified; let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string(); @@ -116,17 +116,3 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { extract_msrv_attr!(LateContext); } - -fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool { - stmts.iter().any(|stmt| { - let Stmt { - kind: StmtKind::Semi(e), - .. - } = stmt - else { - return false; - }; - - contains_return(e) - }) -} diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs index c635120b88245..d8ead1c9d9f68 100644 --- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use hir::PatKind; +use hir::{Node, PatKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -37,6 +37,17 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + match cx.tcx.hir().get_parent(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.hir().get_parent(param.hir_id), Node::Item(_)) => { + // Ignore function parameters + return; + }, + Node::Local(local) if local.ty.is_some() => { + // Ignore let bindings with explicit type + return; + }, + _ => {}, + } if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index c6d1acad3a0f1..ec9044bba5cc3 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; -use rustc_hir::def_id::LocalDefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Body, FnDecl, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind, ItemKind, TraitBoundModifier, TraitItem, @@ -9,7 +9,7 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ClauseKind, TyCtxt}; +use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -45,52 +45,169 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub IMPLIED_BOUNDS_IN_IMPLS, - complexity, + nursery, "specifying bounds that are implied by other bounds in `impl Trait` type" } declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]); -/// This function tries to, for all type parameters in a supertype predicate `GenericTrait`, -/// check if the substituted type in the implied-by bound matches with what's subtituted in the -/// implied type. +#[allow(clippy::too_many_arguments)] +fn emit_lint( + cx: &LateContext<'_>, + poly_trait: &rustc_hir::PolyTraitRef<'_>, + opaque_ty: &rustc_hir::OpaqueTy<'_>, + index: usize, + // The bindings that were implied + implied_bindings: &[rustc_hir::TypeBinding<'_>], + // The original bindings that `implied_bindings` are implied from + implied_by_bindings: &[rustc_hir::TypeBinding<'_>], + implied_by_args: &[GenericArg<'_>], + implied_by_span: Span, +) { + let implied_by = snippet(cx, implied_by_span, ".."); + + span_lint_and_then( + cx, + IMPLIED_BOUNDS_IN_IMPLS, + poly_trait.span, + &format!("this bound is already specified as the supertrait of `{implied_by}`"), + |diag| { + // If we suggest removing a bound, we may also need to extend the span + // to include the `+` token that is ahead or behind, + // so we don't end up with something like `impl + B` or `impl A + ` + + let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { + poly_trait.span.to(next_bound.span().shrink_to_lo()) + } else if index > 0 + && let Some(prev_bound) = opaque_ty.bounds.get(index - 1) + { + prev_bound.span().shrink_to_hi().to(poly_trait.span.shrink_to_hi()) + } else { + poly_trait.span + }; + + let mut sugg = vec![(implied_span_extended, String::new())]; + + // We also might need to include associated type binding that were specified in the implied bound, + // but omitted in the implied-by bound: + // `fn f() -> impl Deref + DerefMut` + // If we're going to suggest removing `Deref<..>`, we'll need to put `` on `DerefMut` + let omitted_assoc_tys: Vec<_> = implied_bindings + .iter() + .filter(|binding| !implied_by_bindings.iter().any(|b| b.ident == binding.ident)) + .collect(); + + if !omitted_assoc_tys.is_empty() { + // `<>` needs to be added if there aren't yet any generic arguments or bindings + let needs_angle_brackets = implied_by_args.is_empty() && implied_by_bindings.is_empty(); + let insert_span = match (implied_by_args, implied_by_bindings) { + ([.., arg], [.., binding]) => arg.span().max(binding.span).shrink_to_hi(), + ([.., arg], []) => arg.span().shrink_to_hi(), + ([], [.., binding]) => binding.span.shrink_to_hi(), + ([], []) => implied_by_span.shrink_to_hi(), + }; + + let mut associated_tys_sugg = if needs_angle_brackets { + "<".to_owned() + } else { + // If angle brackets aren't needed (i.e., there are already generic arguments or bindings), + // we need to add a comma: + // `impl A` + // ^ if we insert `Assoc=i32` without a comma here, that'd be invalid syntax: + // `impl A` + ", ".to_owned() + }; + + for (index, binding) in omitted_assoc_tys.into_iter().enumerate() { + if index > 0 { + associated_tys_sugg += ", "; + } + associated_tys_sugg += &snippet(cx, binding.span, ".."); + } + if needs_angle_brackets { + associated_tys_sugg += ">"; + } + sugg.push((insert_span, associated_tys_sugg)); + } + + diag.multipart_suggestion_with_style( + "try removing this bound", + sugg, + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, + ); +} + +/// Tries to "resolve" a type. +/// The index passed to this function must start with `Self=0`, i.e. it must be a valid +/// type parameter index. +/// If the index is out of bounds, it means that the generic parameter has a default type. +fn try_resolve_type<'tcx>( + tcx: TyCtxt<'tcx>, + args: &'tcx [GenericArg<'tcx>], + generics: &'tcx Generics, + index: usize, +) -> Option> { + match args.get(index - 1) { + Some(GenericArg::Type(ty)) => Some(hir_ty_to_ty(tcx, ty)), + Some(_) => None, + None => Some(tcx.type_of(generics.params[index].def_id).skip_binder()), + } +} + +/// This function tries to, for all generic type parameters in a supertrait predicate `trait ...: +/// GenericTrait`, check if the substituted type in the implied-by bound matches with what's +/// subtituted in the implied bound. /// /// Consider this example. /// ```rust,ignore /// trait GenericTrait {} /// trait GenericSubTrait: GenericTrait {} -/// ^ trait_predicate_args: [Self#0, U#2] +/// ^^^^^^^^^^^^^^^ trait_predicate_args: [Self#0, U#2] +/// (the Self#0 is implicit: `>`) /// impl GenericTrait for () {} /// impl GenericSubTrait<(), i32, ()> for () {} -/// impl GenericSubTrait<(), [u8; 8], ()> for () {} +/// impl GenericSubTrait<(), i64, ()> for () {} /// -/// fn f() -> impl GenericTrait + GenericSubTrait<(), [u8; 8], ()> { -/// ^^^ implied_args ^^^^^^^^^^^^^^^ implied_by_args -/// (we are interested in `[u8; 8]` specifically, as that -/// is what `U` in `GenericTrait` is substituted with) -/// () +/// fn f() -> impl GenericTrait + GenericSubTrait<(), i64, ()> { +/// ^^^ implied_args ^^^^^^^^^^^ implied_by_args +/// (we are interested in `i64` specifically, as that +/// is what `U` in `GenericTrait` is substituted with) /// } /// ``` -/// Here i32 != [u8; 8], so this will return false. -fn is_same_generics( - tcx: TyCtxt<'_>, - trait_predicate_args: &[ty::GenericArg<'_>], - implied_by_args: &[GenericArg<'_>], - implied_args: &[GenericArg<'_>], +/// Here i32 != i64, so this will return false. +fn is_same_generics<'tcx>( + tcx: TyCtxt<'tcx>, + trait_predicate_args: &'tcx [ty::GenericArg<'tcx>], + implied_by_args: &'tcx [GenericArg<'tcx>], + implied_args: &'tcx [GenericArg<'tcx>], + implied_by_def_id: DefId, + implied_def_id: DefId, ) -> bool { + // Get the generics of the two traits to be able to get default generic parameter. + let implied_by_generics = tcx.generics_of(implied_by_def_id); + let implied_generics = tcx.generics_of(implied_def_id); + trait_predicate_args .iter() .enumerate() .skip(1) // skip `Self` implicit arg .all(|(arg_index, arg)| { - if let Some(ty) = arg.as_type() - && let &ty::Param(ty::ParamTy{ index, .. }) = ty.kind() - // Since `trait_predicate_args` and type params in traits start with `Self=0` - // and generic argument lists `GenericTrait` don't have `Self`, - // we need to subtract 1 from the index. - && let GenericArg::Type(ty_a) = implied_by_args[index as usize - 1] - && let GenericArg::Type(ty_b) = implied_args[arg_index - 1] - { - hir_ty_to_ty(tcx, ty_a) == hir_ty_to_ty(tcx, ty_b) + if let Some(ty) = arg.as_type() { + if let &ty::Param(ty::ParamTy { index, .. }) = ty.kind() + // `index == 0` means that it's referring to `Self`, + // in which case we don't try to substitute it + && index != 0 + && let Some(ty_a) = try_resolve_type(tcx, implied_by_args, implied_by_generics, index as usize) + && let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) + { + ty_a == ty_b + } else if let Some(ty_b) = try_resolve_type(tcx, implied_args, implied_generics, arg_index) { + ty == ty_b + } else { + false + } } else { false } @@ -121,7 +238,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates && !predicates.is_empty() // If the trait has no supertrait, there is nothing to add. { - Some((bound.span(), path.args.map_or([].as_slice(), |a| a.args), predicates)) + Some((bound.span(), path, predicates, trait_def_id)) } else { None } @@ -134,43 +251,42 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) { if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound && let [.., path] = poly_trait.trait_ref.path.segments && let implied_args = path.args.map_or([].as_slice(), |a| a.args) + && let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings) && let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id() - && let Some(implied_by_span) = implied_bounds.iter().find_map(|&(span, implied_by_args, preds)| { - preds.iter().find_map(|(clause, _)| { - if let ClauseKind::Trait(tr) = clause.kind().skip_binder() - && tr.def_id() == def_id - && is_same_generics(cx.tcx, tr.trait_ref.args, implied_by_args, implied_args) - { - Some(span) - } else { - None - } + && let Some((implied_by_span, implied_by_args, implied_by_bindings)) = implied_bounds + .iter() + .find_map(|&(span, implied_by_path, preds, implied_by_def_id)| { + let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args); + let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings); + + preds.iter().find_map(|(clause, _)| { + if let ClauseKind::Trait(tr) = clause.kind().skip_binder() + && tr.def_id() == def_id + && is_same_generics( + cx.tcx, + tr.trait_ref.args, + implied_by_args, + implied_args, + implied_by_def_id, + def_id, + ) + { + Some((span, implied_by_args, implied_by_bindings)) + } else { + None + } + }) }) - }) { - let implied_by = snippet(cx, implied_by_span, ".."); - span_lint_and_then( - cx, IMPLIED_BOUNDS_IN_IMPLS, - poly_trait.span, - &format!("this bound is already specified as the supertrait of `{implied_by}`"), - |diag| { - // If we suggest removing a bound, we may also need extend the span - // to include the `+` token, so we don't end up with something like `impl + B` - - let implied_span_extended = if let Some(next_bound) = opaque_ty.bounds.get(index + 1) { - poly_trait.span.to(next_bound.span().shrink_to_lo()) - } else { - poly_trait.span - }; - - diag.span_suggestion_with_style( - implied_span_extended, - "try removing this bound", - "", - Applicability::MachineApplicable, - SuggestionStyle::ShowAlways - ); - } + emit_lint( + cx, + poly_trait, + opaque_ty, + index, + implied_bindings, + implied_by_bindings, + implied_by_args, + implied_by_span ); } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 20fdf26dc521b..f52614b62088f 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -156,7 +156,6 @@ mod implicit_saturating_add; mod implicit_saturating_sub; mod implied_bounds_in_impls; mod inconsistent_struct_constructor; -mod incorrect_impls; mod index_refutable_slice; mod indexing_slicing; mod infinite_iter; @@ -212,6 +211,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; @@ -245,6 +245,7 @@ mod neg_multiply; mod new_without_default; mod no_effect; mod no_mangle_with_rust_abi; +mod non_canonical_impls; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; @@ -697,7 +698,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(|_| Box::::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::::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); @@ -1070,7 +1072,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: avoid_breaking_exported_api, )) }); - store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls)); + store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); store.register_late_pass(move |_| { Box::new(single_call_fn::SingleCallFn { avoid_breaking_exported_api, @@ -1101,6 +1103,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::::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/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 7b8c88235a97d..6ab256ef0010b 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/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/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index ffd29ab763027..1fb16adad7a19 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/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/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index cc19ac55e5e44..3d8a4cd948afc 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -1,13 +1,13 @@ 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::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::iter::{once, Iterator}; pub(super) fn check<'tcx>( @@ -18,7 +18,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 +39,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) -> 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, + local_labels: &mut Vec<(HirId, bool)>, main_loop_id: HirId, ) -> NeverLoopResult { let iter = block @@ -107,15 +116,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 +146,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, + 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 +219,78 @@ 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, + }; + let result = combine_seq(result, || { + if cx.typeck_results().expr_ty(expr).is_never() { + NeverLoopResult::Diverging + } else { + NeverLoopResult::Normal + } + }); + if let NeverLoopResult::Diverging = result && + let Some(macro_call) = root_macro_call_first_node(cx, expr) && + let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id) + { + // We return MayContinueMainLoop here because we treat `todo!()` + // as potentially containing any code, including a continue of the main loop. + // This effectively silences the lint whenever a loop contains this macro anywhere. + NeverLoopResult::MayContinueMainLoop + } else { + result } } fn never_loop_expr_all<'tcx, T: Iterator>>( cx: &LateContext<'tcx>, - es: &mut T, - ignore_ids: &mut Vec, - 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>>( - cx: &LateContext<'tcx>, - e: &mut T, - ignore_ids: &mut Vec, + 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/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index 39d8b20d38d02..90557b5556085 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -6,6 +7,7 @@ use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{Span, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -49,6 +51,29 @@ fn expr_as_i128(expr: &Expr<'_>) -> Option { } } +#[derive(Copy, Clone)] +struct Num { + val: i128, + span: Span, +} + +impl Num { + fn new(expr: &Expr<'_>) -> Option { + Some(Self { + val: expr_as_i128(expr)?, + span: expr.span, + }) + } + + fn dummy(val: i128) -> Self { + Self { val, span: DUMMY_SP } + } + + fn min(self, other: Self) -> Self { + if self.val < other.val { self } else { other } + } +} + impl LateLintPass<'_> for ManualRangePatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) { if in_external_macro(cx.sess(), pat.span) { @@ -56,71 +81,83 @@ impl LateLintPass<'_> for ManualRangePatterns { } // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives + // or at least one range if let PatKind::Or(pats) = pat.kind - && pats.len() >= 3 + && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))) { - let mut min = i128::MAX; - let mut max = i128::MIN; + let mut min = Num::dummy(i128::MAX); + let mut max = Num::dummy(i128::MIN); + let mut range_kind = RangeEnd::Included; let mut numbers_found = FxHashSet::default(); let mut ranges_found = Vec::new(); for pat in pats { if let PatKind::Lit(lit) = pat.kind - && let Some(num) = expr_as_i128(lit) + && let Some(num) = Num::new(lit) { - numbers_found.insert(num); + numbers_found.insert(num.val); min = min.min(num); - max = max.max(num); + if num.val >= max.val { + max = num; + range_kind = RangeEnd::Included; + } } else if let PatKind::Range(Some(left), Some(right), end) = pat.kind - && let Some(left) = expr_as_i128(left) - && let Some(right) = expr_as_i128(right) - && right >= left + && let Some(left) = Num::new(left) + && let Some(mut right) = Num::new(right) { + if let RangeEnd::Excluded = end { + right.val -= 1; + } + min = min.min(left); - max = max.max(right); - ranges_found.push(left..=match end { - RangeEnd::Included => right, - RangeEnd::Excluded => right - 1, - }); + if right.val > max.val { + max = right; + range_kind = end; + } + ranges_found.push(left.val..=right.val); } else { return; } } - let contains_whole_range = 'contains: { - let mut num = min; - while num <= max { - if numbers_found.contains(&num) { - num += 1; - } - // Given a list of (potentially overlapping) ranges like: - // 1..=5, 3..=7, 6..=10 - // We want to find the range with the highest end that still contains the current number - else if let Some(range) = ranges_found - .iter() - .filter(|range| range.contains(&num)) - .max_by_key(|range| range.end()) - { - num = range.end() + 1; - } else { - break 'contains false; - } + let mut num = min.val; + while num <= max.val { + if numbers_found.contains(&num) { + num += 1; + } + // Given a list of (potentially overlapping) ranges like: + // 1..=5, 3..=7, 6..=10 + // We want to find the range with the highest end that still contains the current number + else if let Some(range) = ranges_found + .iter() + .filter(|range| range.contains(&num)) + .max_by_key(|range| range.end()) + { + num = range.end() + 1; + } else { + return; } - break 'contains true; - }; - - if contains_whole_range { - span_lint_and_sugg( - cx, - MANUAL_RANGE_PATTERNS, - pat.span, - "this OR pattern can be rewritten using a range", - "try", - format!("{min}..={max}"), - Applicability::MachineApplicable, - ); } + + span_lint_and_then( + cx, + MANUAL_RANGE_PATTERNS, + pat.span, + "this OR pattern can be rewritten using a range", + |diag| { + if let Some(min) = snippet_opt(cx, min.span) + && let Some(max) = snippet_opt(cx, max.span) + { + diag.span_suggestion( + pat.span, + "try", + format!("{min}{range_kind}{max}"), + Applicability::MachineApplicable, + ); + } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs new file mode 100644 index 0000000000000..79c6d63254b28 --- /dev/null +++ b/src/tools/clippy/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 { + 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 { + 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, 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/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index ee405a3e30c3c..a49dd98db8713 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -21,7 +21,7 @@ pub(super) enum Op<'a> { RmCloned, // rm `.cloned()` - // e.g. `map` `for_each` + // e.g. `map` `for_each` `all` `any` NeedlessMove(&'a str, &'a Expr<'a>), // later `.cloned()` diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 5075137caa0cb..81223fa8d954f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/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. @@ -3873,6 +3899,12 @@ impl Methods { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); }, + ("all", [arg]) => { + if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { + iter_overeager_cloned::check(cx, expr, recv, recv2, + iter_overeager_cloned::Op::NeedlessMove(name, arg), false); + } + } ("and_then", [arg]) => { let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); @@ -3880,12 +3912,16 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, - ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind - && let body = cx.tcx.hir().body(arg.body) - && let [param] = body.params - && let Some(("chars", recv, _, _, _)) = method_call(recv) => - { - string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + ("any", [arg]) => { + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false), + Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params => { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + } + _ => {} + } } ("arg", [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); @@ -4136,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, @@ -4163,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/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs new file mode 100644 index 0000000000000..08fec2b8ec820 --- /dev/null +++ b/src/tools/clippy/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(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, + comparison: LengthComparison, + }, + /// Indexing without an `assert!` + IndexWithoutAssert { + highest_index: usize, + indexes: Vec, + 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 { + 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>>) { + 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>>) { + 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>>) { + 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/src/tools/clippy/clippy_lints/src/incorrect_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs similarity index 88% rename from src/tools/clippy/clippy_lints/src/incorrect_impls.rs rename to src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index 3c59b839a39c6..4b24f059afdf5 100644 --- a/src/tools/clippy/clippy_lints/src/incorrect_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -13,11 +13,12 @@ use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does - /// Checks for manual implementations of `Clone` when `Copy` is already implemented. + /// Checks for non-canonical implementations of `Clone` when `Copy` is already implemented. /// /// ### Why is this bad? - /// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing - /// `self` in `Clone`'s implementation. Anything else is incorrect. + /// If both `Clone` and `Copy` are implemented, they must agree. This can done by dereferencing + /// `self` in `Clone`'s implementation, which will avoid any possibility of the implementations + /// becoming out of sync. /// /// ### Example /// ```rust,ignore @@ -46,14 +47,13 @@ declare_clippy_lint! { /// impl Copy for A {} /// ``` #[clippy::version = "1.72.0"] - pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE, - correctness, - "manual implementation of `Clone` on a `Copy` type" + pub NON_CANONICAL_CLONE_IMPL, + suspicious, + "non-canonical implementation of `Clone` on a `Copy` type" } declare_clippy_lint! { /// ### What it does - /// Checks for manual implementations of both `PartialOrd` and `Ord` when only `Ord` is - /// necessary. + /// Checks for non-canonical implementations of `PartialOrd` when `Ord` is already implemented. /// /// ### Why is this bad? /// If both `PartialOrd` and `Ord` are implemented, they must agree. This is commonly done by @@ -61,11 +61,8 @@ declare_clippy_lint! { /// introduce an error upon refactoring. /// /// ### Known issues - /// Code that calls the `.into()` method instead will be flagged as incorrect, despite `.into()` - /// wrapping it in `Some`. - /// - /// ### Limitations - /// Will not lint if `Self` and `Rhs` do not have the same type. + /// Code that calls the `.into()` method instead will be flagged, despite `.into()` wrapping it + /// in `Some`. /// /// ### Example /// ```rust @@ -107,13 +104,13 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.72.0"] - pub INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, - correctness, - "manual implementation of `PartialOrd` when `Ord` is already implemented" + pub NON_CANONICAL_PARTIAL_ORD_IMPL, + suspicious, + "non-canonical implementation of `PartialOrd` on an `Ord` type" } -declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE, INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE]); +declare_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); -impl LateLintPass<'_> for IncorrectImpls { +impl LateLintPass<'_> for NonCanonicalImpls { #[expect(clippy::too_many_lines)] fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { let Some(Node::Item(item)) = get_parent_node(cx.tcx, impl_item.hir_id()) else { @@ -154,9 +151,9 @@ impl LateLintPass<'_> for IncorrectImpls { {} else { span_lint_and_sugg( cx, - INCORRECT_CLONE_IMPL_ON_COPY_TYPE, + NON_CANONICAL_CLONE_IMPL, block.span, - "incorrect implementation of `clone` on a `Copy` type", + "non-canonical implementation of `clone` on a `Copy` type", "change this to", "{ *self }".to_owned(), Applicability::MaybeIncorrect, @@ -169,9 +166,9 @@ impl LateLintPass<'_> for IncorrectImpls { if impl_item.ident.name == sym::clone_from { span_lint_and_sugg( cx, - INCORRECT_CLONE_IMPL_ON_COPY_TYPE, + NON_CANONICAL_CLONE_IMPL, impl_item.span, - "incorrect implementation of `clone_from` on a `Copy` type", + "unnecessary implementation of `clone_from` on a `Copy` type", "remove it", String::new(), Applicability::MaybeIncorrect, @@ -222,9 +219,9 @@ impl LateLintPass<'_> for IncorrectImpls { span_lint_and_then( cx, - INCORRECT_PARTIAL_ORD_IMPL_ON_ORD_TYPE, + NON_CANONICAL_PARTIAL_ORD_IMPL, item.span, - "incorrect implementation of `partial_cmp` on an `Ord` type", + "non-canonical implementation of `partial_cmp` on an `Ord` type", |diag| { let [_, other] = body.params else { return; diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index a687c7d29b56e..8072aded8513b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -14,7 +14,12 @@ use {rustc_ast as ast, rustc_hir as hir}; const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; -const INTEGER_METHODS: &[Symbol] = &[sym::saturating_div, sym::wrapping_div, sym::wrapping_rem, sym::wrapping_rem_euclid]; +const INTEGER_METHODS: &[Symbol] = &[ + sym::saturating_div, + sym::wrapping_div, + sym::wrapping_rem, + sym::wrapping_rem_euclid, +]; #[derive(Debug)] pub struct ArithmeticSideEffects { @@ -93,7 +98,14 @@ impl ArithmeticSideEffects { let is_non_zero_u = |symbol: Option| { matches!( symbol, - Some(sym::NonZeroU128 | sym::NonZeroU16 | sym::NonZeroU32 | sym::NonZeroU64 | sym::NonZeroU8 | sym::NonZeroUsize) + Some( + sym::NonZeroU128 + | sym::NonZeroU16 + | sym::NonZeroU32 + | sym::NonZeroU64 + | sym::NonZeroU8 + | sym::NonZeroUsize + ) ) }; let is_sat_or_wrap = |ty: Ty<'_>| { diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index f3e0c58a78711..bce6bdcaf61cd 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -17,7 +17,7 @@ pub(crate) fn check<'tcx>( left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) { + if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { Some((c, s)) if !is_allowed(&c) => s.is_local(), Some(_) => return, diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index ccabb577cb722..e8018462d75f6 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/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,33 @@ 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); + + if end.is_empty() { + diag.span_suggestion( + start, + "use a string literal instead", + format!("\"{}\"", str), + Applicability::MachineApplicable, + ); + } else { + diag.multipart_suggestion( + "try", + vec![(start, String::new()), (end, String::new())], + Applicability::MachineApplicable, + ); + } + }, ); return; @@ -96,13 +116,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 +139,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/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index fc1fabcc0ae59..613f1ecc6fbee 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -15,6 +15,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), + ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), ("clippy::new_without_default_derive", "clippy::new_without_default"), @@ -38,12 +40,12 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::drop_bounds", "drop_bounds"), ("clippy::drop_copy", "dropping_copy_types"), ("clippy::drop_ref", "dropping_references"), + ("clippy::fn_null_check", "useless_ptr_null_checks"), ("clippy::for_loop_over_option", "for_loops_over_fallibles"), ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::forget_copy", "forgetting_copy_types"), ("clippy::forget_ref", "forgetting_references"), - ("clippy::fn_null_check", "useless_ptr_null_checks"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index c9ab622ad25d3..9db18c2976c22 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, @@ -148,6 +149,15 @@ impl SlowVectorInit { /// - `Some(InitializedSize::Uninitialized)` for `Vec::new()` /// - `None` for other, unrelated kinds of expressions fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option> { + // Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro. + // This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an + // empty vec + if expr.span.from_expansion() + && root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro) + { + return None; + } + if let ExprKind::Call(func, [len_expr]) = expr.kind && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY) { @@ -205,7 +215,7 @@ impl SlowVectorInit { span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diag.span_suggestion( - vec_alloc.allocation_expr.span, + vec_alloc.allocation_expr.span.source_callsite(), "consider replacing this with", format!("vec![0; {len_expr}]"), Applicability::Unspecified, diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index f239165276ffe..5f54a10d1c41d 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, Path, PathSegment}; @@ -99,17 +100,17 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id) { - let (lint, msg, help) = match first_segment.ident.name { + let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => ( STD_INSTEAD_OF_CORE, - "used import from `std` instead of `core`", - "consider importing the item from `core`", + "std", + "core", ), sym::alloc => ( STD_INSTEAD_OF_ALLOC, - "used import from `std` instead of `alloc`", - "consider importing the item from `alloc`", + "std", + "alloc", ), _ => { self.prev_span = path.span; @@ -120,8 +121,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { ( ALLOC_INSTEAD_OF_CORE, - "used import from `alloc` instead of `core`", - "consider importing the item from `core`", + "alloc", + "core", ) } else { self.prev_span = path.span; @@ -131,7 +132,14 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { _ => return, }; if path.span != self.prev_span { - span_lint_and_help(cx, lint, path.span, msg, None, help); + span_lint_and_sugg( + cx, + lint, + first_segment.ident.span, + &format!("used import from `{used_mod}` instead of `{replace_with}`"), + &format!("consider importing the item from `{replace_with}`"), + replace_with.to_string(), + Applicability::MachineApplicable); self.prev_span = path.span; } } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index da8d8ed4c0f2c..6193fdeb433a2 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -341,44 +341,21 @@ fn block_parents_have_safety_comment( id: hir::HirId, ) -> bool { if let Some(node) = get_parent_node(cx.tcx, id) { - return match node { - Node::Expr(expr) => { - if let Some( - Node::Local(hir::Local { span, .. }) - | Node::Item(hir::Item { - kind: hir::ItemKind::Const(..) | ItemKind::Static(..), - span, - .. - }), - ) = get_parent_node(cx.tcx, expr.hir_id) - { - let hir_id = match get_parent_node(cx.tcx, expr.hir_id) { - Some(Node::Local(hir::Local { hir_id, .. })) => *hir_id, - Some(Node::Item(hir::Item { owner_id, .. })) => { - cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id) - }, - _ => unreachable!(), - }; - - // if unsafe block is part of a let/const/static statement, - // and accept_comment_above_statement is set to true - // we accept the safety comment in the line the precedes this statement. - accept_comment_above_statement - && span_with_attrs_in_body_has_safety_comment( - cx, - *span, - hir_id, - accept_comment_above_attributes, - ) - } else { - !is_branchy(expr) - && span_with_attrs_in_body_has_safety_comment( - cx, - expr.span, - expr.hir_id, - accept_comment_above_attributes, - ) - } + let (span, hir_id) = match node { + Node::Expr(expr) => match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Local(hir::Local { span, hir_id, .. })) => (*span, *hir_id), + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + span, + owner_id, + .. + })) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)), + _ => { + if is_branchy(expr) { + return false; + } + (expr.span, expr.hir_id) + }, }, Node::Stmt(hir::Stmt { kind: @@ -387,28 +364,27 @@ fn block_parents_have_safety_comment( | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), .. }) - | Node::Local(hir::Local { span, hir_id, .. }) => { - span_with_attrs_in_body_has_safety_comment(cx, *span, *hir_id, accept_comment_above_attributes) - }, + | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), span, owner_id, .. - }) => span_with_attrs_in_body_has_safety_comment( - cx, - *span, - cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id), - accept_comment_above_attributes, - ), - _ => false, + }) => (*span, cx.tcx.hir().local_def_id_to_hir_id(owner_id.def_id)), + _ => return false, }; + // if unsafe block is part of a let/const/static statement, + // and accept_comment_above_statement is set to true + // we accept the safety comment in the line the precedes this statement. + accept_comment_above_statement + && span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attributes) + } else { + false } - false } /// Extends `span` to also include its attributes, then checks if that span has a safety comment. -fn span_with_attrs_in_body_has_safety_comment( +fn span_with_attrs_has_safety_comment( cx: &LateContext<'_>, span: Span, hir_id: HirId, @@ -420,7 +396,7 @@ fn span_with_attrs_in_body_has_safety_comment( span }; - span_in_body_has_safety_comment(cx, span) + span_has_safety_comment(cx, span) } /// Checks if an expression is "branchy", e.g. loop, match/if/etc. @@ -444,7 +420,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { matches!( span_from_macro_expansion_has_safety_comment(cx, span), HasSafetyComment::Yes(_) - ) || span_in_body_has_safety_comment(cx, span) + ) || span_has_safety_comment(cx, span) } fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Span { @@ -633,29 +609,36 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option { let body = cx.enclosing_body?; let map = cx.tcx.hir(); let mut span = map.body(body).value.span; + let mut maybe_global_var = false; for (_, node) in map.parent_iter(body.hir_id) { match node { Node::Expr(e) => span = e.span, - Node::Block(_) - | Node::Arm(_) - | Node::Stmt(_) - | Node::Local(_) - | Node::Item(hir::Item { + Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (), + Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), .. - }) => (), + }) => maybe_global_var = true, + Node::Item(hir::Item { + kind: hir::ItemKind::Mod(_), + span: item_span, + .. + }) => { + span = *item_span; + break; + }, + Node::Crate(mod_) if maybe_global_var => { + span = mod_.spans.inner_span; + }, _ => break, } } Some(span) } -fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); - if ctxt == SyntaxContext::root() - && let Some(search_span) = get_body_search_span(cx) - { + if ctxt.is_root() && let Some(search_span) = get_body_search_span(cx) { if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root()) && let Ok(body_line) = source_map.lookup_line(body_span.lo()) diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index dd829ded0d0d6..de4b8738e35b9 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/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/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index c99b0290c0c0e..9a0d83d83f1f7 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -1,15 +1,18 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::is_potentially_mutated; +use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -192,6 +195,55 @@ fn collect_unwrap_info<'tcx>( Vec::new() } +/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated, +/// *except* for if `Option::as_mut` is called. +/// The reason for why we allow that one specifically is that `.as_mut()` cannot change +/// the option to `None`, and that is important because this lint relies on the fact that +/// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if +/// the option is changed to None between `is_some` and `unwrap`. +/// (And also `.as_mut()` is a somewhat common method that is still worth linting on.) +struct MutationVisitor<'tcx> { + is_mutated: bool, + local_id: HirId, + tcx: TyCtxt<'tcx>, +} + +/// Checks if the parent of the expression pointed at by the given `HirId` is a call to +/// `Option::as_mut`. +/// +/// Used by the mutation visitor to specifically allow `.as_mut()` calls. +/// In particular, the `HirId` that the visitor receives is the id of the local expression +/// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent +/// expression: that will be where the actual method call is. +fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { + if let Node::Expr(mutating_expr) = tcx.hir().get_parent(expr_id) + && let ExprKind::MethodCall(path, ..) = mutating_expr.kind + { + path.ident.name.as_str() == "as_mut" + } else { + false + } +} + +impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> { + fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { + if let ty::BorrowKind::MutBorrow = bk + && is_potentially_local_place(self.local_id, &cat.place) + && !is_option_as_mut_use(self.tcx, diag_expr_id) + { + self.is_mutated = true; + } + } + + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) { + self.is_mutated = true; + } + + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} +} + impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { fn visit_branch( &mut self, @@ -202,10 +254,26 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { ) { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { - if is_potentially_mutated(unwrap_info.local_id, cond, self.cx) - || is_potentially_mutated(unwrap_info.local_id, branch, self.cx) - { - // if the variable is mutated, we don't know whether it can be unwrapped: + let mut delegate = MutationVisitor { + tcx: self.cx.tcx, + is_mutated: false, + local_id: unwrap_info.local_id, + }; + + let infcx = self.cx.tcx.infer_ctxt().build(); + let mut vis = ExprUseVisitor::new( + &mut delegate, + &infcx, + cond.hir_id.owner.def_id, + self.cx.param_env, + self.cx.typeck_results(), + ); + vis.walk_expr(cond); + vis.walk_expr(branch); + + if delegate.is_mutated { + // if the variable is mutated, we don't know whether it can be unwrapped. + // it might have been changed to `None` in between `is_some` + `unwrap`. continue; } self.unwrappables.push(unwrap_info); @@ -215,6 +283,27 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { } } +enum AsRefKind { + AsRef, + AsMut, +} + +/// Checks if the expression is a method call to `as_{ref,mut}` and returns the receiver of it. +/// If it isn't, the expression itself is returned. +fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option) { + if let ExprKind::MethodCall(path, recv, ..) = expr.kind { + if path.ident.name == sym::as_ref { + (recv, Some(AsRefKind::AsRef)) + } else if path.ident.name.as_str() == "as_mut" { + (recv, Some(AsRefKind::AsMut)) + } else { + (expr, None) + } + } else { + (expr, None) + } +} + impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; @@ -233,6 +322,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { // find `unwrap[_err]()` calls: if_chain! { if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; + let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg); if let Some(id) = path_to_local(self_arg); if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); @@ -268,7 +358,12 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), "try", format!( - "if let {suggested_pattern} = {unwrappable_variable_name}", + "if let {suggested_pattern} = {borrow_prefix}{unwrappable_variable_name}", + borrow_prefix = match as_ref_kind { + Some(AsRefKind::AsRef) => "&", + Some(AsRefKind::AsMut) => "&mut ", + None => "", + }, ), // We don't track how the unwrapped value is used inside the // block or suggest deleting the unwrap, so we can't offer a diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 58ae0656db73e..2688947552253 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/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 = 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/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6c4cec5952471..4ef3ec19647e9 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -5,6 +5,7 @@ #![feature(lint_reasons)] #![feature(never_type)] #![feature(rustc_private)] +#![feature(assert_matches)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -110,6 +111,7 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span}; use rustc_target::abi::Integer; +use visitors::Visitable; use crate::consts::{constant, miri_to_const, Constant}; use crate::higher::Range; @@ -1286,7 +1288,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext< } /// Returns `true` if `expr` contains a return expression -pub fn contains_return(expr: &hir::Expr<'_>) -> bool { +pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool { for_each_expr(expr, |e| { if matches!(e.kind, hir::ExprKind::Ret(..)) { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index a567c5cbdc92a..2fb24b5c7ed9d 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/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", "", "then"]; +pub const ARRAY_INTO_ITER: [&str; 4] = ["core", "array", "iter", "IntoIter"]; diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 600cd084c19f5..31cb421095ede 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/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/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index ee5a49a207331..ae8ee371ffae3 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/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/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index a05f682aa8cda..f0b4ede35fbc5 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -27,6 +27,7 @@ use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use std::assert_matches::debug_assert_matches; use std::iter; use crate::{match_def_path, path_res, paths}; @@ -259,7 +260,11 @@ pub fn implements_trait_with_env_from_iter<'tcx>( })), ); - debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait); + debug_assert_matches!( + tcx.def_kind(trait_id), + DefKind::Trait | DefKind::TraitAlias, + "`DefId` must belong to a trait or trait alias" + ); #[cfg(debug_assertions)] assert_generic_args_match(tcx, trait_id, trait_ref.args); diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 06fd952904405..dc7756533acb0 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/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/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 39ef76348d751..ec131c7f6a318 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -4,7 +4,7 @@ use core::ops::ControlFlow; use hir::def::Res; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet}; -use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -37,6 +37,17 @@ pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: & mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable)) } +pub fn is_potentially_local_place(local_id: HirId, place: &Place<'_>) -> bool { + match place.base { + PlaceBase::Local(id) => id == local_id, + PlaceBase::Upvar(_) => { + // Conservatively assume yes. + true + }, + _ => false, + } +} + struct MutVarsDelegate { used_mutably: HirIdSet, skip: bool, diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 19c09b58b9862..9f5116eb73bb2 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-08-24" +channel = "nightly-2023-09-07" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index c1c3d4a868f92..9fcc269dbf8c2 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -4,7 +4,7 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] -use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling, RustfixMode}; +use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; use std::collections::BTreeMap; use std::env::{self, set_var, var_os}; @@ -114,36 +114,26 @@ fn canonicalize(path: impl AsRef) -> 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: RustfixMode::Everything, + 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(); @@ -186,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 { @@ -211,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 { @@ -245,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,10 +289,16 @@ fn run_ui_cargo() { let quiet = args.quiet; + let ignored_32bit = |path: &Path| { + // FIXME: for some reason the modules are linted in a different order for this test + cfg!(target_pointer_width = "32") && path.ends_with("tests/ui-cargo/module_style/fail_mod/Cargo.toml") + }; + 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) && !ignored_32bit(path) + }, |config, path, _file_contents| { config.out_dir = canonicalize( std::env::current_dir() diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr index e161507b53395..4d97d54963d3d 100644 --- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail` is missing `either package.license or package.license_file` metadata diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr index dbf494cc342d3..9eb884f08900b 100644 --- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail_publish` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail_publish` is missing `either package.license or package.license_file` metadata diff --git a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr index ae5967406f62f..f9685a784a33e 100644 --- a/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/cargo_common_metadata/fail_publish_true/Cargo.stderr @@ -1,6 +1,7 @@ error: package `cargo_common_metadata_fail_publish_true` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cargo_common_metadata)]` error: package `cargo_common_metadata_fail_publish_true` is missing `either package.license or package.license_file` metadata diff --git a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr index fde3a1e659915..912ea9bb4d111 100644 --- a/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/Cargo.stderr @@ -9,6 +9,7 @@ error: file is loaded as a module multiple times: `src/b.rs` | = help: replace all but one `mod` item with `use` items = note: `-D clippy::duplicate-mod` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::duplicate_mod)]` error: file is loaded as a module multiple times: `src/c.rs` --> src/main.rs:9:1 diff --git a/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr index da2db45d3b836..388f49fb21351 100644 --- a/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/feature_name/fail/Cargo.stderr @@ -2,6 +2,7 @@ error: the "no-" prefix in the feature name "no-qaq" is negative | = help: consider renaming the feature to "qaq", but make sure the feature adds functionality = note: `-D clippy::negative-feature-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::negative_feature_names)]` error: the "no_" prefix in the feature name "no_qaq" is negative | @@ -19,6 +20,7 @@ error: the "-support" suffix in the feature name "qvq-support" is redundant | = help: consider renaming the feature to "qvq" = note: `-D clippy::redundant-feature-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_feature_names)]` error: the "_support" suffix in the feature name "qvq_support" is redundant | diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr index c2907f319e643..902330e178530 100644 --- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod/Cargo.stderr @@ -6,6 +6,7 @@ error: `mod.rs` files are required, found `src/bad/inner.rs` | = help: move `src/bad/inner.rs` to `src/bad/inner/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` error: `mod.rs` files are required, found `src/bad/inner/stuff.rs` --> src/bad/inner/stuff.rs:1:1 diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr index fcf1a3c5e66c6..d776feb7f2e69 100644 --- a/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_mod_remap/Cargo.stderr @@ -6,5 +6,6 @@ error: `mod.rs` files are required, found `src/bad.rs` | = help: move `src/bad.rs` to `src/bad/mod.rs` = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::self_named_module_files)]` error: could not compile `fail-mod-remap` (bin "fail-mod-remap") due to previous error diff --git a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr index f61642ca2efd7..22558bc4ce89d 100644 --- a/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/module_style/fail_no_mod/Cargo.stderr @@ -6,5 +6,6 @@ error: `mod.rs` files are not allowed, found `src/bad/mod.rs` | = help: move `src/bad/mod.rs` to `src/bad.rs` = note: `-D clippy::mod-module-files` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mod_module_files)]` error: could not compile `fail-no-mod` (bin "fail-no-mod") due to previous error diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr index 5bcce920455cf..4beedc10830a1 100644 --- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.stderr @@ -1,5 +1,6 @@ error: multiple versions for dependency `winapi`: 0.2.8, 0.3.9 | = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::multiple_crate_versions)]` error: could not compile `multiple_crate_versions` (bin "multiple_crate_versions") due to previous error diff --git a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr index b1578c9f32490..65a19bb0718da 100644 --- a/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr +++ b/src/tools/clippy/tests/ui-cargo/wildcard_dependencies/fail/Cargo.stderr @@ -1,5 +1,6 @@ error: wildcard dependency for `regex` | = note: `-D clippy::wildcard-dependencies` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wildcard_dependencies)]` error: could not compile `wildcard_dependencies` (bin "wildcard_dependencies") due to previous error diff --git a/src/tools/clippy/tests/ui-internal/check_formulation.stderr b/src/tools/clippy/tests/ui-internal/check_formulation.stderr index 10eabca4b9d74..96fa617601ff4 100644 --- a/src/tools/clippy/tests/ui-internal/check_formulation.stderr +++ b/src/tools/clippy/tests/ui-internal/check_formulation.stderr @@ -6,6 +6,7 @@ LL | /// Check for lint formulations that are correct | = help: try using `Checks for` instead = note: `-D clippy::almost-standard-lint-formulation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::almost_standard_lint_formulation)]` error: non-standard lint formulation --> $DIR/check_formulation.rs:33:5 diff --git a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr index b12df2786520e..ea04955323d18 100644 --- a/src/tools/clippy/tests/ui-internal/if_chain_style.stderr +++ b/src/tools/clippy/tests/ui-internal/if_chain_style.stderr @@ -16,6 +16,7 @@ help: this `let` statement can also be in the `if_chain!` LL | let x = ""; | ^^^^^^^^^^^ = note: `-D clippy::if-chain-style` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::if_chain_style)]` error: `if a && b;` should be `if a; if b;` --> $DIR/if_chain_style.rs:24:12 diff --git a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr index 0e85088691765..988d32d5259e2 100644 --- a/src/tools/clippy/tests/ui-internal/invalid_paths.stderr +++ b/src/tools/clippy/tests/ui-internal/invalid_paths.stderr @@ -5,6 +5,7 @@ LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::invalid-paths` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::invalid_paths)]` error: invalid path --> $DIR/invalid_paths.rs:18:5 diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index 3ca45404e44bb..58b1fd92b5dc9 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -6,6 +6,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; | = help: convert all references to use `sym::Deref` = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]` error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs b/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs index fcaeace0e9890..f4166b227fc68 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/auxiliary/macros.rs @@ -30,3 +30,18 @@ macro_rules! item { const ITEM: usize = 1; }; } + +#[macro_export] +macro_rules! binop { + ($t:tt) => { + $t + $t + }; +} + +#[macro_export] +macro_rules! attr { + ($i:item) => { + #[repr(C)] + $i + }; +} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml index c8fe8be9a7704..85f1b71eb66b9 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/clippy.toml @@ -8,4 +8,6 @@ disallowed-macros = [ "macros::ty", "macros::pat", "macros::item", + "macros::binop", + "macros::attr", ] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs index ba919b4878854..4a3d55e13c9dc 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.rs @@ -20,11 +20,14 @@ fn main() { let macros::pat!() = 1; let _: macros::ty!() = ""; macros::item!(); + let _ = macros::binop!(1); eprintln!("allowed"); } -struct S; +macros::attr! { + struct S; +} impl S { macros::item!(); diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr index 0eebd587a5f10..3c6f59b16e7c3 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr +++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr @@ -63,23 +63,37 @@ error: use of a disallowed macro `macros::item` LL | macros::item!(); | ^^^^^^^^^^^^^^^ +error: use of a disallowed macro `macros::binop` + --> $DIR/disallowed_macros.rs:23:13 + | +LL | let _ = macros::binop!(1); + | ^^^^^^^^^^^^^^^^^ + +error: use of a disallowed macro `macros::attr` + --> $DIR/disallowed_macros.rs:28:1 + | +LL | / macros::attr! { +LL | | struct S; +LL | | } + | |_^ + error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:30:5 + --> $DIR/disallowed_macros.rs:33:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:34:5 + --> $DIR/disallowed_macros.rs:37:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ error: use of a disallowed macro `macros::item` - --> $DIR/disallowed_macros.rs:38:5 + --> $DIR/disallowed_macros.rs:41:5 | LL | macros::item!(); | ^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs deleted file mode 100644 index 60fbaaea3d340..0000000000000 --- a/src/tools/clippy/tests/ui-toml/excessive_nesting/auxiliary/proc_macros.rs +++ /dev/null @@ -1,469 +0,0 @@ -// NOTE: Copied from `ui/auxiliary/proc_macros.rs`, couldn't get `../` to work for some reason - -#![feature(let_chains)] -#![feature(proc_macro_span)] -#![allow(clippy::excessive_nesting, dead_code)] - -extern crate proc_macro; - -use core::mem; -use proc_macro::token_stream::IntoIter; -use proc_macro::Delimiter::{self, Brace, Parenthesis}; -use proc_macro::Spacing::{self, Alone, Joint}; -use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; - -type Result = core::result::Result; - -/// Make a `compile_error!` pointing to the given span. -fn make_error(msg: &str, span: Span) -> TokenStream { - TokenStream::from_iter([ - TT::Ident(Ident::new("compile_error", span)), - TT::Punct(punct_with_span('!', Alone, span)), - TT::Group({ - let mut msg = Literal::string(msg); - msg.set_span(span); - group_with_span(Parenthesis, TokenStream::from_iter([TT::Literal(msg)]), span) - }), - ]) -} - -fn expect_tt(tt: Option, f: impl FnOnce(TT) -> Option, expected: &str, span: Span) -> Result { - match tt { - None => Err(make_error( - &format!("unexpected end of input, expected {expected}"), - span, - )), - Some(tt) => { - let span = tt.span(); - match f(tt) { - Some(x) => Ok(x), - None => Err(make_error(&format!("unexpected token, expected {expected}"), span)), - } - }, - } -} - -fn punct_with_span(c: char, spacing: Spacing, span: Span) -> Punct { - let mut p = Punct::new(c, spacing); - p.set_span(span); - p -} - -fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Group { - let mut g = Group::new(delimiter, stream); - g.set_span(span); - g -} - -/// Token used to escape the following token from the macro's span rules. -const ESCAPE_CHAR: char = '$'; - -/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their -/// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`. -#[proc_macro] -pub fn with_span(input: TokenStream) -> TokenStream { - let mut iter = input.into_iter(); - let span = iter.next().unwrap().span(); - let mut res = TokenStream::new(); - if let Err(e) = write_with_span(span, iter, &mut res) { - e - } else { - res - } -} - -/// Takes a sequence of tokens and return the tokens with the span set such that they appear to be -/// from an external macro. Tokens may be escaped with either `#ident` or `#(tokens)`. -#[proc_macro] -pub fn external(input: TokenStream) -> TokenStream { - let mut res = TokenStream::new(); - if let Err(e) = write_with_span(Span::mixed_site(), input.into_iter(), &mut res) { - e - } else { - res - } -} - -/// Copies all the tokens, replacing all their spans with the given span. Tokens can be escaped -/// either by `#ident` or `#(tokens)`. -fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Result<()> { - while let Some(tt) = input.next() { - match tt { - TT::Punct(p) if p.as_char() == ESCAPE_CHAR => { - expect_tt( - input.next(), - |tt| match tt { - tt @ (TT::Ident(_) | TT::Literal(_)) => { - out.extend([tt]); - Some(()) - }, - TT::Punct(mut p) if p.as_char() == ESCAPE_CHAR => { - p.set_span(s); - out.extend([TT::Punct(p)]); - Some(()) - }, - TT::Group(g) if g.delimiter() == Parenthesis => { - out.extend([TT::Group(group_with_span(Delimiter::None, g.stream(), g.span()))]); - Some(()) - }, - _ => None, - }, - "an ident, a literal, or parenthesized tokens", - p.span(), - )?; - }, - TT::Group(g) => { - let mut stream = TokenStream::new(); - write_with_span(s, g.stream().into_iter(), &mut stream)?; - out.extend([TT::Group(group_with_span(g.delimiter(), stream, s))]); - }, - mut tt => { - tt.set_span(s); - out.extend([tt]); - }, - } - } - Ok(()) -} - -/// Within the item this attribute is attached to, an `inline!` macro is available which expands the -/// contained tokens as though they came from a macro expansion. -/// -/// Within the `inline!` macro, any token preceded by `$` is passed as though it were an argument -/// with an automatically chosen fragment specifier. `$ident` will be passed as `ident`, `$1` or -/// `$"literal"` will be passed as `literal`, `$'lt` will be passed as `lifetime`, and `$(...)` will -/// pass the contained tokens as a `tt` sequence (the wrapping parenthesis are removed). If another -/// specifier is required it can be specified within parenthesis like `$(@expr ...)`. This will -/// expand the remaining tokens as a single argument. -/// -/// Multiple `inline!` macros may be nested within each other. This will expand as nested macro -/// calls. However, any arguments will be passed as though they came from the outermost context. -#[proc_macro_attribute] -pub fn inline_macros(args: TokenStream, input: TokenStream) -> TokenStream { - let mut args = args.into_iter(); - let mac_name = match args.next() { - Some(TT::Ident(name)) => Some(name), - Some(tt) => { - return make_error( - "unexpected argument, expected either an ident or no arguments", - tt.span(), - ); - }, - None => None, - }; - if let Some(tt) = args.next() { - return make_error( - "unexpected argument, expected either an ident or no arguments", - tt.span(), - ); - }; - - let mac_name = if let Some(mac_name) = mac_name { - Ident::new(&format!("__inline_mac_{mac_name}"), Span::call_site()) - } else { - let mut input = match LookaheadIter::new(input.clone().into_iter()) { - Some(x) => x, - None => return input, - }; - loop { - match input.next() { - None => break Ident::new("__inline_mac", Span::call_site()), - Some(TT::Ident(kind)) => match &*kind.to_string() { - "impl" => break Ident::new("__inline_mac_impl", Span::call_site()), - kind @ ("struct" | "enum" | "union" | "fn" | "mod" | "trait" | "type" | "const" | "static") => { - if let TT::Ident(name) = &input.tt { - break Ident::new(&format!("__inline_mac_{kind}_{name}"), Span::call_site()); - } else { - break Ident::new(&format!("__inline_mac_{kind}"), Span::call_site()); - } - }, - _ => {}, - }, - _ => {}, - } - } - }; - - let mut expander = Expander::default(); - let mut mac = MacWriter::new(mac_name); - if let Err(e) = expander.expand(input.into_iter(), &mut mac) { - return e; - } - let mut out = TokenStream::new(); - mac.finish(&mut out); - out.extend(expander.expn); - out -} - -/// Wraps a `TokenStream` iterator with a single token lookahead. -struct LookaheadIter { - tt: TT, - iter: IntoIter, -} -impl LookaheadIter { - fn new(mut iter: IntoIter) -> Option { - iter.next().map(|tt| Self { tt, iter }) - } - - /// Get's the lookahead token, replacing it with the next token in the stream. - /// Note: If there isn't a next token, this will not return the lookahead token. - fn next(&mut self) -> Option { - self.iter.next().map(|tt| mem::replace(&mut self.tt, tt)) - } -} - -/// Builds the macro used to implement all the `inline!` macro calls. -struct MacWriter { - name: Ident, - macros: TokenStream, - next_idx: usize, -} -impl MacWriter { - fn new(name: Ident) -> Self { - Self { - name, - macros: TokenStream::new(), - next_idx: 0, - } - } - - /// Inserts a new `inline!` call. - fn insert(&mut self, name_span: Span, bang_span: Span, body: Group, expander: &mut Expander) -> Result<()> { - let idx = self.next_idx; - self.next_idx += 1; - - let mut inner = Expander::for_arm(idx); - inner.expand(body.stream().into_iter(), self)?; - let new_arm = inner.arm.unwrap(); - - self.macros.extend([ - TT::Group(Group::new(Parenthesis, new_arm.args_def)), - TT::Punct(Punct::new('=', Joint)), - TT::Punct(Punct::new('>', Alone)), - TT::Group(Group::new(Parenthesis, inner.expn)), - TT::Punct(Punct::new(';', Alone)), - ]); - - expander.expn.extend([ - TT::Ident({ - let mut name = self.name.clone(); - name.set_span(name_span); - name - }), - TT::Punct(punct_with_span('!', Alone, bang_span)), - ]); - let mut call_body = TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]); - if let Some(arm) = expander.arm.as_mut() { - if !new_arm.args.is_empty() { - arm.add_sub_args(new_arm.args, &mut call_body); - } - } else { - call_body.extend(new_arm.args); - } - let mut g = Group::new(body.delimiter(), call_body); - g.set_span(body.span()); - expander.expn.extend([TT::Group(g)]); - Ok(()) - } - - /// Creates the macro definition. - fn finish(self, out: &mut TokenStream) { - if self.next_idx != 0 { - out.extend([ - TT::Ident(Ident::new("macro_rules", Span::call_site())), - TT::Punct(Punct::new('!', Alone)), - TT::Ident(self.name), - TT::Group(Group::new(Brace, self.macros)), - ]) - } - } -} - -struct MacroArm { - args_def: TokenStream, - args: Vec, -} -impl MacroArm { - fn add_single_arg_def(&mut self, kind: &str, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(Ident::new(kind, Span::call_site())), - ]); - name.set_span(arg_span); - out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); - } - - fn add_parenthesized_arg_def(&mut self, kind: Ident, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(kind), - ]), - ))]); - name.set_span(arg_span); - out.extend([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]); - } - - fn add_multi_arg_def(&mut self, dollar_span: Span, arg_span: Span, out: &mut TokenStream) { - let mut name = Ident::new(&format!("_{}", self.args.len()), Span::call_site()); - self.args_def.extend([TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Group(Group::new( - Parenthesis, - TokenStream::from_iter([ - TT::Punct(Punct::new('$', Alone)), - TT::Ident(name.clone()), - TT::Punct(Punct::new(':', Alone)), - TT::Ident(Ident::new("tt", Span::call_site())), - ]), - )), - TT::Punct(Punct::new('*', Alone)), - ]), - ))]); - name.set_span(arg_span); - out.extend([ - TT::Punct(punct_with_span('$', Alone, dollar_span)), - TT::Group(group_with_span( - Parenthesis, - TokenStream::from_iter([TT::Punct(punct_with_span('$', Alone, dollar_span)), TT::Ident(name)]), - dollar_span, - )), - TT::Punct(punct_with_span('*', Alone, dollar_span)), - ]); - } - - fn add_arg(&mut self, dollar_span: Span, tt: TT, input: &mut IntoIter, out: &mut TokenStream) -> Result<()> { - match tt { - TT::Punct(p) if p.as_char() == ESCAPE_CHAR => out.extend([TT::Punct(p)]), - TT::Punct(p) if p.as_char() == '\'' && p.spacing() == Joint => { - let lt_name = expect_tt( - input.next(), - |tt| match tt { - TT::Ident(x) => Some(x), - _ => None, - }, - "lifetime name", - p.span(), - )?; - let arg_span = p.span().join(lt_name.span()).unwrap_or(p.span()); - self.add_single_arg_def("lifetime", dollar_span, arg_span, out); - self.args.extend([TT::Punct(p), TT::Ident(lt_name)]); - }, - TT::Ident(x) => { - self.add_single_arg_def("ident", dollar_span, x.span(), out); - self.args.push(TT::Ident(x)); - }, - TT::Literal(x) => { - self.add_single_arg_def("literal", dollar_span, x.span(), out); - self.args.push(TT::Literal(x)); - }, - TT::Group(g) if g.delimiter() == Parenthesis => { - let mut inner = g.stream().into_iter(); - if let Some(TT::Punct(p)) = inner.next() - && p.as_char() == '@' - { - let kind = expect_tt( - inner.next(), - |tt| match tt { - TT::Ident(kind) => Some(kind), - _ => None, - }, - "a macro fragment specifier", - p.span(), - )?; - self.add_parenthesized_arg_def(kind, dollar_span, g.span(), out); - self.args.push(TT::Group(group_with_span(Parenthesis, inner.collect(), g.span()))) - } else { - self.add_multi_arg_def(dollar_span, g.span(), out); - self.args.push(TT::Group(g)); - } - }, - tt => return Err(make_error("unsupported escape", tt.span())), - }; - Ok(()) - } - - fn add_sub_args(&mut self, args: Vec, out: &mut TokenStream) { - self.add_multi_arg_def(Span::call_site(), Span::call_site(), out); - self.args - .extend([TT::Group(Group::new(Parenthesis, TokenStream::from_iter(args)))]); - } -} - -#[derive(Default)] -struct Expander { - arm: Option, - expn: TokenStream, -} -impl Expander { - fn for_arm(idx: usize) -> Self { - Self { - arm: Some(MacroArm { - args_def: TokenStream::from_iter([TT::Literal(Literal::usize_unsuffixed(idx))]), - args: Vec::new(), - }), - expn: TokenStream::new(), - } - } - - fn write_tt(&mut self, tt: TT, mac: &mut MacWriter) -> Result<()> { - match tt { - TT::Group(g) => { - let outer = mem::take(&mut self.expn); - self.expand(g.stream().into_iter(), mac)?; - let inner = mem::replace(&mut self.expn, outer); - self.expn - .extend([TT::Group(group_with_span(g.delimiter(), inner, g.span()))]); - }, - tt => self.expn.extend([tt]), - } - Ok(()) - } - - fn expand(&mut self, input: IntoIter, mac: &mut MacWriter) -> Result<()> { - let Some(mut input) = LookaheadIter::new(input) else { - return Ok(()); - }; - while let Some(tt) = input.next() { - if let TT::Punct(p) = &tt - && p.as_char() == ESCAPE_CHAR - && let Some(arm) = self.arm.as_mut() - { - arm.add_arg(p.span(), mem::replace(&mut input.tt, tt), &mut input.iter, &mut self.expn)?; - if input.next().is_none() { - return Ok(()); - } - } else if let TT::Punct(p) = &input.tt - && p.as_char() == '!' - && let TT::Ident(name) = &tt - && name.to_string() == "inline" - { - let g = expect_tt( - input.iter.next(), - |tt| match tt { - TT::Group(g) => Some(g), - _ => None, - }, - "macro arguments", - p.span(), - )?; - mac.insert(name.span(), p.span(), g, self)?; - if input.next().is_none() { - return Ok(()); - } - } else { - self.write_tt(tt, mac)?; - } - } - self.write_tt(input.tt, mac) - } -} diff --git a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs index 25f0d0d6230a7..d737a832dd169 100644 --- a/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs +++ b/src/tools/clippy/tests/ui-toml/excessive_nesting/excessive_nesting.rs @@ -1,4 +1,4 @@ -//@aux-build:proc_macros.rs +//@aux-build:../../ui/auxiliary/proc_macros.rs #![rustfmt::skip] #![feature(custom_inner_attributes)] #![allow(unused)] @@ -156,7 +156,7 @@ fn main() { for i in {{{{xx}}}} {{{{{{{{}}}}}}}} while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}} - + while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}} let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} }; diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index cdabe6460cdca..b97bb144468b0 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs deleted file mode 100644 index 1c591fc76f3e9..0000000000000 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/auxiliary/proc_macro_unsafe.rs +++ /dev/null @@ -1,13 +0,0 @@ -extern crate proc_macro; - -use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree}; - -#[proc_macro] -pub fn unsafe_block(input: TokenStream) -> TokenStream { - let span = input.into_iter().next().unwrap().span(); - TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), { - let mut group = Group::new(Delimiter::Brace, TokenStream::new()); - group.set_span(span); - TokenTree::Group(group) - }]) -} diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index c0976f0d6007b..b28e1b7d1802d 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -1,4 +1,4 @@ -//@aux-build:proc_macro_unsafe.rs +//@aux-build:../../ui/auxiliary/proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(deref_nullptr, clippy::let_unit_value, clippy::missing_safety_doc)] @@ -564,4 +564,18 @@ fn issue_8679() { unsafe {} } +mod issue_11246 { + // Safety: foo + const _: () = unsafe {}; + + // Safety: A safety comment + const FOO: () = unsafe {}; + + // Safety: bar + static BAR: u8 = unsafe { 0 }; +} + +// Safety: Another safety comment +const FOO: () = unsafe {}; + fn main() {} diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed index db85247f47d24..e3f2ca72d1cbd 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.fixed +++ b/src/tools/clippy/tests/ui/bool_comparison.fixed @@ -1,6 +1,6 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs index 0915f88544bc7..d1bc20d6831a5 100644 --- a/src/tools/clippy/tests/ui/bool_comparison.rs +++ b/src/tools/clippy/tests/ui/bool_comparison.rs @@ -1,6 +1,6 @@ #![allow(clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.stderr b/src/tools/clippy/tests/ui/cast_size.32bit.stderr similarity index 78% rename from src/tools/clippy/tests/ui/cast_size_32bit.stderr rename to src/tools/clippy/tests/ui/cast_size.32bit.stderr index fb51783a48788..379ca60862b9c 100644 --- a/src/tools/clippy/tests/ui/cast_size_32bit.stderr +++ b/src/tools/clippy/tests/ui/cast_size.32bit.stderr @@ -1,44 +1,46 @@ error: casting `isize` to `i8` may truncate the value - --> $DIR/cast_size_32bit.rs:12:5 + --> $DIR/cast_size.rs:15:5 | LL | 1isize as i8; | ^^^^^^^^^^^^ | = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` help: ... or use `try_from` and handle the error accordingly | LL | i8::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size_32bit.rs:15:5 + --> $DIR/cast_size.rs:18:5 | LL | x0 as f64; | ^^^^^^^^^ | = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size_32bit.rs:16:5 + --> $DIR/cast_size.rs:19:5 | LL | x1 as f64; | ^^^^^^^^^ error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:17:5 + --> $DIR/cast_size.rs:20:5 | LL | x0 as f32; | ^^^^^^^^^ error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:18:5 + --> $DIR/cast_size.rs:21:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:19:5 + --> $DIR/cast_size.rs:22:5 | LL | 1isize as i32; | ^^^^^^^^^^^^^ @@ -50,7 +52,7 @@ LL | i32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:20:5 + --> $DIR/cast_size.rs:23:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ @@ -62,7 +64,7 @@ LL | u32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:21:5 + --> $DIR/cast_size.rs:24:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ @@ -74,7 +76,7 @@ LL | u32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:22:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -86,15 +88,16 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:22:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:24:5 + --> $DIR/cast_size.rs:26:5 | LL | 1i64 as isize; | ^^^^^^^^^^^^^ @@ -106,7 +109,7 @@ LL | isize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:25:5 + --> $DIR/cast_size.rs:27:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ @@ -118,7 +121,7 @@ LL | usize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:26:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -130,13 +133,13 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> $DIR/cast_size_32bit.rs:26:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:27:5 + --> $DIR/cast_size.rs:29:5 | LL | 1u64 as usize; | ^^^^^^^^^^^^^ @@ -148,24 +151,31 @@ LL | usize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size_32bit.rs:28:5 + --> $DIR/cast_size.rs:30:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size_32bit.rs:33:5 + --> $DIR/cast_size.rs:35:5 | LL | 999_999_999 as f32; | ^^^^^^^^^^^^^^^^^^ -error: casting integer literal to `f64` is unnecessary - --> $DIR/cast_size_32bit.rs:34:5 +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:36:5 + | +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: literal out of range for `usize` + --> $DIR/cast_size.rs:36:5 | -LL | 3_999_999_999usize as f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64` +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295` + = note: `#[deny(overflowing_literals)]` on by default -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/cast_size.stderr b/src/tools/clippy/tests/ui/cast_size.64bit.stderr similarity index 94% rename from src/tools/clippy/tests/ui/cast_size.stderr rename to src/tools/clippy/tests/ui/cast_size.64bit.stderr index bc9224be64401..7fae92b12503d 100644 --- a/src/tools/clippy/tests/ui/cast_size.stderr +++ b/src/tools/clippy/tests/ui/cast_size.64bit.stderr @@ -1,5 +1,5 @@ error: casting `isize` to `i8` may truncate the value - --> $DIR/cast_size.rs:12:5 + --> $DIR/cast_size.rs:15:5 | LL | 1isize as i8; | ^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | i8::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size.rs:16:5 + --> $DIR/cast_size.rs:18:5 | LL | x0 as f64; | ^^^^^^^^^ @@ -28,19 +28,19 @@ LL | x1 as f64; | ^^^^^^^^^ error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:21:5 + --> $DIR/cast_size.rs:20:5 | LL | x0 as f32; | ^^^^^^^^^ error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:23:5 + --> $DIR/cast_size.rs:21:5 | LL | x1 as f32; | ^^^^^^^^^ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:25:5 + --> $DIR/cast_size.rs:22:5 | LL | 1isize as i32; | ^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | i32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:27:5 + --> $DIR/cast_size.rs:23:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | u32::try_from(1isize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:29:5 + --> $DIR/cast_size.rs:24:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | u32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:31:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | i32::try_from(1usize); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:31:5 + --> $DIR/cast_size.rs:25:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | 1usize as i32; = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:36:5 + --> $DIR/cast_size.rs:26:5 | LL | 1i64 as isize; | ^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | isize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:38:5 + --> $DIR/cast_size.rs:27:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | usize::try_from(1i64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:40:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | isize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers - --> $DIR/cast_size.rs:40:5 + --> $DIR/cast_size.rs:28:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:43:5 + --> $DIR/cast_size.rs:29:5 | LL | 1u64 as usize; | ^^^^^^^^^^^^^ @@ -151,19 +151,19 @@ LL | usize::try_from(1u64); | ~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers - --> $DIR/cast_size.rs:45:5 + --> $DIR/cast_size.rs:30:5 | LL | 1u32 as isize; | ^^^^^^^^^^^^^ error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) - --> $DIR/cast_size.rs:51:5 + --> $DIR/cast_size.rs:35:5 | LL | 999_999_999 as f32; | ^^^^^^^^^^^^^^^^^^ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) - --> $DIR/cast_size.rs:53:5 + --> $DIR/cast_size.rs:36:5 | LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index 95626b20b27bf..d063a70ccdfcf 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -1,56 +1,37 @@ -//@ignore-32bit -#[warn( +//@stderr-per-bitwidth +//@no-rustfix + +#![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap, clippy::cast_lossless )] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + fn main() { // Casting from *size 1isize as i8; - //~^ ERROR: casting `isize` to `i8` may truncate the value let x0 = 1isize; let x1 = 1usize; x0 as f64; - //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit - //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings` x1 as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit x0 as f32; - //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b x1 as f32; - //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b 1isize as i32; - //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid 1isize as u32; - //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid 1usize as u32; - //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid 1usize as i32; - //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid - //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit - //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings` - // Casting to *size 1i64 as isize; - //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid 1i64 as usize; - //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid 1u64 as isize; - //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid - //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit 1u64 as usize; - //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid 1u32 as isize; - //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit 1u32 as usize; // Should not trigger any lint 1i32 as isize; // Neither should this 1i32 as usize; // Big integer literal to float 999_999_999 as f32; - //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, 9_999_999_999_999_999usize as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit } -//@no-rustfix diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.rs b/src/tools/clippy/tests/ui/cast_size_32bit.rs deleted file mode 100644 index 5a06e34bdb8fb..0000000000000 --- a/src/tools/clippy/tests/ui/cast_size_32bit.rs +++ /dev/null @@ -1,56 +0,0 @@ -//@ignore-64bit -#[warn( - clippy::cast_precision_loss, - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_possible_wrap, - clippy::cast_lossless -)] -#[allow(clippy::no_effect, clippy::unnecessary_operation)] -fn main() { - // Casting from *size - 1isize as i8; - //~^ ERROR: casting `isize` to `i8` may truncate the value - let x0 = 1isize; - let x1 = 1usize; - x0 as f64; - //~^ ERROR: casting `isize` to `f64` causes a loss of precision on targets with 64-bit - //~| NOTE: `-D clippy::cast-precision-loss` implied by `-D warnings` - x1 as f64; - //~^ ERROR: casting `usize` to `f64` causes a loss of precision on targets with 64-bit - x0 as f32; - //~^ ERROR: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 b - x1 as f32; - //~^ ERROR: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 b - 1isize as i32; - //~^ ERROR: casting `isize` to `i32` may truncate the value on targets with 64-bit wid - 1isize as u32; - //~^ ERROR: casting `isize` to `u32` may truncate the value on targets with 64-bit wid - 1usize as u32; - //~^ ERROR: casting `usize` to `u32` may truncate the value on targets with 64-bit wid - 1usize as i32; - //~^ ERROR: casting `usize` to `i32` may truncate the value on targets with 64-bit wid - //~| ERROR: casting `usize` to `i32` may wrap around the value on targets with 32-bit - //~| NOTE: `-D clippy::cast-possible-wrap` implied by `-D warnings` - // Casting to *size - 1i64 as isize; - //~^ ERROR: casting `i64` to `isize` may truncate the value on targets with 32-bit wid - 1i64 as usize; - //~^ ERROR: casting `i64` to `usize` may truncate the value on targets with 32-bit wid - 1u64 as isize; - //~^ ERROR: casting `u64` to `isize` may truncate the value on targets with 32-bit wid - //~| ERROR: casting `u64` to `isize` may wrap around the value on targets with 64-bit - 1u64 as usize; - //~^ ERROR: casting `u64` to `usize` may truncate the value on targets with 32-bit wid - 1u32 as isize; - //~^ ERROR: casting `u32` to `isize` may wrap around the value on targets with 32-bit - 1u32 as usize; // Should not trigger any lint - 1i32 as isize; // Neither should this - 1i32 as usize; - // Big integer literal to float - 999_999_999 as f32; - //~^ ERROR: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, - 3_999_999_999usize as f64; - //~^ ERROR: casting integer literal to `f64` is unnecessary - //~| NOTE: `-D clippy::unnecessary-cast` implied by `-D warnings` -} diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs index e82e7bcb06cb0..02f80cc52ac72 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -128,6 +128,57 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); } +fn issue11371() { + let option = Some(()); + + if option.is_some() { + option.as_ref().unwrap(); + //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some` + } else { + option.as_ref().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + result.as_ref().unwrap(); + //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok` + } else { + result.as_ref().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let mut option = Some(()); + if option.is_some() { + option.as_mut().unwrap(); + //~^ ERROR: called `unwrap` on `option` after checking its variant with `is_some` + } else { + option.as_mut().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + let mut result = Ok::<(), ()>(()); + if result.is_ok() { + result.as_mut().unwrap(); + //~^ ERROR: called `unwrap` on `result` after checking its variant with `is_ok` + } else { + result.as_mut().unwrap(); + //~^ ERROR: this call to `unwrap()` will always panic + } + + // This should not lint. Statics are, at the time of writing, not linted on anyway, + // but if at some point they are supported by this lint, it should correctly see that + // `X` is being mutated and not suggest `if let Some(..) = X {}` + static mut X: Option = Some(123); + unsafe { + if X.is_some() { + X = None; + X.unwrap(); + } + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr index ed603581ecddb..a5afbba7317e9 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -168,5 +168,73 @@ LL | if x.is_err() { LL | x.unwrap_err(); | ^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: called `unwrap` on `option` after checking its variant with `is_some` + --> $DIR/simple_conditionals.rs:135:9 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some(..) = &option` +LL | option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:138:9 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | option.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:145:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok(..) = &result` +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:148:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_ref().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `option` after checking its variant with `is_some` + --> $DIR/simple_conditionals.rs:154:9 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some(..) = &mut option` +LL | option.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:157:9 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | option.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:163:9 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok(..) = &mut result` +LL | result.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> $DIR/simple_conditionals.rs:166:9 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | result.as_mut().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs index b7c186bef7724..2d03544ad8b6b 100644 --- a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs +++ b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type)] +#![allow(clippy::non_canonical_clone_impl)] use std::fmt; use std::marker::PhantomData; diff --git a/src/tools/clippy/tests/ui/crashes/ice-11337.rs b/src/tools/clippy/tests/ui/crashes/ice-11337.rs new file mode 100644 index 0000000000000..0bed4035f6bf4 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11337.rs @@ -0,0 +1,9 @@ +#![feature(trait_alias)] + +trait Confusing = Fn(i32) where F: Fn(u32); + +fn alias, F>(_: T, _: F) {} + +fn main() { + alias(|_| {}, |_| {}); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.fixed b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed new file mode 100644 index 0000000000000..ca5721cbb2bae --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed @@ -0,0 +1,25 @@ +#![warn(clippy::implied_bounds_in_impls)] + +use std::fmt::Debug; +use std::ops::*; + +fn gen() -> impl PartialOrd + Debug {} + +struct Bar {} +trait Foo {} +trait FooNested> {} +impl Foo for Bar {} +impl FooNested for Bar {} + +fn foo() -> impl Foo + FooNested { + Bar {} +} + +fn test_impl_ops() -> impl Add + Sub + Mul + Div { + 1 +} +fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign { + 1 +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.rs b/src/tools/clippy/tests/ui/crashes/ice-11422.rs new file mode 100644 index 0000000000000..355ec2480bba4 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.rs @@ -0,0 +1,25 @@ +#![warn(clippy::implied_bounds_in_impls)] + +use std::fmt::Debug; +use std::ops::*; + +fn gen() -> impl PartialOrd + PartialEq + Debug {} + +struct Bar {} +trait Foo {} +trait FooNested> {} +impl Foo for Bar {} +impl FooNested for Bar {} + +fn foo() -> impl Foo + FooNested { + Bar {} +} + +fn test_impl_ops() -> impl Add + Sub + Mul + Div { + 1 +} +fn test_impl_assign_ops() -> impl AddAssign + SubAssign + MulAssign + DivAssign { + 1 +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.stderr b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr new file mode 100644 index 0000000000000..fb80b5b147f57 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr @@ -0,0 +1,16 @@ +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/ice-11422.rs:6:31 + | +LL | fn gen() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ + | + = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implied_bounds_in_impls)]` +help: try removing this bound + | +LL - fn gen() -> impl PartialOrd + PartialEq + Debug {} +LL + fn gen() -> impl PartialOrd + Debug {} + | + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.rs b/src/tools/clippy/tests/ui/crashes/ice-360.rs index 28589e1efeda5..0d10932b0987e 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-360.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-360.rs @@ -3,7 +3,8 @@ fn main() {} fn no_panic(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/src/tools/clippy/tests/ui/crashes/ice-360.stderr b/src/tools/clippy/tests/ui/crashes/ice-360.stderr index dd016355b5325..a84697a9f291d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-360.stderr +++ b/src/tools/clippy/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 | | } @@ -14,7 +28,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::while_let_loop)]` error: empty `loop {}` wastes CPU cycles - --> $DIR/ice-360.rs:12:9 + --> $DIR/ice-360.rs:13:9 | LL | loop {} | ^^^^^^^ @@ -23,5 +37,5 @@ LL | loop {} = note: `-D clippy::empty-loop` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed index 6cc202414f5f4..68c5a5c5ca457 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.fixed +++ b/src/tools/clippy/tests/ui/derivable_impls.fixed @@ -287,4 +287,17 @@ mod issue10158 { } } +mod issue11368 { + pub struct A { + a: u32, + } + + impl Default for A { + #[track_caller] + fn default() -> Self { + Self { a: 0 } + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs index 0aa9acd752dcc..21d73ba8b7778 100644 --- a/src/tools/clippy/tests/ui/derivable_impls.rs +++ b/src/tools/clippy/tests/ui/derivable_impls.rs @@ -323,4 +323,17 @@ mod issue10158 { } } +mod issue11368 { + pub struct A { + a: u32, + } + + impl Default for A { + #[track_caller] + fn default() -> Self { + Self { a: 0 } + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs index 310c701765ba9..20ac8a6e6be35 100644 --- a/src/tools/clippy/tests/ui/derive.rs +++ b/src/tools/clippy/tests/ui/derive.rs @@ -1,8 +1,4 @@ -#![allow( - clippy::incorrect_clone_impl_on_copy_type, - clippy::incorrect_partial_ord_impl_on_ord_type, - dead_code -)] +#![allow(clippy::non_canonical_clone_impl, clippy::non_canonical_partial_ord_impl, dead_code)] #![warn(clippy::expl_impl_clone_on_copy)] diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr index e9b2ee34760f2..88942d9543226 100644 --- a/src/tools/clippy/tests/ui/derive.stderr +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:12:1 + --> $DIR/derive.rs:8:1 | LL | / impl Clone for Qux { LL | | @@ -10,7 +10,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:12:1 + --> $DIR/derive.rs:8:1 | LL | / impl Clone for Qux { LL | | @@ -23,7 +23,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:37:1 + --> $DIR/derive.rs:33:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -34,7 +34,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:37:1 + --> $DIR/derive.rs:33:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -45,7 +45,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:49:1 + --> $DIR/derive.rs:45:1 | LL | / impl Clone for BigArray { LL | | @@ -56,7 +56,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:49:1 + --> $DIR/derive.rs:45:1 | LL | / impl Clone for BigArray { LL | | @@ -67,7 +67,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:61:1 + --> $DIR/derive.rs:57:1 | LL | / impl Clone for FnPtr { LL | | @@ -78,7 +78,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:61:1 + --> $DIR/derive.rs:57:1 | LL | / impl Clone for FnPtr { LL | | @@ -89,7 +89,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:82:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:82:1 + --> $DIR/derive.rs:78:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs index 2c19942d420c9..1c7e6d1c20230 100644 --- a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs +++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,6 +1,6 @@ #![warn(clippy::derive_ord_xor_partial_ord)] #![allow(clippy::unnecessary_wraps)] -#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)] +#![allow(clippy::non_canonical_partial_ord_impl)] use std::cmp::Ordering; diff --git a/src/tools/clippy/tests/ui/empty_loop.rs b/src/tools/clippy/tests/ui/empty_loop.rs index 54e8fb4907c0f..be347563135c3 100644 --- a/src/tools/clippy/tests/ui/empty_loop.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/empty_loop.stderr b/src/tools/clippy/tests/ui/empty_loop.stderr index 84d7d61c7daa8..113556f673c8c 100644 --- a/src/tools/clippy/tests/ui/empty_loop.stderr +++ b/src/tools/clippy/tests/ui/empty_loop.stderr @@ -9,7 +9,7 @@ LL | loop {} = help: to override `-D warnings` add `#[allow(clippy::empty_loop)]` error: empty `loop {}` wastes CPU cycles - --> $DIR/empty_loop.rs:11:9 + --> $DIR/empty_loop.rs:12:9 | LL | loop {} | ^^^^^^^ @@ -17,7 +17,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/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs index 964e5634ddb11..c50404c5047bb 100644 --- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs @@ -1,4 +1,4 @@ -//@ignore-target-x86 +//@ignore-32bit #![warn(clippy::enum_clike_unportable_variant)] #![allow(unused, non_upper_case_globals)] diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr index 5935eea5e036e..93ad4daa97f77 100644 --- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr @@ -5,51 +5,52 @@ LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::enum_clike_unportable_variant)]` error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:15:5 + --> $DIR/enum_clike_unportable_variant.rs:17:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:18:5 + --> $DIR/enum_clike_unportable_variant.rs:21:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:25:5 + --> $DIR/enum_clike_unportable_variant.rs:29:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:26:5 + --> $DIR/enum_clike_unportable_variant.rs:31:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:28:5 + --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:34:5 + --> $DIR/enum_clike_unportable_variant.rs:41:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:35:5 + --> $DIR/enum_clike_unportable_variant.rs:43:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ error: C-like enum variant discriminant is not portable to 32-bit targets - --> $DIR/enum_clike_unportable_variant.rs:40:5 + --> $DIR/enum_clike_unportable_variant.rs:49:5 | LL | X = ::Number, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed index 86bee11eb8774..12158d0d12a2e 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed +++ b/src/tools/clippy/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,32 @@ 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!(), + }; + + // Issue #11474 + pub struct Variant { + pub anonymous: Variant0, + } + + pub union Variant0 { + pub anonymous: std::mem::ManuallyDrop, + } + + pub struct Variant00 { + pub anonymous: Variant000, + } + + pub union Variant000 { + pub val: i32, + } + + unsafe { + let mut p = core::mem::zeroed::(); + (*p.anonymous.anonymous).anonymous.val = 1; + } } diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs index 7a505bdf558c3..dec021c183443 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs @@ -293,4 +293,32 @@ 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!(), + }; + + // Issue #11474 + pub struct Variant { + pub anonymous: Variant0, + } + + pub union Variant0 { + pub anonymous: std::mem::ManuallyDrop, + } + + pub struct Variant00 { + pub anonymous: Variant000, + } + + pub union Variant000 { + pub val: i32, + } + + unsafe { + let mut p = core::mem::zeroed::(); + (*p.anonymous.anonymous).anonymous.val = 1; + } } diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr index bea014d8ad555..3d2a7b0d9f4f3 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr @@ -194,10 +194,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 @@ -235,5 +235,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/src/tools/clippy/tests/ui/explicit_iter_loop.fixed b/src/tools/clippy/tests/ui/explicit_iter_loop.fixed index 57292114e3882..f08397defa539 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.fixed +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/explicit_iter_loop.rs b/src/tools/clippy/tests/ui/explicit_iter_loop.rs index 66280c23843d2..2ee6825d445c2 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/explicit_iter_loop.stderr b/src/tools/clippy/tests/ui/explicit_iter_loop.stderr index af46d74e989af..725d9b63cf8d6 100644 --- a/src/tools/clippy/tests/ui/explicit_iter_loop.stderr +++ b/src/tools/clippy/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,133 +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` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` - -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/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs index a547a67430fd2..5057c64373291 100644 --- a/src/tools/clippy/tests/ui/float_cmp.rs +++ b/src/tools/clippy/tests/ui/float_cmp.rs @@ -41,6 +41,16 @@ impl PartialEq for X { } } +impl PartialEq for X { + fn eq(&self, o: &f32) -> bool { + if self.val.is_nan() { + o.is_nan() + } else { + self.val == *o // no error, inside "eq" fn + } + } +} + fn main() { ZERO == 0f32; //no error, comparison with zero is ok 1.0f32 != f32::INFINITY; // also comparison with infinity @@ -48,6 +58,9 @@ fn main() { ZERO == 0.0; //no error, comparison with zero is ok ZERO + ZERO != 1.0; //no error, comparison with zero is ok + let x = X { val: 1.0 }; + x == 1.0; // no error, custom type that implement PartialOrder for float is not checked + ONE == 1f32; ONE == 1.0 + 0.0; ONE + ONE == ZERO + ONE + ONE; diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr index cbe529954d0c2..217e2987917ab 100644 --- a/src/tools/clippy/tests/ui/float_cmp.stderr +++ b/src/tools/clippy/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:57:5 + --> $DIR/float_cmp.rs:70:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -9,7 +9,7 @@ LL | ONE as f64 != 2.0; = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:64:5 + --> $DIR/float_cmp.rs:77:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -17,7 +17,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:69:5 + --> $DIR/float_cmp.rs:82:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -25,7 +25,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:91:5 + --> $DIR/float_cmp.rs:104:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -33,7 +33,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:98:5 + --> $DIR/float_cmp.rs:111:5 | LL | a1 == a2; | ^^^^^^^^ @@ -41,7 +41,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:101:5 + --> $DIR/float_cmp.rs:114:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr similarity index 70% rename from src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr rename to src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr index 671347d2bcd54..ea08d8c9cc162 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr @@ -1,141 +1,143 @@ error: casting function pointer `foo` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:10:13 + --> $DIR/fn_to_numeric_cast.rs:10:13 | LL | let _ = foo as i8; | ^^^^^^^^^ help: try: `foo as usize` | = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:11:13 + --> $DIR/fn_to_numeric_cast.rs:11:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` -error: casting function pointer `foo` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:12:13 +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast.rs:12:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i64` - --> $DIR/fn_to_numeric_cast_32bit.rs:13:13 + --> $DIR/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` - | - = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` error: casting function pointer `foo` to `i128` - --> $DIR/fn_to_numeric_cast_32bit.rs:14:13 + --> $DIR/fn_to_numeric_cast.rs:14:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> $DIR/fn_to_numeric_cast_32bit.rs:15:13 + --> $DIR/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:17:13 + --> $DIR/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:18:13 + --> $DIR/fn_to_numeric_cast.rs:18:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` -error: casting function pointer `foo` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:19:13 +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast.rs:19:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> $DIR/fn_to_numeric_cast_32bit.rs:20:13 + --> $DIR/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> $DIR/fn_to_numeric_cast_32bit.rs:21:13 + --> $DIR/fn_to_numeric_cast.rs:21:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:34:13 + --> $DIR/fn_to_numeric_cast.rs:34:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:35:13 + --> $DIR/fn_to_numeric_cast.rs:35:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `abc` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:36:13 +error: casting function pointer `abc` to `i32` + --> $DIR/fn_to_numeric_cast.rs:36:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> $DIR/fn_to_numeric_cast_32bit.rs:37:13 + --> $DIR/fn_to_numeric_cast.rs:37:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> $DIR/fn_to_numeric_cast_32bit.rs:38:13 + --> $DIR/fn_to_numeric_cast.rs:38:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> $DIR/fn_to_numeric_cast_32bit.rs:39:13 + --> $DIR/fn_to_numeric_cast.rs:39:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:41:13 + --> $DIR/fn_to_numeric_cast.rs:41:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:42:13 + --> $DIR/fn_to_numeric_cast.rs:42:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `abc` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:43:13 +error: casting function pointer `abc` to `u32` + --> $DIR/fn_to_numeric_cast.rs:43:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> $DIR/fn_to_numeric_cast_32bit.rs:44:13 + --> $DIR/fn_to_numeric_cast.rs:44:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> $DIR/fn_to_numeric_cast_32bit.rs:45:13 + --> $DIR/fn_to_numeric_cast.rs:45:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` -error: casting function pointer `f` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast_32bit.rs:52:5 +error: casting function pointer `f` to `i32` + --> $DIR/fn_to_numeric_cast.rs:52:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr similarity index 85% rename from src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr rename to src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr index 53e8ac3c4b420..62f3bfa70ea50 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr @@ -8,19 +8,19 @@ LL | let _ = foo as i8; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:13:13 + --> $DIR/fn_to_numeric_cast.rs:11:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:15:13 + --> $DIR/fn_to_numeric_cast.rs:12:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i64` - --> $DIR/fn_to_numeric_cast.rs:17:13 + --> $DIR/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` @@ -29,115 +29,115 @@ LL | let _ = foo as i64; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i128` - --> $DIR/fn_to_numeric_cast.rs:20:13 + --> $DIR/fn_to_numeric_cast.rs:14:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> $DIR/fn_to_numeric_cast.rs:22:13 + --> $DIR/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:25:13 + --> $DIR/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:27:13 + --> $DIR/fn_to_numeric_cast.rs:18:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:29:13 + --> $DIR/fn_to_numeric_cast.rs:19:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> $DIR/fn_to_numeric_cast.rs:31:13 + --> $DIR/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> $DIR/fn_to_numeric_cast.rs:33:13 + --> $DIR/fn_to_numeric_cast.rs:21:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:47:13 + --> $DIR/fn_to_numeric_cast.rs:34:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:49:13 + --> $DIR/fn_to_numeric_cast.rs:35:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:51:13 + --> $DIR/fn_to_numeric_cast.rs:36:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> $DIR/fn_to_numeric_cast.rs:53:13 + --> $DIR/fn_to_numeric_cast.rs:37:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> $DIR/fn_to_numeric_cast.rs:55:13 + --> $DIR/fn_to_numeric_cast.rs:38:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> $DIR/fn_to_numeric_cast.rs:57:13 + --> $DIR/fn_to_numeric_cast.rs:39:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:60:13 + --> $DIR/fn_to_numeric_cast.rs:41:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:62:13 + --> $DIR/fn_to_numeric_cast.rs:42:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:64:13 + --> $DIR/fn_to_numeric_cast.rs:43:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> $DIR/fn_to_numeric_cast.rs:66:13 + --> $DIR/fn_to_numeric_cast.rs:44:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> $DIR/fn_to_numeric_cast.rs:68:13 + --> $DIR/fn_to_numeric_cast.rs:45:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `f` to `i32`, which truncates the value - --> $DIR/fn_to_numeric_cast.rs:76:5 + --> $DIR/fn_to_numeric_cast.rs:52:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs index 09128d8176e08..9501eb5da4b20 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@stderr-per-bitwidth //@no-rustfix #![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] @@ -8,30 +8,17 @@ fn foo() -> String { fn test_function_to_numeric_cast() { let _ = foo as i8; - //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value - //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` let _ = foo as i16; - //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value let _ = foo as i32; - //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value let _ = foo as i64; - //~^ ERROR: casting function pointer `foo` to `i64` - //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` let _ = foo as i128; - //~^ ERROR: casting function pointer `foo` to `i128` let _ = foo as isize; - //~^ ERROR: casting function pointer `foo` to `isize` let _ = foo as u8; - //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value let _ = foo as u16; - //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value let _ = foo as u32; - //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value let _ = foo as u64; - //~^ ERROR: casting function pointer `foo` to `u64` let _ = foo as u128; - //~^ ERROR: casting function pointer `foo` to `u128` // Casting to usize is OK and should not warn let _ = foo as usize; @@ -45,28 +32,17 @@ fn test_function_var_to_numeric_cast() { let abc: fn() -> String = foo; let _ = abc as i8; - //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value let _ = abc as i16; - //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value let _ = abc as i32; - //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value let _ = abc as i64; - //~^ ERROR: casting function pointer `abc` to `i64` let _ = abc as i128; - //~^ ERROR: casting function pointer `abc` to `i128` let _ = abc as isize; - //~^ ERROR: casting function pointer `abc` to `isize` let _ = abc as u8; - //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value let _ = abc as u16; - //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value let _ = abc as u32; - //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value let _ = abc as u64; - //~^ ERROR: casting function pointer `abc` to `u64` let _ = abc as u128; - //~^ ERROR: casting function pointer `abc` to `u128` // Casting to usize is OK and should not warn let _ = abc as usize; @@ -74,7 +50,6 @@ fn test_function_var_to_numeric_cast() { fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { f as i32 - //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value } fn main() {} diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs deleted file mode 100644 index 93e9361f4dc5b..0000000000000 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs +++ /dev/null @@ -1,80 +0,0 @@ -//@ignore-64bit - -#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] - -fn foo() -> String { - String::new() -} - -fn test_function_to_numeric_cast() { - let _ = foo as i8; - //~^ ERROR: casting function pointer `foo` to `i8`, which truncates the value - //~| NOTE: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` - let _ = foo as i16; - //~^ ERROR: casting function pointer `foo` to `i16`, which truncates the value - let _ = foo as i32; - //~^ ERROR: casting function pointer `foo` to `i32`, which truncates the value - let _ = foo as i64; - //~^ ERROR: casting function pointer `foo` to `i64` - //~| NOTE: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` - let _ = foo as i128; - //~^ ERROR: casting function pointer `foo` to `i128` - let _ = foo as isize; - //~^ ERROR: casting function pointer `foo` to `isize` - - let _ = foo as u8; - //~^ ERROR: casting function pointer `foo` to `u8`, which truncates the value - let _ = foo as u16; - //~^ ERROR: casting function pointer `foo` to `u16`, which truncates the value - let _ = foo as u32; - //~^ ERROR: casting function pointer `foo` to `u32`, which truncates the value - let _ = foo as u64; - //~^ ERROR: casting function pointer `foo` to `u64` - let _ = foo as u128; - //~^ ERROR: casting function pointer `foo` to `u128` - - // Casting to usize is OK and should not warn - let _ = foo as usize; - - // Cast `f` (a `FnDef`) to `fn()` should not warn - fn f() {} - let _ = f as fn(); -} - -fn test_function_var_to_numeric_cast() { - let abc: fn() -> String = foo; - - let _ = abc as i8; - //~^ ERROR: casting function pointer `abc` to `i8`, which truncates the value - let _ = abc as i16; - //~^ ERROR: casting function pointer `abc` to `i16`, which truncates the value - let _ = abc as i32; - //~^ ERROR: casting function pointer `abc` to `i32`, which truncates the value - let _ = abc as i64; - //~^ ERROR: casting function pointer `abc` to `i64` - let _ = abc as i128; - //~^ ERROR: casting function pointer `abc` to `i128` - let _ = abc as isize; - //~^ ERROR: casting function pointer `abc` to `isize` - - let _ = abc as u8; - //~^ ERROR: casting function pointer `abc` to `u8`, which truncates the value - let _ = abc as u16; - //~^ ERROR: casting function pointer `abc` to `u16`, which truncates the value - let _ = abc as u32; - //~^ ERROR: casting function pointer `abc` to `u32`, which truncates the value - let _ = abc as u64; - //~^ ERROR: casting function pointer `abc` to `u64` - let _ = abc as u128; - //~^ ERROR: casting function pointer `abc` to `u128` - - // Casting to usize is OK and should not warn - let _ = abc as usize; -} - -fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { - f as i32 - //~^ ERROR: casting function pointer `f` to `i32`, which truncates the value -} - -fn main() {} diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs index 8fa0f34a6c423..77abd663e0a6e 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs @@ -117,3 +117,15 @@ fn f(b: bool, v: Option<()>) -> Option<()> { None } } + +fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { + let x = if b { + #[allow(clippy::let_unit_value)] + let _ = v?; + Some(()) + } else { + None + }; + + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed index 2912eede4d9b6..6c6f21fee16b0 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed @@ -1,5 +1,5 @@ #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::redundant_pattern_matching, clippy::single_match)] +#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] fn foo() -> Result<(), ()> { unimplemented!() @@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> { fn main() { match foo() { - Ok(()) => {}, - Err(()) => {}, + Ok(()) => {}, //~ ERROR: matching over `()` is more explicit + Err(()) => {}, //~ ERROR: matching over `()` is more explicit } if let Ok(()) = foo() {} + //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|()| todo!()); + //~^ ERROR: matching over `()` is more explicit +} + +#[allow(unused)] +pub fn moo(_: ()) { + let () = foo().unwrap(); + //~^ ERROR: matching over `()` is more explicit + let _: () = foo().unwrap(); + let _: () = (); } diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs index d180cd8d2fd36..5e8c2e03ba2cb 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs @@ -1,5 +1,5 @@ #![warn(clippy::ignored_unit_patterns)] -#![allow(clippy::redundant_pattern_matching, clippy::single_match)] +#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)] fn foo() -> Result<(), ()> { unimplemented!() @@ -7,9 +7,19 @@ fn foo() -> Result<(), ()> { fn main() { match foo() { - Ok(_) => {}, - Err(_) => {}, + Ok(_) => {}, //~ ERROR: matching over `()` is more explicit + Err(_) => {}, //~ ERROR: matching over `()` is more explicit } if let Ok(_) = foo() {} + //~^ ERROR: matching over `()` is more explicit let _ = foo().map_err(|_| todo!()); + //~^ ERROR: matching over `()` is more explicit +} + +#[allow(unused)] +pub fn moo(_: ()) { + let _ = foo().unwrap(); + //~^ ERROR: matching over `()` is more explicit + let _: () = foo().unwrap(); + let _: () = (); } diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr index a8ced22397dbb..df5e1d89e906e 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr @@ -20,10 +20,16 @@ LL | if let Ok(_) = foo() {} | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> $DIR/ignored_unit_patterns.rs:14:28 + --> $DIR/ignored_unit_patterns.rs:15:28 | LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` -error: aborting due to 4 previous errors +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:21:9 + | +LL | let _ = foo().unwrap(); + | ^ help: use `()` instead of `_`: `()` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed index 952c2b70619bb..a50fa0ccf6e7f 100644 --- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed +++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.fixed @@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct { } } +mod issue11422 { + use core::fmt::Debug; + // Some additional tests that would cause ICEs: + + // `PartialOrd` has a default generic parameter and does not need to be explicitly specified. + // This needs special handling. + fn default_generic_param1() -> impl PartialOrd + Debug {} + fn default_generic_param2() -> impl PartialOrd + Debug {} + + // Referring to `Self` in the supertrait clause needs special handling. + trait Trait1 {} + trait Trait2: Trait1 {} + impl Trait1<()> for () {} + impl Trait2 for () {} + + fn f() -> impl Trait1<()> + Trait2 {} +} + +mod issue11435 { + // Associated type needs to be included on DoubleEndedIterator in the suggestion + fn my_iter() -> impl DoubleEndedIterator { + 0..5 + } + + // Removing the `Clone` bound should include the `+` behind it in its remove suggestion + fn f() -> impl Copy { + 1 + } + + trait Trait1 { + type U; + } + impl Trait1 for () { + type U = i64; + } + trait Trait2: Trait1 {} + impl Trait2 for () {} + + // When the other trait has generics, it shouldn't add another pair of `<>` + fn f2() -> impl Trait2 {} + + trait Trait3 { + type X; + type Y; + } + trait Trait4: Trait3 {} + impl Trait3 for () { + type X = i32; + type Y = i128; + } + impl Trait4 for () {} + + // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved + // over + fn f3() -> impl Trait4 {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs index 475f2621bbf62..e74ed4425b81e 100644 --- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs +++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.rs @@ -65,4 +65,61 @@ impl SomeTrait for SomeStruct { } } +mod issue11422 { + use core::fmt::Debug; + // Some additional tests that would cause ICEs: + + // `PartialOrd` has a default generic parameter and does not need to be explicitly specified. + // This needs special handling. + fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} + fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} + + // Referring to `Self` in the supertrait clause needs special handling. + trait Trait1 {} + trait Trait2: Trait1 {} + impl Trait1<()> for () {} + impl Trait2 for () {} + + fn f() -> impl Trait1<()> + Trait2 {} +} + +mod issue11435 { + // Associated type needs to be included on DoubleEndedIterator in the suggestion + fn my_iter() -> impl Iterator + DoubleEndedIterator { + 0..5 + } + + // Removing the `Clone` bound should include the `+` behind it in its remove suggestion + fn f() -> impl Copy + Clone { + 1 + } + + trait Trait1 { + type U; + } + impl Trait1 for () { + type U = i64; + } + trait Trait2: Trait1 {} + impl Trait2 for () {} + + // When the other trait has generics, it shouldn't add another pair of `<>` + fn f2() -> impl Trait1 + Trait2 {} + + trait Trait3 { + type X; + type Y; + } + trait Trait4: Trait3 {} + impl Trait3 for () { + type X = i32; + type Y = i128; + } + impl Trait4 for () {} + + // Associated type `X` is specified, but `Y` is not, so only that associated type should be moved + // over + fn f3() -> impl Trait3 + Trait4 {} +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr b/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr index e2b1ecb9f1ec7..72dc2a183a383 100644 --- a/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr +++ b/src/tools/clippy/tests/ui/implied_bounds_in_impls.stderr @@ -120,5 +120,77 @@ LL - fn f() -> impl Deref + DerefMut { LL + fn f() -> impl DerefMut { | -error: aborting due to 10 previous errors +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/implied_bounds_in_impls.rs:74:41 + | +LL | fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} + | ^^^^^^^^^ + | +help: try removing this bound + | +LL - fn default_generic_param1() -> impl PartialEq + PartialOrd + Debug {} +LL + fn default_generic_param1() -> impl PartialOrd + Debug {} + | + +error: this bound is already specified as the supertrait of `PartialOrd` + --> $DIR/implied_bounds_in_impls.rs:75:54 + | +LL | fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ + | +help: try removing this bound + | +LL - fn default_generic_param2() -> impl PartialOrd + PartialEq + Debug {} +LL + fn default_generic_param2() -> impl PartialOrd + Debug {} + | + +error: this bound is already specified as the supertrait of `DoubleEndedIterator` + --> $DIR/implied_bounds_in_impls.rs:88:26 + | +LL | fn my_iter() -> impl Iterator + DoubleEndedIterator { + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn my_iter() -> impl Iterator + DoubleEndedIterator { +LL + fn my_iter() -> impl DoubleEndedIterator { + | + +error: this bound is already specified as the supertrait of `Copy` + --> $DIR/implied_bounds_in_impls.rs:93:27 + | +LL | fn f() -> impl Copy + Clone { + | ^^^^^ + | +help: try removing this bound + | +LL - fn f() -> impl Copy + Clone { +LL + fn f() -> impl Copy { + | + +error: this bound is already specified as the supertrait of `Trait2` + --> $DIR/implied_bounds_in_impls.rs:107:21 + | +LL | fn f2() -> impl Trait1 + Trait2 {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn f2() -> impl Trait1 + Trait2 {} +LL + fn f2() -> impl Trait2 {} + | + +error: this bound is already specified as the supertrait of `Trait4` + --> $DIR/implied_bounds_in_impls.rs:122:21 + | +LL | fn f3() -> impl Trait3 + Trait4 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing this bound + | +LL - fn f3() -> impl Trait3 + Trait4 {} +LL + fn f3() -> impl Trait4 {} + | + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/iter_out_of_bounds.rs b/src/tools/clippy/tests/ui/iter_out_of_bounds.rs new file mode 100644 index 0000000000000..3cfe6e82fc1e3 --- /dev/null +++ b/src/tools/clippy/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 { + 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::; + + 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/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr b/src/tools/clippy/tests/ui/iter_out_of_bounds.stderr new file mode 100644 index 0000000000000..f235faec8e59c --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index 9dd046fec1fee..7d8a584b0224c 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -63,11 +63,9 @@ fn main() { let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet - let _ = vec.iter().cloned().all(|x| x.len() == 1); + let _ = vec.iter().all(|x| x.len() == 1); - // Not implemented yet - let _ = vec.iter().cloned().any(|x| x.len() == 1); + let _ = vec.iter().any(|x| x.len() == 1); // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index 3cab366955481..58c374ab8cd10 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -64,10 +64,8 @@ fn main() { let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - // Not implemented yet let _ = vec.iter().cloned().all(|x| x.len() == 1); - // Not implemented yet let _ = vec.iter().cloned().any(|x| x.len() == 1); // Should probably stay as it is. diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr index fbcd33066d876..a9a739688ebf2 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr @@ -148,5 +148,21 @@ LL | let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); | | | help: try: `.for_each(|x| assert!(!x.is_empty()))` -error: aborting due to 17 previous errors +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:67:13 + | +LL | let _ = vec.iter().cloned().all(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.all(|x| x.len() == 1)` + +error: unneeded cloning of iterator items + --> $DIR/iter_overeager_cloned.rs:69:13 + | +LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); + | ^^^^^^^^^^------------------------------- + | | + | help: try: `.any(|x| x.len() == 1)` + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/iter_skip_next.fixed b/src/tools/clippy/tests/ui/iter_skip_next.fixed index 310b24a9cde17..3e41b36324902 100644 --- a/src/tools/clippy/tests/ui/iter_skip_next.fixed +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/iter_skip_next.rs b/src/tools/clippy/tests/ui/iter_skip_next.rs index 222d6a2a1848b..6d96441ca96f5 100644 --- a/src/tools/clippy/tests/ui/iter_skip_next.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr index 0b6bf652b1f1f..39b173e758693 100644 --- a/src/tools/clippy/tests/ui/iter_skip_next.stderr +++ b/src/tools/clippy/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)` @@ -8,37 +8,37 @@ LL | let _ = some_vec.iter().skip(42).next(); = help: to override `-D warnings` add `#[allow(clippy::iter_skip_next)]` 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/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs b/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs index 9c224f4117d3f..6c98bdc8c8808 100644 --- a/src/tools/clippy/tests/ui/iter_skip_next_unfixable.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/iter_skip_zero.fixed b/src/tools/clippy/tests/ui/iter_skip_zero.fixed index 62a83d5905b41..447d07100e953 100644 --- a/src/tools/clippy/tests/ui/iter_skip_zero.fixed +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/iter_skip_zero.rs b/src/tools/clippy/tests/ui/iter_skip_zero.rs index c96696dde65d4..ba63c3981808e 100644 --- a/src/tools/clippy/tests/ui/iter_skip_zero.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr b/src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr new file mode 100644 index 0000000000000..0e0eee21cf30a --- /dev/null +++ b/src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr @@ -0,0 +1,280 @@ +error: large size difference between variants + --> $DIR/large_enum_variant.rs:11:1 + | +LL | / enum LargeEnum { +LL | | A(i32), + | | ------ the second-largest variant contains at least 4 bytes +LL | | B([i32; 8000]), + | | -------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[i32; 8000]>), + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:35:1 + | +LL | / enum LargeEnum2 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | ContainingLargeEnum(LargeEnum), + | | ------------------------------ the largest variant contains at least 32004 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingLargeEnum(Box), + | ~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:40:1 + | +LL | / enum LargeEnum3 { +LL | | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), + | | --------------------------------------------------------- the largest variant contains at least 70004 bytes +LL | | VoidVariant, +LL | | StructLikeLittle { x: i32, y: i32 }, + | | ----------------------------------- the second-largest variant contains at least 8 bytes +LL | | } + | |_^ the entire enum is at least 70008 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:46:1 + | +LL | / enum LargeEnum4 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | StructLikeLarge { x: [i32; 8000], y: i32 }, + | | ------------------------------------------ the largest variant contains at least 32004 bytes +LL | | } + | |_^ the entire enum is at least 32008 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:51:1 + | +LL | / enum LargeEnum5 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | StructLikeLarge2 { x: [i32; 8000] }, + | | ----------------------------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, + | ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:67:1 + | +LL | / enum LargeEnum7 { +LL | | A, +LL | | B([u8; 1255]), + | | ------------- the largest variant contains at least 1255 bytes +LL | | C([u8; 200]), + | | ------------ the second-largest variant contains at least 200 bytes +LL | | } + | |_^ the entire enum is at least 1256 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[u8; 1255]>), + | ~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:73:1 + | +LL | / enum LargeEnum8 { +LL | | VariantOk(i32, u32), + | | ------------------- the second-largest variant contains at least 8 bytes +LL | | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]), + | | ------------------------------------------------------------------------- the largest variant contains at least 70128 bytes +LL | | } + | |_^ the entire enum is at least 70132 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]), + | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:78:1 + | +LL | / enum LargeEnum9 { +LL | | A(Struct<()>), + | | ------------- the second-largest variant contains at least 4 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:83:1 + | +LL | / enum LargeEnumOk2 { +LL | | A(T), + | | ---- the second-largest variant contains at least 0 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32000 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:88:1 + | +LL | / enum LargeEnumOk3 { +LL | | A(Struct), + | | ------------ the second-largest variant contains at least 4 bytes +LL | | B(Struct2), + | | ---------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32000 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box), + | ~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:103:1 + | +LL | / enum CopyableLargeEnum { +LL | | A(bool), + | | ------- the second-largest variant contains at least 1 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes +LL | | } + | |_^ the entire enum is at least 64004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:103:6 + | +LL | enum CopyableLargeEnum { + | ^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:105:5 + | +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:108:1 + | +LL | / enum ManuallyCopyLargeEnum { +LL | | A(bool), + | | ------- the second-largest variant contains at least 1 bytes +LL | | B([u64; 8000]), + | | -------------- the largest variant contains at least 64000 bytes +LL | | } + | |_^ the entire enum is at least 64004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:108:6 + | +LL | enum ManuallyCopyLargeEnum { + | ^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:110:5 + | +LL | B([u64; 8000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:121:1 + | +LL | / enum SomeGenericPossiblyCopyEnum { +LL | | A(bool, std::marker::PhantomData), + | | ------------------------------------ the second-largest variant contains at least 1 bytes +LL | | B([u64; 4000]), + | | -------------- the largest variant contains at least 32000 bytes +LL | | } + | |_^ the entire enum is at least 32004 bytes + | +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:121:6 + | +LL | enum SomeGenericPossiblyCopyEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:123:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:134:1 + | +LL | / enum LargeEnumWithGenerics { +LL | | Small, + | | ----- the second-largest variant carries no data at all +LL | | Large((T, [u8; 512])), + | | --------------------- the largest variant contains at least 512 bytes +LL | | } + | |_^ the entire enum is at least 512 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Large(Box<(T, [u8; 512])>), + | ~~~~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:143:1 + | +LL | / enum WithGenerics { +LL | | Large([Foo; 64]), + | | --------------------- the largest variant contains at least 512 bytes +LL | | Small(u8), + | | --------- the second-largest variant contains at least 1 bytes +LL | | } + | |_^ the entire enum is at least 516 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Large(Box<[Foo; 64]>), + | ~~~~~~~~~~~~~~~~~~~ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:153:1 + | +LL | / enum LargeEnumOfConst { +LL | | Ok, + | | -- the second-largest variant carries no data at all +LL | | Error(PossiblyLargeEnumWithConst<256>), + | | -------------------------------------- the largest variant contains at least 514 bytes +LL | | } + | |_^ the entire enum is at least 514 bytes + | +help: consider boxing the large fields to reduce the total size of the enum + | +LL | Error(Box>), + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr similarity index 91% rename from src/tools/clippy/tests/ui/large_enum_variant.stderr rename to src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr index 7026ca785f34c..3eba43e05ece5 100644 --- a/src/tools/clippy/tests/ui/large_enum_variant.stderr +++ b/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr @@ -1,5 +1,5 @@ error: large size difference between variants - --> $DIR/large_enum_variant.rs:10:1 + --> $DIR/large_enum_variant.rs:11:1 | LL | / enum LargeEnum { LL | | A(i32), @@ -17,7 +17,7 @@ LL | B(Box<[i32; 8000]>), | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:34:1 + --> $DIR/large_enum_variant.rs:35:1 | LL | / enum LargeEnum2 { LL | | VariantOk(i32, u32), @@ -33,7 +33,7 @@ LL | ContainingLargeEnum(Box), | ~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:39:1 + --> $DIR/large_enum_variant.rs:40:1 | LL | / enum LargeEnum3 { LL | | ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), @@ -50,7 +50,7 @@ LL | ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:45:1 + --> $DIR/large_enum_variant.rs:46:1 | LL | / enum LargeEnum4 { LL | | VariantOk(i32, u32), @@ -66,7 +66,7 @@ LL | StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:50:1 + --> $DIR/large_enum_variant.rs:51:1 | LL | / enum LargeEnum5 { LL | | VariantOk(i32, u32), @@ -82,7 +82,7 @@ LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, | ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:66:1 + --> $DIR/large_enum_variant.rs:67:1 | LL | / enum LargeEnum7 { LL | | A, @@ -99,7 +99,7 @@ LL | B(Box<[u8; 1255]>), | ~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:72:1 + --> $DIR/large_enum_variant.rs:73:1 | LL | / enum LargeEnum8 { LL | | VariantOk(i32, u32), @@ -115,7 +115,7 @@ LL | ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]> | ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:77:1 + --> $DIR/large_enum_variant.rs:78:1 | LL | / enum LargeEnum9 { LL | | A(Struct<()>), @@ -131,7 +131,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:82:1 + --> $DIR/large_enum_variant.rs:83:1 | LL | / enum LargeEnumOk2 { LL | | A(T), @@ -147,7 +147,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:87:1 + --> $DIR/large_enum_variant.rs:88:1 | LL | / enum LargeEnumOk3 { LL | | A(Struct), @@ -163,7 +163,7 @@ LL | B(Box), | ~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:102:1 + --> $DIR/large_enum_variant.rs:103:1 | LL | / enum CopyableLargeEnum { LL | | A(bool), @@ -174,18 +174,18 @@ LL | | } | |_^ the entire enum is at least 64008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:102:6 + --> $DIR/large_enum_variant.rs:103:6 | LL | enum CopyableLargeEnum { | ^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:104:5 + --> $DIR/large_enum_variant.rs:105:5 | LL | B([u64; 8000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:107:1 + --> $DIR/large_enum_variant.rs:108:1 | LL | / enum ManuallyCopyLargeEnum { LL | | A(bool), @@ -196,18 +196,18 @@ LL | | } | |_^ the entire enum is at least 64008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:107:6 + --> $DIR/large_enum_variant.rs:108:6 | LL | enum ManuallyCopyLargeEnum { | ^^^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:109:5 + --> $DIR/large_enum_variant.rs:110:5 | LL | B([u64; 8000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:120:1 + --> $DIR/large_enum_variant.rs:121:1 | LL | / enum SomeGenericPossiblyCopyEnum { LL | | A(bool, std::marker::PhantomData), @@ -218,18 +218,18 @@ LL | | } | |_^ the entire enum is at least 32008 bytes | note: boxing a variant would require the type no longer be `Copy` - --> $DIR/large_enum_variant.rs:120:6 + --> $DIR/large_enum_variant.rs:121:6 | LL | enum SomeGenericPossiblyCopyEnum { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:122:5 + --> $DIR/large_enum_variant.rs:123:5 | LL | B([u64; 4000]), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:133:1 + --> $DIR/large_enum_variant.rs:134:1 | LL | / enum LargeEnumWithGenerics { LL | | Small, @@ -245,7 +245,7 @@ LL | Large(Box<(T, [u8; 512])>), | ~~~~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:142:1 + --> $DIR/large_enum_variant.rs:143:1 | LL | / enum WithGenerics { LL | | Large([Foo; 64]), @@ -261,7 +261,7 @@ LL | Large(Box<[Foo; 64]>), | ~~~~~~~~~~~~~~~~~~~ error: large size difference between variants - --> $DIR/large_enum_variant.rs:152:1 + --> $DIR/large_enum_variant.rs:153:1 | LL | / enum LargeEnumOfConst { LL | | Ok, diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs index f101bda76a895..3625c011dbfa1 100644 --- a/src/tools/clippy/tests/ui/large_enum_variant.rs +++ b/src/tools/clippy/tests/ui/large_enum_variant.rs @@ -1,3 +1,4 @@ +//@stderr-per-bitwidth //@aux-build:proc_macros.rs //@no-rustfix #![allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.fixed b/src/tools/clippy/tests/ui/manual_range_patterns.fixed index ea06e27d1534e..b348d7071f6e9 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.fixed +++ b/src/tools/clippy/tests/ui/manual_range_patterns.fixed @@ -13,8 +13,8 @@ fn main() { let _ = matches!(f, 1 | 2147483647); let _ = matches!(f, 0 | 2147483647); let _ = matches!(f, -2147483647 | 2147483647); - let _ = matches!(f, 1 | (2..=4)); - let _ = matches!(f, 1 | (2..4)); + let _ = matches!(f, 1..=4); + let _ = matches!(f, 1..4); let _ = matches!(f, 1..=48324729); let _ = matches!(f, 0..=48324730); let _ = matches!(f, 0..=3); @@ -25,9 +25,20 @@ fn main() { }; let _ = matches!(f, -5..=3); let _ = matches!(f, -1 | -5 | 3 | -2 | -4 | -3 | 0 | 1); // 2 is missing - let _ = matches!(f, -1000001..=1000001); + let _ = matches!(f, -1_000_001..=1_000_001); let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); + matches!(f, 0x00..=0x03); + matches!(f, 0x00..=0x07); + matches!(f, -0x09..=0x00); + + matches!(f, 0..=5); + matches!(f, 0..5); + + matches!(f, 0..10); + matches!(f, 0..=10); + matches!(f, 0..=10); + macro_rules! mac { ($e:expr) => { matches!($e, 1..=10) diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.rs b/src/tools/clippy/tests/ui/manual_range_patterns.rs index eb29987b89fd1..a0750f54b73f1 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.rs +++ b/src/tools/clippy/tests/ui/manual_range_patterns.rs @@ -28,6 +28,17 @@ fn main() { let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_002); + matches!(f, 0x00 | 0x01 | 0x02 | 0x03); + matches!(f, 0x00..=0x05 | 0x06 | 0x07); + matches!(f, -0x09 | -0x08 | -0x07..=0x00); + + matches!(f, 0..5 | 5); + matches!(f, 0 | 1..5); + + matches!(f, 0..=5 | 6..10); + matches!(f, 0..5 | 5..=10); + matches!(f, 5..=10 | 0..5); + macro_rules! mac { ($e:expr) => { matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.stderr b/src/tools/clippy/tests/ui/manual_range_patterns.stderr index 3eee39af86e7b..fbeb9455769df 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.stderr +++ b/src/tools/clippy/tests/ui/manual_range_patterns.stderr @@ -13,6 +13,18 @@ error: this OR pattern can be rewritten using a range LL | let _ = matches!(f, 4 | 2 | 3 | 1 | 5 | 6 | 9 | 7 | 8 | 10); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:16:25 + | +LL | let _ = matches!(f, 1 | (2..=4)); + | ^^^^^^^^^^^ help: try: `1..=4` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:17:25 + | +LL | let _ = matches!(f, 1 | (2..4)); + | ^^^^^^^^^^ help: try: `1..4` + error: this OR pattern can be rewritten using a range --> $DIR/manual_range_patterns.rs:18:25 | @@ -47,10 +59,58 @@ error: this OR pattern can be rewritten using a range --> $DIR/manual_range_patterns.rs:28:25 | LL | let _ = matches!(f, -1_000_000..=1_000_000 | -1_000_001 | 1_000_001); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1000001..=1000001` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-1_000_001..=1_000_001` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:31:17 + | +LL | matches!(f, 0x00 | 0x01 | 0x02 | 0x03); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x03` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:32:17 + | +LL | matches!(f, 0x00..=0x05 | 0x06 | 0x07); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `0x00..=0x07` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:33:17 + | +LL | matches!(f, -0x09 | -0x08 | -0x07..=0x00); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-0x09..=0x00` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:35:17 + | +LL | matches!(f, 0..5 | 5); + | ^^^^^^^^ help: try: `0..=5` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:36:17 + | +LL | matches!(f, 0 | 1..5); + | ^^^^^^^^ help: try: `0..5` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:38:17 + | +LL | matches!(f, 0..=5 | 6..10); + | ^^^^^^^^^^^^^ help: try: `0..10` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:39:17 + | +LL | matches!(f, 0..5 | 5..=10); + | ^^^^^^^^^^^^^ help: try: `0..=10` + +error: this OR pattern can be rewritten using a range + --> $DIR/manual_range_patterns.rs:40:17 + | +LL | matches!(f, 5..=10 | 0..5); + | ^^^^^^^^^^^^^ help: try: `0..=10` error: this OR pattern can be rewritten using a range - --> $DIR/manual_range_patterns.rs:33:26 + --> $DIR/manual_range_patterns.rs:44:26 | LL | matches!($e, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1..=10` @@ -60,5 +120,5 @@ LL | mac!(f); | = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 9 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed new file mode 100644 index 0000000000000..a96827259f530 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs new file mode 100644 index 0000000000000..0b4b883acf8ac --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr new file mode 100644 index 0000000000000..a3e66d7958e98 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr @@ -0,0 +1,253 @@ +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` + = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` + +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/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs new file mode 100644 index 0000000000000..4346ed892f277 --- /dev/null +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr new file mode 100644 index 0000000000000..12c9eed5d6619 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -0,0 +1,164 @@ +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` + = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` + +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/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed index ee67224b4a800..0a52b25229d76 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.fixed +++ b/src/tools/clippy/tests/ui/needless_borrow.fixed @@ -503,3 +503,16 @@ mod issue_10535 { { } } + +mod issue_10253 { + struct S; + trait X { + fn f(&self); + } + impl X for &S { + fn f(&self) {} + } + fn f() { + (&S).f::<()>(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs index 1444f47d92099..34a95d184635d 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.rs +++ b/src/tools/clippy/tests/ui/needless_borrow.rs @@ -503,3 +503,16 @@ mod issue_10535 { { } } + +mod issue_10253 { + struct S; + trait X { + fn f(&self); + } + impl X for &S { + fn f(&self) {} + } + fn f() { + (&S).f::<()>(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_raw_string.fixed b/src/tools/clippy/tests/ui/needless_raw_string.fixed index 4db375178b4f2..855498105135e 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.fixed +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/needless_raw_string.rs b/src/tools/clippy/tests/ui/needless_raw_string.rs index 59c75fda41bef..06d4973038717 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/needless_raw_string.stderr b/src/tools/clippy/tests/ui/needless_raw_string.stderr index 83cc6d332ee56..e6806b31b1d96 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.stderr +++ b/src/tools/clippy/tests/ui/needless_raw_string.stderr @@ -2,16 +2,58 @@ 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: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]` +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/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed index 84902157ab4fc..e980adeeff4c1 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs index 62abae8385926..6113c5f25ae64 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr index 94c51c423b86d..5a8e3d04543af 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr +++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr @@ -1,41 +1,167 @@ error: unnecessary hashes around raw string literal - --> $DIR/needless_raw_string_hashes.rs:7:5 + --> $DIR/needless_raw_string_hashes.rs:6:5 | -LL | r##"Hello "world"!"##; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `r#"Hello "world"!"#` +LL | r#"\aaa"#; + | ^^^^^^^^^ | = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]` +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: remove one hash from both sides of the literal + | +LL - r##"Hello "world"!"##; +LL + r#"Hello "world"!"#; + | 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/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs index 001b94391ca81..c67a6d4494e05 100644 --- a/src/tools/clippy/tests/ui/never_loop.rs +++ b/src/tools/clippy/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,59 @@ 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() { + loop { + //~^ ERROR: this loop never actually loops + panic!("oh no"); + } + loop { + //~^ ERROR: this loop never actually loops + unimplemented!("not yet"); + } + loop { + // no error + todo!("maybe later"); + } +} + fn main() { test1(); test2(); diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr index 234780007b1af..3982f36cea9f8 100644 --- a/src/tools/clippy/tests/ui/never_loop.stderr +++ b/src/tools/clippy/tests/ui/never_loop.stderr @@ -154,16 +154,31 @@ 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: this loop never actually loops + --> $DIR/never_loop.rs:393:5 + | +LL | / loop { +LL | | +LL | | unimplemented!("not yet"); LL | | } | |_____^ -error: aborting due to 14 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed b/src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed similarity index 100% rename from src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.fixed rename to src/tools/clippy/tests/ui/non_canonical_clone_impl.fixed diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs b/src/tools/clippy/tests/ui/non_canonical_clone_impl.rs similarity index 100% rename from src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.rs rename to src/tools/clippy/tests/ui/non_canonical_clone_impl.rs diff --git a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr similarity index 54% rename from src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr rename to src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr index 566a1a4b14b58..44196751b053b 100644 --- a/src/tools/clippy/tests/ui/incorrect_clone_impl_on_copy_type.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_clone_impl.stderr @@ -1,5 +1,5 @@ -error: incorrect implementation of `clone` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:9:29 +error: non-canonical implementation of `clone` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:9:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -7,10 +7,11 @@ LL | | Self(self.0) LL | | } | |_____^ help: change this to: `{ *self }` | - = note: `#[deny(clippy::incorrect_clone_impl_on_copy_type)]` on by default + = note: `-D clippy::non-canonical-clone-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_clone_impl)]` -error: incorrect implementation of `clone_from` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:13:5 +error: unnecessary implementation of `clone_from` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:13:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); @@ -18,8 +19,8 @@ LL | | *self = source.clone(); LL | | } | |_____^ help: remove it -error: incorrect implementation of `clone` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:80:29 +error: non-canonical implementation of `clone` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:80:29 | LL | fn clone(&self) -> Self { | _____________________________^ @@ -27,8 +28,8 @@ LL | | Self(self.0) LL | | } | |_____^ help: change this to: `{ *self }` -error: incorrect implementation of `clone_from` on a `Copy` type - --> $DIR/incorrect_clone_impl_on_copy_type.rs:84:5 +error: unnecessary implementation of `clone_from` on a `Copy` type + --> $DIR/non_canonical_clone_impl.rs:84:5 | LL | / fn clone_from(&mut self, source: &Self) { LL | | source.clone(); diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed similarity index 100% rename from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.fixed rename to src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.fixed diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs similarity index 100% rename from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.rs rename to src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.rs diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr similarity index 63% rename from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr rename to src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr index 1f706984662b6..05cc717b9ba1d 100644 --- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl.stderr @@ -1,5 +1,5 @@ -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:16:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl.rs:16:1 | LL | / impl PartialOrd for A { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -10,10 +10,11 @@ LL | || } LL | | } | |__^ | - = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type.rs:50:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl.rs:50:1 | LL | / impl PartialOrd for C { LL | | fn partial_cmp(&self, _: &Self) -> Option { diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs similarity index 82% rename from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs rename to src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs index 1173a95d06599..2f8d5cf30c77a 100644 --- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.rs @@ -21,8 +21,6 @@ impl cmp::Ord for A { } impl PartialOrd for A { - //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type - //~| NOTE: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default fn partial_cmp(&self, other: &Self) -> Option { // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't // automatically applied @@ -46,7 +44,6 @@ impl cmp::Ord for B { } impl PartialOrd for B { - //~^ ERROR: incorrect implementation of `partial_cmp` on an `Ord` type fn partial_cmp(&self, other: &Self) -> Option { // This calls `B.cmp`, not `Ord::cmp`! Some(self.cmp(other)) diff --git a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr similarity index 67% rename from src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr rename to src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr index 09d7a32e3342a..4978d7a873960 100644 --- a/src/tools/clippy/tests/ui/incorrect_partial_ord_impl_on_ord_type_fully_qual.stderr +++ b/src/tools/clippy/tests/ui/non_canonical_partial_ord_impl_fully_qual.stderr @@ -1,9 +1,7 @@ -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:23:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:23:1 | LL | / impl PartialOrd for A { -LL | | -LL | | LL | | fn partial_cmp(&self, other: &Self) -> Option { | | _____________________________________________________________- LL | || // NOTE: This suggestion is wrong, as `Ord` is not in scope. But this should be fine as it isn't @@ -14,13 +12,13 @@ LL | || } LL | | } | |__^ | - = note: `#[deny(clippy::incorrect_partial_ord_impl_on_ord_type)]` on by default + = note: `-D clippy::non-canonical-partial-ord-impl` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_canonical_partial_ord_impl)]` -error: incorrect implementation of `partial_cmp` on an `Ord` type - --> $DIR/incorrect_partial_ord_impl_on_ord_type_fully_qual.rs:48:1 +error: non-canonical implementation of `partial_cmp` on an `Ord` type + --> $DIR/non_canonical_partial_ord_impl_fully_qual.rs:46:1 | LL | / impl PartialOrd for B { -LL | | LL | | fn partial_cmp(&self, other: &Self) -> Option { | | _____________________________________________________________- LL | || // This calls `B.cmp`, not `Ord::cmp`! diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 0fd68eb92b1f2..4df9be2c21d9a 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -14,6 +14,8 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::non_canonical_clone_impl)] +#![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] @@ -33,10 +35,10 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -62,6 +64,8 @@ #![warn(clippy::mixed_read_write_in_expression)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] +#![warn(clippy::non_canonical_clone_impl)] +#![warn(clippy::non_canonical_partial_ord_impl)] #![warn(clippy::arithmetic_side_effects)] #![warn(clippy::overly_complex_bool_expr)] #![warn(clippy::new_without_default)] @@ -85,12 +89,12 @@ #![warn(drop_bounds)] #![warn(dropping_copy_types)] #![warn(dropping_references)] +#![warn(useless_ptr_null_checks)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(for_loops_over_fallibles)] #![warn(forgetting_copy_types)] #![warn(forgetting_references)] -#![warn(useless_ptr_null_checks)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 927937fba83a9..940e60068e7b9 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -14,6 +14,8 @@ #![allow(clippy::mixed_read_write_in_expression)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] +#![allow(clippy::non_canonical_clone_impl)] +#![allow(clippy::non_canonical_partial_ord_impl)] #![allow(clippy::arithmetic_side_effects)] #![allow(clippy::overly_complex_bool_expr)] #![allow(clippy::new_without_default)] @@ -33,10 +35,10 @@ #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] +#![allow(useless_ptr_null_checks)] #![allow(for_loops_over_fallibles)] #![allow(forgetting_copy_types)] #![allow(forgetting_references)] -#![allow(useless_ptr_null_checks)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] @@ -62,6 +64,8 @@ #![warn(clippy::eval_order_dependence)] #![warn(clippy::identity_conversion)] #![warn(clippy::if_let_some_result)] +#![warn(clippy::incorrect_clone_impl_on_copy_type)] +#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] #![warn(clippy::integer_arithmetic)] #![warn(clippy::logic_bug)] #![warn(clippy::new_without_default_derive)] @@ -85,12 +89,12 @@ #![warn(clippy::drop_bounds)] #![warn(clippy::drop_copy)] #![warn(clippy::drop_ref)] +#![warn(clippy::fn_null_check)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::forget_copy)] #![warn(clippy::forget_ref)] -#![warn(clippy::fn_null_check)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index d98fc7b30db57..30824e154b8bf 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,322 +8,334 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:59:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` + --> $DIR/rename.rs:67:9 + | +LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` + +error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` + --> $DIR/rename.rs:68:9 + | +LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` + error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` +error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` + --> $DIR/rename.rs:92:9 + | +LL | #![warn(clippy::fn_null_check)] + | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` + error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` -error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> $DIR/rename.rs:93:9 - | -LL | #![warn(clippy::fn_null_check)] - | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` - error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:101:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:103:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:100:9 + --> $DIR/rename.rs:104:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:101:9 + --> $DIR/rename.rs:105:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:102:9 + --> $DIR/rename.rs:106:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> $DIR/rename.rs:103:9 + --> $DIR/rename.rs:107:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:104:9 + --> $DIR/rename.rs:108:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:105:9 + --> $DIR/rename.rs:109:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 54 previous errors +error: aborting due to 56 previous errors diff --git a/src/tools/clippy/tests/ui/result_large_err.rs b/src/tools/clippy/tests/ui/result_large_err.rs index 14a1f7e1db57f..b25348bf99617 100644 --- a/src/tools/clippy/tests/ui/result_large_err.rs +++ b/src/tools/clippy/tests/ui/result_large_err.rs @@ -1,3 +1,5 @@ +//@ignore-32bit + #![warn(clippy::result_large_err)] #![allow(clippy::large_enum_variant)] diff --git a/src/tools/clippy/tests/ui/result_large_err.stderr b/src/tools/clippy/tests/ui/result_large_err.stderr index d42dd6600a324..6602f396a9c0d 100644 --- a/src/tools/clippy/tests/ui/result_large_err.stderr +++ b/src/tools/clippy/tests/ui/result_large_err.stderr @@ -1,5 +1,5 @@ error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:8:23 + --> $DIR/result_large_err.rs:10:23 | LL | pub fn large_err() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -9,7 +9,7 @@ LL | pub fn large_err() -> Result<(), [u8; 512]> { = help: to override `-D warnings` add `#[allow(clippy::result_large_err)]` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:20:21 + --> $DIR/result_large_err.rs:22:21 | LL | pub fn ret() -> Result<(), Self> { | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes @@ -17,7 +17,7 @@ LL | pub fn ret() -> Result<(), Self> { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:26:26 + --> $DIR/result_large_err.rs:28:26 | LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes @@ -25,7 +25,7 @@ LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:32:45 + --> $DIR/result_large_err.rs:34:45 | LL | pub fn large_err_via_type_alias(x: T) -> Fdlr { | ^^^^^^^ the `Err`-variant is at least 240 bytes @@ -33,7 +33,7 @@ LL | pub fn large_err_via_type_alias(x: T) -> Fdlr { = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:41:34 + --> $DIR/result_large_err.rs:43:34 | LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeError)> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes @@ -41,7 +41,7 @@ LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeErro = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:53:34 + --> $DIR/result_large_err.rs:55:34 | LL | _Omg([u8; 512]), | --------------- the largest variant contains at least 512 bytes @@ -52,7 +52,7 @@ LL | pub fn large_enum_error() -> Result<(), Self> { = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:66:30 + --> $DIR/result_large_err.rs:68:30 | LL | _Biggest([u8; 1024]), | -------------------- the largest variant contains at least 1024 bytes @@ -65,7 +65,7 @@ LL | fn large_enum_error() -> Result<(), Self> { = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:73:25 + --> $DIR/result_large_err.rs:75:25 | LL | fn large_error() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -73,7 +73,7 @@ LL | fn large_error() -> Result<(), [u8; 512]> { = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:93:29 + --> $DIR/result_large_err.rs:95:29 | LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -81,7 +81,7 @@ LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:103:40 + --> $DIR/result_large_err.rs:105:40 | LL | pub fn param_large_union() -> Result<(), UnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -89,7 +89,7 @@ LL | pub fn param_large_union() -> Result<(), UnionError> { = help: try reducing the size of `UnionError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:113:34 + --> $DIR/result_large_err.rs:115:34 | LL | pub fn array_error_subst() -> Result<(), ArrayError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes @@ -97,7 +97,7 @@ LL | pub fn array_error_subst() -> Result<(), ArrayError> { = help: try reducing the size of `ArrayError`, for example by boxing large elements or replacing it with `Box>` error: the `Err`-variant returned from this function is very large - --> $DIR/result_large_err.rs:118:31 + --> $DIR/result_large_err.rs:120:31 | LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes diff --git a/src/tools/clippy/tests/ui/similar_names.rs b/src/tools/clippy/tests/ui/similar_names.rs index c5a941316da30..f46af56c6e2b0 100644 --- a/src/tools/clippy/tests/ui/similar_names.rs +++ b/src/tools/clippy/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/src/tools/clippy/tests/ui/similar_names.stderr b/src/tools/clippy/tests/ui/similar_names.stderr index 011dbe679c082..44ae3532a4862 100644 --- a/src/tools/clippy/tests/ui/similar_names.stderr +++ b/src/tools/clippy/tests/ui/similar_names.stderr @@ -1,11 +1,11 @@ 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; | ^^^^^ @@ -13,73 +13,73 @@ LL | let apple: i32; = help: to override `-D warnings` add `#[allow(clippy::similar_names)]` 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/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs index d6493f234133d..3cc8061647dff 100644 --- a/src/tools/clippy/tests/ui/single_call_fn.rs +++ b/src/tools/clippy/tests/ui/single_call_fn.rs @@ -1,3 +1,4 @@ +//@ignore-32bit //@aux-build:proc_macros.rs #![allow(clippy::redundant_closure_call, unused)] #![warn(clippy::single_call_fn)] diff --git a/src/tools/clippy/tests/ui/single_call_fn.stderr b/src/tools/clippy/tests/ui/single_call_fn.stderr index 4acb383c40846..d5cd707754c2f 100644 --- a/src/tools/clippy/tests/ui/single_call_fn.stderr +++ b/src/tools/clippy/tests/ui/single_call_fn.stderr @@ -1,5 +1,5 @@ error: this function is only used once - --> $DIR/single_call_fn.rs:33:1 + --> $DIR/single_call_fn.rs:34:1 | LL | / fn c() { LL | | println!("really"); @@ -9,7 +9,7 @@ LL | | } | |_^ | help: used here - --> $DIR/single_call_fn.rs:40:5 + --> $DIR/single_call_fn.rs:41:5 | LL | c(); | ^ @@ -17,37 +17,37 @@ LL | c(); = help: to override `-D warnings` add `#[allow(clippy::single_call_fn)]` error: this function is only used once - --> $DIR/single_call_fn.rs:12:1 + --> $DIR/single_call_fn.rs:13:1 | LL | fn i() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:17:13 + --> $DIR/single_call_fn.rs:18:13 | LL | let a = i; | ^ error: this function is only used once - --> $DIR/single_call_fn.rs:43:1 + --> $DIR/single_call_fn.rs:44:1 | LL | fn a() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:46:5 + --> $DIR/single_call_fn.rs:47:5 | LL | a(); | ^ error: this function is only used once - --> $DIR/single_call_fn.rs:13:1 + --> $DIR/single_call_fn.rs:14:1 | LL | fn j() {} | ^^^^^^^^^ | help: used here - --> $DIR/single_call_fn.rs:24:9 + --> $DIR/single_call_fn.rs:25:9 | LL | j(); | ^ diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs index f8d85ed38cd5a..5c3086c9d69ea 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -1,5 +1,5 @@ -use std::iter::repeat; //@no-rustfix +use std::iter::repeat; fn main() { resize_vector(); extend_vector(); @@ -86,6 +86,20 @@ fn from_empty_vec() { vec1 = Vec::new(); vec1.resize(10, 0); //~^ ERROR: slow zero-filling initialization + + vec1 = vec![]; + vec1.resize(10, 0); + //~^ ERROR: slow zero-filling initialization + + macro_rules! x { + () => { + vec![] + }; + } + + // `vec![]` comes from another macro, don't warn + vec1 = x!(); + vec1.resize(10, 0); } fn do_stuff(vec: &mut [u8]) {} diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr index f4501551656a2..4d24400ecb596 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -97,8 +97,16 @@ LL | vec1 = Vec::new(); LL | vec1.resize(10, 0); | ^^^^^^^^^^^^^^^^^^ +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:91:5 + | +LL | vec1 = vec![]; + | ------ help: consider replacing this with: `vec![0; 10]` +LL | vec1.resize(10, 0); + | ^^^^^^^^^^^^^^^^^^ + error: this argument is a mutable reference, but not used mutably - --> $DIR/slow_vector_initialization.rs:91:18 + --> $DIR/slow_vector_initialization.rs:105:18 | LL | fn do_stuff(vec: &mut [u8]) {} | ^^^^^^^^^ help: consider changing to: `&[u8]` @@ -106,5 +114,5 @@ LL | fn do_stuff(vec: &mut [u8]) {} = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]` -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed new file mode 100644 index 0000000000000..8027c053fb5b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -0,0 +1,62 @@ +#![warn(clippy::std_instead_of_core)] +#![allow(unused_imports)] + +extern crate alloc; + +#[warn(clippy::std_instead_of_core)] +fn std_instead_of_core() { + // Regular import + use core::hash::Hasher; + //~^ ERROR: used import from `std` instead of `core` + // Absolute path + use ::core::hash::Hash; + //~^ ERROR: used import from `std` instead of `core` + // Don't lint on `env` macro + use std::env; + + // Multiple imports + use core::fmt::{Debug, Result}; + //~^ ERROR: used import from `std` instead of `core` + + // Function calls + let ptr = core::ptr::null::(); + //~^ ERROR: used import from `std` instead of `core` + let ptr_mut = ::core::ptr::null_mut::(); + //~^ ERROR: used import from `std` instead of `core` + + // Types + let cell = core::cell::Cell::new(8u32); + //~^ ERROR: used import from `std` instead of `core` + let cell_absolute = ::core::cell::Cell::new(8u32); + //~^ ERROR: used import from `std` instead of `core` + + let _ = std::env!("PATH"); + + // do not lint until `error_in_core` is stable + use std::error::Error; + + // lint items re-exported from private modules, `core::iter::traits::iterator::Iterator` + use core::iter::Iterator; + //~^ ERROR: used import from `std` instead of `core` +} + +#[warn(clippy::std_instead_of_alloc)] +fn std_instead_of_alloc() { + // Only lint once. + use alloc::vec; + //~^ ERROR: used import from `std` instead of `alloc` + use alloc::vec::Vec; + //~^ ERROR: used import from `std` instead of `alloc` +} + +#[warn(clippy::alloc_instead_of_core)] +fn alloc_instead_of_core() { + use core::slice::from_ref; + //~^ ERROR: used import from `alloc` instead of `core` +} + +fn main() { + std_instead_of_core(); + std_instead_of_alloc(); + alloc_instead_of_core(); +} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index 2e44487d77e35..63a096384d716 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -17,7 +17,6 @@ fn std_instead_of_core() { // Multiple imports use std::fmt::{Debug, Result}; //~^ ERROR: used import from `std` instead of `core` - //~| ERROR: used import from `std` instead of `core` // Function calls let ptr = std::ptr::null::(); diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index 7435d716157ae..ca26f77bd37f9 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -2,103 +2,76 @@ error: used import from `std` instead of `core` --> $DIR/std_instead_of_core.rs:9:9 | LL | use std::hash::Hasher; - | ^^^^^^^^^^^^^^^^^ + | ^^^ help: consider importing the item from `core`: `core` | - = help: consider importing the item from `core` = note: `-D clippy::std-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:12:9 + --> $DIR/std_instead_of_core.rs:12:11 | LL | use ::std::hash::Hash; - | ^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:20 + --> $DIR/std_instead_of_core.rs:18:9 | LL | use std::fmt::{Debug, Result}; - | ^^^^^ - | - = help: consider importing the item from `core` - -error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:18:27 - | -LL | use std::fmt::{Debug, Result}; - | ^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:23:15 + --> $DIR/std_instead_of_core.rs:22:15 | LL | let ptr = std::ptr::null::(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:25:19 + --> $DIR/std_instead_of_core.rs:24:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:29:16 + --> $DIR/std_instead_of_core.rs:28:16 | LL | let cell = std::cell::Cell::new(8u32); - | ^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:31:25 + --> $DIR/std_instead_of_core.rs:30:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> $DIR/std_instead_of_core.rs:40:9 + --> $DIR/std_instead_of_core.rs:39:9 | LL | use std::iter::Iterator; - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider importing the item from `core` + | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:47:9 + --> $DIR/std_instead_of_core.rs:46:9 | LL | use std::vec; - | ^^^^^^^^ + | ^^^ help: consider importing the item from `alloc`: `alloc` | - = help: consider importing the item from `alloc` = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> $DIR/std_instead_of_core.rs:49:9 + --> $DIR/std_instead_of_core.rs:48:9 | LL | use std::vec::Vec; - | ^^^^^^^^^^^^^ - | - = help: consider importing the item from `alloc` + | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> $DIR/std_instead_of_core.rs:55:9 + --> $DIR/std_instead_of_core.rs:54:9 | LL | use alloc::slice::from_ref; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ help: consider importing the item from `core`: `core` | - = help: consider importing the item from `core` = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_32bit.stderr b/src/tools/clippy/tests/ui/transmute_32bit.stderr index 75ddca60d2aaa..baa819e30fded 100644 --- a/src/tools/clippy/tests/ui/transmute_32bit.stderr +++ b/src/tools/clippy/tests/ui/transmute_32bit.stderr @@ -1,39 +1,29 @@ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `f32` to a pointer --> $DIR/transmute_32bit.rs:6:31 | LL | let _: *const usize = std::mem::transmute(6.0f32); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: source type: `f32` (32 bits) - = note: target type: `*const usize` (64 bits) + = note: `-D clippy::wrong-transmute` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::wrong_transmute)]` -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `f32` to a pointer --> $DIR/transmute_32bit.rs:8:29 | LL | let _: *mut usize = std::mem::transmute(6.0f32); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `f32` (32 bits) - = note: target type: `*mut usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `char` to a pointer --> $DIR/transmute_32bit.rs:10:31 | LL | let _: *const usize = std::mem::transmute('x'); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `char` (32 bits) - = note: target type: `*const usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: transmute from a `char` to a pointer --> $DIR/transmute_32bit.rs:12:29 | LL | let _: *mut usize = std::mem::transmute('x'); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: source type: `char` (32 bits) - = note: target type: `*mut usize` (64 bits) + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0512`. diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed index b5dedef5f4c87..f3cf65da2d6ad 100644 --- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)] +#![allow(clippy::non_canonical_clone_impl, unused)] #![warn(clippy::unnecessary_struct_initialization)] struct S { diff --git a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs index 2222c3ddf3708..bd5302f9d85db 100644 --- a/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/tests/ui/unnecessary_struct_initialization.rs @@ -1,4 +1,4 @@ -#![allow(clippy::incorrect_clone_impl_on_copy_type, unused)] +#![allow(clippy::non_canonical_clone_impl, unused)] #![warn(clippy::unnecessary_struct_initialization)] struct S { diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed index 3ff2acbe28f49..bcbca971a7820 100644 --- a/src/tools/clippy/tests/ui/vec.fixed +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -120,6 +120,7 @@ fn issue11075() { stringify!($e) }; } + #[allow(clippy::never_loop)] for _string in [repro!(true), repro!(null)] { unimplemented!(); } diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs index 2ab025f424ac6..087425585de8b 100644 --- a/src/tools/clippy/tests/ui/vec.rs +++ b/src/tools/clippy/tests/ui/vec.rs @@ -120,6 +120,7 @@ fn issue11075() { stringify!($e) }; } + #[allow(clippy::never_loop)] for _string in vec![repro!(true), repro!(null)] { unimplemented!(); } diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr index a28024d236a1e..fc261838fe36d 100644 --- a/src/tools/clippy/tests/ui/vec.stderr +++ b/src/tools/clippy/tests/ui/vec.stderr @@ -86,31 +86,31 @@ 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:124: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:141: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:141: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:160: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:164:14 | LL | for a in vec![String::new(), String::new()] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[String::new(), String::new()]` diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr index fc24dba45437c..6d382a267ad81 100644 --- a/src/tools/clippy/tests/ui/write_literal_2.stderr +++ b/src/tools/clippy/tests/ui/write_literal_2.stderr @@ -2,7 +2,9 @@ error: unnecessary raw string literal --> $DIR/write_literal_2.rs:13:24 | LL | writeln!(v, r"{}", r"{hello}"); - | ^^^^^^^^^^ help: try: `"{hello}"` + | -^^^^^^^^^ + | | + | help: use a string literal instead: `"{hello}"` | = note: `-D clippy::needless-raw-strings` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_raw_strings)]`