diff --git a/.github/driver.sh b/.github/driver.sh index 6ff189fc8592..798782340ee7 100644 --- a/.github/driver.sh +++ b/.github/driver.sh @@ -17,6 +17,13 @@ test "$sysroot" = $desired_sysroot sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot) test "$sysroot" = $desired_sysroot +# Check that the --sysroot argument is only passed once (SYSROOT is ignored) +( + cd rustc_tools_util + touch src/lib.rs + SYSROOT=/tmp RUSTFLAGS="--sysroot=$(rustc --print sysroot)" ../target/debug/cargo-clippy clippy --verbose +) + # Make sure this isn't set - clippy-driver should cope without it unset CARGO_MANIFEST_DIR diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 6448b2d4068d..24e677ce8e17 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -82,13 +82,6 @@ jobs: with: github_token: "${{ secrets.github_token }}" - - name: Install dependencies (Linux-i686) - run: | - sudo dpkg --add-architecture i386 - sudo apt-get update - sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386 - if: matrix.host == 'i686-unknown-linux-gnu' - - name: Checkout uses: actions/checkout@v3.0.2 @@ -164,6 +157,11 @@ jobs: - name: Test metadata collection run: cargo collect-metadata + - name: Test lint_configuration.md is up-to-date + run: | + echo "run \`cargo collect-metadata\` if this fails" + git update-index --refresh + integration_build: needs: changelog runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index f593966c0459..659e8aebcd57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,521 @@ All notable changes to this project will be documented in this file. See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this document. -## Unreleased / In Rust Nightly +## Unreleased / Beta / In Rust Nightly -[3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master) +[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master) + +## Rust 1.67 + +Current stable, released 2023-01-26 + +[4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d) + +### New Lints + +* [`seek_from_current`] + [#9681](https://github.com/rust-lang/rust-clippy/pull/9681) +* [`from_raw_with_void_ptr`] + [#9690](https://github.com/rust-lang/rust-clippy/pull/9690) +* [`misnamed_getters`] + [#9770](https://github.com/rust-lang/rust-clippy/pull/9770) +* [`seek_to_start_instead_of_rewind`] + [#9667](https://github.com/rust-lang/rust-clippy/pull/9667) +* [`suspicious_xor_used_as_pow`] + [#9506](https://github.com/rust-lang/rust-clippy/pull/9506) +* [`unnecessary_safety_doc`] + [#9822](https://github.com/rust-lang/rust-clippy/pull/9822) +* [`unchecked_duration_subtraction`] + [#9570](https://github.com/rust-lang/rust-clippy/pull/9570) +* [`manual_is_ascii_check`] + [#9765](https://github.com/rust-lang/rust-clippy/pull/9765) +* [`unnecessary_safety_comment`] + [#9851](https://github.com/rust-lang/rust-clippy/pull/9851) +* [`let_underscore_future`] + [#9760](https://github.com/rust-lang/rust-clippy/pull/9760) +* [`manual_let_else`] + [#8437](https://github.com/rust-lang/rust-clippy/pull/8437) + +### Moves and Deprecations + +* Moved [`uninlined_format_args`] to `style` (Now warn-by-default) + [#9865](https://github.com/rust-lang/rust-clippy/pull/9865) +* Moved [`needless_collect`] to `nursery` (Now allow-by-default) + [#9705](https://github.com/rust-lang/rust-clippy/pull/9705) +* Moved [`or_fun_call`] to `nursery` (Now allow-by-default) + [#9829](https://github.com/rust-lang/rust-clippy/pull/9829) +* Uplifted [`let_underscore_lock`] into rustc + [#9697](https://github.com/rust-lang/rust-clippy/pull/9697) +* Uplifted [`let_underscore_drop`] into rustc + [#9697](https://github.com/rust-lang/rust-clippy/pull/9697) +* Moved [`bool_to_int_with_if`] to `pedantic` (Now allow-by-default) + [#9830](https://github.com/rust-lang/rust-clippy/pull/9830) +* Move `index_refutable_slice` to `pedantic` (Now warn-by-default) + [#9975](https://github.com/rust-lang/rust-clippy/pull/9975) +* Moved [`manual_clamp`] to `nursery` (Now allow-by-default) + [#10101](https://github.com/rust-lang/rust-clippy/pull/10101) + +### Enhancements + +* The scope of `#![clippy::msrv]` is now tracked correctly + [#9924](https://github.com/rust-lang/rust-clippy/pull/9924) +* `#[clippy::msrv]` can now be used as an outer attribute + [#9860](https://github.com/rust-lang/rust-clippy/pull/9860) +* Clippy will now avoid Cargo's cache, if `Cargo.toml` or `clippy.toml` have changed + [#9707](https://github.com/rust-lang/rust-clippy/pull/9707) +* [`uninlined_format_args`]: Added a new config `allow-mixed-uninlined-format-args` to allow the + lint, if only some arguments can be inlined + [#9865](https://github.com/rust-lang/rust-clippy/pull/9865) +* [`needless_lifetimes`]: Now provides suggests for individual lifetimes + [#9743](https://github.com/rust-lang/rust-clippy/pull/9743) +* [`needless_collect`]: Now detects needless `is_empty` and `contains` calls + [#8744](https://github.com/rust-lang/rust-clippy/pull/8744) +* [`blanket_clippy_restriction_lints`]: Now lints, if `clippy::restriction` is enabled via the + command line arguments + [#9755](https://github.com/rust-lang/rust-clippy/pull/9755) +* [`mutable_key_type`]: Now has the `ignore-interior-mutability` configuration, to add types which + should be ignored by the lint + [#9692](https://github.com/rust-lang/rust-clippy/pull/9692) +* [`uninlined_format_args`]: Now works for multiline `format!` expressions + [#9945](https://github.com/rust-lang/rust-clippy/pull/9945) +* [`cognitive_complexity`]: Now works for async functions + [#9828](https://github.com/rust-lang/rust-clippy/pull/9828) + [#9836](https://github.com/rust-lang/rust-clippy/pull/9836) +* [`vec_box`]: Now avoids an off-by-one error when using the `vec-box-size-threshold` configuration + [#9848](https://github.com/rust-lang/rust-clippy/pull/9848) +* [`never_loop`]: Now correctly handles breaks in nested labeled blocks + [#9858](https://github.com/rust-lang/rust-clippy/pull/9858) + [#9837](https://github.com/rust-lang/rust-clippy/pull/9837) +* [`disallowed_methods`], [`disallowed_types`], [`disallowed_macros`]: Now correctly resolve + paths, if a crate is used multiple times with different versions + [#9800](https://github.com/rust-lang/rust-clippy/pull/9800) +* [`disallowed_methods`]: Can now be used for local methods + [#9800](https://github.com/rust-lang/rust-clippy/pull/9800) +* [`print_stdout`], [`print_stderr`]: Can now be enabled in test with the `allow-print-in-tests` + config value + [#9797](https://github.com/rust-lang/rust-clippy/pull/9797) +* [`from_raw_with_void_ptr`]: Now works for `Rc`, `Arc`, `alloc::rc::Weak` and + `alloc::sync::Weak` types. + [#9700](https://github.com/rust-lang/rust-clippy/pull/9700) +* [`needless_borrowed_reference`]: Now works for struct and tuple patterns with wildcards + [#9855](https://github.com/rust-lang/rust-clippy/pull/9855) +* [`or_fun_call`]: Now supports `map_or` methods + [#9689](https://github.com/rust-lang/rust-clippy/pull/9689) +* [`unwrap_used`], [`expect_used`]: No longer lints in test code + [#9686](https://github.com/rust-lang/rust-clippy/pull/9686) +* [`fn_params_excessive_bools`]: Is now emitted with the lint level at the linted function + [#9698](https://github.com/rust-lang/rust-clippy/pull/9698) + +### False Positive Fixes + +* [`new_ret_no_self`]: No longer lints when `impl Trait` is returned + [#9733](https://github.com/rust-lang/rust-clippy/pull/9733) +* [`unnecessary_lazy_evaluations`]: No longer lints, if the type has a significant drop + [#9750](https://github.com/rust-lang/rust-clippy/pull/9750) +* [`option_if_let_else`]: No longer lints, if any arm has guard + [#9747](https://github.com/rust-lang/rust-clippy/pull/9747) +* [`explicit_auto_deref`]: No longer lints, if the target type is a projection with generic + arguments + [#9813](https://github.com/rust-lang/rust-clippy/pull/9813) +* [`unnecessary_to_owned`]: No longer lints, if the suggestion effects types + [#9796](https://github.com/rust-lang/rust-clippy/pull/9796) +* [`needless_borrow`]: No longer lints, if the suggestion is affected by `Deref` + [#9674](https://github.com/rust-lang/rust-clippy/pull/9674) +* [`unused_unit`]: No longer lints, if lifetimes are bound to the return type + [#9849](https://github.com/rust-lang/rust-clippy/pull/9849) +* [`mut_mut`]: No longer lints cases with unsized mutable references + [#9835](https://github.com/rust-lang/rust-clippy/pull/9835) +* [`bool_to_int_with_if`]: No longer lints in const context + [#9738](https://github.com/rust-lang/rust-clippy/pull/9738) +* [`use_self`]: No longer lints in macros + [#9704](https://github.com/rust-lang/rust-clippy/pull/9704) +* [`unnecessary_operation`]: No longer lints, if multiple macros are involved + [#9981](https://github.com/rust-lang/rust-clippy/pull/9981) +* [`allow_attributes_without_reason`]: No longer lints inside external macros + [#9630](https://github.com/rust-lang/rust-clippy/pull/9630) +* [`question_mark`]: No longer lints for `if let Err()` with an `else` branch + [#9722](https://github.com/rust-lang/rust-clippy/pull/9722) +* [`unnecessary_cast`]: No longer lints if the identifier and cast originate from different macros + [#9980](https://github.com/rust-lang/rust-clippy/pull/9980) +* [`arithmetic_side_effects`]: Now detects operations with associated constants + [#9592](https://github.com/rust-lang/rust-clippy/pull/9592) +* [`explicit_auto_deref`]: No longer lints, if the initial value is not a reference or reference + receiver + [#9997](https://github.com/rust-lang/rust-clippy/pull/9997) +* [`module_name_repetitions`], [`single_component_path_imports`]: Now handle `#[allow]` + attributes correctly + [#9879](https://github.com/rust-lang/rust-clippy/pull/9879) +* [`bool_to_int_with_if`]: No longer lints `if let` statements + [#9714](https://github.com/rust-lang/rust-clippy/pull/9714) +* [`needless_borrow`]: No longer lints, `if`-`else`-statements that require the borrow + [#9791](https://github.com/rust-lang/rust-clippy/pull/9791) +* [`needless_borrow`]: No longer lints borrows, if moves were illegal + [#9711](https://github.com/rust-lang/rust-clippy/pull/9711) +* [`manual_swap`]: No longer lints in const context + [#9871](https://github.com/rust-lang/rust-clippy/pull/9871) + +### Suggestion Fixes/Improvements + +* [`missing_safety_doc`], [`missing_errors_doc`], [`missing_panics_doc`]: No longer show the + entire item in the lint emission. + [#9772](https://github.com/rust-lang/rust-clippy/pull/9772) +* [`needless_lifetimes`]: Only suggests `'_` when it's applicable + [#9743](https://github.com/rust-lang/rust-clippy/pull/9743) +* [`use_self`]: Now suggests full paths correctly + [#9726](https://github.com/rust-lang/rust-clippy/pull/9726) +* [`redundant_closure_call`]: Now correctly deals with macros during suggestion creation + [#9987](https://github.com/rust-lang/rust-clippy/pull/9987) +* [`unnecessary_cast`]: Suggestions now correctly deal with references + [#9996](https://github.com/rust-lang/rust-clippy/pull/9996) +* [`unnecessary_join`]: Suggestions now correctly use [turbofish] operators + [#9779](https://github.com/rust-lang/rust-clippy/pull/9779) +* [`equatable_if_let`]: Can now suggest `matches!` replacements + [#9368](https://github.com/rust-lang/rust-clippy/pull/9368) +* [`string_extend_chars`]: Suggestions now correctly work for `str` slices + [#9741](https://github.com/rust-lang/rust-clippy/pull/9741) +* [`redundant_closure_for_method_calls`]: Suggestions now include angle brackets and generic + arguments if needed + [#9745](https://github.com/rust-lang/rust-clippy/pull/9745) +* [`manual_let_else`]: Suggestions no longer expand macro calls + [#9943](https://github.com/rust-lang/rust-clippy/pull/9943) +* [`infallible_destructuring_match`]: Suggestions now preserve references + [#9850](https://github.com/rust-lang/rust-clippy/pull/9850) +* [`result_large_err`]: The error now shows the largest enum variant + [#9662](https://github.com/rust-lang/rust-clippy/pull/9662) +* [`needless_return`]: Suggestions are now formatted better + [#9967](https://github.com/rust-lang/rust-clippy/pull/9967) +* [`unused_rounding`]: The suggestion now preserves the original float literal notation + [#9870](https://github.com/rust-lang/rust-clippy/pull/9870) + +[turbofish]: https://turbo.fish/::%3CClippy%3E + +### ICE Fixes + +* [`result_large_err`]: Fixed ICE for empty enums + [#10007](https://github.com/rust-lang/rust-clippy/pull/10007) +* [`redundant_allocation`]: Fixed ICE for types with bounded variables + [#9773](https://github.com/rust-lang/rust-clippy/pull/9773) +* [`unused_rounding`]: Fixed ICE, if `_` was used as a separator + [#10001](https://github.com/rust-lang/rust-clippy/pull/10001) + +## Rust 1.66 + +Released 2022-12-15 + +[b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1) + +### New Lints + +* [`manual_clamp`] + [#9484](https://github.com/rust-lang/rust-clippy/pull/9484) +* [`missing_trait_methods`] + [#9670](https://github.com/rust-lang/rust-clippy/pull/9670) +* [`unused_format_specs`] + [#9637](https://github.com/rust-lang/rust-clippy/pull/9637) +* [`iter_kv_map`] + [#9409](https://github.com/rust-lang/rust-clippy/pull/9409) +* [`manual_filter`] + [#9451](https://github.com/rust-lang/rust-clippy/pull/9451) +* [`box_default`] + [#9511](https://github.com/rust-lang/rust-clippy/pull/9511) +* [`implicit_saturating_add`] + [#9549](https://github.com/rust-lang/rust-clippy/pull/9549) +* [`as_ptr_cast_mut`] + [#9572](https://github.com/rust-lang/rust-clippy/pull/9572) +* [`disallowed_macros`] + [#9495](https://github.com/rust-lang/rust-clippy/pull/9495) +* [`partial_pub_fields`] + [#9658](https://github.com/rust-lang/rust-clippy/pull/9658) +* [`uninlined_format_args`] + [#9233](https://github.com/rust-lang/rust-clippy/pull/9233) +* [`cast_nan_to_int`] + [#9617](https://github.com/rust-lang/rust-clippy/pull/9617) + +### Moves and Deprecations + +* `positional_named_format_parameters` was uplifted to rustc under the new name + `named_arguments_used_positionally` + [#8518](https://github.com/rust-lang/rust-clippy/pull/8518) +* Moved [`implicit_saturating_sub`] to `style` (Now warn-by-default) + [#9584](https://github.com/rust-lang/rust-clippy/pull/9584) +* Moved `derive_partial_eq_without_eq` to `nursery` (now allow-by-default) + [#9536](https://github.com/rust-lang/rust-clippy/pull/9536) + +### Enhancements + +* [`nonstandard_macro_braces`]: Now includes `matches!()` in the default lint config + [#9471](https://github.com/rust-lang/rust-clippy/pull/9471) +* [`suboptimal_flops`]: Now supports multiplication and subtraction operations + [#9581](https://github.com/rust-lang/rust-clippy/pull/9581) +* [`arithmetic_side_effects`]: Now detects cases with literals behind references + [#9587](https://github.com/rust-lang/rust-clippy/pull/9587) +* [`upper_case_acronyms`]: Now also checks enum names + [#9580](https://github.com/rust-lang/rust-clippy/pull/9580) +* [`needless_borrowed_reference`]: Now lints nested patterns + [#9573](https://github.com/rust-lang/rust-clippy/pull/9573) +* [`unnecessary_cast`]: Now works for non-trivial non-literal expressions + [#9576](https://github.com/rust-lang/rust-clippy/pull/9576) +* [`arithmetic_side_effects`]: Now detects operations with custom types + [#9559](https://github.com/rust-lang/rust-clippy/pull/9559) +* [`disallowed_methods`], [`disallowed_types`]: Not correctly lints types, functions and macros + with the same path + [#9495](https://github.com/rust-lang/rust-clippy/pull/9495) +* [`self_named_module_files`], [`mod_module_files`]: Now take remapped path prefixes into account + [#9475](https://github.com/rust-lang/rust-clippy/pull/9475) +* [`bool_to_int_with_if`]: Now detects the inverse if case + [#9476](https://github.com/rust-lang/rust-clippy/pull/9476) + +### False Positive Fixes + +* [`arithmetic_side_effects`]: Now allows operations that can't overflow + [#9474](https://github.com/rust-lang/rust-clippy/pull/9474) +* [`unnecessary_lazy_evaluations`]: No longer lints in external macros + [#9486](https://github.com/rust-lang/rust-clippy/pull/9486) +* [`needless_borrow`], [`explicit_auto_deref`]: No longer lint on unions that require the reference + [#9490](https://github.com/rust-lang/rust-clippy/pull/9490) +* [`almost_complete_letter_range`]: No longer lints in external macros + [#9467](https://github.com/rust-lang/rust-clippy/pull/9467) +* [`drop_copy`]: No longer lints on idiomatic cases in match arms + [#9491](https://github.com/rust-lang/rust-clippy/pull/9491) +* [`question_mark`]: No longer lints in const context + [#9487](https://github.com/rust-lang/rust-clippy/pull/9487) +* [`collapsible_if`]: Suggestion now work in macros + [#9410](https://github.com/rust-lang/rust-clippy/pull/9410) +* [`std_instead_of_core`]: No longer triggers on unstable modules + [#9545](https://github.com/rust-lang/rust-clippy/pull/9545) +* [`unused_peekable`]: No longer lints, if the peak is done in a closure or function + [#9465](https://github.com/rust-lang/rust-clippy/pull/9465) +* [`useless_attribute`]: No longer lints on `#[allow]` attributes for [`unsafe_removed_from_name`] + [#9593](https://github.com/rust-lang/rust-clippy/pull/9593) +* [`unnecessary_lazy_evaluations`]: No longer suggest switching to early evaluation when type has + custom `Drop` implementation + [#9551](https://github.com/rust-lang/rust-clippy/pull/9551) +* [`unnecessary_cast`]: No longer lints on negative hexadecimal literals when cast as floats + [#9609](https://github.com/rust-lang/rust-clippy/pull/9609) +* [`use_self`]: No longer lints in proc macros + [#9454](https://github.com/rust-lang/rust-clippy/pull/9454) +* [`never_loop`]: Now takes `let ... else` statements into consideration. + [#9496](https://github.com/rust-lang/rust-clippy/pull/9496) +* [`default_numeric_fallback`]: Now ignores constants + [#9636](https://github.com/rust-lang/rust-clippy/pull/9636) +* [`uninit_vec`]: No longer lints `Vec::set_len(0)` + [#9519](https://github.com/rust-lang/rust-clippy/pull/9519) +* [`arithmetic_side_effects`]: Now ignores references to integer types + [#9507](https://github.com/rust-lang/rust-clippy/pull/9507) +* [`large_stack_arrays`]: No longer lints inside static items + [#9466](https://github.com/rust-lang/rust-clippy/pull/9466) +* [`ref_option_ref`]: No longer lints if the inner reference is mutable + [#9684](https://github.com/rust-lang/rust-clippy/pull/9684) +* [`ptr_arg`]: No longer lints if the argument is used as an incomplete trait object + [#9645](https://github.com/rust-lang/rust-clippy/pull/9645) +* [`should_implement_trait`]: Now also works for `default` methods + [#9546](https://github.com/rust-lang/rust-clippy/pull/9546) + +### Suggestion Fixes/Improvements + +* [`derivable_impls`]: The suggestion is now machine applicable + [#9429](https://github.com/rust-lang/rust-clippy/pull/9429) +* [`match_single_binding`]: The suggestion now handles scrutinies with side effects better + [#9601](https://github.com/rust-lang/rust-clippy/pull/9601) +* [`zero_prefixed_literal`]: Only suggests using octal numbers, if this is possible + [#9652](https://github.com/rust-lang/rust-clippy/pull/9652) +* [`rc_buffer`]: The suggestion is no longer machine applicable to avoid semantic changes + [#9633](https://github.com/rust-lang/rust-clippy/pull/9633) +* [`print_literal`], [`write_literal`], [`uninlined_format_args`]: The suggestion now ignores + comments after the macro call. + [#9586](https://github.com/rust-lang/rust-clippy/pull/9586) +* [`expect_fun_call`]:Improved the suggestion for `format!` calls with captured variables + [#9586](https://github.com/rust-lang/rust-clippy/pull/9586) +* [`nonstandard_macro_braces`]: The suggestion is now machine applicable and will no longer + replace brackets inside the macro argument. + [#9499](https://github.com/rust-lang/rust-clippy/pull/9499) +* [`from_over_into`]: The suggestion is now a machine applicable and contains explanations + [#9649](https://github.com/rust-lang/rust-clippy/pull/9649) +* [`needless_return`]: The automatic suggestion now removes all required semicolons + [#9497](https://github.com/rust-lang/rust-clippy/pull/9497) +* [`to_string_in_format_args`]: The suggestion now keeps parenthesis around values + [#9590](https://github.com/rust-lang/rust-clippy/pull/9590) +* [`manual_assert`]: The suggestion now preserves comments + [#9479](https://github.com/rust-lang/rust-clippy/pull/9479) +* [`redundant_allocation`]: The suggestion applicability is now marked `MaybeIncorrect` to + avoid semantic changes + [#9634](https://github.com/rust-lang/rust-clippy/pull/9634) +* [`assertions_on_result_states`]: The suggestion has been corrected, for cases where the + `assert!` is not in a statement. + [#9453](https://github.com/rust-lang/rust-clippy/pull/9453) +* [`nonminimal_bool`]: The suggestion no longer expands macros + [#9457](https://github.com/rust-lang/rust-clippy/pull/9457) +* [`collapsible_match`]: Now specifies field names, when a struct is destructed + [#9685](https://github.com/rust-lang/rust-clippy/pull/9685) +* [`unnecessary_cast`]: The suggestion now adds parenthesis for negative numbers + [#9577](https://github.com/rust-lang/rust-clippy/pull/9577) +* [`redundant_closure`]: The suggestion now works for `impl FnMut` arguments + [#9556](https://github.com/rust-lang/rust-clippy/pull/9556) + +### ICE Fixes + +* [`unnecessary_to_owned`]: Avoid ICEs in favor of false negatives if information is missing + [#9505](https://github.com/rust-lang/rust-clippy/pull/9505) + [#10027](https://github.com/rust-lang/rust-clippy/pull/10027) +* [`manual_range_contains`]: No longer ICEs on values behind references + [#9627](https://github.com/rust-lang/rust-clippy/pull/9627) +* [`needless_pass_by_value`]: No longer ICEs on unsized `dyn Fn` arguments + [#9531](https://github.com/rust-lang/rust-clippy/pull/9531) +* `*_interior_mutable_const` lints: no longer ICE on const unions containing `!Freeze` types + [#9539](https://github.com/rust-lang/rust-clippy/pull/9539) + +### Others + +* Released `rustc_tools_util` for version information on `Crates.io`. (Further adjustments will + not be published as part of this changelog) + +## Rust 1.65 + +Released 2022-11-03 + +[3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523) + +### Important Changes + +* Clippy now has an `--explain ` command to show the lint description in the console + [#8952](https://github.com/rust-lang/rust-clippy/pull/8952) + +### New Lints + +* [`unused_peekable`] + [#9258](https://github.com/rust-lang/rust-clippy/pull/9258) +* [`collapsible_str_replace`] + [#9269](https://github.com/rust-lang/rust-clippy/pull/9269) +* [`manual_string_new`] + [#9295](https://github.com/rust-lang/rust-clippy/pull/9295) +* [`iter_on_empty_collections`] + [#9187](https://github.com/rust-lang/rust-clippy/pull/9187) +* [`iter_on_single_items`] + [#9187](https://github.com/rust-lang/rust-clippy/pull/9187) +* [`bool_to_int_with_if`] + [#9412](https://github.com/rust-lang/rust-clippy/pull/9412) +* [`multi_assignments`] + [#9379](https://github.com/rust-lang/rust-clippy/pull/9379) +* [`result_large_err`] + [#9373](https://github.com/rust-lang/rust-clippy/pull/9373) +* [`partialeq_to_none`] + [#9288](https://github.com/rust-lang/rust-clippy/pull/9288) +* [`suspicious_to_owned`] + [#8984](https://github.com/rust-lang/rust-clippy/pull/8984) +* [`cast_slice_from_raw_parts`] + [#9247](https://github.com/rust-lang/rust-clippy/pull/9247) +* [`manual_instant_elapsed`] + [#9264](https://github.com/rust-lang/rust-clippy/pull/9264) + +### Moves and Deprecations + +* Moved [`significant_drop_in_scrutinee`] to `nursery` (now allow-by-default) + [#9302](https://github.com/rust-lang/rust-clippy/pull/9302) +* Rename `logic_bug` to [`overly_complex_bool_expr`] + [#9306](https://github.com/rust-lang/rust-clippy/pull/9306) +* Rename `arithmetic` to [`arithmetic_side_effects`] + [#9443](https://github.com/rust-lang/rust-clippy/pull/9443) +* Moved [`only_used_in_recursion`] to complexity (now warn-by-default) + [#8804](https://github.com/rust-lang/rust-clippy/pull/8804) +* Moved [`assertions_on_result_states`] to restriction (now allow-by-default) + [#9273](https://github.com/rust-lang/rust-clippy/pull/9273) +* Renamed `blacklisted_name` to [`disallowed_names`] + [#8974](https://github.com/rust-lang/rust-clippy/pull/8974) + +### Enhancements + +* [`option_if_let_else`]: Now also checks for match expressions + [#8696](https://github.com/rust-lang/rust-clippy/pull/8696) +* [`explicit_auto_deref`]: Now lints on implicit returns in closures + [#9126](https://github.com/rust-lang/rust-clippy/pull/9126) +* [`needless_borrow`]: Now considers trait implementations + [#9136](https://github.com/rust-lang/rust-clippy/pull/9136) +* [`suboptimal_flops`], [`imprecise_flops`]: Now lint on constant expressions + [#9404](https://github.com/rust-lang/rust-clippy/pull/9404) +* [`if_let_mutex`]: Now detects mutex behind references and warns about deadlocks + [#9318](https://github.com/rust-lang/rust-clippy/pull/9318) + +### False Positive Fixes + +* [`unit_arg`] [`default_trait_access`] [`missing_docs_in_private_items`]: No longer + trigger in code generated from proc-macros + [#8694](https://github.com/rust-lang/rust-clippy/pull/8694) +* [`unwrap_used`]: Now lints uses of `unwrap_err` + [#9338](https://github.com/rust-lang/rust-clippy/pull/9338) +* [`expect_used`]: Now lints uses of `expect_err` + [#9338](https://github.com/rust-lang/rust-clippy/pull/9338) +* [`transmute_undefined_repr`]: Now longer lints if the first field is compatible + with the other type + [#9287](https://github.com/rust-lang/rust-clippy/pull/9287) +* [`unnecessary_to_owned`]: No longer lints, if type change cased errors in + the caller function + [#9424](https://github.com/rust-lang/rust-clippy/pull/9424) +* [`match_like_matches_macro`]: No longer lints, if there are comments inside the + match expression + [#9276](https://github.com/rust-lang/rust-clippy/pull/9276) +* [`partialeq_to_none`]: No longer trigger in code generated from macros + [#9389](https://github.com/rust-lang/rust-clippy/pull/9389) +* [`arithmetic_side_effects`]: No longer lints expressions that only use literals + [#9365](https://github.com/rust-lang/rust-clippy/pull/9365) +* [`explicit_auto_deref`]: Now ignores references on block expressions when the type + is `Sized`, on `dyn Trait` returns and when the suggestion is non-trivial + [#9126](https://github.com/rust-lang/rust-clippy/pull/9126) +* [`trait_duplication_in_bounds`]: Now better tracks bounds to avoid false positives + [#9167](https://github.com/rust-lang/rust-clippy/pull/9167) +* [`format_in_format_args`]: Now suggests cases where the result is formatted again + [#9349](https://github.com/rust-lang/rust-clippy/pull/9349) +* [`only_used_in_recursion`]: No longer lints on function without recursions and + takes external functions into account + [#8804](https://github.com/rust-lang/rust-clippy/pull/8804) +* [`missing_const_for_fn`]: No longer lints in proc-macros + [#9308](https://github.com/rust-lang/rust-clippy/pull/9308) +* [`non_ascii_literal`]: Allow non-ascii comments in tests and make sure `#[allow]` + attributes work in tests + [#9327](https://github.com/rust-lang/rust-clippy/pull/9327) +* [`question_mark`]: No longer lint `if let`s with subpatterns + [#9348](https://github.com/rust-lang/rust-clippy/pull/9348) +* [`needless_collect`]: No longer lints in loops + [#8992](https://github.com/rust-lang/rust-clippy/pull/8992) +* [`mut_mutex_lock`]: No longer lints if the mutex is behind an immutable reference + [#9418](https://github.com/rust-lang/rust-clippy/pull/9418) +* [`needless_return`]: Now ignores returns with arguments + [#9381](https://github.com/rust-lang/rust-clippy/pull/9381) +* [`range_plus_one`], [`range_minus_one`]: Now ignores code with macros + [#9446](https://github.com/rust-lang/rust-clippy/pull/9446) +* [`assertions_on_result_states`]: No longer lints on the unit type + [#9273](https://github.com/rust-lang/rust-clippy/pull/9273) + +### Suggestion Fixes/Improvements + +* [`unwrap_or_else_default`]: Now suggests `unwrap_or_default()` for empty strings + [#9421](https://github.com/rust-lang/rust-clippy/pull/9421) +* [`if_then_some_else_none`]: Now also suggests `bool::then_some` + [#9289](https://github.com/rust-lang/rust-clippy/pull/9289) +* [`redundant_closure_call`]: The suggestion now works for async closures + [#9053](https://github.com/rust-lang/rust-clippy/pull/9053) +* [`suboptimal_flops`]: Now suggests parenthesis when they are required + [#9394](https://github.com/rust-lang/rust-clippy/pull/9394) +* [`case_sensitive_file_extension_comparisons`]: Now suggests `map_or(..)` instead of `map(..).unwrap_or` + [#9341](https://github.com/rust-lang/rust-clippy/pull/9341) +* Deprecated configuration values can now be updated automatically + [#9252](https://github.com/rust-lang/rust-clippy/pull/9252) +* [`or_fun_call`]: Now suggest `Entry::or_default` for `Entry::or_insert(Default::default())` + [#9342](https://github.com/rust-lang/rust-clippy/pull/9342) +* [`unwrap_used`]: Only suggests `expect` if [`expect_used`] is allowed + [#9223](https://github.com/rust-lang/rust-clippy/pull/9223) + +### ICE Fixes + +* Fix ICE in [`useless_format`] for literals + [#9406](https://github.com/rust-lang/rust-clippy/pull/9406) +* Fix infinite loop in [`vec_init_then_push`] + [#9441](https://github.com/rust-lang/rust-clippy/pull/9441) +* Fix ICE when reading literals with weird proc-macro spans + [#9303](https://github.com/rust-lang/rust-clippy/pull/9303) ## Rust 1.64 -Current stable, released 2022-09-22 +Released 2022-09-22 [d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc) @@ -3731,6 +4239,7 @@ Released 2018-09-13 [`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason [`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range +[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects @@ -3822,6 +4331,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq +[`derived_hash_with_manual_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq [`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods @@ -3873,6 +4383,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes +[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file @@ -3888,6 +4399,7 @@ Released 2018-09-13 [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const [`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons +[`fn_null_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_null_check [`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any @@ -3903,6 +4415,7 @@ Released 2018-09-13 [`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into +[`from_raw_with_void_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_raw_with_void_ptr [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first @@ -3978,6 +4491,7 @@ Released 2018-09-13 [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop +[`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value @@ -3996,6 +4510,8 @@ Released 2018-09-13 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed +[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check +[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive @@ -4040,6 +4556,7 @@ Released 2018-09-13 [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os [`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order +[`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_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 @@ -4049,6 +4566,7 @@ Released 2018-09-13 [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop +[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression @@ -4060,6 +4578,7 @@ Released 2018-09-13 [`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments [`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions [`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl +[`multiple_unsafe_ops_per_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_unsafe_ops_per_block [`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate [`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref @@ -4134,10 +4653,12 @@ Released 2018-09-13 [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap +[`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch +[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence @@ -4196,10 +4717,14 @@ Released 2018-09-13 [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`seek_from_current`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current +[`seek_to_start_instead_of_rewind`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_to_start_instead_of_rewind [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned +[`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block +[`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block [`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse @@ -4219,6 +4744,7 @@ Released 2018-09-13 [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count +[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive @@ -4245,6 +4771,7 @@ Released 2018-09-13 [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting +[`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment @@ -4263,6 +4790,7 @@ Released 2018-09-13 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn [`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr [`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref @@ -4275,6 +4803,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc @@ -4296,6 +4825,8 @@ Released 2018-09-13 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings +[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment +[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned @@ -4315,6 +4846,7 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect +[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index dec13e44a17f..e3708bc48539 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,70 +1,3 @@ # The Rust Code of Conduct -A version of this document [can be found online](https://www.rust-lang.org/conduct.html). - -## Conduct - -**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) - -* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, - gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, - religion, nationality, or other similar characteristic. -* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and - welcoming environment for all. -* Please be kind and courteous. There's no need to be mean or rude. -* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and - numerous costs. There is seldom a right answer. -* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and - see how it works. -* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We - interpret the term "harassment" as including the definition in the Citizen - Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their - definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. -* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or - made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation - team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a - safe place for you and we've got your back. -* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. - -## Moderation - - -These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, -please contact the [Rust moderation team][mod_team]. - -1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, - are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) -2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. -3. Moderators will first respond to such remarks with a warning. -4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. -5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. -6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended - party a genuine apology. -7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a - different moderator, **in private**. Complaints about bans in-channel are not allowed. -8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate - situation, they should expect less leeway than others. - -In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically -unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly -if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can -drive people away from the community entirely. - -And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was -they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good -there was something you could've communicated better — remember that it's your responsibility to make your fellow -Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about -cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their -trust. - -The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, -#rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); -GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org -(users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the -maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider -explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. - -*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the -[Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* - -[mod_team]: https://www.rust-lang.org/team.html#Moderation-team +The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c977b2cacab..3158080d2b30 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,13 +23,14 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) - [Contributions](#contributions) + - [License](#license) [Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct ## The Clippy book -If you're new to Clippy and don't know where to start the [Clippy book] includes +If you're new to Clippy and don't know where to start, the [Clippy book] includes a [developer guide] and is a good place to start your journey. [Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html @@ -245,6 +246,38 @@ Contributions to Clippy should be made in the form of GitHub pull requests. Each be reviewed by a core contributor (someone with permission to land patches) and either landed in the main tree or given feedback for changes that would be required. +All PRs should include a `changelog` entry with a short comment explaining the change. The rule of thumb is basically, +"what do you believe is important from an outsider's perspective?" Often, PRs are only related to a single property of a +lint, and then it's good to mention that one. Otherwise, it's better to include too much detail than too little. + +Clippy's [changelog] is created from these comments. Every release, someone gets all commits from bors with a +`changelog: XYZ` entry and combines them into the changelog. This is a manual process. + +Examples: +- New lint + ``` + changelog: new lint: [`missing_trait_methods`] + ``` +- False positive fix + ``` + changelog: Fix [`unused_peekable`] false positive when peeked in a closure or called as `f(&mut peekable)` + ``` +- Purely internal change + ``` + changelog: none + ``` + +Note this it is fine for a PR to include multiple `changelog` entries, e.g.: +``` +changelog: Something 1 +changelog: Something 2 +changelog: Something 3 +``` + +[changelog]: CHANGELOG.md + +## License + All code in this repository is under the [Apache-2.0] or the [MIT] license. diff --git a/Cargo.toml b/Cargo.toml index 5223dca073fd..70d1268090f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.66" +version = "0.1.69" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -23,7 +23,7 @@ path = "src/driver.rs" [dependencies] clippy_lints = { path = "clippy_lints" } semver = "1.0" -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" tempfile = { version = "3.2", optional = true } termize = "0.1" @@ -42,7 +42,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies -clap = { version = "3.1", features = ["derive"] } +clap = { version = "4.1.4", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" @@ -56,7 +56,7 @@ tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" [build-dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/README.md b/README.md index a8a6b86d2a15..95f6d2cc45c8 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Clippy -[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto) [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license) A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. @@ -194,21 +194,25 @@ value` mapping e.g. ```toml avoid-breaking-exported-api = false disallowed-names = ["toto", "tata", "titi"] -cognitive-complexity-threshold = 30 ``` -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. +The [table of configurations](https://doc.rust-lang.org/nightly/clippy/lint_configuration.html) +contains all config values, their default, and a list of lints they affect. +Each [configurable lint](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration) +, also contains information about these values. -> **Note** -> -> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. +For configurations that are a list type with default values such as +[disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), +you can use the unique value `".."` to extend the default values instead of replacing them. + +```toml +# default of disallowed-names is ["foo", "baz", "quux"] +disallowed-names = ["bar", ".."] # -> ["bar", "foo", "baz", "quux"] +``` > **Note** > -> Configuration changes will not apply for code that has already been compiled and cached under `./target/`; -> for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure -> that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch. +> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints. To deactivate the “for further information visit *lint-link*” message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. @@ -230,7 +234,7 @@ in the `Cargo.toml` can be used. rust-version = "1.30" ``` -The MSRV can also be specified as an inner attribute, like below. +The MSRV can also be specified as an attribute, like below. ```rust #![feature(custom_inner_attributes)] diff --git a/book/src/README.md b/book/src/README.md index 6248d588a890..df4a1f2702e4 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,12 +1,12 @@ # Clippy -[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto) [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 0b945faf9b78..0649f7a631df 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -5,6 +5,7 @@ - [Installation](installation.md) - [Usage](usage.md) - [Configuration](configuration.md) + - [Lint Configuration](lint_configuration.md) - [Clippy's Lints](lints.md) - [Continuous Integration](continuous_integration/README.md) - [GitHub Actions](continuous_integration/github_actions.md) @@ -21,3 +22,4 @@ - [The Clippy Book](development/infrastructure/book.md) - [Proposals](development/proposals/README.md) - [Roadmap 2021](development/proposals/roadmap-2021.md) + - [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md) diff --git a/book/src/configuration.md b/book/src/configuration.md index 77f1d2e8797a..87f4a697af9f 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -8,11 +8,21 @@ basic `variable = value` mapping eg. ```toml avoid-breaking-exported-api = false disallowed-names = ["toto", "tata", "titi"] -cognitive-complexity-threshold = 30 ``` -See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which -lints can be configured and the meaning of the variables. +The [table of configurations](./lint_configuration.md) +contains all config values, their default, and a list of lints they affect. +Each [configurable lint](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration) +, also contains information about these values. + +For configurations that are a list type with default values such as +[disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names), +you can use the unique value `".."` to extend the default values instead of replacing them. + +```toml +# default of disallowed-names is ["foo", "baz", "quux"] +disallowed-names = ["bar", ".."] # -> ["bar", "foo", "baz", "quux"] +``` To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable. @@ -72,7 +82,7 @@ minimum supported Rust version (MSRV) in the clippy configuration file. msrv = "1.30.0" ``` -The MSRV can also be specified as an inner attribute, like below. +The MSRV can also be specified as an attribute, like below. ```rust #![feature(custom_inner_attributes)] diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index b1e843bc7f4c..f57dc627dce4 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -146,7 +146,8 @@ For cargo lints, the process of testing differs in that we are interested in the manifest. If our new lint is named e.g. `foo_categories`, after running `cargo dev -new_lint` we will find by default two new crates, each with its manifest file: +new_lint --name=foo_categories --type=cargo --category=cargo` 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. @@ -443,27 +444,27 @@ value is passed to the constructor in `clippy_lints/lib.rs`. ```rust pub struct ManualStrip { - msrv: Option, + msrv: Msrv, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } ``` The project's MSRV can then be matched against the feature MSRV in the LintPass -using the `meets_msrv` utility function. +using the `Msrv::meets` method. ``` rust -if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { +if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { return; } ``` -The project's MSRV can also be specified as an inner attribute, which overrides +The project's MSRV can also be specified as an attribute, which overrides the value from `clippy.toml`. This can be accounted for using the `extract_msrv_attr!(LintContext)` macro and passing `LateContext`/`EarlyContext`. @@ -478,8 +479,23 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { ``` Once the `msrv` is added to the lint, a relevant test case should be added to -`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted -if the project's MSRV is lower. +the lint's test file, `tests/ui/manual_strip.rs` in this example. It should +have a case for the version below the MSRV and one with the same contents but +for the MSRV version itself. + +```rust +... + +#[clippy::msrv = "1.44"] +fn msrv_1_44() { + /* something that would trigger the lint */ +} + +#[clippy::msrv = "1.45"] +fn msrv_1_45() { + /* something that would trigger the lint */ +} +``` As a last step, the lint should be added to the lint documentation. This is done in `clippy_lints/src/utils/conf.rs`: @@ -684,6 +700,10 @@ for some users. Adding a configuration is done in the following steps: `clippy.toml` file with the configuration value and a rust file that should be linted by Clippy. The test can otherwise be written as usual. +5. Update [Lint Configuration](../lint_configuration.md) + + Run `cargo collect-metadata` to generate documentation changes for the book. + [`clippy_lints::utils::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/conf.rs [`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs [`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 44ba6e32755e..6fb53236e6f1 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -69,7 +69,7 @@ the reference file with: cargo dev bless ``` -For example, this is necessary, if you fix a typo in an error message of a lint +For example, this is necessary if you fix a typo in an error message of a lint, or if you modify a test file to add a test case. > _Note:_ This command may update more files than you intended. In that case @@ -101,8 +101,9 @@ cargo dev setup intellij cargo dev dogfood ``` -More about intellij command usage and reasons -[here](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust) +More about [intellij] command usage and reasons. + +[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust ## lintcheck diff --git a/book/src/development/infrastructure/book.md b/book/src/development/infrastructure/book.md index a48742191850..dbd624ecd738 100644 --- a/book/src/development/infrastructure/book.md +++ b/book/src/development/infrastructure/book.md @@ -3,15 +3,15 @@ This document explains how to make additions and changes to the Clippy book, the guide to Clippy that you're reading right now. The Clippy book is formatted with [Markdown](https://www.markdownguide.org) and generated by -[mdbook](https://github.com/rust-lang/mdBook). +[mdBook](https://github.com/rust-lang/mdBook). -- [Get mdbook](#get-mdbook) +- [Get mdBook](#get-mdbook) - [Make changes](#make-changes) -## Get mdbook +## Get mdBook While not strictly necessary since the book source is simply Markdown text -files, having mdbook locally will allow you to build, test and serve the book +files, having mdBook locally will allow you to build, test and serve the book locally to view changes before you commit them to the repository. You likely already have `cargo` installed, so the easiest option is to simply: @@ -19,7 +19,7 @@ already have `cargo` installed, so the easiest option is to simply: cargo install mdbook ``` -See the mdbook [installation](https://github.com/rust-lang/mdBook#installation) +See the mdBook [installation](https://github.com/rust-lang/mdBook#installation) instructions for other options. ## Make changes @@ -27,7 +27,7 @@ instructions for other options. The book's [src](https://github.com/rust-lang/rust-clippy/tree/master/book/src) directory contains all of the markdown files used to generate the book. If you -want to see your changes in real time, you can use the mdbook `serve` command to +want to see your changes in real time, you can use the mdBook `serve` command to run a web server locally that will automatically update changes as they are made. From the top level of your `rust-clippy` directory: @@ -38,5 +38,5 @@ mdbook serve book --open Then navigate to `http://localhost:3000` to see the generated book. While the server is running, changes you make will automatically be updated. -For more information, see the mdbook +For more information, see the mdBook [guide](https://rust-lang.github.io/mdBook/). diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index 80a47affe30d..d1ac7237b5e3 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -95,11 +95,23 @@ As section headers, we use: Please also be sure to update the Beta/Unreleased sections at the top with the relevant commit ranges. -If you have the time, it would be appreciated if you double-check, that the -`#[clippy::version]` attributes for the added lints contains the correct version. +#### 3.1 Include `beta-accepted` PRs + +Look for the [`beta-accepted`] label and make sure to also include the PRs with +that label in the changelog. If you can, remove the `beta-accepted` labels +**after** the changelog PR was merged. + +> _Note:_ Some of those PRs might even got backported to the previous `beta`. +> Those have to be included in the changelog of the _previous_ release. + +### 4. Update `clippy::version` attributes + +Next, make sure to check that the `#[clippy::version]` attributes for the added +lints contain the correct version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ [rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases +[`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ diff --git a/book/src/development/proposals/syntax-tree-patterns.md b/book/src/development/proposals/syntax-tree-patterns.md new file mode 100644 index 000000000000..c5587c4bf908 --- /dev/null +++ b/book/src/development/proposals/syntax-tree-patterns.md @@ -0,0 +1,986 @@ +- Feature Name: syntax-tree-patterns +- Start Date: 2019-03-12 +- RFC PR: [#3875](https://github.com/rust-lang/rust-clippy/pull/3875) + +# Summary + +Introduce a domain-specific language (similar to regular expressions) that +allows to describe lints using *syntax tree patterns*. + + +# Motivation + +Finding parts of a syntax tree (AST, HIR, ...) that have certain properties +(e.g. "*an if that has a block as its condition*") is a major task when writing +lints. For non-trivial lints, it often requires nested pattern matching of AST / +HIR nodes. For example, testing that an expression is a boolean literal requires +the following checks: + +```rust +if let ast::ExprKind::Lit(lit) = &expr.node { + if let ast::LitKind::Bool(_) = &lit.node { + ... + } +} +``` + +Writing this kind of matching code quickly becomes a complex task and the +resulting code is often hard to comprehend. The code below shows a simplified +version of the pattern matching required by the `collapsible_if` lint: + +```rust +// simplified version of the collapsible_if lint +if let ast::ExprKind::If(check, then, None) = &expr.node { + if then.stmts.len() == 1 { + if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node { + if let ast::ExprKind::If(check_inner, content, None) = &inner.node { + ... + } + } + } +} +``` + +The `if_chain` macro can improve readability by flattening the nested if +statements, but the resulting code is still quite hard to read: + +```rust +// simplified version of the collapsible_if lint +if_chain! { + if let ast::ExprKind::If(check, then, None) = &expr.node; + if then.stmts.len() == 1; + if let ast::StmtKind::Expr(inner) | ast::StmtKind::Semi(inner) = &then.stmts[0].node; + if let ast::ExprKind::If(check_inner, content, None) = &inner.node; + then { + ... + } +} +``` + +The code above matches if expressions that contain only another if expression +(where both ifs don't have an else branch). While it's easy to explain what the +lint does, it's hard to see that from looking at the code samples above. + +Following the motivation above, the first goal this RFC is to **simplify writing +and reading lints**. + +The second part of the motivation is clippy's dependence on unstable +compiler-internal data structures. Clippy lints are currently written against +the compiler's AST / HIR which means that even small changes in these data +structures might break a lot of lints. The second goal of this RFC is to **make +lints independant of the compiler's AST / HIR data structures**. + +# Approach + +A lot of complexity in writing lints currently seems to come from having to +manually implement the matching logic (see code samples above). It's an +imparative style that describes *how* to match a syntax tree node instead of +specifying *what* should be matched against declaratively. In other areas, it's +common to use declarative patterns to describe desired information and let the +implementation do the actual matching. A well-known example of this approach are +[regular expressions](https://en.wikipedia.org/wiki/Regular_expression). Instead +of writing code that detects certain character sequences, one can describe a +search pattern using a domain-specific language and search for matches using +that pattern. The advantage of using a declarative domain-specific language is +that its limited domain (e.g. matching character sequences in the case of +regular expressions) allows to express entities in that domain in a very natural +and expressive way. + +While regular expressions are very useful when searching for patterns in flat +character sequences, they cannot easily be applied to hierarchical data +structures like syntax trees. This RFC therefore proposes a pattern matching +system that is inspired by regular expressions and designed for hierarchical +syntax trees. + +# Guide-level explanation + +This proposal adds a `pattern!` macro that can be used to specify a syntax tree +pattern to search for. A simple pattern is shown below: + +```rust +pattern!{ + my_pattern: Expr = + Lit(Bool(false)) +} +``` + +This macro call defines a pattern named `my_pattern` that can be matched against +an `Expr` syntax tree node. The actual pattern (`Lit(Bool(false))` in this case) +defines which syntax trees should match the pattern. This pattern matches +expressions that are boolean literals with value `false`. + +The pattern can then be used to implement lints in the following way: + +```rust +... + +impl EarlyLintPass for MyAwesomeLint { + fn check_expr(&mut self, cx: &EarlyContext, expr: &syntax::ast::Expr) { + + if my_pattern(expr).is_some() { + cx.span_lint( + MY_AWESOME_LINT, + expr.span, + "This is a match for a simple pattern. Well done!", + ); + } + + } +} +``` + +The `pattern!` macro call expands to a function `my_pattern` that expects a +syntax tree expression as its argument and returns an `Option` that indicates +whether the pattern matched. + +> Note: The result type is explained in more detail in [a later +> section](#the-result-type). For now, it's enough to know that the result is +> `Some` if the pattern matched and `None` otherwise. + +## Pattern syntax + +The following examples demonstate the pattern syntax: + + +#### Any (`_`) + +The simplest pattern is the any pattern. It matches anything and is therefore +similar to regex's `*`. + +```rust +pattern!{ + // matches any expression + my_pattern: Expr = + _ +} +``` + +#### Node (`()`) + +Nodes are used to match a specific variant of an AST node. A node has a name and +a number of arguments that depends on the node type. For example, the `Lit` node +has a single argument that describes the type of the literal. As another +example, the `If` node has three arguments describing the if's condition, then +block and else block. + +```rust +pattern!{ + // matches any expression that is a literal + my_pattern: Expr = + Lit(_) +} + +pattern!{ + // matches any expression that is a boolean literal + my_pattern: Expr = + Lit(Bool(_)) +} + +pattern!{ + // matches if expressions that have a boolean literal in their condition + // Note: The `_?` syntax here means that the else branch is optional and can be anything. + // This is discussed in more detail in the section `Repetition`. + my_pattern: Expr = + If( Lit(Bool(_)) , _, _?) +} +``` + + +#### Literal (``) + +A pattern can also contain Rust literals. These literals match themselves. + +```rust +pattern!{ + // matches the boolean literal false + my_pattern: Expr = + Lit(Bool(false)) +} + +pattern!{ + // matches the character literal 'x' + my_pattern: Expr = + Lit(Char('x')) +} +``` + +#### Alternations (`a | b`) + +```rust +pattern!{ + // matches if the literal is a boolean or integer literal + my_pattern: Lit = + Bool(_) | Int(_) +} + +pattern!{ + // matches if the expression is a char literal with value 'x' or 'y' + my_pattern: Expr = + Lit( Char('x' | 'y') ) +} +``` + +#### Empty (`()`) + +The empty pattern represents an empty sequence or the `None` variant of an +optional. + +```rust +pattern!{ + // matches if the expression is an empty array + my_pattern: Expr = + Array( () ) +} + +pattern!{ + // matches if expressions that don't have an else clause + my_pattern: Expr = + If(_, _, ()) +} +``` + +#### Sequence (` `) + +```rust +pattern!{ + // matches the array [true, false] + my_pattern: Expr = + Array( Lit(Bool(true)) Lit(Bool(false)) ) +} +``` + +#### Repetition (`*`, `+`, `?`, `{n}`, `{n,m}`, `{n,}`) + +Elements may be repeated. The syntax for specifying repetitions is identical to +[regex's syntax](https://docs.rs/regex/1.1.2/regex/#repetitions). + +```rust +pattern!{ + // matches arrays that contain 2 'x's as their last or second-last elements + // Examples: + // ['x', 'x'] match + // ['x', 'x', 'y'] match + // ['a', 'b', 'c', 'x', 'x', 'y'] match + // ['x', 'x', 'y', 'z'] no match + my_pattern: Expr = + Array( _* Lit(Char('x')){2} _? ) +} + +pattern!{ + // matches if expressions that **may or may not** have an else block + // Attn: `If(_, _, _)` matches only ifs that **have** an else block + // + // | if with else block | if witout else block + // If(_, _, _) | match | no match + // If(_, _, _?) | match | match + // If(_, _, ()) | no match | match + my_pattern: Expr = + If(_, _, _?) +} +``` + +#### Named submatch (`#`) + +```rust +pattern!{ + // matches character literals and gives the literal the name foo + my_pattern: Expr = + Lit(Char(_)#foo) +} + +pattern!{ + // matches character literals and gives the char the name bar + my_pattern: Expr = + Lit(Char(_#bar)) +} + +pattern!{ + // matches character literals and gives the expression the name baz + my_pattern: Expr = + Lit(Char(_))#baz +} +``` + +The reason for using named submatches is described in the section [The result +type](#the-result-type). + +### Summary + +The following table gives an summary of the pattern syntax: + +| Syntax | Concept | Examples | +|-------------------------|------------------|--------------------------------------------| +|`_` | Any | `_` | +|`()` | Node | `Lit(Bool(true))`, `If(_, _, _)` | +|`` | Literal | `'x'`, `false`, `101` | +|` \| ` | Alternation | `Char(_) \| Bool(_)` | +|`()` | Empty | `Array( () )` | +|` ` | Sequence | `Tuple( Lit(Bool(_)) Lit(Int(_)) Lit(_) )` | +|`*`
`
+`
`
?`
`
{n}`
`
{n,m}`
`
{n,}` | Repetition





| `Array( _* )`,
`Block( Semi(_)+ )`,
`If(_, _, Block(_)?)`,
`Array( Lit(_){10} )`,
`Lit(_){5,10}`,
`Lit(Bool(_)){10,}` | +|`
#` | Named submatch | `Lit(Int(_))#foo` `Lit(Int(_#bar))` | + + +## The result type + +A lot of lints require checks that go beyond what the pattern syntax described +above can express. For example, a lint might want to check whether a node was +created as part of a macro expansion or whether there's no comment above a node. +Another example would be a lint that wants to match two nodes that have the same +value (as needed by lints like `almost_swapped`). Instead of allowing users to +write these checks into the pattern directly (which might make patterns hard to +read), the proposed solution allows users to assign names to parts of a pattern +expression. When matching a pattern against a syntax tree node, the return value +will contain references to all nodes that were matched by these named +subpatterns. This is similar to capture groups in regular expressions. + +For example, given the following pattern + +```rust +pattern!{ + // matches character literals + my_pattern: Expr = + Lit(Char(_#val_inner)#val)#val_outer +} +``` + +one could get references to the nodes that matched the subpatterns in the +following way: + +```rust +... +fn check_expr(expr: &syntax::ast::Expr) { + if let Some(result) = my_pattern(expr) { + result.val_inner // type: &char + result.val // type: &syntax::ast::Lit + result.val_outer // type: &syntax::ast::Expr + } +} +``` + +The types in the `result` struct depend on the pattern. For example, the +following pattern + +```rust +pattern!{ + // matches arrays of character literals + my_pattern_seq: Expr = + Array( Lit(_)*#foo ) +} +``` + +matches arrays that consist of any number of literal expressions. Because those +expressions are named `foo`, the result struct contains a `foo` attribute which +is a vector of expressions: + +```rust +... +if let Some(result) = my_pattern_seq(expr) { + result.foo // type: Vec<&syntax::ast::Expr> +} +``` + +Another result type occurs when a name is only defined in one branch of an +alternation: + +```rust +pattern!{ + // matches if expression is a boolean or integer literal + my_pattern_alt: Expr = + Lit( Bool(_#bar) | Int(_) ) +} +``` + +In the pattern above, the `bar` name is only defined if the pattern matches a +boolean literal. If it matches an integer literal, the name isn't set. To +account for this, the result struct's `bar` attribute is an option type: + +```rust +... +if let Some(result) = my_pattern_alt(expr) { + result.bar // type: Option<&bool> +} +``` + +It's also possible to use a name in multiple alternation branches if they have +compatible types: + +```rust +pattern!{ + // matches if expression is a boolean or integer literal + my_pattern_mult: Expr = + Lit(_#baz) | Array( Lit(_#baz) ) +} +... +if let Some(result) = my_pattern_mult(expr) { + result.baz // type: &syntax::ast::Lit +} +``` + +Named submatches are a **flat** namespace and this is intended. In the example +above, two different sub-structures are assigned to a flat name. I expect that +for most lints, a flat namespace is sufficient and easier to work with than a +hierarchical one. + +#### Two stages + +Using named subpatterns, users can write lints in two stages. First, a coarse +selection of possible matches is produced by the pattern syntax. In the second +stage, the named subpattern references can be used to do additional tests like +asserting that a node hasn't been created as part of a macro expansion. + +## Implementing clippy lints using patterns + +As a "real-world" example, I re-implemented the `collapsible_if` lint using +patterns. The code can be found +[here](https://github.com/fkohlgrueber/rust-clippy-pattern/blob/039b07ecccaf96d6aa7504f5126720d2c9cceddd/clippy_lints/src/collapsible_if.rs#L88-L163). +The pattern-based version passes all test cases that were written for +`collapsible_if`. + + +# Reference-level explanation + +## Overview + +The following diagram shows the dependencies between the main parts of the +proposed solution: + +``` + Pattern syntax + | + | parsing / lowering + v + PatternTree + ^ + | + | + IsMatch trait + | + | + +---------------+-----------+---------+ + | | | | + v v v v + syntax::ast rustc::hir syn ... +``` + +The pattern syntax described in the previous section is parsed / lowered into +the so-called *PatternTree* data structure that represents a valid syntax tree +pattern. Matching a *PatternTree* against an actual syntax tree (e.g. rust ast / +hir or the syn ast, ...) is done using the *IsMatch* trait. + +The *PatternTree* and the *IsMatch* trait are introduced in more detail in the +following sections. + +## PatternTree + +The core data structure of this RFC is the **PatternTree**. + +It's a data structure similar to rust's AST / HIR, but with the following +differences: + +- The PatternTree doesn't contain parsing information like `Span`s +- The PatternTree can represent alternatives, sequences and optionals + +The code below shows a simplified version of the current PatternTree: + +> Note: The current implementation can be found +> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/pattern_tree.rs#L50-L96). + + +```rust +pub enum Expr { + Lit(Alt), + Array(Seq), + Block_(Alt), + If(Alt, Alt, Opt), + IfLet( + Alt, + Opt, + ), +} + +pub enum Lit { + Char(Alt), + Bool(Alt), + Int(Alt), +} + +pub enum Stmt { + Expr(Alt), + Semi(Alt), +} + +pub enum BlockType { + Block(Seq), +} +``` + +The `Alt`, `Seq` and `Opt` structs look like these: + +> Note: The current implementation can be found +> [here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/matchers.rs#L35-L60). + +```rust +pub enum Alt { + Any, + Elmt(Box), + Alt(Box, Box), + Named(Box, ...) +} + +pub enum Opt { + Any, // anything, but not None + Elmt(Box), + None, + Alt(Box, Box), + Named(Box, ...) +} + +pub enum Seq { + Any, + Empty, + Elmt(Box), + Repeat(Box, RepeatRange), + Seq(Box, Box), + Alt(Box, Box), + Named(Box, ...) +} + +pub struct RepeatRange { + pub start: usize, + pub end: Option // exclusive +} +``` + +## Parsing / Lowering + +The input of a `pattern!` macro call is parsed into a `ParseTree` first and then +lowered to a `PatternTree`. + +Valid patterns depend on the *PatternTree* definitions. For example, the pattern +`Lit(Bool(_)*)` isn't valid because the parameter type of the `Lit` variant of +the `Expr` enum is `Any` and therefore doesn't support repetition (`*`). As +another example, `Array( Lit(_)* )` is a valid pattern because the parameter of +`Array` is of type `Seq` which allows sequences and repetitions. + +> Note: names in the pattern syntax correspond to *PatternTree* enum +> **variants**. For example, the `Lit` in the pattern above refers to the `Lit` +> variant of the `Expr` enum (`Expr::Lit`), not the `Lit` enum. + +## The IsMatch Trait + +The pattern syntax and the *PatternTree* are independant of specific syntax tree +implementations (rust ast / hir, syn, ...). When looking at the different +pattern examples in the previous sections, it can be seen that the patterns +don't contain any information specific to a certain syntax tree implementation. +In contrast, clippy lints currently match against ast / hir syntax tree nodes +and therefore directly depend on their implementation. + +The connection between the *PatternTree* and specific syntax tree +implementations is the `IsMatch` trait. It defines how to match *PatternTree* +nodes against specific syntax tree nodes. A simplified implementation of the +`IsMatch` trait is shown below: + +```rust +pub trait IsMatch { + fn is_match(&self, other: &'o O) -> bool; +} +``` + +This trait needs to be implemented on each enum of the *PatternTree* (for the +corresponding syntax tree types). For example, the `IsMatch` implementation for +matching `ast::LitKind` against the *PatternTree's* `Lit` enum might look like +this: + +```rust +impl IsMatch for Lit { + fn is_match(&self, other: &ast::LitKind) -> bool { + match (self, other) { + (Lit::Char(i), ast::LitKind::Char(j)) => i.is_match(j), + (Lit::Bool(i), ast::LitKind::Bool(j)) => i.is_match(j), + (Lit::Int(i), ast::LitKind::Int(j, _)) => i.is_match(j), + _ => false, + } + } +} +``` + +All `IsMatch` implementations for matching the current *PatternTree* against +`syntax::ast` can be found +[here](https://github.com/fkohlgrueber/pattern-matching/blob/dfb3bc9fbab69cec7c91e72564a63ebaa2ede638/pattern-match/src/ast_match.rs). + + +# Drawbacks + +#### Performance + +The pattern matching code is currently not optimized for performance, so it +might be slower than hand-written matching code. Additionally, the two-stage +approach (matching against the coarse pattern first and checking for additional +properties later) might be slower than the current practice of checking for +structure and additional properties in one pass. For example, the following lint + +```rust +pattern!{ + pat_if_without_else: Expr = + If( + _, + Block( + Expr( If(_, _, ())#inner ) + | Semi( If(_, _, ())#inner ) + )#then, + () + ) +} +... +fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if let Some(result) = pat_if_without_else(expr) { + if !block_starts_with_comment(cx, result.then) { + ... + } +} +``` + +first matches against the pattern and then checks that the `then` block doesn't +start with a comment. Using clippy's current approach, it's possible to check +for these conditions earlier: + +```rust +fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if_chain! { + if let ast::ExprKind::If(ref check, ref then, None) = expr.node; + if !block_starts_with_comment(cx, then); + if let Some(inner) = expr_block(then); + if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node; + then { + ... + } + } +} +``` + +Whether or not this causes performance regressions depends on actual patterns. +If it turns out to be a problem, the pattern matching algorithms could be +extended to allow "early filtering" (see the [Early Filtering](#early-filtering) +section in Future Possibilities). + +That being said, I don't see any conceptual limitations regarding pattern +matching performance. + +#### Applicability + +Even though I'd expect that a lot of lints can be written using the proposed +pattern syntax, it's unlikely that all lints can be expressed using patterns. I +suspect that there will still be lints that need to be implemented by writing +custom pattern matching code. This would lead to mix within clippy's codebase +where some lints are implemented using patterns and others aren't. This +inconsistency might be considered a drawback. + + +# Rationale and alternatives + +Specifying lints using syntax tree patterns has a couple of advantages compared +to the current approach of manually writing matching code. First, syntax tree +patterns allow users to describe patterns in a simple and expressive way. This +makes it easier to write new lints for both novices and experts and also makes +reading / modifying existing lints simpler. + +Another advantage is that lints are independent of specific syntax tree +implementations (e.g. AST / HIR, ...). When these syntax tree implementations +change, only the `IsMatch` trait implementations need to be adapted and existing +lints can remain unchanged. This also means that if the `IsMatch` trait +implementations were integrated into the compiler, updating the `IsMatch` +implementations would be required for the compiler to compile successfully. This +could reduce the number of times clippy breaks because of changes in the +compiler. Another advantage of the pattern's independence is that converting an +`EarlyLintPass` lint into a `LatePassLint` wouldn't require rewriting the whole +pattern matching code. In fact, the pattern might work just fine without any +adaptions. + + +## Alternatives + +### Rust-like pattern syntax + +The proposed pattern syntax requires users to know the structure of the +`PatternTree` (which is very similar to the AST's / HIR's structure) and also +the pattern syntax. An alternative would be to introduce a pattern syntax that +is similar to actual Rust syntax (probably like the `quote!` macro). For +example, a pattern that matches `if` expressions that have `false` in their +condition could look like this: + +```rust +if false { + #[*] +} +``` + +#### Problems + +Extending Rust syntax (which is quite complex by itself) with additional syntax +needed for specifying patterns (alternations, sequences, repetisions, named +submatches, ...) might become difficult to read and really hard to parse +properly. + +For example, a pattern that matches a binary operation that has `0` on both +sides might look like this: + +``` +0 #[*:BinOpKind] 0 +``` + +Now consider this slightly more complex example: + +``` +1 + 0 #[*:BinOpKind] 0 +``` + +The parser would need to know the precedence of `#[*:BinOpKind]` because it +affects the structure of the resulting AST. `1 + 0 + 0` is parsed as `(1 + 0) + +0` while `1 + 0 * 0` is parsed as `1 + (0 * 0)`. Since the pattern could be any +`BinOpKind`, the precedence cannot be known in advance. + +Another example of a problem would be named submatches. Take a look at this +pattern: + +```rust +fn test() { + 1 #foo +} +``` + +Which node is `#foo` referring to? `int`, `ast::Lit`, `ast::Expr`, `ast::Stmt`? +Naming subpatterns in a rust-like syntax is difficult because a lot of AST nodes +don't have a syntactic element that can be used to put the name tag on. In these +situations, the only sensible option would be to assign the name tag to the +outermost node (`ast::Stmt` in the example above), because the information of +all child nodes can be retrieved through the outermost node. The problem with +this then would be that accessing inner nodes (like `ast::Lit`) would again +require manual pattern matching. + +In general, Rust syntax contains a lot of code structure implicitly. This +structure is reconstructed during parsing (e.g. binary operations are +reconstructed using operator precedence and left-to-right) and is one of the +reasons why parsing is a complex task. The advantage of this approach is that +writing code is simpler for users. + +When writing *syntax tree patterns*, each element of the hierarchy might have +alternatives, repetitions, etc.. Respecting that while still allowing +human-friendly syntax that contains structure implicitly seems to be really +complex, if not impossible. + +Developing such a syntax would also require to maintain a custom parser that is +at least as complex as the Rust parser itself. Additionally, future changes in +the Rust syntax might be incompatible with such a syntax. + +In summary, I think that developing such a syntax would introduce a lot of +complexity to solve a relatively minor problem. + +The issue of users not knowing about the *PatternTree* structure could be solved +by a tool that, given a rust program, generates a pattern that matches only this +program (similar to the clippy author lint). + +For some simple cases (like the first example above), it might be possible to +successfully mix Rust and pattern syntax. This space could be further explored +in a future extension. + +# Prior art + +The pattern syntax is heavily inspired by regular expressions (repetitions, +alternatives, sequences, ...). + +From what I've seen until now, other linters also implement lints that directly +work on syntax tree data structures, just like clippy does currently. I would +therefore consider the pattern syntax to be *new*, but please correct me if I'm +wrong. + +# Unresolved questions + +#### How to handle multiple matches? + +When matching a syntax tree node against a pattern, there are possibly multiple +ways in which the pattern can be matched. A simple example of this would be the +following pattern: + +```rust +pattern!{ + my_pattern: Expr = + Array( _* Lit(_)+#literals) +} +``` + +This pattern matches arrays that end with at least one literal. Now given the +array `[x, 1, 2]`, should `1` be matched as part of the `_*` or the `Lit(_)+` +part of the pattern? The difference is important because the named submatch +`#literals` would contain 1 or 2 elements depending how the pattern is matched. +In regular expressions, this problem is solved by matching "greedy" by default +and "non-greedy" optionally. + +I haven't looked much into this yet because I don't know how relevant it is for +most lints. The current implementation simply returns the first match it finds. + +# Future possibilities + +#### Implement rest of Rust Syntax + +The current project only implements a small part of the Rust syntax. In the +future, this should incrementally be extended to more syntax to allow +implementing more lints. Implementing more of the Rust syntax requires extending +the `PatternTree` and `IsMatch` implementations, but should be relatively +straight-forward. + +#### Early filtering + +As described in the *Drawbacks/Performance* section, allowing additional checks +during the pattern matching might be beneficial. + +The pattern below shows how this could look like: + +```rust +pattern!{ + pat_if_without_else: Expr = + If( + _, + Block( + Expr( If(_, _, ())#inner ) + | Semi( If(_, _, ())#inner ) + )#then, + () + ) + where + !in_macro(#then.span); +} +``` + +The difference compared to the currently proposed two-stage filtering is that +using early filtering, the condition (`!in_macro(#then.span)` in this case) +would be evaluated as soon as the `Block(_)#then` was matched. + +Another idea in this area would be to introduce a syntax for backreferences. +They could be used to require that multiple parts of a pattern should match the +same value. For example, the `assign_op_pattern` lint that searches for `a = a +op b` and recommends changing it to `a op= b` requires that both occurrances of +`a` are the same. Using `=#...` as syntax for backreferences, the lint could be +implemented like this: + +```rust +pattern!{ + assign_op_pattern: Expr = + Assign(_#target, Binary(_, =#target, _) +} +``` + +#### Match descendant + +A lot of lints currently implement custom visitors that check whether any +subtree (which might not be a direct descendant) of the current node matches +some properties. This cannot be expressed with the proposed pattern syntax. +Extending the pattern syntax to allow patterns like "a function that contains at +least two return statements" could be a practical addition. + +#### Negation operator for alternatives + +For patterns like "a literal that is not a boolean literal" one currently needs +to list all alternatives except the boolean case. Introducing a negation +operator that allows to write `Lit(!Bool(_))` might be a good idea. This pattern +would be eqivalent to `Lit( Char(_) | Int(_) )` (given that currently only three +literal types are implemented). + +#### Functional composition + +Patterns currently don't have any concept of composition. This leads to +repetitions within patterns. For example, one of the collapsible-if patterns +currently has to be written like this: + +```rust +pattern!{ + pat_if_else: Expr = + If( + _, + _, + Block_( + Block( + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Semi((If(_, _, _?) | IfLet(_, _?))#else_) + )#block_inner + )#block + ) | + IfLet( + _, + Block_( + Block( + Expr((If(_, _, _?) | IfLet(_, _?))#else_) | + Semi((If(_, _, _?) | IfLet(_, _?))#else_) + )#block_inner + )#block + ) +} +``` + +If patterns supported defining functions of subpatterns, the code could be +simplified as follows: + +```rust +pattern!{ + fn expr_or_semi(expr: Expr) -> Stmt { + Expr(expr) | Semi(expr) + } + fn if_or_if_let(then: Block, else: Opt) -> Expr { + If(_, then, else) | IfLet(then, else) + } + pat_if_else: Expr = + if_or_if_let( + _, + Block_( + Block( + expr_or_semi( if_or_if_let(_, _?)#else_ ) + )#block_inner + )#block + ) +} +``` + +Additionally, common patterns like `expr_or_semi` could be shared between +different lints. + +#### Clippy Pattern Author + +Another improvement could be to create a tool that, given some valid Rust +syntax, generates a pattern that matches this syntax exactly. This would make +starting to write a pattern easier. A user could take a look at the patterns +generated for a couple of Rust code examples and use that information to write a +pattern that matches all of them. + +This is similar to clippy's author lint. + +#### Supporting other syntaxes + +Most of the proposed system is language-agnostic. For example, the pattern +syntax could also be used to describe patterns for other programming languages. + +In order to support other languages' syntaxes, one would need to implement +another `PatternTree` that sufficiently describes the languages' AST and +implement `IsMatch` for this `PatternTree` and the languages' AST. + +One aspect of this is that it would even be possible to write lints that work on +the pattern syntax itself. For example, when writing the following pattern + + +```rust +pattern!{ + my_pattern: Expr = + Array( Lit(Bool(false)) Lit(Bool(false)) ) +} +``` + +a lint that works on the pattern syntax's AST could suggest using this pattern +instead: + +```rust +pattern!{ + my_pattern: Expr = + Array( Lit(Bool(false)){2} ) +} +``` + +In the future, clippy could use this system to also provide lints for custom +syntaxes like those found in macros. diff --git a/book/src/installation.md b/book/src/installation.md index b2a28d0be622..cce888b17d4d 100644 --- a/book/src/installation.md +++ b/book/src/installation.md @@ -1,6 +1,6 @@ # Installation -If you're using `rustup` to install and manage you're Rust toolchains, Clippy is +If you're using `rustup` to install and manage your Rust toolchains, Clippy is usually **already installed**. In that case you can skip this chapter and go to the [Usage] chapter. diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md new file mode 100644 index 000000000000..32e8e218c405 --- /dev/null +++ b/book/src/lint_configuration.md @@ -0,0 +1,543 @@ + + +## Lint Configuration Options +|
Option
| Default Value | +|--|--| +| [arithmetic-side-effects-allowed](#arithmetic-side-effects-allowed) | `{}` | +| [arithmetic-side-effects-allowed-binary](#arithmetic-side-effects-allowed-binary) | `[]` | +| [arithmetic-side-effects-allowed-unary](#arithmetic-side-effects-allowed-unary) | `{}` | +| [avoid-breaking-exported-api](#avoid-breaking-exported-api) | `true` | +| [msrv](#msrv) | `None` | +| [cognitive-complexity-threshold](#cognitive-complexity-threshold) | `25` | +| [disallowed-names](#disallowed-names) | `["foo", "baz", "quux"]` | +| [doc-valid-idents](#doc-valid-idents) | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` | +| [too-many-arguments-threshold](#too-many-arguments-threshold) | `7` | +| [type-complexity-threshold](#type-complexity-threshold) | `250` | +| [single-char-binding-names-threshold](#single-char-binding-names-threshold) | `4` | +| [too-large-for-stack](#too-large-for-stack) | `200` | +| [enum-variant-name-threshold](#enum-variant-name-threshold) | `3` | +| [enum-variant-size-threshold](#enum-variant-size-threshold) | `200` | +| [verbose-bit-mask-threshold](#verbose-bit-mask-threshold) | `1` | +| [literal-representation-threshold](#literal-representation-threshold) | `16384` | +| [trivial-copy-size-limit](#trivial-copy-size-limit) | `None` | +| [pass-by-value-size-limit](#pass-by-value-size-limit) | `256` | +| [too-many-lines-threshold](#too-many-lines-threshold) | `100` | +| [array-size-threshold](#array-size-threshold) | `512000` | +| [vec-box-size-threshold](#vec-box-size-threshold) | `4096` | +| [max-trait-bounds](#max-trait-bounds) | `3` | +| [max-struct-bools](#max-struct-bools) | `3` | +| [max-fn-params-bools](#max-fn-params-bools) | `3` | +| [warn-on-all-wildcard-imports](#warn-on-all-wildcard-imports) | `false` | +| [disallowed-macros](#disallowed-macros) | `[]` | +| [disallowed-methods](#disallowed-methods) | `[]` | +| [disallowed-types](#disallowed-types) | `[]` | +| [unreadable-literal-lint-fractions](#unreadable-literal-lint-fractions) | `true` | +| [upper-case-acronyms-aggressive](#upper-case-acronyms-aggressive) | `false` | +| [matches-for-let-else](#matches-for-let-else) | `WellKnownTypes` | +| [cargo-ignore-publish](#cargo-ignore-publish) | `false` | +| [standard-macro-braces](#standard-macro-braces) | `[]` | +| [enforced-import-renames](#enforced-import-renames) | `[]` | +| [allowed-scripts](#allowed-scripts) | `["Latin"]` | +| [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` | +| [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` | +| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` | +| [max-include-file-size](#max-include-file-size) | `1000000` | +| [allow-expect-in-tests](#allow-expect-in-tests) | `false` | +| [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` | +| [allow-dbg-in-tests](#allow-dbg-in-tests) | `false` | +| [allow-print-in-tests](#allow-print-in-tests) | `false` | +| [large-error-threshold](#large-error-threshold) | `128` | +| [ignore-interior-mutability](#ignore-interior-mutability) | `["bytes::Bytes"]` | +| [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` | +| [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` | + +### arithmetic-side-effects-allowed +Suppress checking of the passed type names in all types of operations. + +If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + +#### Example + +```toml +arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] +``` + +#### Noteworthy + +A type, say `SomeType`, listed in this configuration has the same behavior of +`["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +### arithmetic-side-effects-allowed-binary +Suppress checking of the passed type pair names in binary operations like addition or +multiplication. + +Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless +of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. + +Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as +`["AnotherType", "SomeType"]`. + +#### Example + +```toml +arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] +``` + +**Default Value:** `[]` (`Vec<[String; 2]>`) + +* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +### arithmetic-side-effects-allowed-unary +Suppress checking of the passed type names in unary operations like "negation" (`-`). + +#### Example + +```toml +arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] +``` + +**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet`) + +* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects) + + +### avoid-breaking-exported-api +Suppress lints whenever the suggested change would cause breakage for other crates. + +**Default Value:** `true` (`bool`) + +* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) +* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) +* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) +* [unnecessary_wraps](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps) +* [unused_self](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self) +* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) +* [wrong_self_convention](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention) +* [box_collection](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection) +* [redundant_allocation](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation) +* [rc_buffer](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer) +* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) +* [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option) +* [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist) +* [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex) + + +### msrv +The minimum rust version that the project supports + +**Default Value:** `None` (`Option`) + +* [manual_split_once](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) +* [manual_str_repeat](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) +* [cloned_instead_of_copied](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied) +* [redundant_field_names](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) +* [redundant_static_lifetimes](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) +* [filter_map_next](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next) +* [checked_conversions](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) +* [manual_range_contains](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) +* [use_self](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) +* [mem_replace_with_default](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) +* [manual_non_exhaustive](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive) +* [option_as_ref_deref](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) +* [map_unwrap_or](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or) +* [match_like_matches_macro](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro) +* [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) +* [missing_const_for_fn](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) +* [unnested_or_patterns](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) +* [from_over_into](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) +* [ptr_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) +* [if_then_some_else_none](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) +* [approx_constant](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) +* [deprecated_cfg_attr](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr) +* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +* [map_clone](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) +* [borrow_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) +* [manual_bits](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits) +* [err_expect](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect) +* [cast_abs_to_unsigned](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) +* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) +* [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) +* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) +* [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) +* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) +* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) +* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) +* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) +* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) + + +### cognitive-complexity-threshold +The maximum cognitive complexity a function can have + +**Default Value:** `25` (`u64`) + +* [cognitive_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) + + +### disallowed-names +The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value +`".."` can be used as part of the list to indicate, that the configured values should be appended to the +default configuration of Clippy. By default any configuration will replace the default value. + +**Default Value:** `["foo", "baz", "quux"]` (`Vec`) + +* [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names) + + +### doc-valid-idents +The list of words this lint should not consider as identifiers needing ticks. The value +`".."` can be used as part of the list to indicate, that the configured values should be appended to the +default configuration of Clippy. By default any configuraction will replace the default value. For example: +* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. +* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. + +Default list: + +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` (`Vec`) + +* [doc_markdown](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown) + + +### too-many-arguments-threshold +The maximum number of argument a function or method can have + +**Default Value:** `7` (`u64`) + +* [too_many_arguments](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments) + + +### type-complexity-threshold +The maximum complexity a type can have + +**Default Value:** `250` (`u64`) + +* [type_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity) + + +### single-char-binding-names-threshold +The maximum number of single char bindings a scope may have + +**Default Value:** `4` (`u64`) + +* [many_single_char_names](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names) + + +### too-large-for-stack +The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + +**Default Value:** `200` (`u64`) + +* [boxed_local](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local) +* [useless_vec](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec) + + +### enum-variant-name-threshold +The minimum number of enum variants for the lints about variant names to trigger + +**Default Value:** `3` (`u64`) + +* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names) + + +### enum-variant-size-threshold +The maximum size of an enum's variant to avoid box suggestion + +**Default Value:** `200` (`u64`) + +* [large_enum_variant](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant) + + +### verbose-bit-mask-threshold +The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' + +**Default Value:** `1` (`u64`) + +* [verbose_bit_mask](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask) + + +### literal-representation-threshold +The lower bound for linting decimal literals + +**Default Value:** `16384` (`u64`) + +* [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation) + + +### trivial-copy-size-limit +The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. + +**Default Value:** `None` (`Option`) + +* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) + + +### pass-by-value-size-limit +The minimum size (in bytes) to consider a type for passing by reference instead of by value. + +**Default Value:** `256` (`u64`) + +* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) + + +### too-many-lines-threshold +The maximum number of lines a function or method can have + +**Default Value:** `100` (`u64`) + +* [too_many_lines](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) + + +### array-size-threshold +The maximum allowed size for arrays on the stack + +**Default Value:** `512000` (`u128`) + +* [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays) +* [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays) + + +### vec-box-size-threshold +The size of the boxed type in bytes, where boxing in a `Vec` is allowed + +**Default Value:** `4096` (`u64`) + +* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box) + + +### max-trait-bounds +The maximum number of bounds a trait can have to be linted + +**Default Value:** `3` (`u64`) + +* [type_repetition_in_bounds](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds) + + +### max-struct-bools +The maximum number of bool fields a struct can have + +**Default Value:** `3` (`u64`) + +* [struct_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools) + + +### max-fn-params-bools +The maximum number of bool parameters a function can have + +**Default Value:** `3` (`u64`) + +* [fn_params_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools) + + +### warn-on-all-wildcard-imports +Whether to allow certain wildcard imports (prelude, super in tests). + +**Default Value:** `false` (`bool`) + +* [wildcard_imports](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports) + + +### disallowed-macros +The list of disallowed macros, written as fully qualified paths. + +**Default Value:** `[]` (`Vec`) + +* [disallowed_macros](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros) + + +### disallowed-methods +The list of disallowed methods, written as fully qualified paths. + +**Default Value:** `[]` (`Vec`) + +* [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) + + +### disallowed-types +The list of disallowed types, written as fully qualified paths. + +**Default Value:** `[]` (`Vec`) + +* [disallowed_types](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) + + +### unreadable-literal-lint-fractions +Should the fraction of a decimal be linted to include separators. + +**Default Value:** `true` (`bool`) + +* [unreadable_literal](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal) + + +### upper-case-acronyms-aggressive +Enables verbose mode. Triggers if there is more than one uppercase char next to each other + +**Default Value:** `false` (`bool`) + +* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms) + + +### matches-for-let-else +Whether the matches should be considered by the lint, and whether there should +be filtering for common types. + +**Default Value:** `WellKnownTypes` (`crate::manual_let_else::MatchLintBehaviour`) + +* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) + + +### cargo-ignore-publish +For internal testing only, ignores the current `publish` settings in the Cargo manifest. + +**Default Value:** `false` (`bool`) + +* [_cargo_common_metadata](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata) + + +### standard-macro-braces +Enforce the named macros always use the braces specified. + +A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro +is could be used with a full path two `MacroMatcher`s have to be added one with the full path +`crate_name::macro_name` and one with just the macro name. + +**Default Value:** `[]` (`Vec`) + +* [nonstandard_macro_braces](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces) + + +### enforced-import-renames +The list of imports to always rename, a fully qualified path followed by the rename. + +**Default Value:** `[]` (`Vec`) + +* [missing_enforced_import_renames](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames) + + +### allowed-scripts +The list of unicode scripts allowed to be used in the scope. + +**Default Value:** `["Latin"]` (`Vec`) + +* [disallowed_script_idents](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents) + + +### enable-raw-pointer-heuristic-for-send +Whether to apply the raw pointer heuristic to determine if a type is `Send`. + +**Default Value:** `true` (`bool`) + +* [non_send_fields_in_send_ty](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty) + + +### max-suggested-slice-pattern-length +When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in +the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. +For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. + +**Default Value:** `3` (`u64`) + +* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) + + +### await-holding-invalid-types + + +**Default Value:** `[]` (`Vec`) + +* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type) + + +### max-include-file-size +The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + +**Default Value:** `1000000` (`u64`) + +* [large_include_file](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file) + + +### allow-expect-in-tests +Whether `expect` should be allowed within `#[cfg(test)]` + +**Default Value:** `false` (`bool`) + +* [expect_used](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) + + +### allow-unwrap-in-tests +Whether `unwrap` should be allowed in test cfg + +**Default Value:** `false` (`bool`) + +* [unwrap_used](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) + + +### allow-dbg-in-tests +Whether `dbg!` should be allowed in test functions + +**Default Value:** `false` (`bool`) + +* [dbg_macro](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro) + + +### allow-print-in-tests +Whether print macros (ex. `println!`) should be allowed in test functions + +**Default Value:** `false` (`bool`) + +* [print_stdout](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout) +* [print_stderr](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr) + + +### large-error-threshold +The maximum size of the `Err`-variant in a `Result` returned from a function + +**Default Value:** `128` (`u64`) + +* [result_large_err](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) + + +### ignore-interior-mutability +A list of paths to types that should be treated like `Arc`, i.e. ignored but +for the generic parameters for determining interior mutability + +**Default Value:** `["bytes::Bytes"]` (`Vec`) + +* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) + + +### allow-mixed-uninlined-format-args +Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + +**Default Value:** `true` (`bool`) + +* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) + + +### suppress-restriction-lint-in-const +Whether to suppress a restriction lint in constant code. In same +cases the restructured operation might not be unavoidable, as the +suggested counterparts are unavailable in constant code. This +configuration will cause restriction lints to trigger even +if no suggestion can be made. + +**Default Value:** `false` (`bool`) + +* [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) + + + diff --git a/build.rs b/build.rs index b5484bec3c8b..b79d09b0dd2d 100644 --- a/build.rs +++ b/build.rs @@ -3,17 +3,5 @@ fn main() { println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap()); // Don't rebuild even if nothing changed println!("cargo:rerun-if-changed=build.rs"); - // forward git repo hashes we build at - println!( - "cargo:rustc-env=GIT_HASH={}", - rustc_tools_util::get_commit_hash().unwrap_or_default() - ); - println!( - "cargo:rustc-env=COMMIT_DATE={}", - rustc_tools_util::get_commit_date().unwrap_or_default() - ); - println!( - "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", - rustc_tools_util::get_channel() - ); + rustc_tools_util::setup_version_info!(); } diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 2ac3b4fe2ed4..c3f8a782d273 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -5,12 +5,11 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "3.2" +clap = "4.1.4" indoc = "1.0" itertools = "0.10.1" opener = "0.5" shell-escape = "0.1" -tempfile = "3.2" walkdir = "2.3" [features] diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 80bb83af43b1..e70488165b99 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -5,6 +5,9 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] +// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate. +#[allow(unused_extern_crates)] +extern crate rustc_driver; extern crate rustc_lexer; use std::path::PathBuf; diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 71005449b4dd..aafd0f71a59b 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -36,20 +36,12 @@ pub fn run<'a>(path: &str, args: impl Iterator) { } else { exit_if_err(Command::new("cargo").arg("build").status()); - // Run in a tempdir as changes to clippy do not retrigger linting - let target = tempfile::Builder::new() - .prefix("clippy") - .tempdir() - .expect("failed to create tempdir"); - let status = Command::new(cargo_clippy_path()) .arg("clippy") .args(args) .current_dir(path) - .env("CARGO_TARGET_DIR", target.as_ref()) .status(); - target.close().expect("failed to remove tempdir"); exit_if_err(status); } } diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index d3e036692040..b2d67a72fd2b 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,7 +2,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; @@ -110,24 +110,37 @@ fn get_clap_config() -> ArgMatches { Command::new("bless").about("bless the test output changes").arg( Arg::new("ignore-timestamp") .long("ignore-timestamp") + .action(ArgAction::SetTrue) .help("Include files updated before clippy was built"), ), Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix").long("fix").help("Apply the suggestions when possible"), + Arg::new("fix") + .long("fix") + .action(ArgAction::SetTrue) + .help("Apply the suggestions when possible"), Arg::new("allow-dirty") .long("allow-dirty") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has changes") .requires("fix"), Arg::new("allow-staged") .long("allow-staged") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has staged changes") .requires("fix"), ]), Command::new("fmt") .about("Run rustfmt on all projects and tests") .args([ - Arg::new("check").long("check").help("Use the rustfmt --check option"), - Arg::new("verbose").short('v').long("verbose").help("Echo commands run"), + Arg::new("check") + .long("check") + .action(ArgAction::SetTrue) + .help("Use the rustfmt --check option"), + Arg::new("verbose") + .short('v') + .long("verbose") + .action(ArgAction::SetTrue) + .help("Echo commands run"), ]), Command::new("update_lints") .about("Updates lint registration and information from the source code") @@ -140,13 +153,17 @@ fn get_clap_config() -> ArgMatches { * all lints are registered in the lint store", ) .args([ - Arg::new("print-only").long("print-only").help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), + Arg::new("print-only") + .long("print-only") + .action(ArgAction::SetTrue) + .help( + "Print a table of lints to STDOUT. \ + This does not include deprecated and internal lints. \ + (Does not modify any files)", + ), Arg::new("check") .long("check") + .action(ArgAction::SetTrue) .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ]), Command::new("new_lint") @@ -156,15 +173,13 @@ fn get_clap_config() -> ArgMatches { .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") - .takes_value(true) - .value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) + .value_parser(["early", "late"]) .conflicts_with("type") .required_unless_present("type"), Arg::new("name") .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") - .takes_value(true) .required(true), Arg::new("category") .short('c') @@ -172,25 +187,23 @@ fn get_clap_config() -> ArgMatches { .help("What category the lint belongs to") .default_value("nursery") .value_parser([ - PossibleValue::new("style"), - PossibleValue::new("correctness"), - PossibleValue::new("suspicious"), - PossibleValue::new("complexity"), - PossibleValue::new("perf"), - PossibleValue::new("pedantic"), - PossibleValue::new("restriction"), - PossibleValue::new("cargo"), - PossibleValue::new("nursery"), - PossibleValue::new("internal"), - PossibleValue::new("internal_warn"), - ]) - .takes_value(true), - Arg::new("type") - .long("type") - .help("What directory the lint belongs in") - .takes_value(true) - .required(false), - Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + "internal_warn", + ]), + Arg::new("type").long("type").help("What directory the lint belongs in"), + Arg::new("msrv") + .long("msrv") + .action(ArgAction::SetTrue) + .help("Add MSRV config code to the lint"), ]), Command::new("setup") .about("Support for setting up your personal development environment") @@ -201,13 +214,12 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the dependencies added with 'cargo dev setup intellij'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the dependencies added with 'cargo dev setup intellij'"), Arg::new("rustc-repo-path") .long("repo-path") .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") - .takes_value(true) .value_name("path") .conflicts_with("remove") .required(true), @@ -217,26 +229,26 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of an existing git pre-commit hook") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of an existing git pre-commit hook"), ]), Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .args([ Arg::new("remove") .long("remove") - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of existing vscode tasks") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of existing vscode tasks"), ]), ]), Command::new("remove") @@ -295,6 +307,7 @@ fn get_clap_config() -> ArgMatches { .help("The new name of the lint"), Arg::new("uplift") .long("uplift") + .action(ArgAction::SetTrue) .help("This lint will be uplifted into rustc"), ]), Command::new("deprecate").about("Deprecates the given lint").args([ @@ -305,8 +318,6 @@ fn get_clap_config() -> ArgMatches { Arg::new("reason") .long("reason") .short('r') - .required(false) - .takes_value(true) .help("The reason for deprecation"), ]), ]) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 9e15f1504fa9..ec7f1dd0d846 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -120,7 +120,7 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let new_lint = if enable_msrv { format!( - "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n ", + "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv())));\n ", lint_pass = lint.pass, ctor_arg = if lint.pass == "late" { "_" } else { "" }, module_name = lint.name, @@ -238,10 +238,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( r#" - use clippy_utils::msrvs; + use clippy_utils::msrvs::{{self, Msrv}}; {pass_import} use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; - use rustc_semver::RustcVersion; use rustc_session::{{declare_tool_lint, impl_lint_pass}}; "# @@ -263,12 +262,12 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { formatdoc!( r#" pub struct {name_camel} {{ - msrv: Option, + msrv: Msrv, }} impl {name_camel} {{ #[must_use] - pub fn new(msrv: Option) -> Self {{ + pub fn new(msrv: Msrv) -> Self {{ Self {{ msrv }} }} }} @@ -357,15 +356,14 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R let _ = writedoc!( lint_file_contents, r#" - use clippy_utils::{{meets_msrv, msrvs}}; + use clippy_utils::msrvs::{{self, Msrv}}; use rustc_lint::{{{context_import}, LintContext}}; - use rustc_semver::RustcVersion; use super::{name_upper}; // TODO: Adjust the parameters as necessary - pub(super) fn check(cx: &{context_import}, msrv: Option) {{ - if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ + pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{ + if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{ return; }} todo!(); diff --git a/clippy_dev/src/setup/git_hook.rs b/clippy_dev/src/setup/git_hook.rs index 1de5b1940bae..c7c53bc69d0b 100644 --- a/clippy_dev/src/setup/git_hook.rs +++ b/clippy_dev/src/setup/git_hook.rs @@ -6,7 +6,7 @@ use super::verify_inside_clippy_dir; /// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo. /// I've decided against this for the sake of simplicity and to make sure that it doesn't install /// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool -/// for formatting and should therefor only be used in a normal clone of clippy +/// for formatting and should therefore only be used in a normal clone of clippy const REPO_GIT_DIR: &str = ".git"; const HOOK_SOURCE_FILE: &str = "util/etc/pre-commit.sh"; const HOOK_TARGET_FILE: &str = ".git/hooks/pre-commit"; diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index e690bc369cd4..837618c9294b 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder; use indoc::writedoc; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::Write; use std::fs::{self, OpenOptions}; @@ -36,6 +36,60 @@ pub enum UpdateMode { pub fn update(update_mode: UpdateMode) { let (lints, deprecated_lints, renamed_lints) = gather_all(); generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); + remove_old_files(update_mode); +} + +/// Remove files no longer needed after +/// that may be reintroduced unintentionally +/// +/// FIXME: This is a temporary measure that should be removed when there are no more PRs that +/// include the stray files +fn remove_old_files(update_mode: UpdateMode) { + let mut failed = false; + let mut remove_file = |path: &Path| match update_mode { + UpdateMode::Check => { + if path.exists() { + failed = true; + println!("unexpected file: {}", path.display()); + } + }, + UpdateMode::Change => { + if fs::remove_file(path).is_ok() { + println!("removed file: {}", path.display()); + } + }, + }; + + let files = [ + "clippy_lints/src/lib.register_all.rs", + "clippy_lints/src/lib.register_cargo.rs", + "clippy_lints/src/lib.register_complexity.rs", + "clippy_lints/src/lib.register_correctness.rs", + "clippy_lints/src/lib.register_internal.rs", + "clippy_lints/src/lib.register_lints.rs", + "clippy_lints/src/lib.register_nursery.rs", + "clippy_lints/src/lib.register_pedantic.rs", + "clippy_lints/src/lib.register_perf.rs", + "clippy_lints/src/lib.register_restriction.rs", + "clippy_lints/src/lib.register_style.rs", + "clippy_lints/src/lib.register_suspicious.rs", + "src/docs.rs", + ]; + + for file in files { + remove_file(Path::new(file)); + } + + if let Ok(docs_dir) = fs::read_dir("src/docs") { + for doc_file in docs_dir { + let path = doc_file.unwrap().path(); + remove_file(&path); + } + } + + if failed { + exit_with_failure(); + } } fn generate_lint_files( @@ -104,9 +158,9 @@ fn generate_lint_files( ); process_file( - "clippy_lints/src/lib.register_lints.rs", + "clippy_lints/src/declared_lints.rs", update_mode, - &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), + &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), ); process_file( "clippy_lints/src/lib.deprecated.rs", @@ -114,26 +168,6 @@ fn generate_lint_files( &gen_deprecated(deprecated_lints), ); - let all_group_lints = usable_lints.iter().filter(|l| { - matches!( - &*l.group, - "correctness" | "suspicious" | "style" | "complexity" | "perf" - ) - }); - let content = gen_lint_group_list("all", all_group_lints); - process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content); - - update_docs(update_mode, &usable_lints); - - for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { - let content = gen_lint_group_list(&lint_group, lints.iter()); - process_file( - format!("clippy_lints/src/lib.register_{lint_group}.rs"), - update_mode, - &content, - ); - } - let content = gen_deprecated_lints_test(deprecated_lints); process_file("tests/ui/deprecated.rs", update_mode, &content); @@ -141,62 +175,6 @@ fn generate_lint_files( process_file("tests/ui/rename.rs", update_mode, &content); } -fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) { - replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| { - for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() { - writeln!(res, r#" "{name}","#).unwrap(); - } - }); - - if update_mode == UpdateMode::Check { - let mut extra = BTreeSet::new(); - let mut lint_names = usable_lints - .iter() - .map(|lint| lint.name.clone()) - .collect::>(); - for file in std::fs::read_dir("src/docs").unwrap() { - let filename = file.unwrap().file_name().into_string().unwrap(); - if let Some(name) = filename.strip_suffix(".txt") { - if !lint_names.remove(name) { - extra.insert(name.to_string()); - } - } - } - - let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names); - - if failed { - exit_with_failure(); - } - } else { - if std::fs::remove_dir_all("src/docs").is_err() { - eprintln!("could not remove src/docs directory"); - } - if std::fs::create_dir("src/docs").is_err() { - eprintln!("could not recreate src/docs directory"); - } - } - for lint in usable_lints { - process_file( - Path::new("src/docs").join(lint.name.clone() + ".txt"), - update_mode, - &lint.documentation, - ); - } -} - -fn print_lint_names(header: &str, lints: &BTreeSet) -> bool { - if lints.is_empty() { - return false; - } - println!("{header}"); - for lint in lints.iter().sorted() { - println!(" {lint}"); - } - println!(); - true -} - pub fn print_lints() { let (lint_list, _, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); @@ -641,26 +619,17 @@ struct Lint { desc: String, module: String, declaration_range: Range, - documentation: String, } impl Lint { #[must_use] - fn new( - name: &str, - group: &str, - desc: &str, - module: &str, - declaration_range: Range, - documentation: String, - ) -> Self { + fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range) -> Self { Self { name: name.to_lowercase(), group: group.into(), desc: remove_line_splices(desc), module: module.into(), declaration_range, - documentation, } } @@ -716,25 +685,6 @@ impl RenamedLint { } } -/// Generates the code for registering a group -fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { - let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); - details.sort_unstable(); - - let mut output = GENERATED_FILE_COMMENT.to_string(); - - let _ = writeln!( - output, - "store.register_group(true, \"clippy::{group_name}\", Some(\"clippy_{group_name}\"), vec![", - ); - for (module, name) in details { - let _ = writeln!(output, " LintId::of({module}::{name}),"); - } - output.push_str("])\n"); - - output -} - /// Generates the `register_removed` code #[must_use] fn gen_deprecated(lints: &[DeprecatedLint]) -> String { @@ -759,7 +709,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { /// Generates the code for registering lints #[must_use] -fn gen_register_lint_list<'a>( +fn gen_declared_lints<'a>( internal_lints: impl Iterator, usable_lints: impl Iterator, ) -> String { @@ -770,15 +720,15 @@ fn gen_register_lint_list<'a>( details.sort_unstable(); let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("store.register_lints(&[\n"); + output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n"); for (is_public, module_name, lint_name) in details { if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - let _ = writeln!(output, " {module_name}::{lint_name},"); + let _ = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); } - output.push_str("])\n"); + output.push_str("];\n"); output } @@ -910,35 +860,26 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint", ) { let start = range.start; - let mut docs = String::with_capacity(128); - let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace)); + let mut iter = iter + .by_ref() + .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })); // matches `!{` match_tokens!(iter, Bang OpenBrace); - let mut in_code = false; - while let Some(t) = iter.next() { - match t.token_kind { - TokenKind::LineComment { .. } => { - if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) { - if line.starts_with("```") { - docs += "```\n"; - in_code = !in_code; - } else if !(in_code && line.starts_with("# ")) { - docs += line; - docs.push('\n'); - } - } - }, - TokenKind::Pound => { - match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); - break; - }, - TokenKind::Ident => { - break; - }, - _ => {}, - } + match iter.next() { + // #[clippy::version = "version"] pub + Some(LintDeclSearchResult { + token_kind: TokenKind::Pound, + .. + }) => { + match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident); + }, + // pub + Some(LintDeclSearchResult { + token_kind: TokenKind::Ident, + .. + }) => (), + _ => continue, } - docs.pop(); // remove final newline let (name, group, desc) = match_tokens!( iter, @@ -956,7 +897,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { .. }) = iter.next() { - lints.push(Lint::new(name, group, desc, module, start..range.end, docs)); + lints.push(Lint::new(name, group, desc, module, start..range.end)); } } } @@ -1186,7 +1127,6 @@ mod tests { "\"really long text\"", "module_name", Range::default(), - String::new(), ), Lint::new( "doc_markdown", @@ -1194,7 +1134,6 @@ mod tests { "\"single line\"", "module_name", Range::default(), - String::new(), ), ]; assert_eq!(expected, result); @@ -1234,7 +1173,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), ), Lint::new( "should_assert_eq2", @@ -1242,7 +1180,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), ), Lint::new( "should_assert_eq2", @@ -1250,7 +1187,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), ), ]; let expected = vec![Lint::new( @@ -1259,7 +1195,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), )]; assert_eq!(expected, Lint::usable_lints(&lints)); } @@ -1267,51 +1202,22 @@ mod tests { #[test] fn test_by_lint_group() { let lints = vec![ - Lint::new( - "should_assert_eq", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), Lint::new( "should_assert_eq2", "group2", "\"abc\"", "module_name", Range::default(), - String::new(), - ), - Lint::new( - "incorrect_match", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), ), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ]; let mut expected: HashMap> = HashMap::new(); expected.insert( "group1".to_string(), vec![ - Lint::new( - "should_assert_eq", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - Lint::new( - "incorrect_match", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ], ); expected.insert( @@ -1322,7 +1228,6 @@ mod tests { "\"abc\"", "module_name", Range::default(), - String::new(), )], ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); @@ -1357,48 +1262,4 @@ mod tests { assert_eq!(expected, gen_deprecated(&lints)); } - - #[test] - fn test_gen_lint_group_list() { - let lints = vec![ - Lint::new( - "abc", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - Lint::new( - "should_assert_eq", - "group1", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - Lint::new( - "internal", - "internal_style", - "\"abc\"", - "module_name", - Range::default(), - String::new(), - ), - ]; - let expected = GENERATED_FILE_COMMENT.to_string() - + &[ - "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![", - " LintId::of(module_name::ABC),", - " LintId::of(module_name::INTERNAL),", - " LintId::of(module_name::SHOULD_ASSERT_EQ),", - "])", - ] - .join("\n") - + "\n"; - - let result = gen_lint_group_list("group1", lints.iter()); - - assert_eq!(expected, result); - } } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 1ff976f48f61..989e4d3fa56c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.66" +version = "0.1.69" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -9,8 +9,9 @@ keywords = ["clippy", "lint", "plugin"] edition = "2021" [dependencies] -cargo_metadata = "0.14" +cargo_metadata = "0.15.3" clippy_utils = { path = "../clippy_utils" } +declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" itertools = "0.10.1" pulldown-cmark = { version = "0.9", default-features = false } diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_range.rs similarity index 64% rename from clippy_lints/src/almost_complete_letter_range.rs rename to clippy_lints/src/almost_complete_range.rs index 073e4af1318e..42e14b5cd945 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -1,18 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{trim_span, walk_span_to_context}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; declare_clippy_lint! { /// ### What it does - /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but - /// don't because they're a half open range. + /// Checks for ranges which almost include the entire range of letters from 'a' to 'z' + /// or digits from '0' to '9', but don't because they're a half open range. /// /// ### Why is this bad? /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters. @@ -26,27 +25,27 @@ declare_clippy_lint! { /// let _ = 'a'..='z'; /// ``` #[clippy::version = "1.63.0"] - pub ALMOST_COMPLETE_LETTER_RANGE, + pub ALMOST_COMPLETE_RANGE, suspicious, - "almost complete letter range" + "almost complete range" } -impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); +impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]); -pub struct AlmostCompleteLetterRange { - msrv: Option, +pub struct AlmostCompleteRange { + msrv: Msrv, } -impl AlmostCompleteLetterRange { - pub fn new(msrv: Option) -> Self { +impl AlmostCompleteRange { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } -impl EarlyLintPass for AlmostCompleteLetterRange { +impl EarlyLintPass for AlmostCompleteRange { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { let ctxt = e.span.ctxt(); let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) && let Some(end) = walk_span_to_context(end.span, ctxt) - && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) + && self.msrv.meets(msrvs::RANGE_INCLUSIVE) { Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) } else { @@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange { if let PatKind::Range(Some(start), Some(end), kind) = &p.kind && matches!(kind.node, RangeEnd::Excluded) { - let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) { + let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) { "..=" } else { "..." @@ -73,20 +72,33 @@ impl EarlyLintPass for AlmostCompleteLetterRange { } fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { - if let ExprKind::Lit(start_lit) = &start.peel_parens().kind - && let ExprKind::Lit(end_lit) = &end.peel_parens().kind + if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind + && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind && matches!( - (&start_lit.kind, &end_lit.kind), - (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) - | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) + ( + LitKind::from_token_lit(start_token_lit), + LitKind::from_token_lit(end_token_lit), + ), + ( + Ok(LitKind::Byte(b'a') | LitKind::Char('a')), + Ok(LitKind::Byte(b'z') | LitKind::Char('z')) + ) + | ( + Ok(LitKind::Byte(b'A') | LitKind::Char('A')), + Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')), + ) + | ( + Ok(LitKind::Byte(b'0') | LitKind::Char('0')), + Ok(LitKind::Byte(b'9') | LitKind::Char('9')), + ) ) && !in_external_macro(cx.sess(), span) { span_lint_and_then( cx, - ALMOST_COMPLETE_LETTER_RANGE, + ALMOST_COMPLETE_RANGE, span, - "almost complete ascii letter range", + "almost complete ascii range", |diag| { if let Some((span, sugg)) = sugg { diag.span_suggestion( diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 724490fb4959..ccf82f132f4e 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option); 19] = [ ]; pub struct ApproxConstant { - msrv: Option, + msrv: Msrv, } impl ApproxConstant { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } @@ -87,7 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) { + if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) { span_lint_and_help( cx, APPROX_CONSTANT, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0bd1f8b784e8..751c262673b1 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -2,23 +2,21 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, macro_backtrace}; -use clippy_utils::msrvs; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, }; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_span::{sym, DUMMY_SP}; use semver::Version; static UNIX_SYSTEMS: &[&str] = &[ @@ -303,6 +301,26 @@ declare_lint_pass!(Attributes => [ ]); impl<'tcx> LateLintPass<'tcx> for Attributes { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for (name, level) in &cx.sess().opts.lint_opts { + if name == "clippy::restriction" && *level > Level::Allow { + span_lint_and_then( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + DUMMY_SP, + "`clippy::restriction` is not meant to be enabled as a group", + |diag| { + diag.note(format!( + "because of the command line `--{} clippy::restriction`", + level.as_str() + )); + diag.help("enable the restriction lints you need individually"); + }, + ); + } + } + } + fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { if let Some(items) = &attr.meta_item_list() { if let Some(ident) = attr.ident() { @@ -358,7 +376,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { | "enum_glob_use" | "redundant_pub_crate" | "macro_use_imports" - | "unsafe_removed_from_name", + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" ) }) { @@ -441,9 +461,9 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe cx, BLANKET_CLIPPY_RESTRICTION_LINTS, lint.span(), - "restriction lints are not meant to be all enabled", + "`clippy::restriction` is not meant to be enabled as a group", None, - "try enabling only the lints you really need", + "enable the restriction lints you need individually", ); } } @@ -452,7 +472,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) { // Check for the feature - if !cx.tcx.sess.features_untracked().lint_reasons { + if !cx.tcx.features().lint_reasons { return; } @@ -464,6 +484,11 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem return; } + // Check if the attribute is in an external macro and therefore out of the developer's control + if in_external_macro(cx.sess(), attr.span) { + return; + } + span_lint_and_help( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, @@ -549,7 +574,7 @@ fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribut } } -fn check_semver(cx: &LateContext<'_>, span: Span, lit: &Lit) { +fn check_semver(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind { if Version::parse(is.as_str()).is_ok() { return; @@ -572,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { } pub struct EarlyAttributes { - pub msrv: Option, + pub msrv: Msrv, } impl_lint_pass!(EarlyAttributes => [ @@ -587,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - check_deprecated_cfg_attr(cx, attr, self.msrv); + check_deprecated_cfg_attr(cx, attr, &self.msrv); check_mismatched_target_os(cx, attr); } @@ -627,9 +652,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It } } -fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option) { +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES); + if msrv.meets(msrvs::TOOL_ATTRIBUTES); // check cfg_attr if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 34717811866d..d40a385435af 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -189,7 +188,7 @@ impl LateLintPass<'_> for AwaitHolding { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_invalid_types { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.def_ids.insert(id, conf.clone()); } } diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 4bd55c1429c3..556fa579000c 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,10 +1,11 @@ +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; +use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -43,9 +44,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool { ) && !e.span.from_expansion() } -fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(e); - +fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { cx.tcx .lang_items() .not_trait() @@ -59,7 +58,7 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ) }) .map_or(false, |assoc_item| { - let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, &[])); + let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, [])); let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); nty.is_bool() @@ -77,31 +76,57 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { return; } let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; - if !(is_bool_lit(a) ^ is_bool_lit(b)) { + + let a_span = a.span.source_callsite(); + let b_span = b.span.source_callsite(); + + let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) { + // assert_eq!(true, b) + // ^^^^^^ + (true, false) => (a_span.until(b_span), b), + // assert_eq!(a, true) + // ^^^^^^ + (false, true) => (b_span.with_lo(a_span.hi()), a), // If there are two boolean arguments, we definitely don't understand // what's going on, so better leave things as is... // // Or there is simply no boolean and then we can leave things as is! - return; - } + _ => return, + }; - if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { + let non_lit_ty = cx.typeck_results().expr_ty(non_lit_expr); + + if !is_impl_not_trait_with_bool_out(cx, non_lit_ty) { // At this point the expression which is not a boolean // literal does not implement Not trait with a bool output, // so we cannot suggest to rewrite our code return; } + if !is_copy(cx, non_lit_ty) { + // Only lint with types that are `Copy` because `assert!(x)` takes + // ownership of `x` whereas `assert_eq(x, true)` does not + return; + } + let macro_name = macro_name.as_str(); let non_eq_mac = ¯o_name[..macro_name.len() - 3]; - span_lint_and_sugg( + span_lint_and_then( cx, BOOL_ASSERT_COMPARISON, macro_call.span, &format!("used `{macro_name}!` with a literal bool"), - "replace it with", - format!("{non_eq_mac}!(..)"), - Applicability::MaybeIncorrect, + |diag| { + // assert_eq!(...) + // ^^^^^^^^^ + let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + + diag.multipart_suggestion( + format!("replace it with `{non_eq_mac}!(..)`"), + vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())], + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 001d74c26054..bdb3a0116027 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -1,9 +1,10 @@ +use clippy_utils::higher::If; use rustc_ast::LitKind; use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg}; +use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg}; use rustc_errors::Applicability; declare_clippy_lint! { @@ -12,7 +13,7 @@ declare_clippy_lint! { /// this lint suggests using a `from()` function or an `as` coercion. /// /// ### Why is this bad? - /// Coercion or `from()` is idiomatic way to convert bool to a number. + /// Coercion or `from()` is another way to convert bool to a number. /// Both methods are guaranteed to return 1 for true, and 0 for false. /// /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E @@ -38,23 +39,23 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.65.0"] pub BOOL_TO_INT_WITH_IF, - style, + pedantic, "using if to convert bool to int" } declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { - fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if !expr.span.from_expansion() { - check_if_else(ctx, expr); + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) { + check_if_else(cx, expr); } } } -fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if let ExprKind::If(check, then, Some(else_)) = expr.kind +fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr) && let Some(then_lit) = int_literal(then) - && let Some(else_lit) = int_literal(else_) + && let Some(else_lit) = int_literal(r#else) { let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { false @@ -66,17 +67,17 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx }; let mut applicability = Applicability::MachineApplicable; let snippet = { - let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability); + let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); if inverted { sugg = !sugg; } sugg }; - let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type + let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type let suggestion = { - let wrap_in_curly = is_else_clause(ctx.tcx, expr); + let wrap_in_curly = is_else_clause(cx.tcx, expr); let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into()); if wrap_in_curly { s = s.blockify(); @@ -87,7 +88,7 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx let into_snippet = snippet.clone().maybe_par(); let as_snippet = snippet.as_ty(ty); - span_lint_and_then(ctx, + span_lint_and_then(cx, BOOL_TO_INT_WITH_IF, expr.span, "boolean to int conversion using if", diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 2a15cbc7a3c3..939bdbcdc7cd 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::eq_expr_value; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{eq_expr_value, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -481,9 +481,11 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { } } -fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) + cx.tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) } struct NotSimplificationVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs index f35a79dcc739..9d98a6bab710 100644 --- a/clippy_lints/src/box_default.rs +++ b/clippy_lints/src/box_default.rs @@ -8,7 +8,7 @@ use rustc_hir::{ Block, Expr, ExprKind, Local, Node, QPath, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; +use rustc_middle::{lint::in_external_macro, ty::print::with_forced_trimmed_paths}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -30,7 +30,7 @@ declare_clippy_lint! { /// ```rust /// let x: Box = Box::default(); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub BOX_DEFAULT, perf, "Using Box::new(T::default()) instead of Box::default()" @@ -46,7 +46,7 @@ impl LateLintPass<'_> for BoxDefault { && !in_external_macro(cx.sess(), expr.span) && (expr.span.eq_ctxt(arg.span) || is_vec_expn(cx, arg)) && seg.ident.name == sym::new - && path_def_id(cx, ty) == cx.tcx.lang_items().owned_box() + && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) && is_default_equivalent(cx, arg) { let arg_ty = cx.typeck_results().expr_ty(arg); @@ -59,7 +59,7 @@ impl LateLintPass<'_> for BoxDefault { if is_plain_default(arg_path) || given_type(cx, expr) { "Box::default()".into() } else { - format!("Box::<{arg_ty}>::default()") + with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()")) }, Applicability::MachineApplicable ); @@ -88,7 +88,7 @@ struct InferVisitor(bool); impl<'tcx> Visitor<'tcx> for InferVisitor { fn visit_ty(&mut self, t: &rustc_hir::Ty<'_>) { - self.0 |= matches!(t.kind, TyKind::Infer); + self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..)); if !self.0 { walk_ty(self, t); } diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 3f1edabe6c50..442262983337 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_ABS_TO_UNSIGNED; @@ -15,9 +14,9 @@ pub(super) fn check( cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::UNSIGNED_ABS) + if msrv.meets(msrvs::UNSIGNED_ABS) && let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 13c403234dad..cf07e050ccce 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_constant; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_isize_or_usize; -use clippy_utils::{in_constant, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use rustc_semver::RustcVersion; use super::{utils, CAST_LOSSLESS}; @@ -16,7 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option, + msrv: &Msrv, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { return; @@ -57,13 +57,7 @@ pub(super) fn check( ); } -fn should_lint( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_from: Ty<'_>, - cast_to: Ty<'_>, - msrv: Option, -) -> bool { +fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). if in_constant(cx, expr.hir_id) { return false; @@ -89,7 +83,7 @@ fn should_lint( }; !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, - (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true, + (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true, (_, _) => { matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) }, diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 88deb4565eb2..f3f8b8d87982 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,13 +1,15 @@ use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::expr_or_init; +use clippy_utils::source::snippet; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use rustc_ast::ast; -use rustc_attr::IntType; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_span::Span; +use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; @@ -75,7 +77,14 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b } } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + cast_to_span: Span, +) { let msg = match (cast_from.kind(), cast_to.is_integral()) { (ty::Int(_) | ty::Uint(_), true) => { let from_nbits = apply_reductions( @@ -119,12 +128,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, }; let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); - let cast_from_ptr_size = def.repr().int.map_or(true, |ty| { - matches!( - ty, - IntType::SignedInt(ast::IntTy::Isize) | IntType::UnsignedInt(ast::UintTy::Usize) - ) - }); + let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),)); let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) { (false, false) if from_nbits > to_nbits => "", (true, false) if from_nbits > to_nbits => "", @@ -145,7 +149,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, ); return; } - format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}") }, (ty::Float(_), true) => { @@ -159,5 +163,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => return, }; - span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg); + let name_of_cast_from = snippet(cx, cast_expr.span, ".."); + let cast_to_snip = snippet(cx, cast_to_span, ".."); + let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})"); + + span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| { + diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ..."); + diag.span_suggestion_with_style( + expr.span, + "... or use `try_from` and handle the error accordingly", + suggestion, + Applicability::Unspecified, + // always show the suggestion in a separate line + SuggestionStyle::ShowAlways, + ); + }); } diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index d31d10d22b92..27cc5a1c3f04 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,16 +1,16 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_then, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) { + if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) { return; } @@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Optio diag.span_suggestion( expr.span, - &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), sugg, rustc_errors::Applicability::HasPlaceholders, ); @@ -68,7 +68,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Optio fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let map = cx.tcx.hir(); if_chain! { - if let Some(parent_id) = map.find_parent_node(expr.hir_id); + if let Some(parent_id) = map.opt_parent_id(expr.hir_id); if let Some(parent) = map.find(parent_id); then { let expr = match parent { diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 284ef165998a..627b795d6edd 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_FROM_RAW_PARTS; @@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option { } } -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_expr: &Expr<'_>, - cast_to: Ty<'_>, - msrv: Option, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS); + if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS); if let ty::RawPtr(ptrty) = cast_to.kind(); if let ty::Slice(_) = ptrty.ty.kind(); if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index b72c4c772f1c..362f70d12d18 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -21,11 +21,11 @@ mod ptr_as_ptr; mod unnecessary_cast; mod utils; -use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs}; +use clippy_utils::is_hir_ty_cfg_dependant; +use clippy_utils::msrvs::{self, Msrv}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -80,7 +80,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for casts between numerical types that may /// truncate large values. This is expected behavior, so the cast is `Allow` by - /// default. + /// default. It suggests user either explicitly ignore the lint, + /// or use `try_from()` and handle the truncation, default, or panic explicitly. /// /// ### Why is this bad? /// In some problem domains, it is good practice to avoid @@ -93,6 +94,21 @@ declare_clippy_lint! { /// x as u8 /// } /// ``` + /// Use instead: + /// ``` + /// fn as_u8(x: u64) -> u8 { + /// if let Ok(x) = u8::try_from(x) { + /// x + /// } else { + /// todo!(); + /// } + /// } + /// // Or + /// #[allow(clippy::cast_possible_truncation)] + /// fn as_u16(x: u64) -> u16 { + /// x as u16 + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub CAST_POSSIBLE_TRUNCATION, pedantic, @@ -593,7 +609,7 @@ declare_clippy_lint! { /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); /// ``` /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub CAST_SLICE_FROM_RAW_PARTS, suspicious, "casting a slice created from a pointer and length to a slice pointer" @@ -641,19 +657,19 @@ declare_clippy_lint! { /// ```rust,ignore /// let _: = 0_u64; /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.66.0"] pub CAST_NAN_TO_INT, suspicious, "casting a known floating-point NaN into an integer" } pub struct Casts { - msrv: Option, + msrv: Msrv, } impl Casts { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -686,7 +702,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); } if expr.span.from_expansion() { @@ -705,28 +721,28 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { return; } - cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { - cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to); + cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } as_underscore::check(cx, expr, cast_to_hir); - if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { + if self.msrv.meets(msrvs::BORROW_AS_PTR) { borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); } } @@ -734,8 +750,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); - ptr_as_ptr::check(cx, expr, self.msrv); - cast_slice_different_sizes::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); + cast_slice_different_sizes::check(cx, expr, &self.msrv); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index c2b9253ec35d..15ffb00da88b 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,19 +1,18 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, TypeAndMut}; -use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option) { - if !meets_msrv(msrv, msrvs::POINTER_CAST) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { + if !msrv.meets(msrvs::POINTER_CAST) { return; } @@ -26,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option( lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to); return false; }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => { - return false; - }, LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => @@ -78,13 +75,26 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + if let Some(id) = path_to_local(cast_expr) + && let Some(span) = cx.tcx.hir().opt_span(id) + && span.ctxt() != cast_expr.span.ctxt() + { + // Binding context is different than the identifiers context. + // Weird macro wizardry could be involved here. + return false; + } + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - cast_str, + if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) { + format!("{{ {cast_str} }}") + } else { + cast_str + }, Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 78e9921f036f..9102a89e3772 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,14 +1,14 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +37,12 @@ declare_clippy_lint! { } pub struct CheckedConversions { - msrv: Option, + msrv: Msrv, } impl CheckedConversions { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::TRY_FROM) { + if !self.msrv.meets(msrvs::TRY_FROM) { return; } diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 77af3b53d633..1c3a89a97824 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -4,11 +4,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::LimitStack; +use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -56,15 +56,13 @@ impl CognitiveComplexity { cx: &LateContext<'tcx>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, + expr: &'tcx Expr<'_>, body_span: Span, ) { if body_span.from_expansion() { return; } - let expr = body.value; - let mut cc = 1u64; let mut returns = 0u64; let _: Option = for_each_expr(expr, |e| { @@ -146,7 +144,18 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { ) { let def_id = cx.tcx.hir().local_def_id(hir_id); if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { - self.check(cx, kind, decl, body, span); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(b) => b, + None => { + return; + }, + } + } else { + body.value + }; + + self.check(cx, kind, decl, expr, span); } } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 90430b71a0ef..b38e09dc09f4 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -160,11 +160,13 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; // Prevent triggering on `if c { if let a = b { .. } }`. if !matches!(check_inner.kind, ast::ExprKind::Let(..)); - if expr.span.ctxt() == inner.span.ctxt(); + let ctxt = expr.span.ctxt(); + if inner.span.ctxt() == ctxt; then { span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { - let lhs = Sugg::ast(cx, check, ".."); - let rhs = Sugg::ast(cx, check_inner, ".."); + let mut app = Applicability::MachineApplicable; + let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); + let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); diag.span_suggestion( expr.span, "collapse nested if block", @@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & lhs.and(&rhs), snippet_block(cx, content.span, "..", Some(expr.span)), ), - Applicability::MachineApplicable, // snippet + app, // snippet ); }); } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index a05b41eb3ab5..0fe973b49a35 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq}; +use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -106,7 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { // Check that the type being compared implements `core::cmp::Ord` let ty = cx.typeck_results().expr_ty(lhs1); - let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); + let is_ord = cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])); if !is_ord { return; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 0e3d9317590f..f10c35cde52a 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -525,7 +525,11 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo .iter() .filter(|&&(_, name)| !name.as_str().starts_with('_')) .any(|&(_, name)| { - let mut walker = ContainsName { name, result: false }; + let mut walker = ContainsName { + name, + result: false, + cx, + }; // Scan block block diff --git a/clippy_lints/src/copy_iterator.rs b/clippy_lints/src/copy_iterator.rs index 026683f60062..e38f77268530 100644 --- a/clippy_lints/src/copy_iterator.rs +++ b/clippy_lints/src/copy_iterator.rs @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyIterator { of_trait: Some(ref trait_ref), .. }) = item.kind; - let ty = cx.tcx.type_of(item.def_id); + let ty = cx.tcx.type_of(item.owner_id); if is_copy(cx, ty); if let Some(trait_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id); diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 20cc330e035f..b2fe0386f945 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -55,7 +55,7 @@ impl EarlyLintPass for CrateInMacroDef { if_chain! { if item.attrs.iter().any(is_macro_export); if let ItemKind::MacroDef(macro_def) = &item.kind; - let tts = macro_def.body.inner_tokens(); + let tts = macro_def.body.tokens.clone(); if let Some(span) = contains_unhygienic_crate_reference(&tts); then { span_lint_and_sugg( diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index fe9f4f9ae3cb..799e71e847a9 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -10,11 +10,11 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Checks for usage of dbg!() macro. + /// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro. /// /// ### Why is this bad? - /// `dbg!` macro is intended as a debugging tool. It - /// should not be in version control. + /// The `dbg!` macro is intended as a debugging tool. It should not be present in released + /// software or committed to a version control system. /// /// ### Example /// ```rust,ignore @@ -91,8 +91,8 @@ impl LateLintPass<'_> for DbgMacro { cx, DBG_MACRO, macro_call.span, - "`dbg!` macro is intended as a debugging tool", - "ensure to avoid having uses of it in version control", + "the `dbg!` macro is intended as a debugging tool", + "remove the invocation before committing it to a version control system", suggestion, applicability, ); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs new file mode 100644 index 000000000000..457a25826e79 --- /dev/null +++ b/clippy_lints/src/declared_lints.rs @@ -0,0 +1,638 @@ +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + +pub(crate) static LINTS: &[&crate::LintInfo] = &[ + #[cfg(feature = "internal")] + crate::utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, + crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO, + crate::approx_const::APPROX_CONSTANT_INFO, + crate::as_conversions::AS_CONVERSIONS_INFO, + crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO, + crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO, + crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO, + crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO, + crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO, + crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO, + crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, + crate::attrs::DEPRECATED_CFG_ATTR_INFO, + crate::attrs::DEPRECATED_SEMVER_INFO, + crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, + crate::attrs::INLINE_ALWAYS_INFO, + crate::attrs::MISMATCHED_TARGET_OS_INFO, + crate::attrs::USELESS_ATTRIBUTE_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO, + crate::blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS_INFO, + crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO, + crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO, + crate::booleans::NONMINIMAL_BOOL_INFO, + crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, + crate::borrow_deref_ref::BORROW_DEREF_REF_INFO, + crate::box_default::BOX_DEFAULT_INFO, + crate::cargo::CARGO_COMMON_METADATA_INFO, + crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO, + crate::cargo::NEGATIVE_FEATURE_NAMES_INFO, + crate::cargo::REDUNDANT_FEATURE_NAMES_INFO, + crate::cargo::WILDCARD_DEPENDENCIES_INFO, + crate::casts::AS_PTR_CAST_MUT_INFO, + crate::casts::AS_UNDERSCORE_INFO, + crate::casts::BORROW_AS_PTR_INFO, + crate::casts::CAST_ABS_TO_UNSIGNED_INFO, + crate::casts::CAST_ENUM_CONSTRUCTOR_INFO, + crate::casts::CAST_ENUM_TRUNCATION_INFO, + crate::casts::CAST_LOSSLESS_INFO, + crate::casts::CAST_NAN_TO_INT_INFO, + crate::casts::CAST_POSSIBLE_TRUNCATION_INFO, + crate::casts::CAST_POSSIBLE_WRAP_INFO, + crate::casts::CAST_PRECISION_LOSS_INFO, + crate::casts::CAST_PTR_ALIGNMENT_INFO, + crate::casts::CAST_REF_TO_MUT_INFO, + crate::casts::CAST_SIGN_LOSS_INFO, + crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, + crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, + crate::casts::CHAR_LIT_AS_U8_INFO, + crate::casts::FN_TO_NUMERIC_CAST_INFO, + crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, + crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, + crate::casts::PTR_AS_PTR_INFO, + crate::casts::UNNECESSARY_CAST_INFO, + crate::checked_conversions::CHECKED_CONVERSIONS_INFO, + crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, + crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, + crate::collapsible_if::COLLAPSIBLE_IF_INFO, + crate::comparison_chain::COMPARISON_CHAIN_INFO, + crate::copies::BRANCHES_SHARING_CODE_INFO, + crate::copies::IFS_SAME_COND_INFO, + crate::copies::IF_SAME_THEN_ELSE_INFO, + crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, + crate::copy_iterator::COPY_ITERATOR_INFO, + crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO, + crate::create_dir::CREATE_DIR_INFO, + crate::dbg_macro::DBG_MACRO_INFO, + crate::default::DEFAULT_TRAIT_ACCESS_INFO, + crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO, + crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO, + crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO, + crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO, + crate::dereference::EXPLICIT_AUTO_DEREF_INFO, + crate::dereference::EXPLICIT_DEREF_METHODS_INFO, + crate::dereference::NEEDLESS_BORROW_INFO, + crate::dereference::REF_BINDING_TO_REFERENCE_INFO, + crate::derivable_impls::DERIVABLE_IMPLS_INFO, + crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, + crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, + crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, + crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, + crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO, + crate::disallowed_macros::DISALLOWED_MACROS_INFO, + crate::disallowed_methods::DISALLOWED_METHODS_INFO, + crate::disallowed_names::DISALLOWED_NAMES_INFO, + crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, + crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_LINK_WITH_QUOTES_INFO, + crate::doc::DOC_MARKDOWN_INFO, + crate::doc::MISSING_ERRORS_DOC_INFO, + crate::doc::MISSING_PANICS_DOC_INFO, + crate::doc::MISSING_SAFETY_DOC_INFO, + crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, + crate::doc::UNNECESSARY_SAFETY_DOC_INFO, + crate::double_parens::DOUBLE_PARENS_INFO, + crate::drop_forget_ref::DROP_COPY_INFO, + crate::drop_forget_ref::DROP_NON_DROP_INFO, + crate::drop_forget_ref::DROP_REF_INFO, + crate::drop_forget_ref::FORGET_COPY_INFO, + crate::drop_forget_ref::FORGET_NON_DROP_INFO, + crate::drop_forget_ref::FORGET_REF_INFO, + crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO, + crate::duplicate_mod::DUPLICATE_MOD_INFO, + crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, + crate::empty_drop::EMPTY_DROP_INFO, + crate::empty_enum::EMPTY_ENUM_INFO, + crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO, + crate::entry::MAP_ENTRY_INFO, + crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO, + crate::enum_variants::ENUM_VARIANT_NAMES_INFO, + crate::enum_variants::MODULE_INCEPTION_INFO, + crate::enum_variants::MODULE_NAME_REPETITIONS_INFO, + crate::equatable_if_let::EQUATABLE_IF_LET_INFO, + crate::escape::BOXED_LOCAL_INFO, + crate::eta_reduction::REDUNDANT_CLOSURE_INFO, + crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO, + crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO, + crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO, + crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO, + crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, + crate::exit::EXIT_INFO, + crate::explicit_write::EXPLICIT_WRITE_INFO, + crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO, + crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, + crate::float_literal::EXCESSIVE_PRECISION_INFO, + crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, + crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO, + crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO, + crate::fn_null_check::FN_NULL_CHECK_INFO, + crate::format::USELESS_FORMAT_INFO, + crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, + crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, + crate::format_args::UNINLINED_FORMAT_ARGS_INFO, + crate::format_args::UNUSED_FORMAT_SPECS_INFO, + crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, + crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, + crate::format_push_string::FORMAT_PUSH_STRING_INFO, + crate::formatting::POSSIBLE_MISSING_COMMA_INFO, + crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO, + crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO, + crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO, + crate::from_over_into::FROM_OVER_INTO_INFO, + crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, + crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, + crate::functions::DOUBLE_MUST_USE_INFO, + crate::functions::MISNAMED_GETTERS_INFO, + crate::functions::MUST_USE_CANDIDATE_INFO, + crate::functions::MUST_USE_UNIT_INFO, + crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::RESULT_LARGE_ERR_INFO, + crate::functions::RESULT_UNIT_ERR_INFO, + crate::functions::TOO_MANY_ARGUMENTS_INFO, + crate::functions::TOO_MANY_LINES_INFO, + crate::future_not_send::FUTURE_NOT_SEND_INFO, + crate::if_let_mutex::IF_LET_MUTEX_INFO, + crate::if_not_else::IF_NOT_ELSE_INFO, + crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::implicit_hasher::IMPLICIT_HASHER_INFO, + crate::implicit_return::IMPLICIT_RETURN_INFO, + crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, + crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, + crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, + crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, + crate::indexing_slicing::INDEXING_SLICING_INFO, + crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, + crate::infinite_iter::INFINITE_ITER_INFO, + crate::infinite_iter::MAYBE_INFINITE_ITER_INFO, + crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO, + crate::inherent_to_string::INHERENT_TO_STRING_INFO, + crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, + crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, + crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, + crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, + crate::int_plus_one::INT_PLUS_ONE_INFO, + crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, + crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO, + crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, + crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, + crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, + crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO, + crate::large_include_file::LARGE_INCLUDE_FILE_INFO, + crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO, + crate::len_zero::COMPARISON_TO_EMPTY_INFO, + crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO, + crate::len_zero::LEN_ZERO_INFO, + crate::let_if_seq::USELESS_LET_IF_SEQ_INFO, + crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO, + crate::let_underscore::LET_UNDERSCORE_LOCK_INFO, + crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, + crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, + crate::lifetimes::NEEDLESS_LIFETIMES_INFO, + crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, + crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, + crate::literal_representation::LARGE_DIGIT_GROUPS_INFO, + crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO, + crate::literal_representation::UNREADABLE_LITERAL_INFO, + crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, + crate::loops::EMPTY_LOOP_INFO, + crate::loops::EXPLICIT_COUNTER_LOOP_INFO, + crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, + crate::loops::EXPLICIT_ITER_LOOP_INFO, + crate::loops::FOR_KV_MAP_INFO, + crate::loops::ITER_NEXT_LOOP_INFO, + crate::loops::MANUAL_FIND_INFO, + crate::loops::MANUAL_FLATTEN_INFO, + crate::loops::MANUAL_MEMCPY_INFO, + crate::loops::MISSING_SPIN_LOOP_INFO, + crate::loops::MUT_RANGE_BOUND_INFO, + crate::loops::NEEDLESS_RANGE_LOOP_INFO, + crate::loops::NEVER_LOOP_INFO, + crate::loops::SAME_ITEM_PUSH_INFO, + crate::loops::SINGLE_ELEMENT_LOOP_INFO, + crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, + crate::loops::WHILE_LET_LOOP_INFO, + crate::loops::WHILE_LET_ON_ITERATOR_INFO, + crate::macro_use::MACRO_USE_IMPORTS_INFO, + crate::main_recursion::MAIN_RECURSION_INFO, + crate::manual_assert::MANUAL_ASSERT_INFO, + crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, + crate::manual_bits::MANUAL_BITS_INFO, + crate::manual_clamp::MANUAL_CLAMP_INFO, + crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, + crate::manual_let_else::MANUAL_LET_ELSE_INFO, + crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, + crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, + crate::manual_retain::MANUAL_RETAIN_INFO, + crate::manual_string_new::MANUAL_STRING_NEW_INFO, + crate::manual_strip::MANUAL_STRIP_INFO, + crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, + crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, + crate::match_result_ok::MATCH_RESULT_OK_INFO, + crate::matches::COLLAPSIBLE_MATCH_INFO, + crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, + crate::matches::MANUAL_FILTER_INFO, + crate::matches::MANUAL_MAP_INFO, + crate::matches::MANUAL_UNWRAP_OR_INFO, + crate::matches::MATCH_AS_REF_INFO, + crate::matches::MATCH_BOOL_INFO, + crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO, + crate::matches::MATCH_ON_VEC_ITEMS_INFO, + crate::matches::MATCH_OVERLAPPING_ARM_INFO, + crate::matches::MATCH_REF_PATS_INFO, + crate::matches::MATCH_SAME_ARMS_INFO, + crate::matches::MATCH_SINGLE_BINDING_INFO, + crate::matches::MATCH_STR_CASE_MISMATCH_INFO, + crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, + crate::matches::MATCH_WILD_ERR_ARM_INFO, + crate::matches::NEEDLESS_MATCH_INFO, + crate::matches::REDUNDANT_PATTERN_MATCHING_INFO, + crate::matches::REST_PAT_IN_FULLY_BOUND_STRUCTS_INFO, + crate::matches::SIGNIFICANT_DROP_IN_SCRUTINEE_INFO, + crate::matches::SINGLE_MATCH_INFO, + crate::matches::SINGLE_MATCH_ELSE_INFO, + crate::matches::TRY_ERR_INFO, + crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO, + crate::matches::WILDCARD_IN_OR_PATTERNS_INFO, + crate::mem_forget::MEM_FORGET_INFO, + crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO, + crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, + crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, + crate::methods::BIND_INSTEAD_OF_MAP_INFO, + crate::methods::BYTES_COUNT_TO_LEN_INFO, + crate::methods::BYTES_NTH_INFO, + crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, + crate::methods::CHARS_LAST_CMP_INFO, + crate::methods::CHARS_NEXT_CMP_INFO, + crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, + crate::methods::CLONE_DOUBLE_REF_INFO, + crate::methods::CLONE_ON_COPY_INFO, + crate::methods::CLONE_ON_REF_PTR_INFO, + crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, + crate::methods::ERR_EXPECT_INFO, + crate::methods::EXPECT_FUN_CALL_INFO, + crate::methods::EXPECT_USED_INFO, + crate::methods::EXTEND_WITH_DRAIN_INFO, + crate::methods::FILETYPE_IS_FILE_INFO, + crate::methods::FILTER_MAP_IDENTITY_INFO, + crate::methods::FILTER_MAP_NEXT_INFO, + crate::methods::FILTER_NEXT_INFO, + crate::methods::FLAT_MAP_IDENTITY_INFO, + crate::methods::FLAT_MAP_OPTION_INFO, + crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO, + crate::methods::GET_FIRST_INFO, + crate::methods::GET_LAST_WITH_LEN_INFO, + crate::methods::GET_UNWRAP_INFO, + crate::methods::IMPLICIT_CLONE_INFO, + crate::methods::INEFFICIENT_TO_STRING_INFO, + crate::methods::INSPECT_FOR_EACH_INFO, + crate::methods::INTO_ITER_ON_REF_INFO, + crate::methods::IS_DIGIT_ASCII_RADIX_INFO, + crate::methods::ITERATOR_STEP_BY_ZERO_INFO, + crate::methods::ITER_CLONED_COLLECT_INFO, + crate::methods::ITER_COUNT_INFO, + crate::methods::ITER_KV_MAP_INFO, + crate::methods::ITER_NEXT_SLICE_INFO, + crate::methods::ITER_NTH_INFO, + crate::methods::ITER_NTH_ZERO_INFO, + crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO, + crate::methods::ITER_ON_SINGLE_ITEMS_INFO, + crate::methods::ITER_OVEREAGER_CLONED_INFO, + crate::methods::ITER_SKIP_NEXT_INFO, + crate::methods::ITER_WITH_DRAIN_INFO, + crate::methods::MANUAL_FILTER_MAP_INFO, + crate::methods::MANUAL_FIND_MAP_INFO, + crate::methods::MANUAL_OK_OR_INFO, + crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, + crate::methods::MANUAL_SPLIT_ONCE_INFO, + crate::methods::MANUAL_STR_REPEAT_INFO, + crate::methods::MAP_CLONE_INFO, + crate::methods::MAP_COLLECT_RESULT_UNIT_INFO, + crate::methods::MAP_ERR_IGNORE_INFO, + crate::methods::MAP_FLATTEN_INFO, + crate::methods::MAP_IDENTITY_INFO, + crate::methods::MAP_UNWRAP_OR_INFO, + crate::methods::MUT_MUTEX_LOCK_INFO, + crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_COLLECT_INFO, + crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, + crate::methods::NEEDLESS_OPTION_TAKE_INFO, + crate::methods::NEEDLESS_SPLITN_INFO, + crate::methods::NEW_RET_NO_SELF_INFO, + crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, + crate::methods::NO_EFFECT_REPLACE_INFO, + crate::methods::OBFUSCATED_IF_ELSE_INFO, + crate::methods::OK_EXPECT_INFO, + crate::methods::OPTION_AS_REF_DEREF_INFO, + crate::methods::OPTION_FILTER_MAP_INFO, + crate::methods::OPTION_MAP_OR_NONE_INFO, + crate::methods::OR_FUN_CALL_INFO, + crate::methods::OR_THEN_UNWRAP_INFO, + crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, + crate::methods::RANGE_ZIP_WITH_LEN_INFO, + crate::methods::REPEAT_ONCE_INFO, + crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, + crate::methods::SEARCH_IS_SOME_INFO, + crate::methods::SEEK_FROM_CURRENT_INFO, + crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO, + crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, + crate::methods::SINGLE_CHAR_ADD_STR_INFO, + crate::methods::SINGLE_CHAR_PATTERN_INFO, + crate::methods::SKIP_WHILE_NEXT_INFO, + crate::methods::STABLE_SORT_PRIMITIVE_INFO, + crate::methods::STRING_EXTEND_CHARS_INFO, + crate::methods::SUSPICIOUS_MAP_INFO, + crate::methods::SUSPICIOUS_SPLITN_INFO, + crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::UNINIT_ASSUMED_INIT_INFO, + crate::methods::UNIT_HASH_INFO, + crate::methods::UNNECESSARY_FILTER_MAP_INFO, + crate::methods::UNNECESSARY_FIND_MAP_INFO, + crate::methods::UNNECESSARY_FOLD_INFO, + crate::methods::UNNECESSARY_JOIN_INFO, + crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO, + crate::methods::UNNECESSARY_SORT_BY_INFO, + crate::methods::UNNECESSARY_TO_OWNED_INFO, + crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO, + crate::methods::UNWRAP_USED_INFO, + crate::methods::USELESS_ASREF_INFO, + crate::methods::VEC_RESIZE_TO_ZERO_INFO, + crate::methods::VERBOSE_FILE_READS_INFO, + crate::methods::WRONG_SELF_CONVENTION_INFO, + crate::methods::ZST_OFFSET_INFO, + crate::minmax::MIN_MAX_INFO, + crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, + crate::misc::TOPLEVEL_REF_ARG_INFO, + crate::misc::USED_UNDERSCORE_BINDING_INFO, + crate::misc::ZERO_PTR_INFO, + crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, + crate::misc_early::DOUBLE_NEG_INFO, + crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, + crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO, + crate::misc_early::REDUNDANT_PATTERN_INFO, + crate::misc_early::SEPARATED_LITERAL_SUFFIX_INFO, + crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO, + crate::misc_early::UNNEEDED_WILDCARD_PATTERN_INFO, + crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO, + crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, + crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_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, + crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO, + crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO, + crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO, + crate::mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION_INFO, + crate::module_style::MOD_MODULE_FILES_INFO, + crate::module_style::SELF_NAMED_MODULE_FILES_INFO, + crate::multi_assignments::MULTI_ASSIGNMENTS_INFO, + crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO, + crate::mut_key::MUTABLE_KEY_TYPE_INFO, + crate::mut_mut::MUT_MUT_INFO, + crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, + crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO, + crate::mutex_atomic::MUTEX_ATOMIC_INFO, + crate::mutex_atomic::MUTEX_INTEGER_INFO, + crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO, + crate::needless_bool::BOOL_COMPARISON_INFO, + crate::needless_bool::NEEDLESS_BOOL_INFO, + crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, + crate::needless_continue::NEEDLESS_CONTINUE_INFO, + crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, + crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, + crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, + crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, + crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO, + crate::needless_update::NEEDLESS_UPDATE_INFO, + crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO, + crate::neg_multiply::NEG_MULTIPLY_INFO, + crate::new_without_default::NEW_WITHOUT_DEFAULT_INFO, + crate::no_effect::NO_EFFECT_INFO, + crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, + crate::no_effect::UNNECESSARY_OPERATION_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, + crate::non_expressive_names::MANY_SINGLE_CHAR_NAMES_INFO, + crate::non_expressive_names::SIMILAR_NAMES_INFO, + crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, + crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, + crate::octal_escapes::OCTAL_ESCAPES_INFO, + crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, + crate::operators::ABSURD_EXTREME_COMPARISONS_INFO, + crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, + crate::operators::ASSIGN_OP_PATTERN_INFO, + crate::operators::BAD_BIT_MASK_INFO, + crate::operators::CMP_NAN_INFO, + crate::operators::CMP_OWNED_INFO, + crate::operators::DOUBLE_COMPARISONS_INFO, + crate::operators::DURATION_SUBSEC_INFO, + crate::operators::EQ_OP_INFO, + crate::operators::ERASING_OP_INFO, + crate::operators::FLOAT_ARITHMETIC_INFO, + crate::operators::FLOAT_CMP_INFO, + crate::operators::FLOAT_CMP_CONST_INFO, + crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, + crate::operators::IDENTITY_OP_INFO, + crate::operators::INEFFECTIVE_BIT_MASK_INFO, + crate::operators::INTEGER_ARITHMETIC_INFO, + crate::operators::INTEGER_DIVISION_INFO, + crate::operators::MISREFACTORED_ASSIGN_OP_INFO, + crate::operators::MODULO_ARITHMETIC_INFO, + crate::operators::MODULO_ONE_INFO, + crate::operators::NEEDLESS_BITWISE_BOOL_INFO, + crate::operators::OP_REF_INFO, + crate::operators::PTR_EQ_INFO, + crate::operators::SELF_ASSIGNMENT_INFO, + crate::operators::VERBOSE_BIT_MASK_INFO, + crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO, + crate::option_if_let_else::OPTION_IF_LET_ELSE_INFO, + crate::overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL_INFO, + crate::panic_in_result_fn::PANIC_IN_RESULT_FN_INFO, + crate::panic_unimplemented::PANIC_INFO, + crate::panic_unimplemented::TODO_INFO, + crate::panic_unimplemented::UNIMPLEMENTED_INFO, + crate::panic_unimplemented::UNREACHABLE_INFO, + crate::partial_pub_fields::PARTIAL_PUB_FIELDS_INFO, + crate::partialeq_ne_impl::PARTIALEQ_NE_IMPL_INFO, + crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO, + crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, + crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, + crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, + crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, + crate::precedence::PRECEDENCE_INFO, + crate::ptr::CMP_NULL_INFO, + crate::ptr::INVALID_NULL_PTR_USAGE_INFO, + crate::ptr::MUT_FROM_REF_INFO, + crate::ptr::PTR_ARG_INFO, + crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, + crate::pub_use::PUB_USE_INFO, + crate::question_mark::QUESTION_MARK_INFO, + crate::ranges::MANUAL_RANGE_CONTAINS_INFO, + crate::ranges::RANGE_MINUS_ONE_INFO, + crate::ranges::RANGE_PLUS_ONE_INFO, + crate::ranges::REVERSED_EMPTY_RANGES_INFO, + crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, + crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, + crate::redundant_clone::REDUNDANT_CLONE_INFO, + crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO, + crate::redundant_else::REDUNDANT_ELSE_INFO, + crate::redundant_field_names::REDUNDANT_FIELD_NAMES_INFO, + crate::redundant_pub_crate::REDUNDANT_PUB_CRATE_INFO, + crate::redundant_slicing::DEREF_BY_SLICING_INFO, + crate::redundant_slicing::REDUNDANT_SLICING_INFO, + crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, + crate::ref_option_ref::REF_OPTION_REF_INFO, + crate::reference::DEREF_ADDROF_INFO, + crate::regex::INVALID_REGEX_INFO, + crate::regex::TRIVIAL_REGEX_INFO, + crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, + crate::returns::LET_AND_RETURN_INFO, + crate::returns::NEEDLESS_RETURN_INFO, + crate::same_name_method::SAME_NAME_METHOD_INFO, + crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO, + crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO, + crate::semicolon_block::SEMICOLON_OUTSIDE_BLOCK_INFO, + crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO, + crate::serde_api::SERDE_API_MISUSE_INFO, + crate::shadow::SHADOW_REUSE_INFO, + crate::shadow::SHADOW_SAME_INFO, + crate::shadow::SHADOW_UNRELATED_INFO, + crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, + crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, + crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, + crate::size_of_ref::SIZE_OF_REF_INFO, + crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO, + crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, + crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, + crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, + crate::strings::STRING_ADD_INFO, + crate::strings::STRING_ADD_ASSIGN_INFO, + crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, + crate::strings::STRING_LIT_AS_BYTES_INFO, + crate::strings::STRING_SLICE_INFO, + crate::strings::STRING_TO_STRING_INFO, + crate::strings::STR_TO_STRING_INFO, + crate::strings::TRIM_SPLIT_WHITESPACE_INFO, + crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, + crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, + crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO, + crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO, + crate::suspicious_xor_used_as_pow::SUSPICIOUS_XOR_USED_AS_POW_INFO, + crate::swap::ALMOST_SWAPPED_INFO, + crate::swap::MANUAL_SWAP_INFO, + crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO, + crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, + crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, + crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, + crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, + crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, + crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, + crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, + crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, + crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, + crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, + crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, + crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, + crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, + crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, + crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, + crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, + crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, + crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, + crate::transmute::TRANSMUTING_NULL_INFO, + crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO, + crate::transmute::USELESS_TRANSMUTE_INFO, + crate::transmute::WRONG_TRANSMUTE_INFO, + crate::types::BORROWED_BOX_INFO, + crate::types::BOX_COLLECTION_INFO, + crate::types::LINKEDLIST_INFO, + crate::types::OPTION_OPTION_INFO, + crate::types::RC_BUFFER_INFO, + crate::types::RC_MUTEX_INFO, + crate::types::REDUNDANT_ALLOCATION_INFO, + crate::types::TYPE_COMPLEXITY_INFO, + crate::types::VEC_BOX_INFO, + crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO, + crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO, + crate::unicode::INVISIBLE_CHARACTERS_INFO, + crate::unicode::NON_ASCII_LITERAL_INFO, + crate::unicode::UNICODE_NOT_NFC_INFO, + crate::uninit_vec::UNINIT_VEC_INFO, + crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO, + crate::unit_types::LET_UNIT_VALUE_INFO, + crate::unit_types::UNIT_ARG_INFO, + crate::unit_types::UNIT_CMP_INFO, + crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, + crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO, + crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, + crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, + crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, + crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, + crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, + crate::unused_async::UNUSED_ASYNC_INFO, + crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, + crate::unused_peekable::UNUSED_PEEKABLE_INFO, + crate::unused_rounding::UNUSED_ROUNDING_INFO, + crate::unused_self::UNUSED_SELF_INFO, + crate::unused_unit::UNUSED_UNIT_INFO, + crate::unwrap::PANICKING_UNWRAP_INFO, + crate::unwrap::UNNECESSARY_UNWRAP_INFO, + crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO, + crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO, + crate::use_self::USE_SELF_INFO, + crate::useless_conversion::USELESS_CONVERSION_INFO, + crate::vec::USELESS_VEC_INFO, + crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, + crate::wildcard_imports::ENUM_GLOB_USE_INFO, + crate::wildcard_imports::WILDCARD_IMPORTS_INFO, + crate::write::PRINTLN_EMPTY_STRING_INFO, + crate::write::PRINT_LITERAL_INFO, + crate::write::PRINT_STDERR_INFO, + crate::write::PRINT_STDOUT_INFO, + crate::write::PRINT_WITH_NEWLINE_INFO, + crate::write::USE_DEBUG_INFO, + crate::write::WRITELN_EMPTY_STRING_INFO, + crate::write::WRITE_LITERAL_INFO, + crate::write::WRITE_WITH_NEWLINE_INFO, + crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, + crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, +]; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 7f937de1dd31..a04693f4637a 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -11,6 +11,7 @@ use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -98,9 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { if let ty::Adt(def, ..) = expr_ty.kind(); if !is_from_proc_macro(cx, expr); then { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did())); + let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); span_lint_and_sugg( cx, DEFAULT_TRAIT_ACCESS, @@ -170,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { // find out if and which field was set by this `consecutive_statement` if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) { // interrupt and cancel lint if assign_rhs references the original binding - if contains_name(binding_name, assign_rhs) { + if contains_name(binding_name, assign_rhs, cx) { cancel_lint = true; break; } diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 3ed9cd36a229..03460689e19a 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_node, numeric_literal}; use if_chain::if_chain; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, Visitor}, - Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, + Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{ @@ -55,22 +55,31 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { - let mut visitor = NumericFallbackVisitor::new(cx); + let is_parent_const = if let Some(Node::Item(item)) = get_parent_node(cx.tcx, body.id().hir_id) { + matches!(item.kind, ItemKind::Const(..)) + } else { + false + }; + let mut visitor = NumericFallbackVisitor::new(cx, is_parent_const); visitor.visit_body(body); } } struct NumericFallbackVisitor<'a, 'tcx> { /// Stack manages type bound of exprs. The top element holds current expr type. - ty_bounds: Vec>, + ty_bounds: Vec, cx: &'a LateContext<'tcx>, } impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, is_parent_const: bool) -> Self { Self { - ty_bounds: vec![TyBound::Nothing], + ty_bounds: vec![if is_parent_const { + ExplicitTyBound(true) + } else { + ExplicitTyBound(false) + }], cx, } } @@ -79,10 +88,9 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { if_chain! { if !in_external_macro(self.cx.sess(), lit.span); - if let Some(ty_bound) = self.ty_bounds.last(); + if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if !ty_bound.is_numeric(); then { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), @@ -123,7 +131,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) { // Push found arg type, then visit arg. - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -135,7 +143,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { - self.ty_bounds.push(TyBound::Ty(*bound)); + self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); } @@ -169,7 +177,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { // Visit base with no bound. if let Some(base) = base { - self.ty_bounds.push(TyBound::Nothing); + self.ty_bounds.push(ExplicitTyBound(false)); self.visit_expr(base); self.ty_bounds.pop(); } @@ -192,15 +200,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(local) => { - if local.ty.is_some() { - self.ty_bounds.push(TyBound::Any); - } else { - self.ty_bounds.push(TyBound::Nothing); - } - }, + // we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric` + StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())), - _ => self.ty_bounds.push(TyBound::Nothing), + _ => self.ty_bounds.push(ExplicitTyBound(false)), } walk_stmt(self, stmt); @@ -218,28 +221,18 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { - Any, - Ty(Ty<'tcx>), - Nothing, -} +struct ExplicitTyBound(pub bool); -impl<'tcx> TyBound<'tcx> { - fn is_numeric(self) -> bool { - match self { - TyBound::Any => true, - TyBound::Ty(t) => t.is_numeric(), - TyBound::Nothing => false, - } +impl<'tcx> From> for ExplicitTyBound { + fn from(v: Ty<'tcx>) -> Self { + Self(v.is_numeric()) } } -impl<'tcx> From>> for TyBound<'tcx> { +impl<'tcx> From>> for ExplicitTyBound { fn from(v: Option>) -> Self { - match v { - Some(t) => TyBound::Ty(t), - None => TyBound::Nothing, - } + Self(v.map_or(false, Ty::is_numeric)) } } diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 741edc131960..dec357ab75c3 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { None, &format!( "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", - cx.tcx.def_path_str(item.def_id.to_def_id()) + cx.tcx.def_path_str(item.owner_id.to_def_id()) ), ); } diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 45ee2fce4e47..05f2b92c0370 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,14 +1,16 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +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::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; use clippy_utils::{ - fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local, - walk_to_expr_usage, + fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage, }; + use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ @@ -24,12 +26,11 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ - self, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, + self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults, }; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; +use rustc_span::{symbol::sym, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; use std::collections::VecDeque; @@ -180,12 +181,12 @@ pub struct Dereferencing<'tcx> { possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, // `IntoIterator` for arrays requires Rust 1.53. - msrv: Option, + msrv: Msrv, } impl<'tcx> Dereferencing<'tcx> { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Dereferencing::default() @@ -274,9 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } let typeck = cx.typeck_results(); - let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { - x - } else { + let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { report(cx, expr, state, data); @@ -287,26 +286,27 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv); match kind { RefOp::Deref => { + let sub_ty = typeck.expr_ty(sub_expr); if let Position::FieldAccess { name, of_union: false, } = position - && !ty_contains_field(typeck.expr_ty(sub_expr), name) + && !ty_contains_field(sub_ty, name) { self.state = Some(( State::ExplicitDerefField { name }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); - } else if position.is_deref_stable() { + } else if position.is_deref_stable() && sub_ty.is_ref() { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); } - } + }, RefOp::Method(target_mut) if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) && position.lint_explicit_deref() => @@ -321,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); }, @@ -395,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { msg, snip_expr, }), - StateData { span: expr.span, hir_id: expr.hir_id, position }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + position, + }, )); } else if position.is_deref_stable() // Auto-deref doesn't combine with other adjustments @@ -407,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); } @@ -699,7 +703,7 @@ fn walk_parents<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> (Position, &'tcx [Adjustment<'tcx>]) { let mut adjustments = [].as_slice(); let mut precedence = 0i8; @@ -715,47 +719,47 @@ fn walk_parents<'tcx>( }, Node::Item(&Item { kind: ItemKind::Static(..) | ItemKind::Const(..), - def_id, + owner_id, span, .. }) | Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), - def_id, + owner_id, span, .. }) | Node::ImplItem(&ImplItem { kind: ImplItemKind::Const(..), - def_id, + owner_id, span, .. }) if span.ctxt() == ctxt => { - let ty = cx.tcx.type_of(def_id.def_id); + let ty = cx.tcx.type_of(owner_id.def_id); Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx)) }, Node::Item(&Item { kind: ItemKind::Fn(..), - def_id, + owner_id, span, .. }) | Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), - def_id, + owner_id, span, .. }) | Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(..), - def_id, + owner_id, span, .. }) if span.ctxt() == ctxt => { let output = cx .tcx - .erase_late_bound_regions(cx.tcx.fn_sig(def_id.to_def_id()).output()); + .erase_late_bound_regions(cx.tcx.fn_sig(owner_id.to_def_id()).output()); Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)) }, @@ -806,30 +810,39 @@ fn walk_parents<'tcx>( .position(|arg| arg.hir_id == child_id) .zip(expr_sig(cx, func)) .and_then(|(i, sig)| { - sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty { - // Type inference for closures can depend on how they're called. Only go by the explicit - // types here. - Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), - None => { - if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position( - cx, - possible_borrowers, - parent, - i, - *param_ty, - e, - precedence, - msrv, - ) - } else { - ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) - .position_for_arg() - } - }, + sig.input_with_hir(i).map(|(hir_ty, ty)| { + match hir_ty { + // Type inference for closures can depend on how they're called. Only go by the explicit + // types here. + Some(hir_ty) => { + binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()) + }, + None => { + // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 + // `!call_is_qualified(func)` for https://github.com/rust-lang/rust-clippy/issues/9782 + if e.hir_id == child_id + && !call_is_qualified(func) + && let ty::Param(param_ty) = ty.skip_binder().kind() + { + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) + } else { + ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) + .position_for_arg() + } + }, + } }) }), - ExprKind::MethodCall(_, receiver, args, _) => { + ExprKind::MethodCall(method, receiver, args, _) => { let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); if receiver.hir_id == child_id { // Check for calls to trait methods where the trait is implemented on a reference. @@ -842,25 +855,24 @@ fn walk_parents<'tcx>( } else if let Some(trait_id) = cx.tcx.trait_of_item(id) && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e)) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() - && let subs = match cx + && let subs = cx .typeck_results() - .node_substs_opt(parent.hir_id) - .and_then(|subs| subs.get(1..)) - { - Some(subs) => cx.tcx.mk_substs(subs.iter().copied()), - None => cx.tcx.mk_substs(std::iter::empty::>()), - } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() { + .node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default() + && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() { // Trait methods taking `&self` sub_ty } else { // Trait methods taking `self` arg_ty } && impl_ty.is_ref() - && cx.tcx.infer_ctxt().enter(|infcx| - infcx - .type_implements_trait(trait_id, impl_ty, subs, cx.param_env) - .must_apply_modulo_regions() - ) + && let infcx = cx.tcx.infer_ctxt().build() + && infcx + .type_implements_trait( + trait_id, + [impl_ty.into()].into_iter().chain(subs.iter().copied()), + cx.param_env, + ) + .must_apply_modulo_regions() { return Some(Position::MethodReceiverRefImpl) } @@ -868,7 +880,9 @@ fn walk_parents<'tcx>( } args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; - if let ty::Param(param_ty) = ty.kind() { + // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 + // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782 + if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() { needless_borrow_impl_arg_position( cx, possible_borrowers, @@ -955,14 +969,14 @@ fn binding_ty_auto_deref_stability<'tcx>( precedence: i8, binder_args: &'tcx List, ) -> Position { - let TyKind::Rptr(_, ty) = &ty.kind else { + let TyKind::Ref(_, ty) = &ty.kind else { return Position::Other(precedence); }; let mut ty = ty; loop { break match ty.ty.kind { - TyKind::Rptr(_, ref ref_ty) => { + TyKind::Ref(_, ref ref_ty) => { ty = ref_ty; continue; }, @@ -991,7 +1005,7 @@ fn binding_ty_auto_deref_stability<'tcx>( cx.typeck_results().node_type(ty.ty.hir_id), binder_args, )) - .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()), + .is_sized(cx.tcx, cx.param_env.without_caller_bounds()), ) } }, @@ -1006,7 +1020,7 @@ fn binding_ty_auto_deref_stability<'tcx>( cx.typeck_results().node_type(ty.ty.hir_id), binder_args, )) - .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()), + .is_sized(cx.tcx, cx.param_env.without_caller_bounds()), ), TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => { Position::ReborrowStable(precedence) @@ -1046,13 +1060,25 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } +fn call_is_qualified(expr: &Expr<'_>) -> bool { + if let ExprKind::Path(path) = &expr.kind { + match path { + QPath::Resolved(_, path) => path.segments.last().map_or(false, |segment| segment.args.is_some()), + QPath::TypeRelative(_, segment) => segment.args.is_some(), + QPath::LangItem(..) => false, + } + } else { + false + } +} + // Checks whether: // * child is an expression of the form `&e` in an argument position requiring an `impl Trait` // * `e`'s type implements `Trait` and is copyable // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. -#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -1061,7 +1087,7 @@ fn needless_borrow_impl_arg_position<'tcx>( param_ty: ParamTy, mut expr: &Expr<'tcx>, precedence: i8, - msrv: Option, + msrv: &Msrv, ) -> Position { let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); @@ -1080,7 +1106,7 @@ fn needless_borrow_impl_arg_position<'tcx>( let projection_predicates = predicates .iter() .filter_map(|predicate| { - if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { + if let PredicateKind::Clause(Clause::Projection(projection_predicate)) = predicate.kind().skip_binder() { Some(projection_predicate) } else { None @@ -1094,7 +1120,7 @@ fn needless_borrow_impl_arg_position<'tcx>( if predicates .iter() .filter_map(|predicate| { - if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) { Some(trait_predicate.trait_ref.def_id) @@ -1114,6 +1140,16 @@ fn needless_borrow_impl_arg_position<'tcx>( return Position::Other(precedence); } + // See: + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 + if projection_predicates + .iter() + .any(|projection_predicate| is_mixed_projection_predicate(cx, callee_def_id, projection_predicate)) + { + return Position::Other(precedence); + } + // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); @@ -1146,21 +1182,20 @@ fn needless_borrow_impl_arg_position<'tcx>( } predicates.iter().all(|predicate| { - if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) && let ty::Param(param_ty) = trait_predicate.self_ty().kind() && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack() && ty.is_array() - && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR) + && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { return false; } let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); - let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + let infcx = cx.tcx.infer_ctxt().build(); + infcx.predicate_must_hold_modulo_regions(&obligation) }) }; @@ -1194,17 +1229,50 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } -fn referent_used_exactly_once<'a, 'tcx>( - cx: &'a LateContext<'tcx>, +fn is_mixed_projection_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + projection_predicate: &ProjectionPredicate<'tcx>, +) -> bool { + let generics = cx.tcx.generics_of(callee_def_id); + // The predicate requires the projected type to equal a type parameter from the parent context. + if let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + && (term_param_ty.index as usize) < generics.parent_count + { + // The inner-most self type is a type parameter from the current function. + let mut projection_ty = projection_predicate.projection_ty; + loop { + match projection_ty.self_ty().kind() { + ty::Alias(ty::Projection, inner_projection_ty) => { + projection_ty = *inner_projection_ty; + } + ty::Param(param_ty) => { + return (param_ty.index as usize) >= generics.parent_count; + } + _ => { + return false; + } + } + } + } else { + false + } +} + +fn referent_used_exactly_once<'tcx>( + cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, reference: &Expr<'tcx>, ) -> bool { let mir = enclosing_mir(cx.tcx, reference.hir_id); if let Some(local) = expr_local(cx.tcx, reference) && let [location] = *local_assignments(mir, local).as_slice() - && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = - mir.basic_blocks[location.block].statements[location.statement_index].kind + && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) + && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind && !place.has_deref() + // Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710) + && TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none() { let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); if possible_borrowers @@ -1262,10 +1330,10 @@ fn replace_types<'tcx>( && let Some(term_ty) = projection_predicate.term.ty() && let ty::Param(term_param_ty) = term_ty.kind() { - let item_def_id = projection_predicate.projection_ty.item_def_id; + let item_def_id = projection_predicate.projection_ty.def_id; let assoc_item = cx.tcx.associated_item(item_def_id); let projection = cx.tcx - .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[])); + .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, [])); if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) @@ -1299,7 +1367,7 @@ impl<'tcx> TyPosition<'tcx> { fn position_for_result(self, cx: &LateContext<'tcx>) -> Position { match (self.position, self.ty) { (Position::ReborrowStable(precedence), Some(ty)) => { - Position::DerefStable(precedence, ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env)) + Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env)) }, (position, _) => position, } @@ -1322,9 +1390,15 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), - ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => { - Position::ReborrowStable(precedence).into() + ty::Alias(ty::Projection, _) if ty.has_non_region_param() => { + TyPosition::new_deref_stable_for_result(precedence, ty) }, + ty::Infer(_) + | ty::Error(_) + | ty::Bound(..) + | ty::Alias(ty::Opaque, ..) + | ty::Placeholder(_) + | ty::Dynamic(..) => Position::ReborrowStable(precedence).into(), ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => { Position::ReborrowStable(precedence).into() }, @@ -1348,11 +1422,9 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc | ty::Closure(..) | ty::Never | ty::Tuple(_) - | ty::Projection(_) => Position::DerefStable( - precedence, - ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()), - ) - .into(), + | ty::Alias(ty::Projection, _) => { + Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into() + }, }; } } diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 06ae5abeaeb9..bc18e2e5ed5f 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,12 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{ - def::{DefKind, Res}, - Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, + def::{CtorKind, CtorOf, DefKind, Res}, + Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_middle::ty::{AdtDef, DefIdTree}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { @@ -51,7 +54,18 @@ declare_clippy_lint! { "manual implementation of the `Default` trait which is equal to a derive" } -declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); +pub struct DerivableImpls { + msrv: Msrv, +} + +impl DerivableImpls { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + DerivableImpls { msrv } + } +} + +impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); fn is_path_self(e: &Expr<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind { @@ -61,6 +75,98 @@ fn is_path_self(e: &Expr<'_>) -> bool { } } +fn check_struct<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx Item<'_>, + self_ty: &Ty<'_>, + func_expr: &Expr<'_>, + adt_def: AdtDef<'_>, +) { + if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { + if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { + for arg in a.args { + if !matches!(arg, GenericArg::Lifetime(_)) { + return; + } + } + } + } + let should_emit = match peel_blocks(func_expr).kind { + ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), + _ => false, + }; + + if should_emit { + let struct_span = cx.tcx.def_span(adt_def.did()); + span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { + diag.span_suggestion_hidden( + item.span, + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable, + ); + diag.span_suggestion( + struct_span.shrink_to_lo(), + "...and instead derive it", + "#[derive(Default)]\n".to_string(), + Applicability::MachineApplicable, + ); + }); + } +} + +fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { + if_chain! { + if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind; + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res; + if let variant_id = cx.tcx.parent(id); + if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id); + if variant_def.fields.is_empty(); + if !variant_def.is_field_list_non_exhaustive(); + + then { + let enum_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, enum_span).unwrap_or(0); + let variant_span = cx.tcx.def_span(variant_def.def_id); + let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + span_lint_and_then( + cx, + DERIVABLE_IMPLS, + item.span, + "this `impl` can be derived", + |diag| { + diag.span_suggestion_hidden( + item.span, + "remove the manual implementation...", + String::new(), + Applicability::MachineApplicable + ); + diag.span_suggestion( + enum_span.shrink_to_lo(), + "...and instead derive it...", + format!( + "#[derive(Default)]\n{indent}", + indent = " ".repeat(indent_enum), + ), + Applicability::MachineApplicable + ); + diag.span_suggestion( + variant_span.shrink_to_lo(), + "...and mark the default variant", + format!( + "#[default]\n{indent}", + indent = " ".repeat(indent_variant), + ), + Applicability::MachineApplicable + ); + } + ); + } + } +} + impl<'tcx> LateLintPass<'tcx> for DerivableImpls { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { @@ -70,7 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { self_ty, .. }) = item.kind; - if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); + if !cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived); if !item.span.from_expansion(); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::Default, def_id); @@ -78,54 +184,21 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); if let ImplItemKind::Fn(_, b) = &impl_item.kind; if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); - if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def(); + if let Some(adt_def) = cx.tcx.type_of(item.owner_id).ty_adt_def(); 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 adt_def.is_struct(); - then { - if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind { - if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() { - for arg in a.args { - if !matches!(arg, GenericArg::Lifetime(_)) { - return; - } - } - } - } - let should_emit = match peel_blocks(func_expr).kind { - ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), - ExprKind::Call(callee, args) - if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), - ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), - _ => false, - }; - if should_emit { - let struct_span = cx.tcx.def_span(adt_def.did()); - span_lint_and_then( - cx, - DERIVABLE_IMPLS, - item.span, - "this `impl` can be derived", - |diag| { - diag.span_suggestion_hidden( - item.span, - "remove the manual implementation...", - String::new(), - Applicability::MachineApplicable - ); - diag.span_suggestion( - struct_span.shrink_to_lo(), - "...and instead derive it", - "#[derive(Default)]\n".to_string(), - Applicability::MachineApplicable - ); - } - ); + then { + if adt_def.is_struct() { + check_struct(cx, item, self_ty, func_expr, adt_def); + } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { + check_enum(cx, item, func_expr, adt_def); } } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index fad984d05ca9..248d73884106 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, - Ty, TyCtxt, + self, Binder, BoundConstness, Clause, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, + TraitPredicate, Ty, TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -46,7 +46,7 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "pre 1.29.0"] - pub DERIVE_HASH_XOR_EQ, + pub DERIVED_HASH_WITH_MANUAL_EQ, correctness, "deriving `Hash` but implementing `PartialEq` explicitly" } @@ -197,7 +197,7 @@ declare_clippy_lint! { declare_lint_pass!(Derive => [ EXPL_IMPL_CLONE_ON_COPY, - DERIVE_HASH_XOR_EQ, + DERIVED_HASH_WITH_MANUAL_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE, DERIVE_PARTIAL_EQ_WITHOUT_EQ @@ -210,8 +210,8 @@ impl<'tcx> LateLintPass<'tcx> for Derive { .. }) = item.kind { - let ty = cx.tcx.type_of(item.def_id); - let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); + let ty = cx.tcx.type_of(item.owner_id); + let is_automatically_derived = cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -226,7 +226,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { } } -/// Implementation of the `DERIVE_HASH_XOR_EQ` lint. +/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. fn check_hash_peq<'tcx>( cx: &LateContext<'tcx>, span: Span, @@ -243,7 +243,7 @@ fn check_hash_peq<'tcx>( cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); - if peq_is_automatically_derived == hash_is_automatically_derived { + if !hash_is_automatically_derived || peq_is_automatically_derived { return; } @@ -251,18 +251,12 @@ fn check_hash_peq<'tcx>( // Only care about `impl PartialEq for Foo` // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.substs.type_at(1) == ty { - let mess = if peq_is_automatically_derived { - "you are implementing `Hash` explicitly but have derived `PartialEq`" - } else { - "you are deriving `Hash` but have implemented `PartialEq` explicitly" - }; - + if trait_ref.subst_identity().substs.type_at(1) == ty { span_lint_and_then( cx, - DERIVE_HASH_XOR_EQ, + DERIVED_HASH_WITH_MANUAL_EQ, span, - mess, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); @@ -305,7 +299,7 @@ fn check_ord_partial_ord<'tcx>( // Only care about `impl PartialOrd for Foo` // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.substs.type_at(1) == ty { + if trait_ref.subst_identity().substs.type_at(1) == ty { let mess = if partial_ord_is_automatically_derived { "you are implementing `Ord` explicitly but have derived `PartialOrd`" } else { @@ -366,6 +360,15 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { return; } + // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. + // https://github.com/rust-lang/rust-clippy/issues/10188 + if ty_adt.repr().packed() + && ty_subs + .iter() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + { + return; + } span_lint_and_note( cx, @@ -466,12 +469,12 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); - if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []); // If all of our fields implement `Eq`, we can implement `Eq` too if adt .all_fields() .map(|f| f.ty(cx.tcx, substs)) - .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, [])); then { span_lint_and_sugg( cx, @@ -499,7 +502,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> let ty_predicates = tcx.predicates_of(did).predicates; for (p, _) in ty_predicates { - if let PredicateKind::Trait(p) = p.kind().skip_binder() + if let PredicateKind::Clause(Clause::Trait(p)) = p.kind().skip_binder() && p.trait_ref.def_id == eq_trait_id && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() && p.constness == BoundConstness::NotConst @@ -512,14 +515,11 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv::new( tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain( params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { - tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate { - trait_ref: TraitRef::new( - eq_trait_id, - tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))), - ), + tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate { + trait_ref: tcx.mk_trait_ref(eq_trait_id, [tcx.mk_param_from_def(param)]), constness: BoundConstness::NotConst, polarity: ImplPolarity::Positive, - }))) + })))) }), )), Reveal::UserFacing, diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 5ab7144e2909..1971cab64ef3 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_lint::{LateContext, LateLintPass}; @@ -48,7 +47,7 @@ declare_clippy_lint! { /// value: usize, /// } /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub DISALLOWED_MACROS, style, "use of a disallowed macro" @@ -89,7 +88,7 @@ impl DisallowedMacros { &format!("use of a disallowed macro `{}`", conf.path()), |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }, ); @@ -104,7 +103,7 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 6ac85606d9c7..ca8671c8f1aa 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } @@ -104,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }); } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index c7131fc164d3..1f56d0118a40 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -53,8 +53,8 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedTypes { conf_disallowed: Vec, - def_ids: FxHashMap>, - prim_tys: FxHashMap>, + def_ids: FxHashMap, + prim_tys: FxHashMap, } impl DisallowedTypes { @@ -69,13 +69,13 @@ impl DisallowedTypes { fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { match res { Res::Def(_, did) => { - if let Some(reason) = self.def_ids.get(did) { - emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref()); + if let Some(&index) = self.def_ids.get(did) { + emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]); } }, Res::PrimTy(prim) => { - if let Some(reason) = self.prim_tys.get(prim) { - emit(cx, prim.name_str(), span, reason.as_deref()); + if let Some(&index) = self.prim_tys.get(prim) { + emit(cx, prim.name_str(), span, &self.conf_disallowed[index]); } }, _ => {}, @@ -87,24 +87,28 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { + for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)")); - match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { - Res::Def(_, id) => { - self.def_ids.insert(id, reason); - }, - Res::PrimTy(ty) => { - self.prim_tys.insert(ty, reason); - }, - _ => {}, + + for res in clippy_utils::def_path_res(cx, &segs) { + match res { + Res::Def(_, id) => { + self.def_ids.insert(id, index); + }, + Res::PrimTy(ty) => { + self.prim_tys.insert(ty, index); + }, + _ => {}, + } } } } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { - self.check_res_emit(cx, &path.res, item.span); + for res in &path.res { + self.check_res_emit(cx, res, item.span); + } } } @@ -119,14 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { } } -fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { +fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) { span_lint_and_then( cx, DISALLOWED_TYPES, span, &format!("`{name}` is not allowed according to config"), |diag| { - if let Some(reason) = reason { + if let Some(reason) = conf.reason() { diag.note(reason); } }, diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 36dc7e3396b8..f7a3d6d53f71 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle}; +use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; @@ -221,6 +221,42 @@ declare_clippy_lint! { "possible typo for an intra-doc link" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the doc comments of publicly visible + /// safe functions and traits and warns if there is a `# Safety` section. + /// + /// ### Why is this bad? + /// Safe functions and traits are safe to implement and therefore do not + /// need to describe safety preconditions that users are required to uphold. + /// + /// ### Examples + /// ```rust + ///# type Universe = (); + /// /// # Safety + /// /// + /// /// This function should not be called before the horsemen are ready. + /// pub fn start_apocalypse_but_safely(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + /// + /// The function is safe, so there shouldn't be any preconditions + /// that have to be explained for safety reasons. + /// + /// ```rust + ///# type Universe = (); + /// /// This function should really be documented + /// pub fn start_apocalypse(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub UNNECESSARY_SAFETY_DOC, + restriction, + "`pub fn` or `pub trait` with `# Safety` docs" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -243,7 +279,8 @@ impl_lint_pass!(DocMarkdown => [ MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, - NEEDLESS_DOCTEST_MAIN + NEEDLESS_DOCTEST_MAIN, + UNNECESSARY_SAFETY_DOC, ]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { @@ -254,40 +291,37 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; match item.kind { hir::ItemKind::Fn(ref sig, _, body_id) => { - if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { + if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { let body = cx.tcx.hir().body(body_id); let mut fpu = FindPanicUnwrap { cx, - typeck_results: cx.tcx.typeck(item.def_id.def_id), + typeck_results: cx.tcx.typeck(item.owner_id.def_id), panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers( - cx, - item.def_id.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span); } }, hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, - hir::ItemKind::Trait(_, unsafety, ..) => { - if !headers.safety && unsafety == hir::Unsafety::Unsafe { - span_lint( - cx, - MISSING_SAFETY_DOC, - item.span, - "docs for unsafe trait missing `# Safety` section", - ); - } + hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( + cx, + MISSING_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for unsafe trait missing `# Safety` section", + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for safe trait have unnecessary `# Safety` section", + ), + _ => (), }, _ => (), } @@ -301,17 +335,17 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { - lint_for_missing_headers(cx, item.def_id.def_id, item.span, sig, headers, None, None); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, None, None); } } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } @@ -319,33 +353,24 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { let body = cx.tcx.hir().body(body_id); let mut fpu = FindPanicUnwrap { cx, - typeck_results: cx.tcx.typeck(item.def_id.def_id), + typeck_results: cx.tcx.typeck(item.owner_id.def_id), panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers( - cx, - item.def_id.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span); } } } -fn lint_for_missing_headers<'tcx>( - cx: &LateContext<'tcx>, +fn lint_for_missing_headers( + cx: &LateContext<'_>, def_id: LocalDefId, - span: impl Into + Copy, sig: &hir::FnSig<'_>, headers: DocHeaders, body_id: Option, panic_span: Option, ) { - if !cx.access_levels.is_exported(def_id) { + if !cx.effective_visibilities.is_exported(def_id) { return; // Private functions do not require doc comments } @@ -359,13 +384,21 @@ fn lint_for_missing_headers<'tcx>( return; } - if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { - span_lint( + let span = cx.tcx.def_span(def_id); + match (headers.safety, sig.header.unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( cx, MISSING_SAFETY_DOC, span, "unsafe function's docs miss `# Safety` section", - ); + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + span, + "safe function's docs have unnecessary `# Safety` section", + ), + _ => (), } if !headers.panics && panic_span.is_some() { span_lint_and_note( @@ -394,9 +427,7 @@ fn lint_for_missing_headers<'tcx>( let body = cx.tcx.hir().body(body_id); let ret_ty = typeck.expr_ty(body.value); if implements_trait(cx, ret_ty, future, &[]); - if let ty::Opaque(_, subs) = ret_ty.kind(); - if let Some(gen) = subs.types().next(); - if let ty::Generator(_, subs, _) = gen.kind(); + if let ty::Generator(_, subs, _) = ret_ty.kind(); if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result); then { span_lint( @@ -467,7 +498,7 @@ struct DocHeaders { panics: bool, } -fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &'a [Attribute]) -> DocHeaders { +fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[Attribute]) -> Option { use pulldown_cmark::{BrokenLink, CowStr, Options}; /// 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 @@ -488,11 +519,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs } else if attr.has_name(sym::doc) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check - return DocHeaders { - safety: true, - errors: true, - panics: true, - }; + return None; } } @@ -504,7 +531,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs } if doc.is_empty() { - return DocHeaders::default(); + return Some(DocHeaders::default()); } let mut cb = fake_broken_link_callback; @@ -527,7 +554,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs (previous, current) => Err(((previous, previous_range), (current, current_range))), } }); - check_doc(cx, valid_idents, events, &spans) + Some(check_doc(cx, valid_idents, events, &spans)) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; @@ -691,6 +718,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, None, false, + false, ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 0f1d701865e7..29425b2e5541 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -61,10 +61,10 @@ impl EarlyLintPass for DoubleParens { } } }, - ExprKind::MethodCall(_, _, ref params, _) => { - if let [ref param] = params[..] { - if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, msg); + ExprKind::MethodCall(ref call) => { + if let [ref arg] = call.args[..] { + if let ExprKind::Paren(_) = arg.kind { + span_lint(cx, DOUBLE_PARENS, arg.span, msg); } } }, diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 4721a7b37056..11e1bcdf12d1 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { let is_copy = is_copy(cx, arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let (lint, msg) = match fn_name { - sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY), + sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => (DROP_REF, DROP_REF_SUMMARY), sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY), sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY), sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY), diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index bbebc0244141..0570c2a10138 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for EmptyEnum { } if let ItemKind::Enum(..) = item.kind { - let ty = cx.tcx.type_of(item.def_id); + let ty = cx.tcx.type_of(item.owner_id); let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); if adt.variants().is_empty() { span_lint_and_help( diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index 08bf80a42290..c3a020433de8 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -45,7 +45,7 @@ impl EarlyLintPass for EmptyStructsWithBrackets { span_after_ident, "remove the brackets", ";", - Applicability::MachineApplicable); + Applicability::Unspecified); }, ); } diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index b019d07d53d1..4c69dacf381a 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -250,7 +250,7 @@ impl LateLintPass<'_> for EnumVariantNames { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { + if let Some((mod_name, mod_camel)) = self.modules.last() { // constants don't have surrounding modules if !mod_camel.is_empty() { if mod_name == &item.ident.name { @@ -265,7 +265,7 @@ impl LateLintPass<'_> for EnumVariantNames { } // The `module_name_repetitions` lint should only trigger if the item has the module in its // name. Having the same name is accepted. - if cx.tcx.visibility(item.def_id).is_public() && item_camel.len() > mod_camel.len() { + if cx.tcx.visibility(item.owner_id).is_public() && item_camel.len() > mod_camel.len() { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); @@ -277,7 +277,7 @@ impl LateLintPass<'_> for EnumVariantNames { Some(c) if is_word_beginning(c) => span_lint( cx, MODULE_NAME_REPETITIONS, - item.span, + item.ident.span, "item name starts with its containing module's name", ), _ => (), @@ -287,7 +287,7 @@ impl LateLintPass<'_> for EnumVariantNames { span_lint( cx, MODULE_NAME_REPETITIONS, - item.span, + item.ident.span, "item name ends with its containing module's name", ); } @@ -296,7 +296,7 @@ impl LateLintPass<'_> for EnumVariantNames { } } if let ItemKind::Enum(ref def, _) = item.kind { - if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.def_id.def_id)) { + if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) { check_variant(cx, self.threshold, def, item_name, item.span); } } diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index b40cb7cddaf1..c691e6c5402d 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,16 +66,14 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Let(let_expr) = expr.kind; - if unary_pattern(let_expr.pat); + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Let(let_expr) = expr.kind + && unary_pattern(let_expr.pat) { let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); - if is_structural_partial_eq(cx, exp_ty, pat_ty); - then { + let mut applicability = Applicability::MachineApplicable; - let mut applicability = Applicability::MachineApplicable; + if is_structural_partial_eq(cx, exp_ty, pat_ty) { let pat_str = match let_expr.pat.kind { PatKind::Struct(..) => format!( "({})", @@ -96,6 +93,20 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { ), applicability, ); + } else { + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using `matches!`", + "try", + format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + applicability, + ); } } } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 2e608fe527fd..dfb43893326e 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { // be sure we have `self` parameter in this function if trait_item.kind == (AssocItemKind::Fn { has_self: true }) { trait_self_ty = Some( - TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id()) + TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()) .self_ty() .skip_binder(), ); @@ -106,9 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { }; let fn_def_id = cx.tcx.hir().local_def_id(hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); for node in v.set { span_lint_hir( @@ -132,7 +131,7 @@ fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool { _ => return false, } - matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_))) + matches!(map.find_parent(id), Some(Node::Param(_))) } impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { @@ -157,8 +156,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { let map = &self.cx.tcx.hir(); if is_argument(*map, cmt.hir_id) { // Skip closure arguments - let parent_id = map.get_parent_node(cmt.hir_id); - if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) { + let parent_id = map.parent_id(cmt.hir_id); + if let Some(Node::Expr(..)) = map.find_parent(parent_id) { return; } @@ -177,13 +176,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read( - &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 7b9786d7e570..3543910c3b55 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, Ty, TypeVisitable}; +use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -119,11 +119,18 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); + if let ty::Closure(_, substs) = *closure_ty.kind(); then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() - && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &[]) + && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs() + && implements_trait( + cx, + callee_ty.peel_refs(), + fn_mut_id, + &args.iter().copied().map(Into::into).collect::>(), + ) && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) { // Mutable closure is used after current expr; we cannot consume it. @@ -150,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { - let name = get_ufcs_type_name(cx, method_def_id); + let name = get_ufcs_type_name(cx, method_def_id, substs); diag.span_suggestion( expr.span, "replace the closure with the method itself", @@ -220,7 +227,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) } -fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String { +fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String { let assoc_item = cx.tcx.associated_item(method_def_id); let def_id = assoc_item.container_id(cx.tcx); match assoc_item.container { @@ -229,6 +236,15 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String { let ty = cx.tcx.type_of(def_id); match ty.kind() { ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()), + ty::Array(..) + | ty::Dynamic(..) + | ty::Never + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Slice(_) + | ty::Tuple(_) => { + format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs)) + }, _ => ty.to_string(), } }, diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 453471c8cdda..fc2912f696e0 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,8 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// ### What it does @@ -83,6 +86,12 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +enum Kind { + Struct, + Fn, +} + impl ExcessiveBools { #[must_use] pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { @@ -92,21 +101,20 @@ impl ExcessiveBools { } } - fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) { - match fn_sig.header.ext { - Extern::Implicit(_) | Extern::Explicit(_, _) => return, - Extern::None => (), + fn too_many_bools<'tcx>(&self, tys: impl Iterator>, kind: Kind) -> bool { + if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { + (if Kind::Fn == kind { + self.max_fn_params_bools + } else { + self.max_struct_bools + }) < bools + } else { + false } + } - let fn_sig_bools = fn_sig - .decl - .inputs - .iter() - .filter(|param| is_bool_ty(¶m.ty)) - .count() - .try_into() - .unwrap(); - if self.max_fn_params_bools < fn_sig_bools { + fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { + if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { span_lint_and_help( cx, FN_PARAMS_EXCESSIVE_BOOLS, @@ -121,56 +129,55 @@ impl ExcessiveBools { impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); -fn is_bool_ty(ty: &Ty) -> bool { - if let TyKind::Path(None, path) = &ty.kind { - if let [name] = path.segments.as_slice() { - return name.ident.name == sym::bool; +impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if item.span.from_expansion() { + return; + } + if let ItemKind::Struct(variant_data, _) = &item.kind { + if has_repr_attr(cx, item.hir_id()) { + return; + } + + if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + &format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ); + } } } - false -} -impl EarlyLintPass for ExcessiveBools { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if item.span.from_expansion() { - return; + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + // functions with a body are already checked by `check_fn` + if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind + && fn_sig.header.abi == Abi::Rust + { + self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); } - match &item.kind { - ItemKind::Struct(variant_data, _) => { - if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { - return; - } + } - let struct_bools = variant_data - .fields() - .iter() - .filter(|field| is_bool_ty(&field.ty)) - .count() - .try_into() - .unwrap(); - if self.max_struct_bools < struct_bools { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - &format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ); - } - }, - ItemKind::Impl(box Impl { - of_trait: None, items, .. - }) - | ItemKind::Trait(box Trait { items, .. }) => { - for item in items { - if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.check_fn_sig(cx, sig, item.span); - } - } - }, - ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span), - _ => (), + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + _: &'tcx Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let Some(fn_header) = fn_kind.header() + && fn_header.abi == Abi::Rust + && get_parent_as_impl(cx.tcx, hir_id) + .map_or(true, + |impl_item| impl_item.of_trait.is_none() + ) + { + self.check_fn_sig(cx, fn_decl, span); } } } diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index be6242bd20b8..1fece5d1c480 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -73,7 +73,7 @@ impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if_chain! { if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; - if cx.access_levels.is_exported(item.def_id.def_id); + if cx.effective_visibilities.is_exported(item.owner_id.def_id); let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 407dd1b39575..9c8b0d076dfd 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// `exit()` terminates the program and doesn't provide a - /// stack trace. + /// Detects calls to the `exit()` function which terminates the program. /// /// ### Why is this bad? - /// Ideally a program is terminated by finishing + /// Exit terminates the program at the location it is called. For unrecoverable + /// errors `panics` should be used to provide a stacktrace and potentualy other + /// information. A normal termination or one with an error code should happen in /// the main function. /// /// ### Example - /// ```ignore + /// ``` /// std::process::exit(0) /// ``` + /// + /// Use instead: + /// + /// ```ignore + /// // To provide a stacktrace and additional information + /// panic!("message"); + /// + /// // or a main method with a return + /// fn main() -> Result<(), i32> { + /// Ok(()) + /// } + /// ``` #[clippy::version = "1.41.0"] pub EXIT, restriction, - "`std::process::exit` is called, terminating the program" + "detects `std::process::exit` calls" } declare_lint_pass!(Exit => [EXIT]); diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs new file mode 100644 index 000000000000..2fdd8a71466c --- /dev/null +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -0,0 +1,178 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::MultiSpan; +use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; +use rustc_hir::{ + GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{def_id::DefId, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters in generics that are never used anywhere else. + /// + /// ### Why is this bad? + /// Functions cannot infer the value of unused type parameters; therefore, calling them + /// requires using a turbofish, which serves no purpose but to satisfy the compiler. + /// + /// ### Example + /// ```rust + /// // unused type parameters + /// fn unused_ty(x: u8) { + /// // .. + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn no_unused_ty(x: u8) { + /// // .. + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub EXTRA_UNUSED_TYPE_PARAMETERS, + complexity, + "unused type parameters in function definitions" +} +declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); + +/// A visitor struct that walks a given function and gathers generic type parameters, plus any +/// trait bounds those parameters have. +struct TypeWalker<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + /// Collection of all the type parameters and their spans. + ty_params: FxHashMap, + /// Collection of any (inline) trait bounds corresponding to each type parameter. + bounds: FxHashMap, + /// The entire `Generics` object of the function, useful for querying purposes. + generics: &'tcx Generics<'tcx>, + /// The value of this will remain `true` if *every* parameter: + /// 1. Is a type parameter, and + /// 2. Goes unused in the function. + /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic + /// parameters are present, this will be set to `false`. + all_params_unused: bool, +} + +impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { + fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self { + let mut all_params_unused = true; + let ty_params = generics + .params + .iter() + .filter_map(|param| { + if let GenericParamKind::Type { .. } = param.kind { + Some((param.def_id.into(), param.span)) + } else { + if !param.is_elided_lifetime() { + all_params_unused = false; + } + None + } + }) + .collect(); + Self { + cx, + ty_params, + bounds: FxHashMap::default(), + generics, + all_params_unused, + } + } + + fn emit_lint(&self) { + let (msg, help) = match self.ty_params.len() { + 0 => return, + 1 => ( + "type parameter goes unused in function definition", + "consider removing the parameter", + ), + _ => ( + "type parameters go unused in function definition", + "consider removing the parameters", + ), + }; + + let source_map = self.cx.tcx.sess.source_map(); + let span = if self.all_params_unused { + self.generics.span.into() // Remove the entire list of generics + } else { + MultiSpan::from_spans( + self.ty_params + .iter() + .map(|(def_id, &span)| { + // Extend the span past any trait bounds, and include the comma at the end. + let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi); + let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false); + let comma_span = source_map.span_through_char(comma_range, ','); + span.with_hi(comma_span.hi()) + }) + .collect(), + ) + }; + + span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help); + } +} + +impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + if let Some((def_id, _)) = t.peel_refs().as_generic_param() { + if self.ty_params.remove(&def_id).is_some() { + self.all_params_unused = false; + } + } else if let TyKind::OpaqueDef(id, _, _) = t.kind { + // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls + // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're + // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. + let item = self.nested_visit_map().item(id); + walk_item(self, item); + } else { + walk_ty(self, t); + } + } + + fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { + if let WherePredicate::BoundPredicate(predicate) = predicate { + // Collect spans for bounds that appear in the list of generics (not in a where-clause) + // for use in forming the help message + if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() + && let PredicateOrigin::GenericParam = predicate.origin + { + self.bounds.insert(def_id, predicate.span); + } + // Only walk the right-hand side of where-bounds + for bound in predicate.bounds { + walk_param_bound(self, bound); + } + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} + +impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn(_, generics, _) = item.kind { + let mut walker = TypeWalker::new(cx, generics); + walk_item(&mut walker, item); + walker.emit_lint(); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + // Only lint on inherent methods, not trait methods. + if let ImplItemKind::Fn(..) = item.kind && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + let mut walker = TypeWalker::new(cx, item.generics); + walk_impl_item(&mut walker, item); + walker.emit_lint(); + } + } +} diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index ef24a5d06ad0..2ef547526d4f 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { // check for `impl From for ..` if_chain! { if let hir::ItemKind::Impl(impl_) = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); - if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.def_id); + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); + if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id); then { lint_impl_body(cx, item.span, impl_.items); } @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { +fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, ImplItemKind}; @@ -107,7 +107,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h let body = cx.tcx.hir().body(body_id); let mut fpu = FindPanicUnwrap { lcx: cx, - typeck_results: cx.tcx.typeck(impl_item.id.def_id.def_id), + typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id), result: Vec::new(), }; fpu.visit_expr(body.value); diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 0ed301964758..f95b628e6c31 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -324,7 +324,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: let maybe_neg_sugg = |expr, hir_id| { let sugg = Sugg::hir(cx, expr, ".."); if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id { - format!("-{sugg}") + format!("-{}", sugg.maybe_par()) } else { sugg.to_string() } diff --git a/clippy_lints/src/fn_null_check.rs b/clippy_lints/src/fn_null_check.rs new file mode 100644 index 000000000000..91c8c340ce28 --- /dev/null +++ b/clippy_lints/src/fn_null_check.rs @@ -0,0 +1,106 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for comparing a function pointer to null. + /// + /// ### Why is this bad? + /// Function pointers are assumed to not be null. + /// + /// ### Example + /// ```rust,ignore + /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ + /// + /// if (fn_ptr as *const ()).is_null() { ... } + /// ``` + /// Use instead: + /// ```rust,ignore + /// let fn_ptr: Option = /* somehow obtained nullable function pointer */ + /// + /// if fn_ptr.is_none() { ... } + /// ``` + #[clippy::version = "1.67.0"] + pub FN_NULL_CHECK, + correctness, + "`fn()` type assumed to be nullable" +} +declare_lint_pass!(FnNullCheck => [FN_NULL_CHECK]); + +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_help( + cx, + FN_NULL_CHECK, + expr.span, + "function pointer assumed to be nullable, even though it isn't", + None, + "try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value", + ); +} + +fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let ExprKind::Cast(cast_expr, cast_ty) = expr.kind + && let TyKind::Ptr(_) = cast_ty.kind + { + cx.typeck_results().expr_ty_adjusted(cast_expr).is_fn() + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for FnNullCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + match expr.kind { + // Catching: + // (fn_ptr as * ).is_null() + ExprKind::MethodCall(method_name, receiver, _, _) + if method_name.ident.as_str() == "is_null" && is_fn_ptr_cast(cx, receiver) => + { + lint_expr(cx, expr); + }, + + ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { + let to_check: &Expr<'_>; + if is_fn_ptr_cast(cx, left) { + to_check = right; + } else if is_fn_ptr_cast(cx, right) { + to_check = left; + } else { + return; + } + + match to_check.kind { + // Catching: + // (fn_ptr as * ) == (0 as ) + ExprKind::Cast(cast_expr, _) if is_integer_literal(cast_expr, 0) => { + lint_expr(cx, expr); + }, + + // Catching: + // (fn_ptr as * ) == std::ptr::null() + ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::ptr_null) => { + lint_expr(cx, expr); + }, + + // Catching: + // (fn_ptr as * ) == + _ if matches!( + constant(cx, cx.typeck_results(), to_check), + Some((Constant::RawPtr(0), _)) + ) => + { + lint_expr(cx, expr); + }, + + _ => {}, + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index bc0c68f535a9..d0fab6949604 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { if format_args.format_string.parts == [kw::Empty]; if arg.format.is_default(); if match cx.typeck_results().expr_ty(value).peel_refs().kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()), + ty::Adt(adt, _) => Some(adt.did()) == cx.tcx.lang_items().string(), ty::Str => true, _ => false, }; diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 4f06fc8fa5a2..d6b02f174f93 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,17 +1,23 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; -use clippy_utils::macros::{is_format_macro, is_panic, FormatArgsExpn, FormatParam, FormatParamUsage}; +use clippy_utils::is_diag_trait_item; +use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred}; +use clippy_utils::macros::{ + is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, + FormatParamUsage, +}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; +use clippy_utils::ty::{implements_trait, is_type_lang_item}; use if_chain::if_chain; use itertools::Itertools; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, QPath}; +use rustc_errors::{ + Applicability, + SuggestionStyle::{CompletelyHidden, ShowCode}, +}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::DefId; use rustc_span::edition::Edition::Edition2021; @@ -101,58 +107,103 @@ declare_clippy_lint! { /// format!("{var:.prec$}"); /// ``` /// - /// ### Known Problems - /// - /// There may be a false positive if the format string is expanded from certain proc macros: - /// - /// ```ignore - /// println!(indoc!("{}"), var); + /// If allow-mixed-uninlined-format-args is set to false in clippy.toml, + /// the following code will also trigger the lint: + /// ```rust + /// # let var = 42; + /// format!("{} {}", var, 1+2); + /// ``` + /// Use instead: + /// ```rust + /// # let var = 42; + /// format!("{var} {}", 1+2); /// ``` /// + /// ### Known Problems + /// /// If a format string contains a numbered argument that cannot be inlined /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - style, + pedantic, "using non-inlined variables in `format!` calls" } -impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]); +declare_clippy_lint! { + /// ### What it does + /// Detects [formatting parameters] that have no effect on the output of + /// `format!()`, `println!()` or similar macros. + /// + /// ### Why is this bad? + /// Shorter format specifiers are easier to read, it may also indicate that + /// an expected formatting operation such as adding padding isn't happening. + /// + /// ### Example + /// ```rust + /// println!("{:.}", 1.0); + /// + /// println!("not padded: {:5}", format_args!("...")); + /// ``` + /// Use instead: + /// ```rust + /// println!("{}", 1.0); + /// + /// println!("not padded: {}", format_args!("...")); + /// // OR + /// println!("padded: {:5}", format!("...")); + /// ``` + /// + /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters + #[clippy::version = "1.66.0"] + pub UNUSED_FORMAT_SPECS, + complexity, + "use of a format specifier that has no effect" +} + +impl_lint_pass!(FormatArgs => [ + FORMAT_IN_FORMAT_ARGS, + TO_STRING_IN_FORMAT_ARGS, + UNINLINED_FORMAT_ARGS, + UNUSED_FORMAT_SPECS, +]); pub struct FormatArgs { - msrv: Option, + msrv: Msrv, + ignore_mixed: bool, } impl FormatArgs { #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } + pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + Self { + msrv, + ignore_mixed: allow_mixed_uninlined_format_args, + } } } impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(cx, expr); - let expr_expn_data = expr.span.ctxt().outer_expn_data(); - let outermost_expn_data = outermost_expn_data(expr_expn_data); - if let Some(macro_def_id) = outermost_expn_data.macro_def_id; - if is_format_macro(cx, macro_def_id); - if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; - then { - for arg in &format_args.args { - if !arg.format.is_default() { - continue; - } - if is_aliased(&format_args, arg.param.value.hir_id) { - continue; - } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); - check_to_string_in_format_args(cx, name, arg.param.value); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr) + && let expr_expn_data = expr.span.ctxt().outer_expn_data() + && let outermost_expn_data = outermost_expn_data(expr_expn_data) + && let Some(macro_def_id) = outermost_expn_data.macro_def_id + && is_format_macro(cx, macro_def_id) + && let ExpnKind::Macro(_, name) = outermost_expn_data.kind + { + for arg in &format_args.args { + check_unused_format_specifier(cx, arg); + if !arg.format.is_default() { + continue; } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); + if is_aliased(&format_args, arg.param.value.hir_id) { + continue; } + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); + check_to_string_in_format_args(cx, name, arg.param.value); + } + if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed); } } } @@ -160,12 +211,89 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { extract_msrv_attr!(LateContext); } -fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { +fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { + let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs(); + + if let Count::Implied(Some(mut span)) = arg.format.precision + && !span.is_empty() + { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + span, + "empty precision specifier has no effect", + |diag| { + if param_ty.is_floating_point() { + diag.note("a precision specifier is not required to format floats"); + } + + if arg.format.is_default() { + // If there's no other specifiers remove the `:` too + span = arg.format_span(); + } + + diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable); + }, + ); + } + + if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() { + span_lint_and_then( + cx, + UNUSED_FORMAT_SPECS, + arg.span, + "format specifiers have no effect on `format_args!()`", + |diag| { + let mut suggest_format = |spec, span| { + let message = format!("for the {spec} to apply consider using `format!()`"); + + if let Some(mac_call) = root_macro_call(arg.param.value.span) + && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) + && arg.span.eq_ctxt(mac_call.span) + { + diag.span_suggestion( + cx.sess().source_map().span_until_char(mac_call.span, '!'), + message, + "format", + Applicability::MaybeIncorrect, + ); + } else if let Some(span) = span { + diag.span_help(span, message); + } + }; + + if !arg.format.width.is_implied() { + suggest_format("width", arg.format.width.span()); + } + + if !arg.format.precision.is_implied() { + suggest_format("precision", arg.format.precision.span()); + } + + diag.span_suggestion_verbose( + arg.format_span(), + "if the current behavior is intentional, remove the format specifiers", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + } +} + +fn check_uninlined_args( + cx: &LateContext<'_>, + args: &FormatArgsExpn<'_>, + call_site: Span, + def_id: DefId, + ignore_mixed: bool, +) { if args.format_string.span.from_expansion() { return; } - if call_site.edition() < Edition2021 && is_panic(cx, def_id) { - // panic! before 2021 edition considers a single string argument as non-format + if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) { + // panic!, assert!, and debug_assert! before 2021 edition considers a single string argument as + // non-format return; } @@ -175,14 +303,13 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si // we cannot remove any other arguments in the format string, // because the index numbers might be wrong after inlining. // Example of an un-inlinable format: print!("{}{1}", foo, 2) - if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() { + if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() { return; } - // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 - if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) { - return; - } + // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 + // in those cases, make the code suggestion hidden + let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)); span_lint_and_then( cx, @@ -190,12 +317,22 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si call_site, "variables can be used directly in the `format!` string", |diag| { - diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable); + diag.multipart_suggestion_with_style( + "change this to", + fixes, + Applicability::MachineApplicable, + if multiline_fix { CompletelyHidden } else { ShowCode }, + ); }, ); } -fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool { +fn check_one_arg( + args: &FormatArgsExpn<'_>, + param: &FormatParam<'_>, + fixes: &mut Vec<(Span, String)>, + ignore_mixed: bool, +) -> bool { if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind && let [segment] = path.segments @@ -210,8 +347,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut fixes.push((arg_span, String::new())); true // successful inlining, continue checking } else { - // if we can't inline a numbered argument, we can't continue - param.kind != Numbered + // Do not continue inlining (return false) in case + // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)` + // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already + param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_))) } } @@ -238,7 +377,7 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb call_site, &format!("`format!` in `{name}!` args"), |diag| { - diag.help(&format!( + diag.help(format!( "combine the `format!(..)` arguments with the outer `{name}!(..)` call" )); diag.help("or consider changing `format!` to `format_args!`"); diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs index 9b9f1872bfc1..68c5c3673fe1 100644 --- a/clippy_lints/src/format_push_string.rs +++ b/clippy_lints/src/format_push_string.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{match_def_path, paths, peel_hir_expr_refs}; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -41,7 +41,7 @@ declare_clippy_lint! { declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) + is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) } fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id { diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 5d25c1d06341..bd66ace4500a 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,11 +1,19 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_hir as hir; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::span_is_local; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::path_def_id; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::{ + GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, + TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; +use rustc_middle::{hir::nested_filter::OnlyBodies, ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -41,12 +49,12 @@ declare_clippy_lint! { } pub struct FromOverInto { - msrv: Option, + msrv: Msrv, } impl FromOverInto { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { FromOverInto { msrv } } } @@ -54,28 +62,153 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } - if_chain! { - if let hir::ItemKind::Impl{ .. } = &item.kind; - if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); - if cx.tcx.is_diagnostic_item(sym::Into, impl_trait_ref.def_id); - - then { - span_lint_and_help( - cx, - FROM_OVER_INTO, - cx.tcx.sess.source_map().guess_head_span(item.span), - "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", - None, - &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), - ); - } + if let ItemKind::Impl(Impl { + of_trait: Some(hir_trait_ref), + self_ty, + items: [impl_item_ref], + .. + }) = item.kind + && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() + // `impl Into for self_ty` + && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args + && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::subst_identity) + && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) + && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _)) + { + span_lint_and_then( + cx, + FROM_OVER_INTO, + cx.tcx.sess.source_map().guess_head_span(item.span), + "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", + |diag| { + // If the target type is likely foreign mention the orphan rules as it's a common source of confusion + if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) { + diag.help( + "`impl From for Foreign` is allowed by the orphan rules, for more information see\n\ + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence" + ); + } + + let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty()); + if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) { + diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable); + } else { + diag.help(message); + } + }, + ); } } extract_msrv_attr!(LateContext); } + +/// Finds the occurences of `Self` and `self` +struct SelfFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// Occurences of `Self` + upper: Vec, + /// Occurences of `self` + lower: Vec, + /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding + /// already named `val` + invalid: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> { + type NestedFilter = OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { + for segment in path.segments { + match segment.ident.name { + kw::SelfLower => self.lower.push(segment.ident.span), + kw::SelfUpper => self.upper.push(segment.ident.span), + _ => continue, + } + } + + self.invalid |= path.span.from_expansion(); + if !self.invalid { + walk_path(self, path); + } + } + + fn visit_name(&mut self, name: Symbol) { + if name == sym::val { + self.invalid = true; + } + } +} + +fn convert_to_from( + cx: &LateContext<'_>, + into_trait_seg: &PathSegment<'_>, + target_ty: &Ty<'_>, + self_ty: &Ty<'_>, + impl_item_ref: &ImplItemRef, +) -> Option> { + let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id); + let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None }; + let body = cx.tcx.hir().body(body_id); + let [input] = body.params else { return None }; + let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None }; + + let from = snippet_opt(cx, self_ty.span)?; + let into = snippet_opt(cx, target_ty.span)?; + + let mut suggestions = vec![ + // impl Into for U -> impl From for U + // ~~~~ ~~~~ + (into_trait_seg.ident.span, String::from("From")), + // impl Into for U -> impl Into for U + // ~ ~ + (target_ty.span, from.clone()), + // impl Into for U -> impl Into for T + // ~ ~ + (self_ty.span, into), + // fn into(self) -> T -> fn from(self) -> T + // ~~~~ ~~~~ + (impl_item.ident.span, String::from("from")), + // fn into([mut] self) -> T -> fn into([mut] v: T) -> T + // ~~~~ ~~~~ + (self_ident.span, format!("val: {from}")), + // fn into(self) -> T -> fn into(self) -> Self + // ~ ~~~~ + (sig.decl.output.span(), String::from("Self")), + ]; + + let mut finder = SelfFinder { + cx, + upper: Vec::new(), + lower: Vec::new(), + invalid: false, + }; + finder.visit_expr(body.value); + + if finder.invalid { + return None; + } + + // don't try to replace e.g. `Self::default()` with `&[T]::default()` + if !finder.upper.is_empty() && !matches!(self_ty.kind, TyKind::Path(_)) { + return None; + } + + for span in finder.upper { + suggestions.push((span, from.clone())); + } + for span in finder.lower { + suggestions.push((span, String::from("val"))); + } + + Some(suggestions) +} diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs new file mode 100644 index 000000000000..096508dc4f11 --- /dev/null +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_c_void; +use clippy_utils::{match_def_path, path_def_id, paths}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::RawPtr; +use rustc_middle::ty::TypeAndMut; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)` + /// + /// ### Why is this bad? + /// When dealing with `c_void` raw pointers in FFI, it is easy to run into the pitfall of calling `from_raw` with the `c_void` pointer. + /// The type signature of `Box::from_raw` is `fn from_raw(raw: *mut T) -> Box`, so if you pass a `*mut c_void` you will get a `Box` (and similarly for `Rc`, `Arc` and `Weak`). + /// For this to be safe, `c_void` would need to have the same memory layout as the original type, which is often not the case. + /// + /// ### Example + /// ```rust + /// # use std::ffi::c_void; + /// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr) }; + /// ``` + /// Use instead: + /// ```rust + /// # use std::ffi::c_void; + /// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr as *mut usize) }; + /// ``` + /// + #[clippy::version = "1.67.0"] + pub FROM_RAW_WITH_VOID_PTR, + suspicious, + "creating a `Box` from a void raw pointer" +} +declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]); + +impl LateLintPass<'_> for FromRawWithVoidPtr { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(box_from_raw, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind + && seg.ident.name == sym!(from_raw) + && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) + && let arg_kind = cx.typeck_results().expr_ty(arg).kind() + && let RawPtr(TypeAndMut { ty, .. }) = arg_kind + && is_c_void(cx, *ty) { + let msg = format!("creating a `{type_str}` from a void raw pointer"); + span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type"); + } + } +} + +/// Checks whether a `DefId` matches `Box`, `Rc`, `Arc`, or one of the `Weak` types. +/// Returns a static string slice with the name of the type, if one was found. +fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + // Box + if Some(def_id) == cx.tcx.lang_items().owned_box() { + return Some("Box"); + } + + if let Some(symbol) = cx.tcx.get_diagnostic_name(def_id) { + if symbol == sym::Arc { + return Some("Arc"); + } else if symbol == sym::Rc { + return Some("Rc"); + } + } + + if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) { + Some("Weak") + } else { + None + } +} diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index cf8b7acd66d2..74a60b6a0d24 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_integer_literal; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind}; +use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -98,5 +98,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { /// Checks if a Ty is `String` or `&str` fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - is_type_diagnostic_item(cx, ty, sym::String) || is_type_diagnostic_item(cx, ty, sym::str) + is_type_lang_item(cx, ty, LangItem::String) || is_type_diagnostic_item(cx, ty, sym::str) } diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs new file mode 100644 index 000000000000..27acad45ccf7 --- /dev/null +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use std::iter; + +use super::MISNAMED_GETTERS; + +pub fn check_fn( + cx: &LateContext<'_>, + kind: FnKind<'_>, + decl: &FnDecl<'_>, + body: &Body<'_>, + span: Span, + _hir_id: HirId, +) { + let FnKind::Method(ref ident, sig) = kind else { + return; + }; + + // Takes only &(mut) self + if decl.inputs.len() != 1 { + return; + } + + let name = ident.name.as_str(); + + let name = match decl.implicit_self { + ImplicitSelfKind::MutRef => { + let Some(name) = name.strip_suffix("_mut") else { + return; + }; + name + }, + ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name, + ImplicitSelfKind::None => return, + }; + + let name = if sig.header.unsafety == Unsafety::Unsafe { + name.strip_suffix("_unchecked").unwrap_or(name) + } else { + name + }; + + // Body must be &(mut) .name + // self_data is not neccessarilly self, to also lint sub-getters, etc… + + let block_expr = if_chain! { + if let ExprKind::Block(block,_) = body.value.kind; + if block.stmts.is_empty(); + if let Some(block_expr) = block.expr; + then { + block_expr + } else { + return; + } + }; + let expr_span = block_expr.span; + + // Accept &, &mut and + let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind { + tmp + } else { + block_expr + }; + let (self_data, used_ident) = if_chain! { + if let ExprKind::Field(self_data, ident) = expr.kind; + if ident.name.as_str() != name; + then { + (self_data, ident) + } else { + return; + } + }; + + let mut used_field = None; + let mut correct_field = None; + let typeck_results = cx.typeck_results(); + for adjusted_type in iter::once(typeck_results.expr_ty(self_data)) + .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target)) + { + let ty::Adt(def,_) = adjusted_type.kind() else { + continue; + }; + + for f in def.all_fields() { + if f.name.as_str() == name { + correct_field = Some(f); + } + if f.name == used_ident.name { + used_field = Some(f); + } + } + } + + let Some(used_field) = used_field else { + // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number. + return; + }; + + let Some(correct_field) = correct_field else { + // There is no field corresponding to the getter name. + // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is + return; + }; + + if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { + let left_span = block_expr.span.until(used_ident.span); + let snippet = snippet(cx, left_span, ".."); + let sugg = format!("{snippet}{name}"); + span_lint_and_then( + cx, + MISNAMED_GETTERS, + span, + "getter function appears to return the wrong field", + |diag| { + diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect); + }, + ); + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 90911e0bf259..9dbce3f889be 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,3 +1,4 @@ +mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; mod result; @@ -62,23 +63,40 @@ declare_clippy_lint! { /// arguments but are not marked `unsafe`. /// /// ### Why is this bad? - /// The function should probably be marked `unsafe`, since - /// for an arbitrary raw pointer, there is no way of telling for sure if it is - /// valid. + /// The function should almost definitely be marked `unsafe`, since for an + /// arbitrary raw pointer, there is no way of telling for sure if it is valid. + /// + /// In general, this lint should **never be disabled** unless it is definitely a + /// false positive (please submit an issue if so) since it breaks Rust's + /// soundness guarantees, directly exposing API users to potentially dangerous + /// program behavior. This is also true for internal APIs, as it is easy to leak + /// unsoundness. + /// + /// ### Context + /// In Rust, an `unsafe {...}` block is used to indicate that the code in that + /// section has been verified in some way that the compiler can not. For a + /// function that accepts a raw pointer then accesses the pointer's data, this is + /// generally impossible as the incoming pointer could point anywhere, valid or + /// not. So, the signature should be marked `unsafe fn`: this indicates that the + /// function's caller must provide some verification that the arguments it sends + /// are valid (and then call the function within an `unsafe` block). /// /// ### Known problems /// * It does not check functions recursively so if the pointer is passed to a /// private non-`unsafe` function which does the dereferencing, the lint won't - /// trigger. + /// trigger (false negative). /// * It only checks for arguments whose type are raw pointers, not raw pointers /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or - /// `some_argument.get_raw_ptr()`). + /// `some_argument.get_raw_ptr()`) (false negative). /// /// ### Example /// ```rust,ignore /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this call "looks" safe but will segfault or worse! + /// // foo(invalid_ptr); /// ``` /// /// Use instead: @@ -86,6 +104,12 @@ declare_clippy_lint! { /// pub unsafe fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// + /// // this would cause a compiler error for calling without `unsafe` + /// // foo(invalid_ptr); + /// + /// // sound call if the caller knows the pointer is valid + /// unsafe { foo(valid_ptr); } /// ``` #[clippy::version = "pre 1.29.0"] pub NOT_UNSAFE_PTR_ARG_DEREF, @@ -254,12 +278,54 @@ declare_clippy_lint! { /// Ok(()) /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub RESULT_LARGE_ERR, perf, "function returning `Result` with large `Err` type" } +declare_clippy_lint! { + /// ### What it does + /// Checks for getter methods that return a field that doesn't correspond + /// to the name of the method, when there is a field's whose name matches that of the method. + /// + /// ### Why is this bad? + /// It is most likely that such a method is a bug caused by a typo or by copy-pasting. + /// + /// ### Example + + /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// + /// impl A { + /// fn a(&self) -> &str{ + /// &self.b + /// } + /// } + + /// ``` + /// Use instead: + /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// + /// impl A { + /// fn a(&self) -> &str{ + /// &self.a + /// } + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub MISNAMED_GETTERS, + suspicious, + "getter method returning the wrong field" +} + #[derive(Copy, Clone)] pub struct Functions { too_many_arguments_threshold: u64, @@ -286,6 +352,7 @@ impl_lint_pass!(Functions => [ MUST_USE_CANDIDATE, RESULT_UNIT_ERR, RESULT_LARGE_ERR, + MISNAMED_GETTERS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -301,6 +368,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); + misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d263804f32cf..d22bede36b41 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -7,14 +7,14 @@ use rustc_middle::{ lint::in_external_macro, ty::{self, Ty}, }; -use rustc_span::{sym, Span}; +use rustc_span::{sym, Span, Symbol}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; +use clippy_utils::{return_ty, trait_ref_of_method}; use core::ops::ControlFlow; @@ -22,9 +22,9 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); + let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind { - let is_public = cx.access_levels.is_exported(item.def_id.def_id); + let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); @@ -34,7 +34,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> sig.decl, cx.tcx.hir().body(*body_id), item.span, - item.def_id.def_id, + item.owner_id.def_id, item.span.with_hi(sig.decl.output.span().hi()), "this function could have a `#[must_use]` attribute", ); @@ -44,20 +44,22 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { - let is_public = cx.access_levels.is_exported(item.def_id.def_id); + let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); + let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id.def_id).is_none() + } else if is_public + && !is_proc_macro(cx.sess(), attrs) + && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { check_must_use_candidate( cx, sig.decl, cx.tcx.hir().body(*body_id), item.span, - item.def_id.def_id, + item.owner_id.def_id, item.span.with_hi(sig.decl.output.span().hi()), "this method could have a `#[must_use]` attribute", ); @@ -67,11 +69,11 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { - let is_public = cx.access_levels.is_exported(item.def_id.def_id); + let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); + let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if let hir::TraitFn::Provided(eid) = *eid { @@ -82,7 +84,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr sig.decl, body, item.span, - item.def_id.def_id, + item.owner_id.def_id, item.span.with_hi(sig.decl.output.span().hi()), "this method could have a `#[must_use]` attribute", ); @@ -137,7 +139,7 @@ fn check_must_use_candidate<'tcx>( || mutates_static(cx, body) || in_external_macro(cx.sess(), item_span) || returns_unit(decl) - || !cx.access_levels.is_exported(item_id) + || !cx.effective_visibilities.is_exported(item_id) || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id))) { return; @@ -175,27 +177,29 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) return false; // ignore `_` patterns } if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { - is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), pat.span, tys) + is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), tys) } else { false } } -static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]]; +static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool { match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Adt(adt, substs) => { - tys.insert(adt.did()) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) - || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path)) - && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env) + || KNOWN_WRAPPER_TYS + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) + && substs.types().any(|ty| is_mutable_ty(cx, ty, tys)) }, - ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), - ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys) }, // calling something constitutes a side effect, so return true on all callables // also never calls need not be used, so return true for them, too @@ -223,12 +227,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo let mut tys = DefIdSet::default(); for arg in args { if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - cx, - cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) + && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys) && is_mutated_static(arg) { return ControlFlow::Break(()); @@ -241,12 +240,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo let mut tys = DefIdSet::default(); for arg in std::iter::once(receiver).chain(args.iter()) { if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - cx, - cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) + && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys) && is_mutated_static(arg) { return ControlFlow::Break(()); diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index b7595d101e0f..2c0bf551fd7e 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -31,7 +31,7 @@ pub(super) fn check_fn<'tcx>( pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); - check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id.def_id); + check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.owner_id.def_id); } } @@ -42,7 +42,7 @@ fn check_raw_ptr<'tcx>( body: &'tcx hir::Body<'tcx>, def_id: LocalDefId, ) { - if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) { + if unsafety == hir::Unsafety::Normal && cx.effective_visibilities.is_exported(def_id) { let raw_ptrs = iter_input_pats(decl, body) .filter_map(|arg| raw_ptr_arg(cx, arg)) .collect::(); diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 113c4e9f5091..23da145d0382 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -2,12 +2,12 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Adt, Ty}; use rustc_span::{sym, Span}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -34,9 +34,9 @@ fn result_err_ty<'tcx>( pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) { if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind - && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id.def_id, item.span) + && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) { - if cx.access_levels.is_exported(item.def_id.def_id) { + if cx.effective_visibilities.is_exported(item.owner_id.def_id) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_result_unit_err(cx, err_ty, fn_header_span); } @@ -47,10 +47,10 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, l pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) { // Don't lint if method is a trait's implementation, we can't do anything about those if let hir::ImplItemKind::Fn(ref sig, _) = item.kind - && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id.def_id, item.span) - && trait_ref_of_method(cx, item.def_id.def_id).is_none() + && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) + && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { - if cx.access_levels.is_exported(item.def_id.def_id) { + if cx.effective_visibilities.is_exported(item.owner_id.def_id) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_result_unit_err(cx, err_ty, fn_header_span); } @@ -61,8 +61,8 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id.def_id, item.span) { - if cx.access_levels.is_exported(item.def_id.def_id) { + if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) { + if cx.effective_visibilities.is_exported(item.owner_id.def_id) { check_result_unit_err(cx, err_ty, fn_header_span); } check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); @@ -84,17 +84,59 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { - let ty_size = approx_ty_size(cx, err_ty); - if ty_size >= large_err_threshold { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { - diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); - }, - ); + if_chain! { + if let Adt(adt, subst) = err_ty.kind(); + if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); + if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(local_def_id); + if let hir::ItemKind::Enum(ref def, _) = item.kind; + then { + let variants_size = AdtVariantInfo::new(cx, *adt, subst); + if let Some((first_variant, variants)) = variants_size.split_first() + && first_variant.size >= large_err_threshold + { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag| { + diag.span_label( + def.variants[first_variant.ind].span, + format!("the largest variant contains at least {} bytes", variants_size[0].size), + ); + + for variant in variants { + if variant.size >= large_err_threshold { + let variant_def = &def.variants[variant.ind]; + diag.span_label( + variant_def.span, + format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), + ); + } + } + + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + } + ); + } + } + else { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); + } + } } } diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index eb2eefe0d5a1..2a79b18b8299 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -4,10 +4,10 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait}; +use rustc_middle::ty::{self, AliasTy, Clause, EarlyBinder, PredicateKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; -use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::{self, FulfillmentError}; declare_clippy_lint! { @@ -62,11 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { return; } let ret_ty = return_ty(cx, hir_id); - if let Opaque(id, subst) = *ret_ty.kind() { - let preds = cx.tcx.explicit_item_bounds(id); + if let ty::Alias(ty::Opaque, AliasTy { def_id, substs, .. }) = *ret_ty.kind() { + let preds = cx.tcx.explicit_item_bounds(def_id); let mut is_future = false; for &(p, _span) in preds { - let p = EarlyBinder(p).subst(cx.tcx, subst); + let p = EarlyBinder(p).subst(cx.tcx, substs); if let Some(trait_pred) = p.to_opt_poly_trait_pred() { if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { is_future = true; @@ -77,10 +77,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if is_future { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); - let send_errors = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = traits::ObligationCause::misc(span, hir_id); - traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait) - }); + let infcx = cx.tcx.infer_ctxt().build(); + let def_id = cx.tcx.hir().local_def_id(hir_id); + let cause = traits::ObligationCause::misc(span, def_id); + let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait); if !send_errors.is_empty() { span_lint_and_then( cx, @@ -88,18 +88,20 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { span, "future cannot be sent between threads safely", |db| { - cx.tcx.infer_ctxt().enter(|infcx| { - for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); - if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() { - db.note(&format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let PredicateKind::Clause(Clause::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() + { + db.note(&format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }); + } }, ); } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0d6718c168a5..9cadaaa493e4 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{ - contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks, -}; +use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -47,12 +45,12 @@ declare_clippy_lint! { } pub struct IfThenSomeElseNone { - msrv: Option, + msrv: Msrv, } impl IfThenSomeElseNone { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { + if !self.msrv.meets(msrvs::BOOL_THEN) { return; } @@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } else { format!("{{ /* snippet */ {arg_snip} }}") }; - let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { "then_some" } else { method_body.insert_str(0, "|| "); diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 93efe957b1dc..64a4a3fa741b 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -66,8 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { use rustc_span::BytePos; - fn suggestion<'tcx>( - cx: &LateContext<'tcx>, + fn suggestion( + cx: &LateContext<'_>, diag: &mut Diagnostic, generics_span: Span, generics_suggestion_span: Span, @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { } } - if !cx.access_levels.is_exported(item.def_id.def_id) { + if !cx.effective_visibilities.is_exported(item.owner_id.def_id) { return; } diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs index bf1351829c6a..6e19343931ec 100644 --- a/clippy_lints/src/implicit_saturating_add.rs +++ b/clippy_lints/src/implicit_saturating_add.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// /// u = u.saturating_add(1); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub IMPLICIT_SATURATING_ADD, style, "Perform saturating addition instead of implicitly checking max bound of data type" diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index c7b5badaae51..bdeddf44df7b 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use if_chain::if_chain; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span}; @@ -47,18 +47,17 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.59.0"] pub INDEX_REFUTABLE_SLICE, - nursery, + pedantic, "avoid indexing on slices which could be destructed" } -#[derive(Copy, Clone)] pub struct IndexRefutableSlice { max_suggested_slice: u64, - msrv: Option, + msrv: Msrv, } impl IndexRefutableSlice { - pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option) -> Self { + pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self { Self { max_suggested_slice: max_suggested_slice_pattern_length, msrv, @@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS); + if self.msrv.meets(msrvs::SLICE_PATTERNS); let found_slices = find_slice_values(cx, let_pat); if !found_slices.is_empty(); @@ -207,8 +206,8 @@ impl SliceLintInformation { } } -fn filter_lintable_slices<'a, 'tcx>( - cx: &'a LateContext<'tcx>, +fn filter_lintable_slices<'tcx>( + cx: &LateContext<'tcx>, slice_lint_info: FxIndexMap, max_suggested_slice: u64, scope: &'tcx hir::Expr<'tcx>, @@ -252,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { let map = cx.tcx.hir(); // Checking for slice indexing - let parent_id = map.get_parent_node(expr.hir_id); + let parent_id = map.parent_id(expr.hir_id); if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind; if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr); @@ -260,7 +259,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { if index_value < max_suggested_slice; // Make sure that this slice index is read only - let maybe_addrof_id = map.get_parent_node(parent_id); + let maybe_addrof_id = map.parent_id(parent_id); if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id); if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind; then { diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4a375752e1d3..eebfb753a0c5 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,13 +1,13 @@ //! lint on indexing and slicing operations use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::higher; use rustc_ast::ast::RangeLimits; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// ### What it does @@ -19,7 +19,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,no_run - /// # #![allow(const_err)] /// let x = [1, 2, 3, 4]; /// /// x[9]; @@ -83,15 +82,29 @@ declare_clippy_lint! { "indexing/slicing usage" } -declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); +impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); + +#[derive(Copy, Clone)] +pub struct IndexingSlicing { + suppress_restriction_lint_in_const: bool, +} + +impl IndexingSlicing { + pub fn new(suppress_restriction_lint_in_const: bool) -> Self { + Self { + suppress_restriction_lint_in_const, + } + } +} impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) { return; } if let ExprKind::Index(array, index) = &expr.kind { + let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] @@ -142,7 +155,13 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { (None, None) => return, // [..] is ok. }; - span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg); + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "slicing may panic", |diag| { + diag.help(help_msg); + + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + diag.note(note); + } + }); } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { @@ -157,14 +176,13 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } } - span_lint_and_help( - cx, - INDEXING_SLICING, - expr.span, - "indexing may panic", - None, - "consider using `.get(n)` or `.get_mut(n)` instead", - ); + span_lint_and_then(cx, INDEXING_SLICING, expr.span, "indexing may panic", |diag| { + diag.help("consider using `.get(n)` or `.get_mut(n)` instead"); + + if cx.tcx.hir().is_inside_const_context(expr.hir_id) { + diag.note(note); + } + }); } } } @@ -172,11 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. -fn to_const_range<'tcx>( - cx: &LateContext<'tcx>, - range: higher::Range<'_>, - array_size: u128, -) -> (Option, Option) { +fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option, Option) { let s = range .start .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index c5abcc462545..e9b2e31a769a 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -52,21 +52,19 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // List of spans to lint. (lint_span, first_span) let mut lint_spans = Vec::new(); - for (_, impl_ids) in cx + let inherent_impls = cx .tcx - .crate_inherent_impls(()) - .inherent_impls - .iter() - .filter(|(&id, impls)| { - impls.len() > 1 - // Check for `#[allow]` on the type definition - && !is_lint_allowed( - cx, - MULTIPLE_INHERENT_IMPL, - cx.tcx.hir().local_def_id_to_hir_id(id), - ) - }) - { + .with_stable_hashing_context(|hcx| cx.tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true)); + + for (_, impl_ids) in inherent_impls.into_iter().filter(|(&id, impls)| { + impls.len() > 1 + // Check for `#[allow]` on the type definition + && !is_lint_allowed( + cx, + MULTIPLE_INHERENT_IMPL, + cx.tcx.hir().local_def_id_to_hir_id(id), + ) + }) { for impl_id in impl_ids.iter().map(|id| id.expect_local()) { match type_map.entry(cx.tcx.type_of(impl_id)) { Entry::Vacant(e) => { diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 676136df572b..aaecc4fa8f25 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::{return_ty, trait_ref_of_method}; use if_chain::if_chain; -use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind}; +use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -105,10 +105,10 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })); // Check if return type is String - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String); + if is_type_lang_item(cx, return_ty(cx, impl_item.hir_id()), LangItem::String); // Filters instances of to_string which are required by a trait - if trait_ref_of_method(cx, impl_item.def_id.def_id).is_none(); + if trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none(); then { show_lint(cx, impl_item); @@ -124,7 +124,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { .expect("Failed to get trait ID of `Display`!"); // Get the real type of 'self' - let self_type = cx.tcx.fn_sig(item.def_id).input(0); + let self_type = cx.tcx.fn_sig(item.owner_id).input(0); let self_type = self_type.skip_binder().peel_refs(); // Emit either a warning or an error diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs new file mode 100644 index 000000000000..668110c7cc08 --- /dev/null +++ b/clippy_lints/src/instant_subtraction.rs @@ -0,0 +1,182 @@ +use clippy_utils::diagnostics::{self, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{source_map::Spanned, sym}; + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between `Instant::now()` and another `Instant`. + /// + /// ### Why is this bad? + /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns + /// as `Instant` subtraction saturates. + /// + /// `prev_instant.elapsed()` also more clearly signals intention. + /// + /// ### Example + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = Instant::now() - prev_instant; + /// ``` + /// Use instead: + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = prev_instant.elapsed(); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_INSTANT_ELAPSED, + pedantic, + "subtraction between `Instant::now()` and previous `Instant`" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between an [`Instant`] and a [`Duration`]. + /// + /// ### Why is this bad? + /// Unchecked subtraction could cause underflow on certain platforms, leading to + /// unintentional panics. + /// + /// ### Example + /// ```rust + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now() - Duration::from_secs(5); + /// ``` + /// + /// Use instead: + /// ```rust + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// ``` + /// + /// [`Duration`]: std::time::Duration + /// [`Instant::now()`]: std::time::Instant::now; + #[clippy::version = "1.67.0"] + pub UNCHECKED_DURATION_SUBTRACTION, + pedantic, + "finds unchecked subtraction of a 'Duration' from an 'Instant'" +} + +pub struct InstantSubtraction { + msrv: Msrv, +} + +impl InstantSubtraction { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); + +impl LateLintPass<'_> for InstantSubtraction { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind + { + if_chain! { + if is_instant_now_call(cx, lhs); + + if is_an_instant(cx, rhs); + if let Some(sugg) = Sugg::hir_opt(cx, rhs); + + then { + print_manual_instant_elapsed_sugg(cx, expr, sugg) + } else { + if_chain! { + if !expr.span.from_expansion(); + if self.msrv.meets(msrvs::TRY_FROM); + + if is_an_instant(cx, lhs); + if is_a_duration(cx, rhs); + + then { + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr) + } + } + } + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { + if let ExprKind::Call(fn_expr, []) = expr_block.kind + && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) + && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) + { + true + } else { + false + } +} + +fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + + match expr_ty.kind() { + rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT), + _ => false, + } +} + +fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) +} + +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); +} + +fn print_unchecked_duration_subtraction_sugg( + cx: &LateContext<'_>, + left_expr: &Expr<'_>, + right_expr: &Expr<'_>, + expr: &Expr<'_>, +) { + let mut applicability = Applicability::MachineApplicable; + + let left_expr = + source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); + let right_expr = source::snippet_with_applicability( + cx, + right_expr.span, + "std::time::Duration::from_secs(1)", + &mut applicability, + ); + + diagnostics::span_lint_and_sugg( + cx, + UNCHECKED_DURATION_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + "try", + format!("{left_expr}.checked_sub({right_expr}).unwrap()"), + applicability, + ); +} diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 33491da3fc5a..1b14e525d9a8 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -52,8 +53,8 @@ enum Side { impl IntPlusOne { #[expect(clippy::cast_sign_loss)] - fn check_lit(lit: &Lit, target_value: i128) -> bool { - if let LitKind::Int(value, ..) = lit.kind { + fn check_lit(token_lit: token::Lit, target_value: i128) -> bool { + if let Ok(LitKind::Int(value, ..)) = LitKind::from_token_lit(token_lit) { return value == (target_value as u128); } false @@ -62,58 +63,54 @@ impl IntPlusOne { fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option { match (binop, &lhs.kind, &rhs.kind) { // case where `x - 1 >= ...` or `-1 + x >= ...` - (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { + (BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y + 1` or `... >= 1 + y` - (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) - if rhskind.node == BinOpKind::Add => - { + (BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } }, // case where `x + 1 <= ...` or `1 + x <= ...` - (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) - if lhskind.node == BinOpKind::Add => - { + (BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` - (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y - 1` or `... >= -1 + y` - (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { + (BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 0ef77e03de90..6ea637412d5b 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); -fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { +fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(FullInt, FullInt)> { if let ExprKind::Cast(cast_exp, _) = expr.kind { let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); let cast_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/invalid_utf8_in_unchecked.rs b/clippy_lints/src/invalid_utf8_in_unchecked.rs index e0a607f9a95b..6a4861747d26 100644 --- a/clippy_lints/src/invalid_utf8_in_unchecked.rs +++ b/clippy_lints/src/invalid_utf8_in_unchecked.rs @@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked { if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) { match &arg.kind { ExprKind::Lit(Spanned { node: lit, .. }) => { - if let LitKind::ByteStr(bytes) = &lit + if let LitKind::ByteStr(bytes, _) = &lit && std::str::from_utf8(bytes).is_err() { lint(cx, expr.span); diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index ea9f046fb973..e76de77f195d 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { let name = item.ident.name.as_str(); if matches!(name, "iter" | "iter_mut") { if let TraitItemKind::Fn(fn_sig, _) = &item.kind { - check_sig(cx, name, fn_sig, item.def_id.def_id); + check_sig(cx, name, fn_sig, item.owner_id.def_id); } } } @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { ) { if let ImplItemKind::Fn(fn_sig, _) = &item.kind { - check_sig(cx, name, fn_sig, item.def_id.def_id); + check_sig(cx, name, fn_sig, item.owner_id.def_id); } } } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 76c83ab47d09..db637dfc068d 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct LargeConstArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeConstArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); if let Ok(element_count) = element_count.try_to_machine_usize(cx.tcx); if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); - if self.maximum_allowed_size < element_count * element_size; + if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size); then { let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 8ed7e4bb196c..b8d4abdbb781 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,12 +1,15 @@ //! lint when there is a large size difference between variants on an enum use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy}; +use clippy_utils::{ + diagnostics::span_lint_and_then, + ty::{approx_ty_size, is_copy, AdtVariantInfo}, +}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty}; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -72,49 +75,6 @@ impl LargeEnumVariant { } } -struct FieldInfo { - ind: usize, - size: u64, -} - -struct VariantInfo { - ind: usize, - size: u64, - fields_size: Vec, -} - -fn variants_size<'tcx>( - cx: &LateContext<'tcx>, - adt: AdtDef<'tcx>, - subst: &'tcx List>, -) -> Vec { - let mut variants_size = adt - .variants() - .iter() - .enumerate() - .map(|(i, variant)| { - let mut fields_size = variant - .fields - .iter() - .enumerate() - .map(|(i, f)| FieldInfo { - ind: i, - size: approx_ty_size(cx, f.ty(cx.tcx, subst)), - }) - .collect::>(); - fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); - - VariantInfo { - ind: i, - size: fields_size.iter().map(|info| info.size).sum(), - fields_size, - } - }) - .collect::>(); - variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); - variants_size -} - impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { @@ -123,14 +83,14 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { return; } if let ItemKind::Enum(ref def, _) = item.kind { - let ty = cx.tcx.type_of(item.def_id); + let ty = cx.tcx.type_of(item.owner_id); let Adt(adt, subst) = ty.kind() else { panic!("already checked whether this is an enum") }; if adt.variants().len() <= 1 { return; } - let variants_size = variants_size(cx, *adt, subst); + let variants_size = AdtVariantInfo::new(cx, *adt, subst); let mut difference = variants_size[0].size - variants_size[1].size; if difference > self.maximum_size_difference_allowed { @@ -151,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ); diag.span_label( def.variants[variants_size[1].ind].span, - &if variants_size[1].fields_size.is_empty() { + if variants_size[1].fields_size.is_empty() { "the second-largest variant carries no data at all".to_owned() } else { format!( @@ -173,16 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { .fields_size .iter() .rev() - .map_while(|val| { + .map_while(|&(ind, size)| { if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); + difference = difference.saturating_sub(size); Some(( - fields[val.ind].ty.span, + fields[ind].ty.span, format!( "Box<{}>", snippet_with_applicability( cx, - fields[val.ind].ty.span, + fields[ind].ty.span, "..", &mut applicability ) diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 84dd61a1e4b0..424c0d9e7982 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -60,7 +60,7 @@ impl LateLintPass<'_> for LargeIncludeFile { then { let len = match &lit.node { // include_bytes - LitKind::ByteStr(bstr) => bstr.len(), + LitKind::ByteStr(bstr, _) => bstr.len(), // include_str LitKind::Str(sym, _) => sym.as_str().len(), _ => return, diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index 5857d81ab1f2..89ae83d48f53 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -24,12 +24,12 @@ declare_clippy_lint! { } pub struct LargeStackArrays { - maximum_allowed_size: u64, + maximum_allowed_size: u128, } impl LargeStackArrays { #[must_use] - pub fn new(maximum_allowed_size: u64) -> Self { + pub fn new(maximum_allowed_size: u128) -> Self { Self { maximum_allowed_size } } } @@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) && !cx.tcx.hir().parent_iter(expr.hir_id) .any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. }))) - && self.maximum_allowed_size < element_count * element_size { + && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) { span_lint_and_help( cx, LARGE_STACK_ARRAYS, diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 3a563736fb07..920ab7f06336 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed}; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefIdSet; use rustc_hir::{ def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, - ItemKind, Mutability, Node, TraitItemRef, TyKind, + ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; @@ -16,6 +16,7 @@ use rustc_span::{ source_map::{Span, Spanned, Symbol}, symbol::sym, }; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -134,7 +135,8 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if item.ident.name == sym::len; if let ImplItemKind::Fn(sig, _) = &item.kind; if sig.decl.implicit_self.has_implicit_self(); - if cx.access_levels.is_exported(item.def_id.def_id); + if sig.decl.inputs.len() == 1; + if cx.effective_visibilities.is_exported(item.owner_id.def_id); if matches!(sig.decl.output, FnRetTy::Return(_)); if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); if imp.of_trait.is_none(); @@ -143,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let Some(local_id) = ty_id.as_local(); let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); - if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder()); + if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).skip_binder()); then { let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), @@ -195,7 +197,7 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool { item.ident.name == name && if let AssocItemKind::Fn { has_self } = item.kind { - has_self && { cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 } + has_self && { cx.tcx.fn_sig(item.id.owner_id).inputs().skip_binder().len() == 1 } } else { false } @@ -210,15 +212,15 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } - if cx.access_levels.is_exported(visited_trait.def_id.def_id) + if cx.effective_visibilities.is_exported(visited_trait.owner_id.def_id) && trait_items.iter().any(|i| is_named_self(cx, i, sym::len)) { let mut current_and_super_traits = DefIdSet::default(); - fill_trait_set(visited_trait.def_id.to_def_id(), &mut current_and_super_traits, cx); + fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx); let is_empty = sym!(is_empty); let is_empty_method_found = current_and_super_traits - .iter() + .items() .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty)) .any(|i| { i.kind == ty::AssocKind::Fn @@ -331,7 +333,7 @@ fn check_for_is_empty<'tcx>( None, None, ), - Some(is_empty) if !cx.access_levels.is_exported(is_empty.def_id.expect_local()) => ( + Some(is_empty) if !cx.effective_visibilities.is_exported(is_empty.def_id.expect_local()) => ( format!( "{item_kind} `{}` has a public `len` method, but a private `is_empty` method", item_name.as_str(), @@ -360,14 +362,13 @@ fn check_for_is_empty<'tcx>( db.span_note(span, "`is_empty` defined here"); } if let Some(self_kind) = self_kind { - db.note(&output.expected_sig(self_kind)); + db.note(output.expected_sig(self_kind)); } }); } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) - { + if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { @@ -429,16 +430,23 @@ fn check_len( fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) { if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { let mut applicability = Applicability::MachineApplicable; + + let lit1 = peel_ref_operators(cx, lit1); + let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability); + + // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will + // cause the code to dereference boolean(won't compile). + if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind { + lit_str = Cow::from(format!("({lit_str})")); + } + span_lint_and_sugg( cx, COMPARISON_TO_EMPTY, span, "comparison to empty slice", &format!("using `{op}is_empty` is clearer and more explicit"), - format!( - "{op}{}.is_empty()", - snippet_with_applicability(cx, lit1.span, "_", &mut applicability) - ), + format!("{op}{lit_str}.is_empty()"), applicability, ); } @@ -494,7 +502,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { .filter_by_name_unhygienic(is_empty) .any(|item| is_is_empty(cx, item)) }), - ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), + ty::Alias(ty::Projection, ref proj) => has_is_empty_impl(cx, proj.def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did()), ty::Array(..) | ty::Slice(..) | ty::Str => true, _ => false, diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 13071d64441a..db41bc67da1a 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { let span = stmt.span.to(if_.span); let has_interior_mutability = !cx.typeck_results().node_type(canonical_id).is_freeze( - cx.tcx.at(span), + cx.tcx, cx.param_env, ); if has_interior_mutability { return; } diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 176787497ebf..f8e359509808 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_must_use_ty, match_type}; +use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_must_use_func_call, paths}; -use if_chain::if_chain; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -29,13 +28,14 @@ declare_clippy_lint! { #[clippy::version = "1.42.0"] pub LET_UNDERSCORE_MUST_USE, restriction, - "non-binding let on a `#[must_use]` expression" + "non-binding `let` on a `#[must_use]` expression" } declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = sync_lock`. - /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`. + /// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in + /// `parking_lot`. For `std` locks see the `rustc` lint + /// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock) /// /// ### Why is this bad? /// This statement immediately drops the lock instead of @@ -56,53 +56,43 @@ declare_clippy_lint! { #[clippy::version = "1.43.0"] pub LET_UNDERSCORE_LOCK, correctness, - "non-binding let on a synchronization lock" + "non-binding `let` on a synchronization lock" } declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = ` - /// where expr has a type that implements `Drop` + /// Checks for `let _ = ` where the resulting type of expr implements `Future` /// /// ### Why is this bad? - /// This statement immediately drops the initializer - /// expression instead of extending its lifetime to the end of the scope, which - /// is often not intended. To extend the expression's lifetime to the end of the - /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to - /// explicitly drop the expression, `std::mem::drop` conveys your intention - /// better and is less error-prone. + /// Futures must be polled for work to be done. The original intention was most likely to await the future + /// and ignore the resulting value. /// /// ### Example /// ```rust - /// # struct DroppableItem; - /// { - /// let _ = DroppableItem; - /// // ^ dropped here - /// /* more code */ + /// async fn foo() -> Result<(), ()> { + /// Ok(()) /// } + /// let _ = foo(); /// ``` /// /// Use instead: /// ```rust - /// # struct DroppableItem; - /// { - /// let _droppable = DroppableItem; - /// /* more code */ - /// // dropped at end of scope + /// # async fn context() { + /// async fn foo() -> Result<(), ()> { + /// Ok(()) /// } + /// let _ = foo().await; + /// # } /// ``` - #[clippy::version = "1.50.0"] - pub LET_UNDERSCORE_DROP, - pedantic, - "non-binding let on a type that implements `Drop`" + #[clippy::version = "1.67.0"] + pub LET_UNDERSCORE_FUTURE, + suspicious, + "non-binding `let` on a future" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]); -const SYNC_GUARD_PATHS: [&[&str]; 6] = [ - &paths::MUTEX_GUARD, - &paths::RWLOCK_READ_GUARD, - &paths::RWLOCK_WRITE_GUARD, +const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, &paths::PARKING_LOT_RWLOCK_READ_GUARD, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD, @@ -110,61 +100,53 @@ const SYNC_GUARD_PATHS: [&[&str]; 6] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if in_external_macro(cx.tcx.sess, local.span) { - return; - } - - if_chain! { - if let PatKind::Wild = local.pat.kind; - if let Some(init) = local.init; - then { - let init_ty = cx.typeck_results().expr_ty(init); - let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => { - SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) - }, - - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }); - if contains_sync_guard { - span_lint_and_help( - cx, - LET_UNDERSCORE_LOCK, - local.span, - "non-binding let on a synchronization lock", - None, - "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" - ); - } else if init_ty.needs_drop(cx.tcx, cx.param_env) { - span_lint_and_help( - cx, - LET_UNDERSCORE_DROP, - local.span, - "non-binding `let` on a type that implements `Drop`", - None, - "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`" - ); - } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { - span_lint_and_help( - cx, - LET_UNDERSCORE_MUST_USE, - local.span, - "non-binding let on an expression with `#[must_use]` type", - None, - "consider explicitly using expression value" - ); - } else if is_must_use_func_call(cx, init) { - span_lint_and_help( - cx, - LET_UNDERSCORE_MUST_USE, - local.span, - "non-binding let on a result of a `#[must_use]` function", - None, - "consider explicitly using function result" - ); - } + if !in_external_macro(cx.tcx.sess, local.span) + && let PatKind::Wild = local.pat.kind + && let Some(init) = local.init + { + let init_ty = cx.typeck_results().expr_ty(init); + let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }); + if contains_sync_guard { + span_lint_and_help( + cx, + LET_UNDERSCORE_LOCK, + local.span, + "non-binding `let` on a synchronization lock", + None, + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`", + ); + } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { + span_lint_and_help( + cx, + LET_UNDERSCORE_FUTURE, + local.span, + "non-binding `let` on a future", + None, + "consider awaiting the future or dropping explicitly with `std::mem::drop`" + ); + } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding `let` on an expression with `#[must_use]` type", + None, + "consider explicitly using expression value", + ); + } else if is_must_use_func_call(cx, init) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding `let` on a result of a `#[must_use]` function", + None, + "consider explicitly using function result", + ); } } } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs deleted file mode 100644 index b5f0e5fd7d64..000000000000 --- a/clippy_lints/src/lib.register_all.rs +++ /dev/null @@ -1,369 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::all", Some("clippy_all"), vec![ - LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), - LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(box_default::BOX_DEFAULT), - LintId::of(casts::CAST_ABS_TO_UNSIGNED), - LintId::of(casts::CAST_ENUM_CONSTRUCTOR), - LintId::of(casts::CAST_ENUM_TRUNCATION), - LintId::of(casts::CAST_NAN_TO_INT), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), - LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), - LintId::of(dereference::EXPLICIT_AUTO_DEREF), - LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(disallowed_macros::DISALLOWED_MACROS), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_names::DISALLOWED_NAMES), - LintId::of(disallowed_types::DISALLOWED_TYPES), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(double_parens::DOUBLE_PARENS), - LintId::of(drop_forget_ref::DROP_COPY), - LintId::of(drop_forget_ref::DROP_NON_DROP), - LintId::of(drop_forget_ref::DROP_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), - LintId::of(duplicate_mod::DUPLICATE_MOD), - LintId::of(entry::MAP_ENTRY), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(escape::BOXED_LOCAL), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(format::USELESS_FORMAT), - LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), - LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), - LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), - LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(functions::RESULT_LARGE_ERR), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::MANUAL_FIND), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::MISSING_SPIN_LOOP), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_clamp::MANUAL_CLAMP), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), - LintId::of(manual_retain::MANUAL_RETAIN), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::COLLAPSIBLE_MATCH), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MANUAL_FILTER), - LintId::of(matches::MANUAL_MAP), - LintId::of(matches::MANUAL_UNWRAP_OR), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::MATCH_STR_CASE_MISMATCH), - LintId::of(matches::NEEDLESS_MATCH), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::BYTES_COUNT_TO_LEN), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::COLLAPSIBLE_STR_REPLACE), - LintId::of(methods::ERR_EXPECT), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::GET_FIRST), - LintId::of(methods::GET_LAST_WITH_LEN), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::IS_DIGIT_ASCII_RADIX), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::ITER_KV_MAP), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::MAP_CLONE), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::MAP_FLATTEN), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::MUT_MUTEX_LOCK), - LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), - LintId::of(methods::NEEDLESS_OPTION_TAKE), - LintId::of(methods::NEEDLESS_SPLITN), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), - LintId::of(methods::NO_EFFECT_REPLACE), - LintId::of(methods::OBFUSCATED_IF_ELSE), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::OR_THEN_UNWRAP), - LintId::of(methods::RANGE_ZIP_WITH_LEN), - LintId::of(methods::REPEAT_ONCE), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::SUSPICIOUS_TO_OWNED), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::UNIT_HASH), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::UNNECESSARY_FIND_MAP), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNNECESSARY_SORT_BY), - LintId::of(methods::UNNECESSARY_TO_OWNED), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::USELESS_ASREF), - LintId::of(methods::VEC_RESIZE_TO_ZERO), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), - LintId::of(multi_assignments::MULTI_ASSIGNMENTS), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(needless_bool::BOOL_COMPARISON), - LintId::of(needless_bool::NEEDLESS_BOOL), - LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), - LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(needless_update::NEEDLESS_UPDATE), - LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), - LintId::of(operators::ABSURD_EXTREME_COMPARISONS), - LintId::of(operators::ASSIGN_OP_PATTERN), - LintId::of(operators::BAD_BIT_MASK), - LintId::of(operators::CMP_NAN), - LintId::of(operators::CMP_OWNED), - LintId::of(operators::DOUBLE_COMPARISONS), - LintId::of(operators::DURATION_SUBSEC), - LintId::of(operators::EQ_OP), - LintId::of(operators::ERASING_OP), - LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(operators::IDENTITY_OP), - LintId::of(operators::INEFFECTIVE_BIT_MASK), - LintId::of(operators::MISREFACTORED_ASSIGN_OP), - LintId::of(operators::MODULO_ONE), - LintId::of(operators::OP_REF), - LintId::of(operators::PTR_EQ), - LintId::of(operators::SELF_ASSIGNMENT), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ptr::PTR_ARG), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(reference::DEREF_ADDROF), - LintId::of(regex::INVALID_REGEX), - LintId::of(returns::LET_AND_RETURN), - LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), - LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(strings::TRIM_SPLIT_WHITESPACE), - LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(swap::MANUAL_SWAP), - LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), - LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(transmute::TRANSMUTING_NULL), - LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(types::BORROWED_BOX), - LintId::of(types::BOX_COLLECTION), - LintId::of(types::REDUNDANT_ALLOCATION), - LintId::of(types::TYPE_COMPLEXITY), - LintId::of(types::VEC_BOX), - LintId::of(unicode::INVISIBLE_CHARACTERS), - LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), - LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(unwrap::PANICKING_UNWRAP), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), -]) diff --git a/clippy_lints/src/lib.register_cargo.rs b/clippy_lints/src/lib.register_cargo.rs deleted file mode 100644 index c890523fe5ae..000000000000 --- a/clippy_lints/src/lib.register_cargo.rs +++ /dev/null @@ -1,11 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ - LintId::of(cargo::CARGO_COMMON_METADATA), - LintId::of(cargo::MULTIPLE_CRATE_VERSIONS), - LintId::of(cargo::NEGATIVE_FEATURE_NAMES), - LintId::of(cargo::REDUNDANT_FEATURE_NAMES), - LintId::of(cargo::WILDCARD_DEPENDENCIES), -]) diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs deleted file mode 100644 index e3849e5a626b..000000000000 --- a/clippy_lints/src/lib.register_complexity.rs +++ /dev/null @@ -1,110 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(dereference::EXPLICIT_AUTO_DEREF), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(double_parens::DOUBLE_PARENS), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(format::USELESS_FORMAT), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::MANUAL_FIND), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(manual_clamp::MANUAL_CLAMP), - LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(matches::MANUAL_FILTER), - LintId::of(matches::MANUAL_UNWRAP_OR), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::NEEDLESS_MATCH), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::BYTES_COUNT_TO_LEN), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::GET_LAST_WITH_LEN), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::ITER_KV_MAP), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MAP_FLATTEN), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), - LintId::of(methods::NEEDLESS_OPTION_TAKE), - LintId::of(methods::NEEDLESS_SPLITN), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::OR_THEN_UNWRAP), - LintId::of(methods::RANGE_ZIP_WITH_LEN), - LintId::of(methods::REPEAT_ONCE), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::UNNECESSARY_FIND_MAP), - LintId::of(methods::UNNECESSARY_SORT_BY), - LintId::of(methods::USELESS_ASREF), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(needless_bool::BOOL_COMPARISON), - LintId::of(needless_bool::NEEDLESS_BOOL), - LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(needless_update::NEEDLESS_UPDATE), - LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), - LintId::of(operators::DOUBLE_COMPARISONS), - LintId::of(operators::DURATION_SUBSEC), - LintId::of(operators::IDENTITY_OP), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(reference::DEREF_ADDROF), - LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), - LintId::of(swap::MANUAL_SWAP), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(types::BORROWED_BOX), - LintId::of(types::TYPE_COMPLEXITY), - LintId::of(types::VEC_BOX), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), -]) diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs deleted file mode 100644 index bb94037ec2e7..000000000000 --- a/clippy_lints/src/lib.register_correctness.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(drop_forget_ref::DROP_COPY), - LintId::of(drop_forget_ref::DROP_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(matches::MATCH_STR_CASE_MISMATCH), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::UNIT_HASH), - LintId::of(methods::VEC_RESIZE_TO_ZERO), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(operators::ABSURD_EXTREME_COMPARISONS), - LintId::of(operators::BAD_BIT_MASK), - LintId::of(operators::CMP_NAN), - LintId::of(operators::EQ_OP), - LintId::of(operators::ERASING_OP), - LintId::of(operators::INEFFECTIVE_BIT_MASK), - LintId::of(operators::MODULO_ONE), - LintId::of(operators::SELF_ASSIGNMENT), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), - LintId::of(regex::INVALID_REGEX), - LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(transmute::TRANSMUTING_NULL), - LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(unicode::INVISIBLE_CHARACTERS), - LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(unwrap::PANICKING_UNWRAP), -]) diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs deleted file mode 100644 index 71dfdab369b9..000000000000 --- a/clippy_lints/src/lib.register_internal.rs +++ /dev/null @@ -1,22 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON), - LintId::of(utils::internal_lints::DEFAULT_LINT), - LintId::of(utils::internal_lints::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::INVALID_PATHS), - LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL), - LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::PRODUCE_ICE), - LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH), - LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), -]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs deleted file mode 100644 index de1253c8510a..000000000000 --- a/clippy_lints/src/lib.register_lints.rs +++ /dev/null @@ -1,618 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_lints(&[ - #[cfg(feature = "internal")] - utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal")] - utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal")] - utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_DEPRECATION_REASON, - #[cfg(feature = "internal")] - utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal")] - utils::internal_lints::IF_CHAIN_STYLE, - #[cfg(feature = "internal")] - utils::internal_lints::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal")] - utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal")] - utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal")] - utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal")] - utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal")] - utils::internal_lints::MISSING_MSRV_ATTR_IMPL, - #[cfg(feature = "internal")] - utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal")] - utils::internal_lints::PRODUCE_ICE, - #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_DEF_PATH, - #[cfg(feature = "internal")] - utils::internal_lints::UNNECESSARY_SYMBOL_STR, - almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, - approx_const::APPROX_CONSTANT, - as_conversions::AS_CONVERSIONS, - asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, - asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, - assertions_on_constants::ASSERTIONS_ON_CONSTANTS, - assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES, - async_yields_async::ASYNC_YIELDS_ASYNC, - attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON, - attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, - attrs::DEPRECATED_CFG_ATTR, - attrs::DEPRECATED_SEMVER, - attrs::EMPTY_LINE_AFTER_OUTER_ATTR, - attrs::INLINE_ALWAYS, - attrs::MISMATCHED_TARGET_OS, - attrs::USELESS_ATTRIBUTE, - await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE, - await_holding_invalid::AWAIT_HOLDING_LOCK, - await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, - blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, - bool_assert_comparison::BOOL_ASSERT_COMPARISON, - bool_to_int_with_if::BOOL_TO_INT_WITH_IF, - booleans::NONMINIMAL_BOOL, - booleans::OVERLY_COMPLEX_BOOL_EXPR, - borrow_deref_ref::BORROW_DEREF_REF, - box_default::BOX_DEFAULT, - cargo::CARGO_COMMON_METADATA, - cargo::MULTIPLE_CRATE_VERSIONS, - cargo::NEGATIVE_FEATURE_NAMES, - cargo::REDUNDANT_FEATURE_NAMES, - cargo::WILDCARD_DEPENDENCIES, - casts::AS_PTR_CAST_MUT, - casts::AS_UNDERSCORE, - casts::BORROW_AS_PTR, - casts::CAST_ABS_TO_UNSIGNED, - casts::CAST_ENUM_CONSTRUCTOR, - casts::CAST_ENUM_TRUNCATION, - casts::CAST_LOSSLESS, - casts::CAST_NAN_TO_INT, - casts::CAST_POSSIBLE_TRUNCATION, - casts::CAST_POSSIBLE_WRAP, - casts::CAST_PRECISION_LOSS, - casts::CAST_PTR_ALIGNMENT, - casts::CAST_REF_TO_MUT, - casts::CAST_SIGN_LOSS, - casts::CAST_SLICE_DIFFERENT_SIZES, - casts::CAST_SLICE_FROM_RAW_PARTS, - casts::CHAR_LIT_AS_U8, - casts::FN_TO_NUMERIC_CAST, - casts::FN_TO_NUMERIC_CAST_ANY, - casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - casts::PTR_AS_PTR, - casts::UNNECESSARY_CAST, - checked_conversions::CHECKED_CONVERSIONS, - cognitive_complexity::COGNITIVE_COMPLEXITY, - collapsible_if::COLLAPSIBLE_ELSE_IF, - collapsible_if::COLLAPSIBLE_IF, - comparison_chain::COMPARISON_CHAIN, - copies::BRANCHES_SHARING_CODE, - copies::IFS_SAME_COND, - copies::IF_SAME_THEN_ELSE, - copies::SAME_FUNCTIONS_IN_IF_CONDITION, - copy_iterator::COPY_ITERATOR, - crate_in_macro_def::CRATE_IN_MACRO_DEF, - create_dir::CREATE_DIR, - dbg_macro::DBG_MACRO, - default::DEFAULT_TRAIT_ACCESS, - default::FIELD_REASSIGN_WITH_DEFAULT, - default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY, - default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, - default_union_representation::DEFAULT_UNION_REPRESENTATION, - dereference::EXPLICIT_AUTO_DEREF, - dereference::EXPLICIT_DEREF_METHODS, - dereference::NEEDLESS_BORROW, - dereference::REF_BINDING_TO_REFERENCE, - derivable_impls::DERIVABLE_IMPLS, - derive::DERIVE_HASH_XOR_EQ, - derive::DERIVE_ORD_XOR_PARTIAL_ORD, - derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, - derive::EXPL_IMPL_CLONE_ON_COPY, - derive::UNSAFE_DERIVE_DESERIALIZE, - disallowed_macros::DISALLOWED_MACROS, - disallowed_methods::DISALLOWED_METHODS, - disallowed_names::DISALLOWED_NAMES, - disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, - disallowed_types::DISALLOWED_TYPES, - doc::DOC_LINK_WITH_QUOTES, - doc::DOC_MARKDOWN, - doc::MISSING_ERRORS_DOC, - doc::MISSING_PANICS_DOC, - doc::MISSING_SAFETY_DOC, - doc::NEEDLESS_DOCTEST_MAIN, - double_parens::DOUBLE_PARENS, - drop_forget_ref::DROP_COPY, - drop_forget_ref::DROP_NON_DROP, - drop_forget_ref::DROP_REF, - drop_forget_ref::FORGET_COPY, - drop_forget_ref::FORGET_NON_DROP, - drop_forget_ref::FORGET_REF, - drop_forget_ref::UNDROPPED_MANUALLY_DROPS, - duplicate_mod::DUPLICATE_MOD, - else_if_without_else::ELSE_IF_WITHOUT_ELSE, - empty_drop::EMPTY_DROP, - empty_enum::EMPTY_ENUM, - empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, - entry::MAP_ENTRY, - enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, - enum_variants::ENUM_VARIANT_NAMES, - enum_variants::MODULE_INCEPTION, - enum_variants::MODULE_NAME_REPETITIONS, - equatable_if_let::EQUATABLE_IF_LET, - escape::BOXED_LOCAL, - eta_reduction::REDUNDANT_CLOSURE, - eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, - excessive_bools::STRUCT_EXCESSIVE_BOOLS, - exhaustive_items::EXHAUSTIVE_ENUMS, - exhaustive_items::EXHAUSTIVE_STRUCTS, - exit::EXIT, - explicit_write::EXPLICIT_WRITE, - fallible_impl_from::FALLIBLE_IMPL_FROM, - float_literal::EXCESSIVE_PRECISION, - float_literal::LOSSY_FLOAT_LITERAL, - floating_point_arithmetic::IMPRECISE_FLOPS, - floating_point_arithmetic::SUBOPTIMAL_FLOPS, - format::USELESS_FORMAT, - format_args::FORMAT_IN_FORMAT_ARGS, - format_args::TO_STRING_IN_FORMAT_ARGS, - format_args::UNINLINED_FORMAT_ARGS, - format_impl::PRINT_IN_FORMAT_IMPL, - format_impl::RECURSIVE_FORMAT_IMPL, - format_push_string::FORMAT_PUSH_STRING, - formatting::POSSIBLE_MISSING_COMMA, - formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, - formatting::SUSPICIOUS_ELSE_FORMATTING, - formatting::SUSPICIOUS_UNARY_OP_FORMATTING, - from_over_into::FROM_OVER_INTO, - from_str_radix_10::FROM_STR_RADIX_10, - functions::DOUBLE_MUST_USE, - functions::MUST_USE_CANDIDATE, - functions::MUST_USE_UNIT, - functions::NOT_UNSAFE_PTR_ARG_DEREF, - functions::RESULT_LARGE_ERR, - functions::RESULT_UNIT_ERR, - functions::TOO_MANY_ARGUMENTS, - functions::TOO_MANY_LINES, - future_not_send::FUTURE_NOT_SEND, - if_let_mutex::IF_LET_MUTEX, - if_not_else::IF_NOT_ELSE, - if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, - implicit_hasher::IMPLICIT_HASHER, - implicit_return::IMPLICIT_RETURN, - implicit_saturating_add::IMPLICIT_SATURATING_ADD, - implicit_saturating_sub::IMPLICIT_SATURATING_SUB, - inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, - index_refutable_slice::INDEX_REFUTABLE_SLICE, - indexing_slicing::INDEXING_SLICING, - indexing_slicing::OUT_OF_BOUNDS_INDEXING, - infinite_iter::INFINITE_ITER, - infinite_iter::MAYBE_INFINITE_ITER, - inherent_impl::MULTIPLE_INHERENT_IMPL, - inherent_to_string::INHERENT_TO_STRING, - inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, - init_numbered_fields::INIT_NUMBERED_FIELDS, - inline_fn_without_body::INLINE_FN_WITHOUT_BODY, - int_plus_one::INT_PLUS_ONE, - invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, - invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED, - items_after_statements::ITEMS_AFTER_STATEMENTS, - iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, - large_const_arrays::LARGE_CONST_ARRAYS, - large_enum_variant::LARGE_ENUM_VARIANT, - large_include_file::LARGE_INCLUDE_FILE, - large_stack_arrays::LARGE_STACK_ARRAYS, - len_zero::COMPARISON_TO_EMPTY, - len_zero::LEN_WITHOUT_IS_EMPTY, - len_zero::LEN_ZERO, - let_if_seq::USELESS_LET_IF_SEQ, - let_underscore::LET_UNDERSCORE_DROP, - let_underscore::LET_UNDERSCORE_LOCK, - let_underscore::LET_UNDERSCORE_MUST_USE, - lifetimes::EXTRA_UNUSED_LIFETIMES, - lifetimes::NEEDLESS_LIFETIMES, - literal_representation::DECIMAL_LITERAL_REPRESENTATION, - literal_representation::INCONSISTENT_DIGIT_GROUPING, - literal_representation::LARGE_DIGIT_GROUPS, - literal_representation::MISTYPED_LITERAL_SUFFIXES, - literal_representation::UNREADABLE_LITERAL, - literal_representation::UNUSUAL_BYTE_GROUPINGS, - loops::EMPTY_LOOP, - loops::EXPLICIT_COUNTER_LOOP, - loops::EXPLICIT_INTO_ITER_LOOP, - loops::EXPLICIT_ITER_LOOP, - loops::FOR_KV_MAP, - loops::FOR_LOOPS_OVER_FALLIBLES, - loops::ITER_NEXT_LOOP, - loops::MANUAL_FIND, - loops::MANUAL_FLATTEN, - loops::MANUAL_MEMCPY, - loops::MISSING_SPIN_LOOP, - loops::MUT_RANGE_BOUND, - loops::NEEDLESS_COLLECT, - loops::NEEDLESS_RANGE_LOOP, - loops::NEVER_LOOP, - loops::SAME_ITEM_PUSH, - loops::SINGLE_ELEMENT_LOOP, - loops::WHILE_IMMUTABLE_CONDITION, - loops::WHILE_LET_LOOP, - loops::WHILE_LET_ON_ITERATOR, - macro_use::MACRO_USE_IMPORTS, - main_recursion::MAIN_RECURSION, - manual_assert::MANUAL_ASSERT, - manual_async_fn::MANUAL_ASYNC_FN, - manual_bits::MANUAL_BITS, - manual_clamp::MANUAL_CLAMP, - manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, - manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, - manual_rem_euclid::MANUAL_REM_EUCLID, - manual_retain::MANUAL_RETAIN, - manual_string_new::MANUAL_STRING_NEW, - manual_strip::MANUAL_STRIP, - map_unit_fn::OPTION_MAP_UNIT_FN, - map_unit_fn::RESULT_MAP_UNIT_FN, - match_result_ok::MATCH_RESULT_OK, - matches::COLLAPSIBLE_MATCH, - matches::INFALLIBLE_DESTRUCTURING_MATCH, - matches::MANUAL_FILTER, - matches::MANUAL_MAP, - matches::MANUAL_UNWRAP_OR, - matches::MATCH_AS_REF, - matches::MATCH_BOOL, - matches::MATCH_LIKE_MATCHES_MACRO, - matches::MATCH_ON_VEC_ITEMS, - matches::MATCH_OVERLAPPING_ARM, - matches::MATCH_REF_PATS, - matches::MATCH_SAME_ARMS, - matches::MATCH_SINGLE_BINDING, - matches::MATCH_STR_CASE_MISMATCH, - matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, - matches::MATCH_WILD_ERR_ARM, - matches::NEEDLESS_MATCH, - matches::REDUNDANT_PATTERN_MATCHING, - matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, - matches::SIGNIFICANT_DROP_IN_SCRUTINEE, - matches::SINGLE_MATCH, - matches::SINGLE_MATCH_ELSE, - matches::TRY_ERR, - matches::WILDCARD_ENUM_MATCH_ARM, - matches::WILDCARD_IN_OR_PATTERNS, - mem_forget::MEM_FORGET, - mem_replace::MEM_REPLACE_OPTION_WITH_NONE, - mem_replace::MEM_REPLACE_WITH_DEFAULT, - mem_replace::MEM_REPLACE_WITH_UNINIT, - methods::BIND_INSTEAD_OF_MAP, - methods::BYTES_COUNT_TO_LEN, - methods::BYTES_NTH, - methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - methods::CHARS_LAST_CMP, - methods::CHARS_NEXT_CMP, - methods::CLONED_INSTEAD_OF_COPIED, - methods::CLONE_DOUBLE_REF, - methods::CLONE_ON_COPY, - methods::CLONE_ON_REF_PTR, - methods::COLLAPSIBLE_STR_REPLACE, - methods::ERR_EXPECT, - methods::EXPECT_FUN_CALL, - methods::EXPECT_USED, - methods::EXTEND_WITH_DRAIN, - methods::FILETYPE_IS_FILE, - methods::FILTER_MAP_IDENTITY, - methods::FILTER_MAP_NEXT, - methods::FILTER_NEXT, - methods::FLAT_MAP_IDENTITY, - methods::FLAT_MAP_OPTION, - methods::FROM_ITER_INSTEAD_OF_COLLECT, - methods::GET_FIRST, - methods::GET_LAST_WITH_LEN, - methods::GET_UNWRAP, - methods::IMPLICIT_CLONE, - methods::INEFFICIENT_TO_STRING, - methods::INSPECT_FOR_EACH, - methods::INTO_ITER_ON_REF, - methods::IS_DIGIT_ASCII_RADIX, - methods::ITERATOR_STEP_BY_ZERO, - methods::ITER_CLONED_COLLECT, - methods::ITER_COUNT, - methods::ITER_KV_MAP, - methods::ITER_NEXT_SLICE, - methods::ITER_NTH, - methods::ITER_NTH_ZERO, - methods::ITER_ON_EMPTY_COLLECTIONS, - methods::ITER_ON_SINGLE_ITEMS, - methods::ITER_OVEREAGER_CLONED, - methods::ITER_SKIP_NEXT, - methods::ITER_WITH_DRAIN, - methods::MANUAL_FILTER_MAP, - methods::MANUAL_FIND_MAP, - methods::MANUAL_OK_OR, - methods::MANUAL_SATURATING_ARITHMETIC, - methods::MANUAL_SPLIT_ONCE, - methods::MANUAL_STR_REPEAT, - methods::MAP_CLONE, - methods::MAP_COLLECT_RESULT_UNIT, - methods::MAP_ERR_IGNORE, - methods::MAP_FLATTEN, - methods::MAP_IDENTITY, - methods::MAP_UNWRAP_OR, - methods::MUT_MUTEX_LOCK, - methods::NAIVE_BYTECOUNT, - methods::NEEDLESS_OPTION_AS_DEREF, - methods::NEEDLESS_OPTION_TAKE, - methods::NEEDLESS_SPLITN, - methods::NEW_RET_NO_SELF, - methods::NONSENSICAL_OPEN_OPTIONS, - methods::NO_EFFECT_REPLACE, - methods::OBFUSCATED_IF_ELSE, - methods::OK_EXPECT, - methods::OPTION_AS_REF_DEREF, - methods::OPTION_FILTER_MAP, - methods::OPTION_MAP_OR_NONE, - methods::OR_FUN_CALL, - methods::OR_THEN_UNWRAP, - methods::PATH_BUF_PUSH_OVERWRITE, - methods::RANGE_ZIP_WITH_LEN, - methods::REPEAT_ONCE, - methods::RESULT_MAP_OR_INTO_OPTION, - methods::SEARCH_IS_SOME, - methods::SHOULD_IMPLEMENT_TRAIT, - methods::SINGLE_CHAR_ADD_STR, - methods::SINGLE_CHAR_PATTERN, - methods::SKIP_WHILE_NEXT, - methods::STABLE_SORT_PRIMITIVE, - methods::STRING_EXTEND_CHARS, - methods::SUSPICIOUS_MAP, - methods::SUSPICIOUS_SPLITN, - methods::SUSPICIOUS_TO_OWNED, - methods::UNINIT_ASSUMED_INIT, - methods::UNIT_HASH, - methods::UNNECESSARY_FILTER_MAP, - methods::UNNECESSARY_FIND_MAP, - methods::UNNECESSARY_FOLD, - methods::UNNECESSARY_JOIN, - methods::UNNECESSARY_LAZY_EVALUATIONS, - methods::UNNECESSARY_SORT_BY, - methods::UNNECESSARY_TO_OWNED, - methods::UNWRAP_OR_ELSE_DEFAULT, - methods::UNWRAP_USED, - methods::USELESS_ASREF, - methods::VEC_RESIZE_TO_ZERO, - methods::VERBOSE_FILE_READS, - methods::WRONG_SELF_CONVENTION, - methods::ZST_OFFSET, - minmax::MIN_MAX, - misc::SHORT_CIRCUIT_STATEMENT, - misc::TOPLEVEL_REF_ARG, - misc::USED_UNDERSCORE_BINDING, - misc::ZERO_PTR, - misc_early::BUILTIN_TYPE_SHADOW, - misc_early::DOUBLE_NEG, - misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, - misc_early::MIXED_CASE_HEX_LITERALS, - misc_early::REDUNDANT_PATTERN, - misc_early::SEPARATED_LITERAL_SUFFIX, - misc_early::UNNEEDED_FIELD_PATTERN, - misc_early::UNNEEDED_WILDCARD_PATTERN, - misc_early::UNSEPARATED_LITERAL_SUFFIX, - misc_early::ZERO_PREFIXED_LITERAL, - mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER, - missing_const_for_fn::MISSING_CONST_FOR_FN, - missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, - missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, - missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, - mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, - mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, - module_style::MOD_MODULE_FILES, - module_style::SELF_NAMED_MODULE_FILES, - multi_assignments::MULTI_ASSIGNMENTS, - mut_key::MUTABLE_KEY_TYPE, - mut_mut::MUT_MUT, - mut_reference::UNNECESSARY_MUT_PASSED, - mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, - mutex_atomic::MUTEX_ATOMIC, - mutex_atomic::MUTEX_INTEGER, - needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, - needless_bool::BOOL_COMPARISON, - needless_bool::NEEDLESS_BOOL, - needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - needless_continue::NEEDLESS_CONTINUE, - needless_for_each::NEEDLESS_FOR_EACH, - needless_late_init::NEEDLESS_LATE_INIT, - needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS, - needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - needless_question_mark::NEEDLESS_QUESTION_MARK, - needless_update::NEEDLESS_UPDATE, - neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, - neg_multiply::NEG_MULTIPLY, - new_without_default::NEW_WITHOUT_DEFAULT, - no_effect::NO_EFFECT, - no_effect::NO_EFFECT_UNDERSCORE_BINDING, - no_effect::UNNECESSARY_OPERATION, - non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, - non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, - non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, - non_expressive_names::MANY_SINGLE_CHAR_NAMES, - non_expressive_names::SIMILAR_NAMES, - non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, - non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, - nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, - octal_escapes::OCTAL_ESCAPES, - only_used_in_recursion::ONLY_USED_IN_RECURSION, - operators::ABSURD_EXTREME_COMPARISONS, - operators::ARITHMETIC_SIDE_EFFECTS, - operators::ASSIGN_OP_PATTERN, - operators::BAD_BIT_MASK, - operators::CMP_NAN, - operators::CMP_OWNED, - operators::DOUBLE_COMPARISONS, - operators::DURATION_SUBSEC, - operators::EQ_OP, - operators::ERASING_OP, - operators::FLOAT_ARITHMETIC, - operators::FLOAT_CMP, - operators::FLOAT_CMP_CONST, - operators::FLOAT_EQUALITY_WITHOUT_ABS, - operators::IDENTITY_OP, - operators::INEFFECTIVE_BIT_MASK, - operators::INTEGER_ARITHMETIC, - operators::INTEGER_DIVISION, - operators::MISREFACTORED_ASSIGN_OP, - operators::MODULO_ARITHMETIC, - operators::MODULO_ONE, - operators::NEEDLESS_BITWISE_BOOL, - operators::OP_REF, - operators::PTR_EQ, - operators::SELF_ASSIGNMENT, - operators::VERBOSE_BIT_MASK, - option_env_unwrap::OPTION_ENV_UNWRAP, - option_if_let_else::OPTION_IF_LET_ELSE, - overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - panic_in_result_fn::PANIC_IN_RESULT_FN, - panic_unimplemented::PANIC, - panic_unimplemented::TODO, - panic_unimplemented::UNIMPLEMENTED, - panic_unimplemented::UNREACHABLE, - partialeq_ne_impl::PARTIALEQ_NE_IMPL, - partialeq_to_none::PARTIALEQ_TO_NONE, - pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, - pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, - pattern_type_mismatch::PATTERN_TYPE_MISMATCH, - precedence::PRECEDENCE, - ptr::CMP_NULL, - ptr::INVALID_NULL_PTR_USAGE, - ptr::MUT_FROM_REF, - ptr::PTR_ARG, - ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, - pub_use::PUB_USE, - question_mark::QUESTION_MARK, - ranges::MANUAL_RANGE_CONTAINS, - ranges::RANGE_MINUS_ONE, - ranges::RANGE_PLUS_ONE, - ranges::REVERSED_EMPTY_RANGES, - rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, - read_zero_byte_vec::READ_ZERO_BYTE_VEC, - redundant_clone::REDUNDANT_CLONE, - redundant_closure_call::REDUNDANT_CLOSURE_CALL, - redundant_else::REDUNDANT_ELSE, - redundant_field_names::REDUNDANT_FIELD_NAMES, - redundant_pub_crate::REDUNDANT_PUB_CRATE, - redundant_slicing::DEREF_BY_SLICING, - redundant_slicing::REDUNDANT_SLICING, - redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, - ref_option_ref::REF_OPTION_REF, - reference::DEREF_ADDROF, - regex::INVALID_REGEX, - regex::TRIVIAL_REGEX, - return_self_not_must_use::RETURN_SELF_NOT_MUST_USE, - returns::LET_AND_RETURN, - returns::NEEDLESS_RETURN, - same_name_method::SAME_NAME_METHOD, - self_named_constructors::SELF_NAMED_CONSTRUCTORS, - semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, - serde_api::SERDE_API_MISUSE, - shadow::SHADOW_REUSE, - shadow::SHADOW_SAME, - shadow::SHADOW_UNRELATED, - single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, - single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, - size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, - slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - std_instead_of_core::ALLOC_INSTEAD_OF_CORE, - std_instead_of_core::STD_INSTEAD_OF_ALLOC, - std_instead_of_core::STD_INSTEAD_OF_CORE, - strings::STRING_ADD, - strings::STRING_ADD_ASSIGN, - strings::STRING_FROM_UTF8_AS_BYTES, - strings::STRING_LIT_AS_BYTES, - strings::STRING_SLICE, - strings::STRING_TO_STRING, - strings::STR_TO_STRING, - strings::TRIM_SPLIT_WHITESPACE, - strlen_on_c_strings::STRLEN_ON_C_STRINGS, - suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, - suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, - suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, - swap::ALMOST_SWAPPED, - swap::MANUAL_SWAP, - swap_ptr_to_ref::SWAP_PTR_TO_REF, - tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, - temporary_assignment::TEMPORARY_ASSIGNMENT, - to_digit_is_some::TO_DIGIT_IS_SOME, - trailing_empty_array::TRAILING_EMPTY_ARRAY, - trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, - trait_bounds::TYPE_REPETITION_IN_BOUNDS, - transmute::CROSSPOINTER_TRANSMUTE, - transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - transmute::TRANSMUTE_BYTES_TO_STR, - transmute::TRANSMUTE_FLOAT_TO_INT, - transmute::TRANSMUTE_INT_TO_BOOL, - transmute::TRANSMUTE_INT_TO_CHAR, - transmute::TRANSMUTE_INT_TO_FLOAT, - transmute::TRANSMUTE_NUM_TO_BYTES, - transmute::TRANSMUTE_PTR_TO_PTR, - transmute::TRANSMUTE_PTR_TO_REF, - transmute::TRANSMUTE_UNDEFINED_REPR, - transmute::TRANSMUTING_NULL, - transmute::UNSOUND_COLLECTION_TRANSMUTE, - transmute::USELESS_TRANSMUTE, - transmute::WRONG_TRANSMUTE, - types::BORROWED_BOX, - types::BOX_COLLECTION, - types::LINKEDLIST, - types::OPTION_OPTION, - types::RC_BUFFER, - types::RC_MUTEX, - types::REDUNDANT_ALLOCATION, - types::TYPE_COMPLEXITY, - types::VEC_BOX, - undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS, - unicode::INVISIBLE_CHARACTERS, - unicode::NON_ASCII_LITERAL, - unicode::UNICODE_NOT_NFC, - uninit_vec::UNINIT_VEC, - unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, - unit_types::LET_UNIT_VALUE, - unit_types::UNIT_ARG, - unit_types::UNIT_CMP, - unnamed_address::FN_ADDRESS_COMPARISONS, - unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, - unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, - unnecessary_wraps::UNNECESSARY_WRAPS, - unnested_or_patterns::UNNESTED_OR_PATTERNS, - unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - unused_async::UNUSED_ASYNC, - unused_io_amount::UNUSED_IO_AMOUNT, - unused_peekable::UNUSED_PEEKABLE, - unused_rounding::UNUSED_ROUNDING, - unused_self::UNUSED_SELF, - unused_unit::UNUSED_UNIT, - unwrap::PANICKING_UNWRAP, - unwrap::UNNECESSARY_UNWRAP, - unwrap_in_result::UNWRAP_IN_RESULT, - upper_case_acronyms::UPPER_CASE_ACRONYMS, - use_self::USE_SELF, - useless_conversion::USELESS_CONVERSION, - vec::USELESS_VEC, - vec_init_then_push::VEC_INIT_THEN_PUSH, - wildcard_imports::ENUM_GLOB_USE, - wildcard_imports::WILDCARD_IMPORTS, - write::PRINTLN_EMPTY_STRING, - write::PRINT_LITERAL, - write::PRINT_STDERR, - write::PRINT_STDOUT, - write::PRINT_WITH_NEWLINE, - write::USE_DEBUG, - write::WRITELN_EMPTY_STRING, - write::WRITE_LITERAL, - write::WRITE_WITH_NEWLINE, - zero_div_zero::ZERO_DIVIDED_BY_ZERO, - zero_sized_map_values::ZERO_SIZED_MAP_VALUES, -]) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs deleted file mode 100644 index a75bc81b2222..000000000000 --- a/clippy_lints/src/lib.register_nursery.rs +++ /dev/null @@ -1,41 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ - LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), - LintId::of(casts::AS_PTR_CAST_MUT), - LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), - LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), - LintId::of(equatable_if_let::EQUATABLE_IF_LET), - LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), - LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), - LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), - LintId::of(future_not_send::FUTURE_NOT_SEND), - LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE), - LintId::of(let_if_seq::USELESS_LET_IF_SEQ), - LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), - LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS), - LintId::of(methods::ITER_ON_SINGLE_ITEMS), - LintId::of(methods::ITER_WITH_DRAIN), - LintId::of(methods::PATH_BUF_PUSH_OVERWRITE), - LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), - LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), - LintId::of(mutex_atomic::MUTEX_ATOMIC), - LintId::of(mutex_atomic::MUTEX_INTEGER), - LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), - LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), - LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), - LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), - LintId::of(regex::TRIVIAL_REGEX), - LintId::of(strings::STRING_LIT_AS_BYTES), - LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), - LintId::of(unused_peekable::UNUSED_PEEKABLE), - LintId::of(unused_rounding::UNUSED_ROUNDING), - LintId::of(use_self::USE_SELF), -]) diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs deleted file mode 100644 index 2ead71a68fab..000000000000 --- a/clippy_lints/src/lib.register_pedantic.rs +++ /dev/null @@ -1,101 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ - LintId::of(attrs::INLINE_ALWAYS), - LintId::of(casts::BORROW_AS_PTR), - LintId::of(casts::CAST_LOSSLESS), - LintId::of(casts::CAST_POSSIBLE_TRUNCATION), - LintId::of(casts::CAST_POSSIBLE_WRAP), - LintId::of(casts::CAST_PRECISION_LOSS), - LintId::of(casts::CAST_PTR_ALIGNMENT), - LintId::of(casts::CAST_SIGN_LOSS), - LintId::of(casts::PTR_AS_PTR), - LintId::of(checked_conversions::CHECKED_CONVERSIONS), - LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(copy_iterator::COPY_ITERATOR), - LintId::of(default::DEFAULT_TRAIT_ACCESS), - LintId::of(dereference::EXPLICIT_DEREF_METHODS), - LintId::of(dereference::REF_BINDING_TO_REFERENCE), - LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), - LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), - LintId::of(doc::DOC_LINK_WITH_QUOTES), - LintId::of(doc::DOC_MARKDOWN), - LintId::of(doc::MISSING_ERRORS_DOC), - LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(empty_enum::EMPTY_ENUM), - LintId::of(enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), - LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), - LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(functions::MUST_USE_CANDIDATE), - LintId::of(functions::TOO_MANY_LINES), - LintId::of(if_not_else::IF_NOT_ELSE), - LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), - LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), - LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR), - LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(let_underscore::LET_UNDERSCORE_DROP), - LintId::of(literal_representation::LARGE_DIGIT_GROUPS), - LintId::of(literal_representation::UNREADABLE_LITERAL), - LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), - LintId::of(loops::EXPLICIT_ITER_LOOP), - LintId::of(macro_use::MACRO_USE_IMPORTS), - LintId::of(manual_assert::MANUAL_ASSERT), - LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED), - LintId::of(manual_string_new::MANUAL_STRING_NEW), - LintId::of(matches::MATCH_BOOL), - LintId::of(matches::MATCH_ON_VEC_ITEMS), - LintId::of(matches::MATCH_SAME_ARMS), - LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), - LintId::of(matches::MATCH_WILD_ERR_ARM), - LintId::of(matches::SINGLE_MATCH_ELSE), - LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), - LintId::of(methods::CLONED_INSTEAD_OF_COPIED), - LintId::of(methods::FILTER_MAP_NEXT), - LintId::of(methods::FLAT_MAP_OPTION), - LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(methods::IMPLICIT_CLONE), - LintId::of(methods::INEFFICIENT_TO_STRING), - LintId::of(methods::MANUAL_OK_OR), - LintId::of(methods::MAP_UNWRAP_OR), - LintId::of(methods::NAIVE_BYTECOUNT), - LintId::of(methods::STABLE_SORT_PRIMITIVE), - LintId::of(methods::UNNECESSARY_JOIN), - LintId::of(misc::USED_UNDERSCORE_BINDING), - LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), - LintId::of(mut_mut::MUT_MUT), - LintId::of(needless_continue::NEEDLESS_CONTINUE), - LintId::of(needless_for_each::NEEDLESS_FOR_EACH), - LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), - LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING), - LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(non_expressive_names::SIMILAR_NAMES), - LintId::of(operators::FLOAT_CMP), - LintId::of(operators::NEEDLESS_BITWISE_BOOL), - LintId::of(operators::VERBOSE_BIT_MASK), - LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), - LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), - LintId::of(ranges::RANGE_MINUS_ONE), - LintId::of(ranges::RANGE_PLUS_ONE), - LintId::of(redundant_else::REDUNDANT_ELSE), - LintId::of(ref_option_ref::REF_OPTION_REF), - LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), - LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(types::LINKEDLIST), - LintId::of(types::OPTION_OPTION), - LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), - LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), - LintId::of(unused_async::UNUSED_ASYNC), - LintId::of(unused_self::UNUSED_SELF), - LintId::of(wildcard_imports::ENUM_GLOB_USE), - LintId::of(wildcard_imports::WILDCARD_IMPORTS), - LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), -]) diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs deleted file mode 100644 index 8e927470e02f..000000000000 --- a/clippy_lints/src/lib.register_perf.rs +++ /dev/null @@ -1,34 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ - LintId::of(box_default::BOX_DEFAULT), - LintId::of(entry::MAP_ENTRY), - LintId::of(escape::BOXED_LOCAL), - LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), - LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), - LintId::of(functions::RESULT_LARGE_ERR), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::MISSING_SPIN_LOOP), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(manual_retain::MANUAL_RETAIN), - LintId::of(methods::COLLAPSIBLE_STR_REPLACE), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::ITER_NTH), - LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(methods::UNNECESSARY_TO_OWNED), - LintId::of(operators::CMP_OWNED), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(types::BOX_COLLECTION), - LintId::of(types::REDUNDANT_ALLOCATION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), -]) diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs deleted file mode 100644 index 6eb9b3d3b9b7..000000000000 --- a/clippy_lints/src/lib.register_restriction.rs +++ /dev/null @@ -1,88 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - LintId::of(as_conversions::AS_CONVERSIONS), - LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), - LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), - LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES), - LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), - LintId::of(casts::AS_UNDERSCORE), - LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), - LintId::of(create_dir::CREATE_DIR), - LintId::of(dbg_macro::DBG_MACRO), - LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), - LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), - LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), - LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(empty_drop::EMPTY_DROP), - LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), - LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), - LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), - LintId::of(exit::EXIT), - LintId::of(float_literal::LOSSY_FLOAT_LITERAL), - LintId::of(format_push_string::FORMAT_PUSH_STRING), - LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), - LintId::of(implicit_return::IMPLICIT_RETURN), - LintId::of(indexing_slicing::INDEXING_SLICING), - LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), - LintId::of(large_include_file::LARGE_INCLUDE_FILE), - LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), - LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), - LintId::of(matches::TRY_ERR), - LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), - LintId::of(mem_forget::MEM_FORGET), - LintId::of(methods::CLONE_ON_REF_PTR), - LintId::of(methods::EXPECT_USED), - LintId::of(methods::FILETYPE_IS_FILE), - LintId::of(methods::GET_UNWRAP), - LintId::of(methods::MAP_ERR_IGNORE), - LintId::of(methods::UNWRAP_USED), - LintId::of(methods::VERBOSE_FILE_READS), - LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX), - LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), - LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), - LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), - LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), - LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), - LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), - LintId::of(module_style::MOD_MODULE_FILES), - LintId::of(module_style::SELF_NAMED_MODULE_FILES), - LintId::of(operators::ARITHMETIC_SIDE_EFFECTS), - LintId::of(operators::FLOAT_ARITHMETIC), - LintId::of(operators::FLOAT_CMP_CONST), - LintId::of(operators::INTEGER_ARITHMETIC), - LintId::of(operators::INTEGER_DIVISION), - LintId::of(operators::MODULO_ARITHMETIC), - LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), - LintId::of(panic_unimplemented::PANIC), - LintId::of(panic_unimplemented::TODO), - LintId::of(panic_unimplemented::UNIMPLEMENTED), - LintId::of(panic_unimplemented::UNREACHABLE), - LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), - LintId::of(pub_use::PUB_USE), - LintId::of(redundant_slicing::DEREF_BY_SLICING), - LintId::of(same_name_method::SAME_NAME_METHOD), - LintId::of(shadow::SHADOW_REUSE), - LintId::of(shadow::SHADOW_SAME), - LintId::of(shadow::SHADOW_UNRELATED), - LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES), - LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE), - LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC), - LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE), - LintId::of(strings::STRING_ADD), - LintId::of(strings::STRING_SLICE), - LintId::of(strings::STRING_TO_STRING), - LintId::of(strings::STR_TO_STRING), - LintId::of(types::RC_BUFFER), - LintId::of(types::RC_MUTEX), - LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), - LintId::of(unicode::NON_ASCII_LITERAL), - LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), - LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(write::PRINT_STDERR), - LintId::of(write::PRINT_STDOUT), - LintId::of(write::USE_DEBUG), -]) diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs deleted file mode 100644 index 6894d69e928a..000000000000 --- a/clippy_lints/src/lib.register_style.rs +++ /dev/null @@ -1,132 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::style", Some("clippy_style"), vec![ - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), - LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(disallowed_macros::DISALLOWED_MACROS), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_names::DISALLOWED_NAMES), - LintId::of(disallowed_types::DISALLOWED_TYPES), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::COLLAPSIBLE_MATCH), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MANUAL_MAP), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::ERR_EXPECT), - LintId::of(methods::GET_FIRST), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::IS_DIGIT_ASCII_RADIX), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MAP_CLONE), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::MUT_MUTEX_LOCK), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::OBFUSCATED_IF_ELSE), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(operators::ASSIGN_OP_PATTERN), - LintId::of(operators::OP_REF), - LintId::of(operators::PTR_EQ), - LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::PTR_ARG), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(returns::LET_AND_RETURN), - LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), - LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(strings::TRIM_SPLIT_WHITESPACE), - LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), - LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), -]) diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs deleted file mode 100644 index ef5cc9ad7f98..000000000000 --- a/clippy_lints/src/lib.register_suspicious.rs +++ /dev/null @@ -1,39 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ - LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), - LintId::of(casts::CAST_ABS_TO_UNSIGNED), - LintId::of(casts::CAST_ENUM_CONSTRUCTOR), - LintId::of(casts::CAST_ENUM_TRUNCATION), - LintId::of(casts::CAST_NAN_TO_INT), - LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), - LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), - LintId::of(drop_forget_ref::DROP_NON_DROP), - LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(duplicate_mod::DUPLICATE_MOD), - LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(methods::NO_EFFECT_REPLACE), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(methods::SUSPICIOUS_TO_OWNED), - LintId::of(multi_assignments::MULTI_ASSIGNMENTS), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(operators::MISREFACTORED_ASSIGN_OP), - LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), -]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ebb0f14fef52..2f5c4adca9f1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -26,13 +26,13 @@ extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; extern crate rustc_hir_pretty; +extern crate rustc_hir_typeck; extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; @@ -46,126 +46,27 @@ extern crate rustc_trait_selection; #[macro_use] extern crate clippy_utils; +#[macro_use] +extern crate declare_clippy_lint; + +use std::io; +use std::path::PathBuf; -use clippy_utils::parse_msrv; +use clippy_utils::msrvs::Msrv; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::LintId; -use rustc_semver::RustcVersion; +use rustc_lint::{Lint, LintId}; use rustc_session::Session; -/// Macro used to declare a Clippy lint. -/// -/// Every lint declaration consists of 4 parts: -/// -/// 1. The documentation, which is used for the website -/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. -/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or -/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. -/// 4. The `description` that contains a short explanation on what's wrong with code where the -/// lint is triggered. -/// -/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are -/// enabled by default. As said in the README.md of this repository, if the lint level mapping -/// changes, please update README.md. -/// -/// # Example -/// -/// ``` -/// #![feature(rustc_private)] -/// extern crate rustc_session; -/// use rustc_session::declare_tool_lint; -/// use clippy_lints::declare_clippy_lint; -/// -/// declare_clippy_lint! { -/// /// ### What it does -/// /// Checks for ... (describe what the lint matches). -/// /// -/// /// ### Why is this bad? -/// /// Supply the reason for linting the code. -/// /// -/// /// ### Example -/// /// ```rust -/// /// Insert a short example of code that triggers the lint -/// /// ``` -/// /// -/// /// Use instead: -/// /// ```rust -/// /// Insert a short example of improved code that doesn't trigger the lint -/// /// ``` -/// pub LINT_NAME, -/// pedantic, -/// "description" -/// } -/// ``` -/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -#[macro_export] -macro_rules! declare_clippy_lint { - { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; -} - #[cfg(feature = "internal")] pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` -mod almost_complete_letter_range; +mod almost_complete_range; mod approx_const; mod as_conversions; mod asm_syntax; @@ -221,15 +122,18 @@ mod excessive_bools; mod exhaustive_items; mod exit; mod explicit_write; +mod extra_unused_type_parameters; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; +mod fn_null_check; mod format; mod format_args; mod format_impl; mod format_push_string; mod formatting; mod from_over_into; +mod from_raw_with_void_ptr; mod from_str_radix_10; mod functions; mod future_not_send; @@ -248,6 +152,7 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; +mod instant_subtraction; mod int_plus_one; mod invalid_upcast_comparisons; mod invalid_utf8_in_unchecked; @@ -269,7 +174,8 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; -mod manual_instant_elapsed; +mod manual_is_ascii_check; +mod manual_let_else; mod manual_non_exhaustive; mod manual_rem_euclid; mod manual_retain; @@ -289,9 +195,11 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod missing_trait_methods; mod mixed_read_write_in_expression; mod module_style; mod multi_assignments; +mod multiple_unsafe_ops_per_block; mod mut_key; mod mut_mut; mod mut_reference; @@ -324,10 +232,12 @@ mod option_if_let_else; mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; +mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; mod pattern_type_mismatch; +mod permissions_set_readonly_false; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -350,18 +260,21 @@ mod return_self_not_must_use; mod returns; mod same_name_method; mod self_named_constructors; +mod semicolon_block; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; +mod size_of_ref; mod slow_vector_initialization; mod std_instead_of_core; mod strings; mod strlen_on_c_strings; mod suspicious_operation_groupings; mod suspicious_trait_impl; +mod suspicious_xor_used_as_pow; mod swap; mod swap_ptr_to_ref; mod tabs_in_doc_comments; @@ -401,8 +314,8 @@ mod zero_div_zero; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` -pub use crate::utils::conf::Conf; use crate::utils::conf::{format_error, TryConf}; +pub use crate::utils::conf::{lookup_conf_file, Conf}; /// Register all pre expansion lints /// @@ -414,63 +327,25 @@ use crate::utils::conf::{format_error, TryConf}; /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); - let msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv })); -} - -fn read_msrv(conf: &Conf, sess: &Session) -> Option { - let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") - .ok() - .and_then(|v| parse_msrv(&v, None, None)); - let clippy_msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - if let Some(cargo_msrv) = cargo_msrv { - if let Some(clippy_msrv) = clippy_msrv { - // if both files have an msrv, let's compare them and emit a warning if they differ - if clippy_msrv != cargo_msrv { - sess.warn(format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" - )); - } - - Some(clippy_msrv) - } else { - Some(cargo_msrv) - } - } else { - clippy_msrv - } + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() })); } #[doc(hidden)] -pub fn read_conf(sess: &Session) -> Conf { - let file_name = match utils::conf::lookup_conf_file() { +pub fn read_conf(sess: &Session, path: &io::Result>) -> Conf { + let file_name = match path { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { - sess.struct_err(&format!("error finding Clippy's configuration file: {error}")) + sess.struct_err(format!("error finding Clippy's configuration file: {error}")) .emit(); return Conf::default(); }, }; - let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); + let TryConf { conf, errors, warnings } = utils::conf::read(file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { sess.err(format!( @@ -481,7 +356,7 @@ pub fn read_conf(sess: &Session) -> Conf { } for warning in warnings { - sess.struct_warn(&format!( + sess.struct_warn(format!( "error reading Clippy's configuration file `{}`: {}", file_name.display(), format_error(warning) @@ -492,31 +367,121 @@ pub fn read_conf(sess: &Session) -> Conf { conf } +#[derive(Default)] +struct RegistrationGroups { + all: Vec, + cargo: Vec, + complexity: Vec, + correctness: Vec, + nursery: Vec, + pedantic: Vec, + perf: Vec, + restriction: Vec, + style: Vec, + suspicious: Vec, + #[cfg(feature = "internal")] + internal: Vec, +} + +impl RegistrationGroups { + #[rustfmt::skip] + fn register(self, store: &mut rustc_lint::LintStore) { + store.register_group(true, "clippy::all", Some("clippy_all"), self.all); + store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo); + store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity); + store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness); + store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery); + store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic); + store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction); + store.register_group(true, "clippy::style", Some("clippy_style"), self.style); + store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); + #[cfg(feature = "internal")] + store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal); + } +} + +#[derive(Copy, Clone)] +pub(crate) enum LintCategory { + Cargo, + Complexity, + Correctness, + Nursery, + Pedantic, + Perf, + Restriction, + Style, + Suspicious, + #[cfg(feature = "internal")] + Internal, +} +#[allow(clippy::enum_glob_use)] +use LintCategory::*; + +impl LintCategory { + fn is_all(self) -> bool { + matches!(self, Correctness | Suspicious | Style | Complexity | Perf) + } + + fn group(self, groups: &mut RegistrationGroups) -> &mut Vec { + match self { + Cargo => &mut groups.cargo, + Complexity => &mut groups.complexity, + Correctness => &mut groups.correctness, + Nursery => &mut groups.nursery, + Pedantic => &mut groups.pedantic, + Perf => &mut groups.perf, + Restriction => &mut groups.restriction, + Style => &mut groups.style, + Suspicious => &mut groups.suspicious, + #[cfg(feature = "internal")] + Internal => &mut groups.internal, + } + } +} + +pub(crate) struct LintInfo { + /// Double reference to maintain pointer equality + lint: &'static &'static Lint, + category: LintCategory, + explanation: &'static str, +} + +pub fn explain(name: &str) { + let target = format!("clippy::{}", name.to_ascii_uppercase()); + match declared_lints::LINTS.iter().find(|info| info.lint.name == target) { + Some(info) => print!("{}", info.explanation), + None => println!("unknown lint: {name}"), + } +} + +fn register_categories(store: &mut rustc_lint::LintStore) { + let mut groups = RegistrationGroups::default(); + + for LintInfo { lint, category, .. } in declared_lints::LINTS { + if category.is_all() { + groups.all.push(LintId::of(lint)); + } + + category.group(&mut groups).push(LintId::of(lint)); + } + + let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect(); + + store.register_lints(&lints); + groups.register(store); +} + /// Register all lints and lint groups with the rustc plugin registry /// /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { register_removed_non_tool_lints(store); + register_categories(store); include!("lib.deprecated.rs"); - include!("lib.register_lints.rs"); - include!("lib.register_restriction.rs"); - include!("lib.register_pedantic.rs"); - - #[cfg(feature = "internal")] - include!("lib.register_internal.rs"); - - include!("lib.register_all.rs"); - include!("lib.register_style.rs"); - include!("lib.register_complexity.rs"); - include!("lib.register_correctness.rs"); - include!("lib.register_suspicious.rs"); - include!("lib.register_perf.rs"); - include!("lib.register_cargo.rs"); - include!("lib.register_nursery.rs"); - #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { @@ -528,23 +493,40 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // all the internal lints #[cfg(feature = "internal")] { - store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); - store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls)); - store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new())); - store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle)); - store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl)); + store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal)); + store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce)); + store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls)); + store.register_late_pass(|_| { + Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) + }); + store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle)); + store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| { + Box::::default() + }); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass)); + store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl)); } let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone(); + let arithmetic_side_effects_allowed_binary = conf.arithmetic_side_effects_allowed_binary.clone(); + let arithmetic_side_effects_allowed_unary = conf.arithmetic_side_effects_allowed_unary.clone(); store.register_late_pass(move |_| { Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new( - arithmetic_side_effects_allowed.clone(), + arithmetic_side_effects_allowed + .iter() + .flat_map(|el| [[el.clone(), "*".to_string()], ["*".to_string(), el.clone()]]) + .chain(arithmetic_side_effects_allowed_binary.clone()) + .collect(), + arithmetic_side_effects_allowed + .iter() + .chain(arithmetic_side_effects_allowed_unary.iter()) + .cloned() + .collect(), )) }); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); @@ -573,7 +555,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|_| Box::new(misc::MiscLints)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); @@ -591,41 +573,45 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - let msrv = read_msrv(conf, sess); + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv))); + let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const; + store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, - msrv, + msrv(), allow_expect_in_tests, allow_unwrap_in_tests, )) }); - store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv))); - store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); - store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); - store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv))); - store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv))); - store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv))); - store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv))); - store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv))); - store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv))); + store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); + let matches_for_let_else = conf.matches_for_let_else; + store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv()))); + store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv()))); + store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv()))); + store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv()))); + store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv()))); + store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv()))); + store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv()))); store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); - store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv))); - store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); + store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv()))); + store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length; store.register_late_pass(move |_| { Box::new(index_refutable_slice::IndexRefutableSlice::new( max_suggested_slice_pattern_length, - msrv, + msrv(), )) }); store.register_late_pass(|_| Box::::default()); @@ -642,7 +628,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|_| Box::new(no_effect::NoEffect)); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv))); + store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; store.register_late_pass(move |_| { Box::new(cognitive_complexity::CognitiveComplexity::new( @@ -655,7 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); - store.register_late_pass(|_| Box::new(derivable_impls::DerivableImpls)); + store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); @@ -714,7 +700,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(|_| Box::new(unwrap::Unwrap)); - store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing)); + store.register_late_pass(move |_| { + Box::new(indexing_slicing::IndexingSlicing::new( + suppress_restriction_lint_in_const, + )) + }); store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst)); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); @@ -726,7 +716,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let max_trait_bounds = conf.max_trait_bounds; store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); - store.register_late_pass(|_| Box::new(mut_key::MutableKeyType)); + let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); + store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new())); @@ -785,10 +776,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_early_pass(|| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); - store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); + store.register_early_pass(|| Box::::default()); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; - store.register_early_pass(move || { + store.register_late_pass(move |_| { Box::new(excessive_bools::ExcessiveBools::new( max_struct_bools, max_fn_params_bools, @@ -799,7 +790,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); - store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); + store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv()))); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); @@ -833,7 +824,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); + store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::new(unused_async::UnusedAsync)); @@ -858,25 +849,27 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv))); + let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args; + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); - store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); + let allow_print_in_tests = conf.allow_print_in_tests; + store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, }) }); - store.register_late_pass(|_| Box::::default()); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); @@ -888,26 +881,37 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); + store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(msrv()))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); - store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); - store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv()))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); store.register_late_pass(|_| Box::new(box_default::BoxDefault)); store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); + store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); + store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); + store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); + store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); + store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock)); + store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck)); + store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); + store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); + store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); + store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 13878eda0f1c..3cccc2cfe2aa 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,6 +1,7 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, @@ -8,13 +9,13 @@ use rustc_hir::intravisit::{ }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, TraitFn, - TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, + Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; -use rustc_middle::ty::TyCtxt; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -33,8 +34,6 @@ declare_clippy_lint! { /// ### Known problems /// - We bail out if the function has a `where` clause where lifetimes /// are mentioned due to potential false positives. - /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the - /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// ### Example /// ```rust @@ -92,7 +91,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, generics, id) = item.kind { - check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -102,10 +101,10 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { if let ImplItemKind::Fn(ref sig, id) = item.kind { - let report_extra_lifetimes = trait_ref_of_method(cx, item.def_id.def_id).is_none(); + let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); check_fn_inner( cx, - sig.decl, + sig, Some(id), None, item.generics, @@ -121,29 +120,21 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); } } } -/// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug, Clone)] -enum RefLt { - Unnamed, - Static, - Named(LocalDefId), -} - fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, - decl: &'tcx FnDecl<'_>, + sig: &'tcx FnSig<'_>, body: Option, trait_sig: Option<&[Ident]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, ) { - if span.from_expansion() || has_where_lifetimes(cx, generics) { + if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; } @@ -151,6 +142,7 @@ fn check_fn_inner<'tcx>( .params .iter() .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); + for typ in types { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { if pred.origin == PredicateOrigin::WhereClause { @@ -161,7 +153,7 @@ fn check_fn_inner<'tcx>( for bound in pred.bounds { let mut visitor = RefVisitor::new(cx); walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { return; } if let GenericBound::Trait(ref trait_ref, _) = *bound { @@ -178,7 +170,7 @@ fn check_fn_inner<'tcx>( _ => None, }); for bound in lifetimes { - if bound.name != LifetimeName::Static && !bound.is_elided() { + if !bound.is_static() && !bound.is_elided() { return; } } @@ -187,20 +179,113 @@ fn check_fn_inner<'tcx>( } } } - if could_use_elision(cx, decl, body, trait_sig, generics.params) { - span_lint( + + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { + let lts = elidable_lts + .iter() + // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a + // `Node::GenericParam`. + .filter_map(|&def_id| cx.tcx.hir().get_by_def_id(def_id).ident()) + .map(|ident| ident.to_string()) + .collect::>() + .join(", "); + + span_lint_and_then( cx, NEEDLESS_LIFETIMES, - span.with_hi(decl.output.span().hi()), - "explicit lifetimes given in parameter types where they could be elided \ - (or replaced with `'_` if needed by type declaration)", + span.with_hi(sig.decl.output.span().hi()), + &format!("the following explicit lifetimes could be elided: {lts}"), + |diag| { + if sig.header.is_async() { + // async functions have usages whose spans point at the lifetime declaration which messes up + // suggestions + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); + } + }, ); } + if report_extra_lifetimes { - self::report_extra_lifetimes(cx, decl, generics); + self::report_extra_lifetimes(cx, sig.decl, generics); } } +fn elision_suggestions( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], +) -> Option> { + let explicit_params = generics + .params + .iter() + .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) + .collect::>(); + + let mut suggestions = if elidable_lts.len() == explicit_params.len() { + // if all the params are elided remove the whole generic block + // + // fn x<'a>() {} + // ^^^^ + vec![(generics.span, String::new())] + } else { + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) + } else { + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) + }; + + Some((span, String::new())) + }) + .collect::>>()? + }; + + suggestions.extend( + usages + .iter() + .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .map(|usage| { + match cx.tcx.hir().get_parent(usage.hir_id) { + Node::Ty(Ty { + kind: TyKind::Ref(..), .. + }) => { + // expand `&'a T` to `&'a T` + // ^^ ^^^ + let span = cx + .sess() + .source_map() + .span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace()) + .unwrap_or(usage.ident.span); + + (span, String::new()) + }, + // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` + _ => (usage.ident.span, String::from("'_")), + } + }), + ); + + Some(suggestions) +} + // elision doesn't work for explicit self types, see rust-lang/rust#69064 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { if_chain! { @@ -220,13 +305,20 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: } } +fn named_lifetime(lt: &Lifetime) -> Option { + match lt.res { + LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + _ => None, + } +} + fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], -) -> bool { +) -> Option<(Vec, Vec)> { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT @@ -234,7 +326,7 @@ fn could_use_elision<'tcx>( // level of the current item. // check named LTs - let allowed_lts = allowed_lts_from(cx.tcx, named_generics); + let allowed_lts = allowed_lts_from(named_generics); // these will collect all the lifetimes for references in arg/return types let mut input_visitor = RefVisitor::new(cx); @@ -253,7 +345,7 @@ fn could_use_elision<'tcx>( } if input_visitor.abort() || output_visitor.abort() { - return false; + return None; } let input_lts = input_visitor.lts; @@ -261,7 +353,7 @@ fn could_use_elision<'tcx>( if let Some(trait_sig) = trait_sig { if explicit_self_type(cx, func, trait_sig.first().copied()) { - return false; + return None; } } @@ -270,7 +362,7 @@ fn could_use_elision<'tcx>( let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); if explicit_self_type(cx, func, first_ident) { - return false; + return None; } let mut checker = BodyLifetimeChecker { @@ -278,99 +370,97 @@ fn could_use_elision<'tcx>( }; checker.visit_expr(body.value); if checker.lifetimes_used_in_body { - return false; + return None; } } // check for lifetimes from higher scopes for lt in input_lts.iter().chain(output_lts.iter()) { - if !allowed_lts.contains(lt) { - return false; + if let Some(id) = named_lifetime(lt) + && !allowed_lts.contains(&id) + { + return None; } } // check for higher-ranked trait bounds if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() { - let allowed_lts: FxHashSet<_> = allowed_lts - .iter() - .filter_map(|lt| match lt { - RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())), - _ => None, - }) - .collect(); + let allowed_lts: FxHashSet<_> = allowed_lts.iter().map(|id| cx.tcx.item_name(id.to_def_id())).collect(); for lt in input_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return false; - } + if allowed_lts.contains(<.ident.name) { + return None; } } for lt in output_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return false; - } + if allowed_lts.contains(<.ident.name) { + return None; } } } - // no input lifetimes? easy case! - if input_lts.is_empty() { - false - } else if output_lts.is_empty() { - // no output lifetimes, check distinctness of input lifetimes - - // only unnamed and static, ok - let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static); - if unnamed_and_static { - return false; - } - // we have no output reference, so we only need all distinct lifetimes - input_lts.len() == unique_lifetimes(&input_lts) - } else { - // we have output references, so we need one input reference, - // and all output lifetimes must be the same - if unique_lifetimes(&output_lts) > 1 { - return false; - } - if input_lts.len() == 1 { - match (&input_lts[0], &output_lts[0]) { - (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true, - (&RefLt::Named(_), &RefLt::Unnamed) => true, - _ => false, /* already elided, different named lifetimes - * or something static going on */ + // A lifetime can be newly elided if: + // - It occurs only once among the inputs. + // - If there are multiple input lifetimes, then the newly elided lifetime does not occur among the + // outputs (because eliding such an lifetime would create an ambiguity). + let elidable_lts = named_lifetime_occurrences(&input_lts) + .into_iter() + .filter_map(|(def_id, occurrences)| { + if occurrences == 1 + && (input_lts.len() == 1 || !output_lts.iter().any(|lt| named_lifetime(lt) == Some(def_id))) + { + Some(def_id) + } else { + None } - } else { - false - } + }) + .collect::>(); + + if elidable_lts.is_empty() { + return None; } + + let usages = itertools::chain(input_lts, output_lts).collect(); + + Some((elidable_lts, usages)) } -fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxHashSet { - let mut allowed_lts = FxHashSet::default(); - for par in named_generics.iter() { - if let GenericParamKind::Lifetime { .. } = par.kind { - allowed_lts.insert(RefLt::Named(tcx.hir().local_def_id(par.hir_id))); - } - } - allowed_lts.insert(RefLt::Unnamed); - allowed_lts.insert(RefLt::Static); - allowed_lts +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { + named_generics + .iter() + .filter_map(|par| { + if let GenericParamKind::Lifetime { .. } = par.kind { + Some(par.def_id) + } else { + None + } + }) + .collect() } -/// Number of unique lifetimes in the given vector. +/// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve +/// relative order. #[must_use] -fn unique_lifetimes(lts: &[RefLt]) -> usize { - lts.iter().collect::>().len() +fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { + let mut occurrences = Vec::new(); + for lt in lts { + if let Some(curr_def_id) = named_lifetime(lt) { + if let Some(pair) = occurrences + .iter_mut() + .find(|(prev_def_id, _)| *prev_def_id == curr_def_id) + { + pair.1 += 1; + } else { + occurrences.push((curr_def_id, 1)); + } + } + } + occurrences } -const CLOSURE_TRAIT_BOUNDS: [LangItem; 3] = [LangItem::Fn, LangItem::FnMut, LangItem::FnOnce]; - -/// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - lts: Vec, - nested_elision_site_lts: Vec, + lts: Vec, + nested_elision_site_lts: Vec, unelided_trait_object_lifetime: bool, } @@ -384,30 +474,11 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } - fn record(&mut self, lifetime: &Option) { - if let Some(ref lt) = *lifetime { - if lt.name == LifetimeName::Static { - self.lts.push(RefLt::Static); - } else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name { - // Fresh lifetimes generated should be ignored. - self.lts.push(RefLt::Unnamed); - } else if lt.is_elided() { - self.lts.push(RefLt::Unnamed); - } else if let LifetimeName::Param(def_id, _) = lt.name { - self.lts.push(RefLt::Named(def_id)); - } else { - self.lts.push(RefLt::Unnamed); - } - } else { - self.lts.push(RefLt::Unnamed); - } - } - - fn all_lts(&self) -> Vec { + fn all_lts(&self) -> Vec { self.lts .iter() .chain(self.nested_elision_site_lts.iter()) - .cloned() + .copied() .collect::>() } @@ -419,17 +490,13 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.record(&Some(*lifetime)); + self.lts.push(*lifetime); } fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) { let trait_ref = &poly_tref.trait_ref; - if CLOSURE_TRAIT_BOUNDS.iter().any(|&item| { - self.cx - .tcx - .lang_items() - .require(item) - .map_or(false, |id| Some(id) == trait_ref.trait_def_id()) + if let Some(id) = trait_ref.trait_def_id() && lang_items::FN_TRAITS.iter().any(|&item| { + self.cx.tcx.lang_items().get(item) == Some(id) }) { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_trait_ref(trait_ref); @@ -448,11 +515,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { walk_item(self, item); self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { - GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id, _) = l.name { - RefLt::Named(def_id) - } else { - RefLt::Unnamed - }), + GenericArg::Lifetime(&l) => Some(l), _ => None, })); }, @@ -489,14 +552,18 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ return true; } // if the bounds define new lifetimes, they are fine to occur - let allowed_lts = allowed_lts_from(cx.tcx, pred.bound_generic_params); + let allowed_lts = allowed_lts_from(pred.bound_generic_params); // now walk the bounds for bound in pred.bounds.iter() { walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { - return true; + for lt in visitor.all_lts() { + if let Some(id) = named_lifetime(<) + && !allowed_lts.contains(&id) + { + return true; + } } }, WherePredicate::EqPredicate(ref pred) => { @@ -537,7 +604,7 @@ where // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.map.remove(&lifetime.name.ident().name); + self.map.remove(&lifetime.ident.name); } fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) { @@ -561,7 +628,9 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, .params .iter() .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit, + } => Some((par.name.ident().name, par.span)), _ => None, }) .collect(); @@ -586,7 +655,9 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' .params .iter() .filter_map(|par| match par.kind { - GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + GenericParamKind::Lifetime { + kind: LifetimeParamKind::Explicit, + } => Some((par.name.ident().name, par.span)), _ => None, }) .collect(); @@ -613,7 +684,7 @@ struct BodyLifetimeChecker { impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime { + if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime { self.lifetimes_used_in_body = true; } } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 25f19b9c6e6c..3a7b7835c990 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -5,11 +5,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use rustc_ast::ast::{Expr, ExprKind, LitKind}; +use rustc_ast::token; 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::Span; use std::iter; declare_clippy_lint! { @@ -236,8 +238,8 @@ impl EarlyLintPass for LiteralDigitGrouping { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -252,12 +254,13 @@ impl LiteralDigitGrouping { } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { if_chain! { - if let Some(src) = snippet_opt(cx, lit.span); - if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit); + if let Some(src) = snippet_opt(cx, span); + if let Ok(lit_kind) = LitKind::from_token_lit(lit); + if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); then { - if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { + if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { return; } @@ -293,14 +296,14 @@ impl LiteralDigitGrouping { | WarningType::InconsistentDigitGrouping | WarningType::UnusualByteGroupings | WarningType::LargeDigitGroups => { - !lit.span.from_expansion() + !span.from_expansion() } WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => { true } }; if should_warn { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); } } } @@ -458,8 +461,8 @@ impl EarlyLintPass for DecimalLiteralRepresentation { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - self.check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + self.check_lit(cx, lit, expr.span); } } } @@ -469,19 +472,20 @@ impl DecimalLiteralRepresentation { pub fn new(threshold: u64) -> Self { Self { threshold } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. if_chain! { - if let LitKind::Int(val, _) = lit.kind; - if let Some(src) = snippet_opt(cx, lit.span); - if let Some(num_lit) = NumericLiteral::from_lit(&src, lit); + if let Ok(lit_kind) = LitKind::from_token_lit(lit); + if let LitKind::Int(val, _) = lit_kind; + if let Some(src) = snippet_opt(cx, span); + if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, lit.span); + warning_type.display(num_lit.format(), cx, span); }); } } diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 14f223481327..1953ee8a7175 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -77,7 +77,7 @@ pub(super) fn check<'tcx>( applicability, ); - diag.note(&format!( + diag.note(format!( "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" )); }, diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs deleted file mode 100644 index 77de90fd7b94..000000000000 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ /dev/null @@ -1,65 +0,0 @@ -use super::FOR_LOOPS_OVER_FALLIBLES; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use rustc_hir::{Expr, Pat}; -use rustc_lint::LateContext; -use rustc_span::symbol::sym; - -/// Checks for `for` loops over `Option`s and `Result`s. -pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) { - let ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, ty, sym::Option) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - let help_string = if let Some(method_name) = method_name { - format!( - "consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - } else { - format!( - "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ) - }; - span_lint_and_help( - cx, - FOR_LOOPS_OVER_FALLIBLES, - arg.span, - &format!( - "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement", - snippet(cx, arg.span, "_") - ), - None, - &help_string, - ); - } -} diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index e640c62ebdac..b8a263817d29 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) { if is_trait_method(cx, arg, sym::Iterator) { span_lint( cx, @@ -14,8 +14,5 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ probably not what you want", ); - true - } else { - false } } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index c0a0444485e3..610a0233eee1 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -3,14 +3,12 @@ mod explicit_counter_loop; mod explicit_into_iter_loop; mod explicit_iter_loop; mod for_kv_map; -mod for_loops_over_fallibles; mod iter_next_loop; mod manual_find; mod manual_flatten; mod manual_memcpy; mod missing_spin_loop; mod mut_range_bound; -mod needless_collect; mod needless_range_loop; mod never_loop; mod same_item_push; @@ -63,7 +61,8 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Just iterating the collection itself makes the intent - /// more clear and is probably faster. + /// more clear and is probably faster because it eliminates + /// the bounds check that is done when indexing. /// /// ### Example /// ```rust @@ -173,49 +172,6 @@ declare_clippy_lint! { "for-looping over `_.next()` which is probably not intended" } -declare_clippy_lint! { - /// ### What it does - /// Checks for `for` loops over `Option` or `Result` values. - /// - /// ### Why is this bad? - /// Readability. This is more clearly expressed as an `if - /// let`. - /// - /// ### Example - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// for x in opt { - /// // .. - /// } - /// - /// for x in &res { - /// // .. - /// } - /// - /// for x in res.iter() { - /// // .. - /// } - /// ``` - /// - /// Use instead: - /// ```rust - /// # let opt = Some(1); - /// # let res: Result = Ok(1); - /// if let Some(x) = opt { - /// // .. - /// } - /// - /// if let Ok(x) = res { - /// // .. - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub FOR_LOOPS_OVER_FALLIBLES, - suspicious, - "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" -} - declare_clippy_lint! { /// ### What it does /// Detects `loop + match` combinations that are easier @@ -249,28 +205,6 @@ declare_clippy_lint! { "`loop { if let { ... } else break }`, which can be written as a `while let` loop" } -declare_clippy_lint! { - /// ### What it does - /// Checks for functions collecting an iterator when collect - /// is not needed. - /// - /// ### Why is this bad? - /// `collect` causes the allocation of a new data structure, - /// when this allocation may not be needed. - /// - /// ### Example - /// ```rust - /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::>().len(); - /// // should be - /// let len = iterator.count(); - /// ``` - #[clippy::version = "1.30.0"] - pub NEEDLESS_COLLECT, - perf, - "collecting an iterator when collect is not needed" -} - declare_clippy_lint! { /// ### What it does /// Checks `for` loops over slices with an explicit counter @@ -648,9 +582,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOPS_OVER_FALLIBLES, WHILE_LET_LOOP, - NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -712,8 +644,6 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_immutable_condition::check(cx, condition, body); missing_spin_loop::check(cx, condition, body); } - - needless_collect::check(expr, cx); } } @@ -739,30 +669,22 @@ fn check_for_loop<'tcx>( manual_find::check(cx, pat, arg, body, span, expr); } -fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { - let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - +fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) { if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind { let method_name = method.ident.as_str(); // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x match method_name { "iter" | "iter_mut" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "into_iter" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); explicit_into_iter_loop::check(cx, self_arg, arg); - for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "next" => { - next_loop_linted = iter_next_loop::check(cx, arg); + iter_next_loop::check(cx, arg); }, _ => {}, } } - - if !next_loop_linted { - for_loops_over_fallibles::check(cx, pat, arg, None); - } } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 0ee42b61c9a5..4dae93f6028d 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -4,7 +4,7 @@ use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::{mir::FakeReadCause, ty}; @@ -52,8 +52,8 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option None } -fn check_for_mutation<'tcx>( - cx: &LateContext<'tcx>, +fn check_for_mutation( + cx: &LateContext<'_>, body: &Expr<'_>, bound_id_start: Option, bound_id_end: Option, @@ -65,16 +65,15 @@ fn check_for_mutation<'tcx>( span_low: None, span_high: None, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - body.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + body.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(body); delegate.mutation_span() } @@ -114,13 +113,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read( - &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl MutatePairDelegate<'_, '_> { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 00cfc6d49f19..d060b6ade24e 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; +use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -81,7 +81,7 @@ pub(super) fn check<'tcx>( let skip = if starts_at_zero { String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) { return; } else { format!(".skip({})", snippet(cx, start.span, "..")) @@ -109,7 +109,7 @@ pub(super) fn check<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() - } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) { return; } else { match limits { @@ -149,7 +149,7 @@ pub(super) fn check<'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator", + "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, )", ident.name)), ( @@ -263,7 +263,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { match res { Res::Local(hir_id) => { let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); - let extent = self.cx + let extent = self + .cx .tcx .region_scope_tree(parent_def_id) .var_scope(hir_id.local_id) @@ -274,11 +275,12 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), ); } else { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); + self.indexed_indirectly + .insert(seqvar.segments[0].ident.name, Some(extent)); } - return false; // no need to walk further *on the variable* - } - Res::Def(DefKind::Static (_)| DefKind::Const, ..) => { + return false; // no need to walk further *on the variable* + }, + Res::Def(DefKind::Static(_) | DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, @@ -287,8 +289,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } else { self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } - return false; // no need to walk further *on the variable* - } + return false; // no need to walk further *on the variable* + }, _ => (), } } @@ -302,17 +304,26 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // a range index op if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind; - if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) - || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); + if let Some(trait_id) = self + .cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); + if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) + || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); if !self.check(args_1, args_0, expr); - then { return } + then { + return; + } } if_chain! { // an index op if let ExprKind::Index(seqexpr, idx) = expr.kind; if !self.check(idx, seqexpr, expr); - then { return } + then { + return; + } } if_chain! { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 16b00ad66378..14f161f51026 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; +use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::Span; use std::iter::{once, Iterator}; @@ -16,7 +16,7 @@ pub(super) fn check( span: Span, for_loop: Option<&ForLoop<'_>>, ) { - match never_loop_block(block, loop_id) { + match never_loop_block(block, &mut Vec::new(), loop_id) { NeverLoopResult::AlwaysBreak => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { @@ -92,35 +92,34 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } } -fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block +fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { + let iter = block .stmts .iter() .filter_map(stmt_to_expr) .chain(block.expr.map(|expr| (expr, None))); - never_loop_expr_seq(&mut iter, main_loop_id) -} -fn never_loop_expr_seq<'a, T: Iterator, Option<&'a Block<'a>>)>>( - es: &mut T, - main_loop_id: HirId, -) -> NeverLoopResult { - es.map(|(e, els)| { - let e = never_loop_expr(e, main_loop_id); - els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + iter.map(|(e, els)| { + let e = never_loop_expr(e, ignore_ids, main_loop_id); + // els is an else block in a let...else binding + els.map_or(e, |els| { + combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id)) + }) }) .fold(NeverLoopResult::Otherwise, combine_seq) } fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)), + // add the let...else expression (if present) StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } -fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { +#[allow(clippy::too_many_lines)] +fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { ExprKind::Box(e) | ExprKind::Unary(_, e) @@ -129,47 +128,56 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), - ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id), - ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id), - ExprKind::MethodCall(_, receiver, es, _) => { - never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id) - }, + | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id), + ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id), + ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id), + ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( + &mut std::iter::once(receiver).chain(es.iter()), + ignore_ids, + main_loop_id, + ), ExprKind::Struct(_, fields, base) => { - let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id); + let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); if let Some(base) = base { - combine_both(fields, never_loop_expr(base, main_loop_id)) + combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id)) } else { fields } }, - ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id), ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. - absorb_break(never_loop_block(b, main_loop_id)) + absorb_break(never_loop_block(b, ignore_ids, main_loop_id)) }, ExprKind::If(e, e2, e3) => { - let e1 = never_loop_expr(e, main_loop_id); - let e2 = never_loop_expr(e2, main_loop_id); - let e3 = e3 - .as_ref() - .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); + let e1 = never_loop_expr(e, ignore_ids, main_loop_id); + let e2 = never_loop_expr(e2, ignore_ids, main_loop_id); + let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { + never_loop_expr(e, ignore_ids, main_loop_id) + }); combine_seq(e1, combine_branches(e2, e3)) }, ExprKind::Match(e, arms, _) => { - let e = never_loop_expr(e, main_loop_id); + let e = never_loop_expr(e, ignore_ids, main_loop_id); if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id); combine_seq(e, arms) } }, - ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, l) => { + if l.is_some() { + ignore_ids.push(b.hir_id); + } + let ret = never_loop_block(b, ignore_ids, main_loop_id); + ignore_ids.pop(); + ret + }, ExprKind::Continue(d) => { let id = d .target_id @@ -180,20 +188,32 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, + // 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::Otherwise, |e| { + combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) + }), ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + combine_seq( + never_loop_expr(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(expr, main_loop_id) + never_loop_expr(expr, ignore_ids, main_loop_id) }, - InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id), - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id) + InlineAsmOperand::Out { expr, .. } => { + never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id) }, + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( + &mut once(*in_expr).chain(out_expr.iter().copied()), + ignore_ids, + main_loop_id, + ), InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, @@ -208,13 +228,21 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { } } -fn never_loop_expr_all<'a, T: Iterator>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_all<'a, T: Iterator>>( + es: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::Otherwise, combine_both) } -fn never_loop_expr_branch<'a, T: Iterator>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { - e.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_branch<'a, T: Iterator>>( + e: &mut T, + ignore_ids: &mut Vec, + main_loop_id: HirId, +) -> NeverLoopResult { + e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::AlwaysBreak, combine_branches) } diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 07edee46fa65..540656a2cd99 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -63,7 +63,7 @@ pub(super) fn check<'tcx>( if let Node::Pat(pat) = node; if let PatKind::Binding(bind_ann, ..) = pat.kind; if !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)); - let parent_node = cx.tcx.hir().get_parent_node(hir_id); + let parent_node = cx.tcx.hir().parent_id(hir_id); if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); if let Some(init) = parent_let_expr.init; then { diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index f4b47808dfaa..744fd61bd135 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -1,6 +1,7 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet_with_applicability}; +use clippy_utils::visitors::contains_break_or_continue; use if_chain::if_chain; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; @@ -67,6 +68,7 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); + if !contains_break_or_continue(body); then { let mut applicability = Applicability::MachineApplicable; let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index b6f4cf7bbb37..28ee24309cc4 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -25,7 +25,6 @@ pub(super) struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: HirIdMap, // incremented variables depth: u32, // depth of conditional expressions - done: bool, } impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { @@ -34,7 +33,6 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { cx, states: HirIdMap::default(), depth: 0, - done: false, } } @@ -51,10 +49,6 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.done { - return; - } - // If node is a variable if let Some(def_id) = path_to_local(expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { @@ -95,7 +89,9 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { walk_expr(self, expr); self.depth -= 1; } else if let ExprKind::Continue(_) = expr.kind { - self.done = true; + // If we see a `continue` block, then we increment depth so that the IncrementVisitor + // state will be set to DontWarn if we see the variable being modified anywhere afterwards. + self.depth += 1; } else { walk_expr(self, expr); } diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index a63422d2a36a..d1a1f773f87b 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' } else { return; }; - let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); + let mutable_static_in_cond = var_visitor.def_ids.items().any(|(_, v)| *v); let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { has_break_or_return: false, diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index f5617a905ff8..26ae4b409c79 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -95,7 +95,10 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { let hir_id = item.hir_id(); let attrs = cx.tcx.hir().attrs(hir_id); if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); - if let Res::Def(DefKind::Mod, id) = path.res; + if let Some(id) = path.res.iter().find_map(|res| match res { + Res::Def(DefKind::Mod, id) => Some(id), + _ => None, + }); if !id.is_local(); then { for kid in cx.tcx.module_children(id).iter() { diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 825ec84b4a81..ce5d657bcf0e 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,8 +1,7 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg}; +use clippy_utils::macros::root_macro_call; +use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -38,51 +37,57 @@ declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if let ExprKind::If(cond, then, None) = expr.kind; - if !matches!(cond.kind, ExprKind::Let(_)); - if !expr.span.from_expansion(); - let then = peel_blocks_with_stmt(then); - if let Some(macro_call) = root_macro_call(then.span); - if cx.tcx.item_name(macro_call.def_id) == sym::panic; - if !cx.tcx.sess.source_map().is_multiline(cond.span); - if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); - then { - let mut applicability = Applicability::MachineApplicable; - let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); - let cond = cond.peel_drop_temps(); - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let (cond, not) = match cond.kind { - ExprKind::Unary(UnOp::Not, e) => (e, ""), - _ => (cond, "!"), - }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block - span_lint_and_then( - cx, - MANUAL_ASSERT, - expr.span, - "only a `panic!` in `if`-then statement", - |diag| { - // comments can be noisy, do not show them to the user - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); - diag.span_suggestion( - expr.span, - "try instead", - sugg, - applicability); - } - - ); + if let ExprKind::If(cond, then, None) = expr.kind + && !matches!(cond.kind, ExprKind::Let(_)) + && !expr.span.from_expansion() + && let then = peel_blocks_with_stmt(then) + && let Some(macro_call) = root_macro_call(then.span) + && cx.tcx.item_name(macro_call.def_id) == sym::panic + && !cx.tcx.sess.source_map().is_multiline(cond.span) + && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span) + && let Some(panic_snippet) = panic_snippet.strip_suffix(')') + && let Some((_, format_args_snip)) = panic_snippet.split_once('(') + // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just + // shuffles the condition around. + // Should this have a config value? + && !is_else_clause(cx.tcx, expr) + { + let mut applicability = Applicability::MachineApplicable; + let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; } + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), + }; + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( + cx, + MANUAL_ASSERT, + expr.span, + "only a `panic!` in `if`-then statement", + |diag| { + // comments can be noisy, do not show them to the user + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability + ); + } + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability + ); + } + ); } } } diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 9a0a26c0991b..63212beaa63d 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_function_call; -use clippy_utils::paths::FUTURE_FROM_GENERATOR; +use clippy_utils::match_function_call_with_def_id; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, - HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, + HirId, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -50,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { ) { if_chain! { if let Some(header) = kind.header(); - if header.asyncness == IsAsync::NotAsync; + if !header.asyncness.is_async(); // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); @@ -77,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { let help = format!("make the function `async` and {ret_sugg}"); diag.span_suggestion( header_span, - &help, + help, format!("async {}{ret_snip}", &header_snip[..ret_pos]), Applicability::MachineApplicable ); @@ -119,7 +118,7 @@ fn future_trait_ref<'tcx>( .iter() .filter_map(|bound| { if let GenericArg::Lifetime(lt) = bound { - Some(lt.name) + Some(lt.res) } else { None } @@ -140,9 +139,9 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t if args.bindings.len() == 1; let binding = &args.bindings[0]; if binding.ident.name == sym::Output; - if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind; + if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; then { - return Some(output) + return Some(output); } } @@ -153,8 +152,8 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) let input_lifetimes: Vec = inputs .iter() .filter_map(|ty| { - if let TyKind::Rptr(lt, _) = ty.kind { - Some(lt.name) + if let TyKind::Ref(lt, _) = ty.kind { + Some(lt.res) } else { None } @@ -175,9 +174,16 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; - if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); + if let Some(args) = cx + .tcx + .lang_items() + .identity_future_fn() + .and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id)); if args.len() == 1; - if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0]; + if let Expr { + kind: ExprKind::Closure(&Closure { body, .. }), + .. + } = args[0]; let closure_body = cx.tcx.hir().body(body); if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block)); then { diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 6655c92b1da8..462d73cf0b97 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -34,12 +34,12 @@ declare_clippy_lint! { #[derive(Clone)] pub struct ManualBits { - msrv: Option, + msrv: Msrv, } impl ManualBits { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]); impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) { + if !self.msrv.meets(msrvs::MANUAL_BITS) { return; } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index ece4df95505c..f239736d38a4 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,28 +1,25 @@ +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::higher::If; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::MaybePath; +use clippy_utils::{ + eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, +}; use itertools::Itertools; +use rustc_errors::Applicability; use rustc_errors::Diagnostic; use rustc_hir::{ def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; use std::ops::Deref; -use clippy_utils::{ - diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, get_trait_def_id, - higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks, - peel_blocks_with_stmt, - sugg::Sugg, - ty::implements_trait, - visitors::is_const_evaluatable, - MaybePath, -}; -use rustc_errors::Applicability; - declare_clippy_lint! { /// ### What it does /// Identifies good opportunities for a clamp function from std or core, and suggests using it. @@ -38,6 +35,9 @@ declare_clippy_lint! { /// Some may consider panicking in these situations to be desirable, but it also may /// introduce panicking where there wasn't any before. /// + /// See also [the discussion in the + /// PR](https://github.com/rust-lang/rust-clippy/pull/9484#issuecomment-1278922613). + /// /// ### Examples /// ```rust /// # let (input, min, max) = (0, -2, 1); @@ -81,17 +81,17 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub MANUAL_CLAMP, - complexity, + nursery, "using a clamp pattern instead of the clamp function" } impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); pub struct ManualClamp { - msrv: Option, + msrv: Msrv, } impl ManualClamp { - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -114,7 +114,7 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } if !expr.span.from_expansion() { @@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } for suggestion in is_two_if_pattern(cx, block) { @@ -190,7 +190,11 @@ impl TypeClampability { fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { if ty.is_floating_point() { Some(TypeClampability::Float) - } else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) { + } else if cx + .tcx + .get_diagnostic_item(sym::Ord) + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { Some(TypeClampability::Ord) } else { None diff --git a/clippy_lints/src/manual_instant_elapsed.rs b/clippy_lints/src/manual_instant_elapsed.rs deleted file mode 100644 index 331cda1db899..000000000000 --- a/clippy_lints/src/manual_instant_elapsed.rs +++ /dev/null @@ -1,69 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between `Instant::now()` and another `Instant`. - /// - /// ### Why is this bad? - /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns - /// as `Instant` subtraction saturates. - /// - /// `prev_instant.elapsed()` also more clearly signals intention. - /// - /// ### Example - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = Instant::now() - prev_instant; - /// ``` - /// Use instead: - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = prev_instant.elapsed(); - /// ``` - #[clippy::version = "1.64.0"] - pub MANUAL_INSTANT_ELAPSED, - pedantic, - "subtraction between `Instant::now()` and previous `Instant`" -} - -declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]); - -impl LateLintPass<'_> for ManualInstantElapsed { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind - && check_instant_now_call(cx, lhs) - && let ty_resolved = cx.typeck_results().expr_ty(rhs) - && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() - && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) - && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) - { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } - } -} - -fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { - if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) - { - true - } else { - false - } -} diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs new file mode 100644 index 000000000000..2fd32c009eaa --- /dev/null +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -0,0 +1,176 @@ +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet}; +use rustc_ast::ast::RangeLimits; +use rustc_ast::LitKind::{Byte, Char}; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{def_id::DefId, sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Suggests to use dedicated built-in methods, + /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range + /// + /// ### Why is this bad? + /// Using the built-in functions is more readable and makes it + /// clear that it's not a specific subset of characters, but all + /// ASCII (lowercase|uppercase|digit) characters. + /// ### Example + /// ```rust + /// fn main() { + /// assert!(matches!('x', 'a'..='z')); + /// assert!(matches!(b'X', b'A'..=b'Z')); + /// assert!(matches!('2', '0'..='9')); + /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// + /// ('0'..='9').contains(&'0'); + /// ('a'..='z').contains(&'a'); + /// ('A'..='Z').contains(&'A'); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// assert!('x'.is_ascii_lowercase()); + /// assert!(b'X'.is_ascii_uppercase()); + /// assert!('2'.is_ascii_digit()); + /// assert!('x'.is_ascii_alphabetic()); + /// + /// '0'.is_ascii_digit(); + /// 'a'.is_ascii_lowercase(); + /// 'A'.is_ascii_uppercase(); + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub MANUAL_IS_ASCII_CHECK, + style, + "use dedicated method to check ascii range" +} +impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); + +pub struct ManualIsAsciiCheck { + msrv: Msrv, +} + +impl ManualIsAsciiCheck { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +#[derive(Debug, PartialEq)] +enum CharRange { + /// 'a'..='z' | b'a'..=b'z' + LowerChar, + /// 'A'..='Z' | b'A'..=b'Z' + UpperChar, + /// AsciiLower | AsciiUpper + FullChar, + /// '0..=9' + Digit, + Otherwise, +} + +impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) { + return; + } + + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { + return; + } + + if let Some(macro_call) = root_macro_call(expr.span) + && is_matches_macro(cx, macro_call.def_id) { + if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { + let range = check_pat(&arm.pat.kind); + check_is_ascii(cx, macro_call.span, recv, &range); + } + } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind + && path.ident.name == sym!(contains) + && let Some(higher::Range { start: Some(start), end: Some(end), limits: RangeLimits::Closed }) + = higher::Range::hir(receiver) { + let range = check_range(start, end); + if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind { + check_is_ascii(cx, expr.span, e, &range); + } else { + check_is_ascii(cx, expr.span, arg, &range); + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) { + if let Some(sugg) = match range { + CharRange::UpperChar => Some("is_ascii_uppercase"), + CharRange::LowerChar => Some("is_ascii_lowercase"), + CharRange::FullChar => Some("is_ascii_alphabetic"), + CharRange::Digit => Some("is_ascii_digit"), + CharRange::Otherwise => None, + } { + let default_snip = ".."; + // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for + // macro span, so we check applicability manually by comparing `recv` is not default. + let recv = snippet(cx, recv.span, default_snip); + + let applicability = if recv == default_snip { + Applicability::HasPlaceholders + } else { + Applicability::MachineApplicable + }; + + span_lint_and_sugg( + cx, + MANUAL_IS_ASCII_CHECK, + span, + "manual check for common ascii range", + "try", + format!("{recv}.{sugg}()"), + applicability, + ); + } +} + +fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { + match pat_kind { + PatKind::Or(pats) => { + let ranges = pats.iter().map(|p| check_pat(&p.kind)).collect::>(); + + if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) { + CharRange::FullChar + } else { + CharRange::Otherwise + } + }, + PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + _ => CharRange::Otherwise, + } +} + +fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { + if let ExprKind::Lit(start_lit) = &start.kind + && let ExprKind::Lit(end_lit) = &end.kind { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } + } else { + CharRange::Otherwise + } +} + +fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { + if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) { + return sym::matches_macro == name; + } + + false +} diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs new file mode 100644 index 000000000000..9c6f8b43c078 --- /dev/null +++ b/clippy_lints/src/manual_let_else.rs @@ -0,0 +1,300 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::{for_each_expr, Descend}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; +use rustc_span::Span; +use serde::Deserialize; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// + /// Warn of cases where `let...else` could be used + /// + /// ### Why is this bad? + /// + /// `let...else` provides a standard construct for this pattern + /// that people can easily recognize. It's also more compact. + /// + /// ### Example + /// + /// ```rust + /// # let w = Some(0); + /// let v = if let Some(v) = w { v } else { return }; + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # #![feature(let_else)] + /// # fn main () { + /// # let w = Some(0); + /// let Some(v) = w else { return }; + /// # } + /// ``` + #[clippy::version = "1.67.0"] + pub MANUAL_LET_ELSE, + pedantic, + "manual implementation of a let...else statement" +} + +pub struct ManualLetElse { + msrv: Msrv, + matches_behaviour: MatchLintBehaviour, +} + +impl ManualLetElse { + #[must_use] + pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { + Self { + msrv, + matches_behaviour, + } + } +} + +impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); + +impl<'tcx> LateLintPass<'tcx> for ManualLetElse { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { + let if_let_or_match = if_chain! { + if self.msrv.meets(msrvs::LET_ELSE); + if !in_external_macro(cx.sess(), stmt.span); + if let StmtKind::Local(local) = stmt.kind; + if let Some(init) = local.init; + if local.els.is_none(); + if local.ty.is_none(); + if init.span.ctxt() == stmt.span.ctxt(); + if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init); + then { + if_let_or_match + } else { + return; + } + }; + + match if_let_or_match { + IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { + if expr_is_simple_identity(let_pat, if_then); + if let Some(if_else) = if_else; + if expr_diverges(cx, if_else); + then { + emit_manual_let_else(cx, stmt.span, if_let_expr, let_pat, if_else); + } + }, + IfLetOrMatch::Match(match_expr, arms, source) => { + if self.matches_behaviour == MatchLintBehaviour::Never { + return; + } + if source != MatchSource::Normal { + return; + } + // Any other number than two arms doesn't (neccessarily) + // have a trivial mapping to let else. + if arms.len() != 2 { + return; + } + // Guards don't give us an easy mapping either + if arms.iter().any(|arm| arm.guard.is_some()) { + return; + } + let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; + let diverging_arm_opt = arms + .iter() + .enumerate() + .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); + let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + let pat_arm = &arms[1 - idx]; + if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { + return; + } + + emit_manual_let_else(cx, stmt.span, match_expr, pat_arm.pat, diverging_arm.body); + }, + } + } + + extract_msrv_attr!(LateContext); +} + +fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: &Pat<'_>, else_body: &Expr<'_>) { + span_lint_and_then( + cx, + MANUAL_LET_ELSE, + span, + "this could be rewritten as `let...else`", + |diag| { + // This is far from perfect, for example there needs to be: + // * mut additions for the bindings + // * renamings of the bindings + // * unused binding collision detection with existing ones + // * putting patterns with at the top level | inside () + // for this to be machine applicable. + let mut app = Applicability::HasPlaceholders; + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); + let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); + + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + sn_else.into_owned() + } else { + format!("{{ {sn_else} }}") + }; + let sn_bl = if matches!(pat.kind, PatKind::Or(..)) { + format!("({sn_pat})") + } else { + sn_pat.into_owned() + }; + let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); + diag.span_suggestion(span, "consider writing", sugg, app); + }, + ); +} + +fn expr_diverges(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { + fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { + if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) { + return ty.is_never(); + } + false + } + // We can't just call is_never on expr and be done, because the type system + // sometimes coerces the ! type to something different before we can get + // our hands on it. So instead, we do a manual search. We do fall back to + // is_never in some places when there is no better alternative. + for_each_expr(expr, |ex| { + match ex.kind { + ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()), + ExprKind::Call(call, _) => { + if is_never(cx, ex) || is_never(cx, call) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::Yes) + }, + ExprKind::MethodCall(..) => { + if is_never(cx, ex) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::Yes) + }, + ExprKind::If(if_expr, if_then, if_else) => { + let else_diverges = if_else.map_or(false, |ex| expr_diverges(cx, ex)); + let diverges = expr_diverges(cx, if_expr) || (else_diverges && expr_diverges(cx, if_then)); + if diverges { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::No) + }, + ExprKind::Match(match_expr, match_arms, _) => { + let diverges = expr_diverges(cx, match_expr) + || match_arms.iter().all(|arm| { + let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(cx, g.body())); + guard_diverges || expr_diverges(cx, arm.body) + }); + if diverges { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::No) + }, + + // Don't continue into loops or labeled blocks, as they are breakable, + // and we'd have to start checking labels. + ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + + // Default: descend + _ => ControlFlow::Continue(Descend::Yes), + } + }) + .is_some() +} + +fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool { + // Check whether the pattern contains any bindings, as the + // binding might potentially be used in the body. + // TODO: only look for *used* bindings. + let mut has_bindings = false; + pat.each_binding_or_first(&mut |_, _, _, _| has_bindings = true); + if has_bindings { + return false; + } + + // If we shouldn't check the types, exit early. + if !check_types { + return true; + } + + // Check whether any possibly "unknown" patterns are included, + // because users might not know which values some enum has. + // Well-known enums are excepted, as we assume people know them. + // We do a deep check, to be able to disallow Err(En::Foo(_)) + // for usage of the En::Foo variant, as we disallow En::Foo(_), + // but we allow Err(_). + let typeck_results = cx.typeck_results(); + let mut has_disallowed = false; + pat.walk_always(|pat| { + // Only do the check if the type is "spelled out" in the pattern + if !matches!( + pat.kind, + PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) + ) { + return; + }; + let ty = typeck_results.pat_ty(pat); + // Option and Result are allowed, everything else isn't. + if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { + has_disallowed = true; + } + }); + !has_disallowed +} + +/// Checks if the passed block is a simple identity referring to bindings created by the pattern +fn expr_is_simple_identity(pat: &'_ Pat<'_>, expr: &'_ Expr<'_>) -> bool { + // We support patterns with multiple bindings and tuples, like: + // let ... = if let (Some(foo), bar) = g() { (foo, bar) } else { ... } + let peeled = peel_blocks(expr); + let paths = match peeled.kind { + ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs, + ExprKind::Path(_) => std::slice::from_ref(peeled), + _ => return false, + }; + let mut pat_bindings = FxHashSet::default(); + pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| { + pat_bindings.insert(ident); + }); + if pat_bindings.len() < paths.len() { + return false; + } + for path in paths { + if_chain! { + if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind; + if let [path_seg] = path.segments; + then { + if !pat_bindings.remove(&path_seg.ident) { + return false; + } + } else { + return false; + } + } + } + true +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)] +pub enum MatchLintBehaviour { + AllTypes, + WellKnownTypes, + Never, +} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 6a42275322b4..bca193be9e71 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::is_doc_hidden; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_doc_hidden, meets_msrv, msrvs}; use rustc_ast::ast::{self, VisibilityKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -8,7 +9,6 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::ty::DefIdTree; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; @@ -63,12 +63,12 @@ declare_clippy_lint! { #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveStruct { - msrv: Option, + msrv: Msrv, } impl ManualNonExhaustiveStruct { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveEnum { - msrv: Option, + msrv: Msrv, constructed_enum_variants: FxHashSet<(DefId, DefId)>, potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, } impl ManualNonExhaustiveEnum { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, constructed_enum_variants: FxHashSet::default(), @@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } @@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } @@ -157,16 +157,16 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { && def.variants.len() > 1 { let mut iter = def.variants.iter().filter_map(|v| { - let id = cx.tcx.hir().local_def_id(v.id); - (matches!(v.data, hir::VariantData::Unit(_)) + let id = cx.tcx.hir().local_def_id(v.hir_id); + (matches!(v.data, hir::VariantData::Unit(..)) && v.ident.as_str().starts_with('_') - && is_doc_hidden(cx.tcx.hir().attrs(v.id))) + && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) .then_some((id, v.span)) }); if let Some((id, span)) = iter.next() && iter.next().is_none() { - self.potential_enums.push((item.def_id.def_id, id, item.span, span)); + self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); } } } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 6f25a2ed8e43..38f41d077c16 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,12 +1,12 @@ use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{in_constant, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct ManualRemEuclid { - msrv: Option, + msrv: Msrv, } impl ManualRemEuclid { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]); impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::REM_EUCLID) { + if !self.msrv.meets(msrvs::REM_EUCLID) { return; } - if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) { + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) { return; } @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { && let Some(hir_id) = path_to_local(expr3) && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) { // Apply only to params or locals with annotated types - match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + match cx.tcx.hir().find_parent(hir_id) { Some(Node::Param(..)) => (), Some(Node::Local(local)) => { let Some(ty) = local.ty else { return }; diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 3181bc86d179..72cdb9c17361 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -50,12 +50,12 @@ declare_clippy_lint! { } pub struct ManualRetain { - msrv: Option, + msrv: Msrv, } impl ManualRetain { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -70,10 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain { && seg.args.is_none() && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) - && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) { - check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv); + && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id) + { + check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv); } } @@ -85,14 +86,14 @@ fn check_into_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER) && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id) - && cx.tcx.lang_items().require(hir::LangItem::IntoIterIntoIter).ok() == Some(into_iter_def_id) + && Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn() && match_acceptable_type(cx, left_expr, msrv) && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) { suggest(cx, parent_expr, left_expr, target_expr); @@ -104,7 +105,7 @@ fn check_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) @@ -127,9 +128,9 @@ fn check_to_owned( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::STRING_RETAIN) + if msrv.meets(msrvs::STRING_RETAIN) && let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD) @@ -140,7 +141,7 @@ fn check_to_owned( && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() - && is_type_diagnostic_item(cx, ty, sym::String) + && is_type_lang_item(cx, ty, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) { suggest(cx, parent_expr, left_expr, filter_expr); } @@ -215,10 +216,10 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo .any(|&method| match_def_path(cx, collect_def_id, method)) } -fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| { is_type_diagnostic_item(cx, expr_ty, *ty) - && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv)) + && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv)) }) } diff --git a/clippy_lints/src/manual_string_new.rs b/clippy_lints/src/manual_string_new.rs index 6acfb2ae3471..c20d7959fc4a 100644 --- a/clippy_lints/src/manual_string_new.rs +++ b/clippy_lints/src/manual_string_new.rs @@ -44,7 +44,7 @@ impl LateLintPass<'_> for ManualStringNew { let ty = cx.typeck_results().expr_ty(expr); match ty.kind() { ty::Adt(adt_def, _) if adt_def.is_struct() => { - if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) { + if cx.tcx.lang_items().string() != Some(adt_def.did()) { return; } }, diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 0976940afac3..c795c1d9a16c 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; -use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; @@ -11,7 +12,6 @@ use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; @@ -48,12 +48,12 @@ declare_clippy_lint! { } pub struct ManualStrip { - msrv: Option, + msrv: Msrv, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -68,7 +68,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { + if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { return; } @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { let test_span = expr.span.until(then.span); span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { - diag.span_note(test_span, &format!("the {kind_word} was tested here")); + diag.span_note(test_span, format!("the {kind_word} was tested here")); multispan_sugg( diag, &format!("try using the `strip_{kind_word}` method"), diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 32da37a862d8..59195d1ae4e0 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -119,7 +119,7 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { /// semicolons, which causes problems when generating a suggestion. Given an /// expression that evaluates to '()' or '!', recursively remove useless braces /// and semi-colons until is suitable for including in the suggestion template -fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option { +fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { if !is_unit_expression(cx, expr) { return None; } diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index fd14d868df34..33a052c41a38 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, @@ -63,7 +64,8 @@ fn check_arm<'tcx>( if !pat_contains_or(inner_then_pat); // the binding must come from the pattern of the containing match arm // .... => match { .. } - if let Some(binding_span) = find_pat_binding(outer_pat, binding_id); + if let (Some(binding_span), is_innermost_parent_pat_struct) + = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); // the "else" branches must be equal if match (outer_else_body, inner_else_body) { (None, None) => true, @@ -88,6 +90,13 @@ fn check_arm<'tcx>( if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, if outer_is_match { "match" } else { "if let" }, ); + // collapsing patterns need an explicit field name in struct pattern matching + // ex: Struct {x: Some(1)} + let replace_msg = if is_innermost_parent_pat_struct { + format!(", prefixed by {}:", snippet(cx, binding_span, "their field name")) + } else { + String::new() + }; span_lint_and_then( cx, COLLAPSIBLE_MATCH, @@ -96,7 +105,7 @@ fn check_arm<'tcx>( |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); - help_span.push_span_label(inner_then_pat.span, "with this pattern"); + help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); @@ -117,8 +126,9 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } } -fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { +fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: HirId) -> (Option, bool) { let mut span = None; + let mut is_innermost_parent_pat_struct = false; pat.walk_short(|p| match &p.kind { // ignore OR patterns PatKind::Or(_) => false, @@ -129,9 +139,12 @@ fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { } !found }, - _ => true, + _ => { + is_innermost_parent_pat_struct = matches!(p.kind, PatKind::Struct(..)); + true + }, }); - span + (span, is_innermost_parent_pat_struct) } fn pat_contains_or(pat: &Pat<'_>) -> bool { diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 2472acb6f6e8..d18c92caba2a 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath}; +use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; @@ -16,7 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { if let PatKind::TupleStruct( QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; - if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; + if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; let body = peel_blocks(arms[0].body); if path_to_local_id(body, arg); @@ -30,8 +30,9 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { Consider using `let`", "try this", format!( - "let {}({}) = {};", + "let {}({}{}) = {};", snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + if binding.0 == ByRef::Yes { "ref " } else { "" }, snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability), ), diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 66ba1f6f9c55..f6bf0e7aa1ad 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; -use rustc_hir::LangItem::OptionSome; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::{sym, SyntaxContext}; @@ -25,15 +25,13 @@ fn get_cond_expr<'tcx>( if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; if let PatKind::Binding(_,target, ..) = pat.kind; - if let (then_visitor, else_visitor) - = (is_some_expr(cx, target, ctxt, then_expr), - is_some_expr(cx, target, ctxt, else_expr)); - if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None` + if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) + || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None` then { return Some(SomeExpr { expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), needs_unsafe_block: contains_unsafe_block(cx, expr), - needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond + needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond }) } }; @@ -62,7 +60,7 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { // // } // Returns true if resolves to `Some(x)`, `false` otherwise -fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { +fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { // there can be not statements in the block as they would be removed when switching to `.filter` if let ExprKind::Call(callee, [arg]) = inner_expr.kind { @@ -74,6 +72,13 @@ fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, false } +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { + return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); + }; + false +} + // given the closure: `|| ` // returns `|&| ` fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String { diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 168c1e4d2e60..158e6caa4de5 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -282,7 +282,7 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. ExprKind::Lit(lit) => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(ref bytes) => Self::LitBytes(bytes), + LitKind::ByteStr(ref bytes, _) => Self::LitBytes(bytes), LitKind::Byte(val) => Self::LitInt(val.into()), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val), diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 1bf8d4e96ad4..065a5c72621c 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -31,19 +31,11 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e }; // Do we need to add ';' to suggestion ? - match match_body.kind { - ExprKind::Block(block, _) => { - // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, - _ => { - // expr_ty(body) == () - if cx.typeck_results().expr_ty(match_body).is_unit() { - snippet_body.push(';'); - } - }, + if let ExprKind::Block(block, _) = match_body.kind { + // macro + expr_ty(body) == () + if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { + snippet_body.push(';'); + } } let mut applicability = Applicability::MaybeIncorrect; @@ -148,8 +140,8 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { let map = &cx.tcx.hir(); - if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)) { - return match map.find(map.get_parent_node(parent_arm_expr.hir_id)) { + if let Some(Node::Expr(parent_arm_expr)) = map.find_parent(ex.hir_id) { + return match map.find_parent(parent_arm_expr.hir_id) { Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { span: parent_let_expr.span, pat_span: parent_let_expr.pat.span(), @@ -191,8 +183,7 @@ fn sugg_with_curlies<'a>( // If the parent is already an arm, and the body is another match statement, // we need curly braces around suggestion - let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); - if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let Node::Arm(arm) = &cx.tcx.hir().get_parent(match_expr.hir_id) { if let ExprKind::Match(..) = arm.body.kind { cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the match diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs index 6647322caa37..675a85ae5553 100644 --- a/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::Span; use super::MATCH_STR_CASE_MISMATCH; @@ -59,7 +59,7 @@ impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> { if let Some(case_method) = get_case_method(segment_ident) { let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs(); - if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str { + if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str { self.case_method = Some(case_method); return true; } diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 6f8d766aef7c..3126b590180e 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -30,7 +30,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let mut has_non_wild = false; for arm in arms { match peel_hir_pat_refs(arm.pat).0.kind { - PatKind::Wild => wildcard_span = Some(arm.pat.span), + PatKind::Wild if arm.guard.is_none() => wildcard_span = Some(arm.pat.span), PatKind::Binding(_, _, ident, None) => { wildcard_span = Some(arm.pat.span); wildcard_ident = Some(ident); @@ -45,8 +45,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden = adt_def.variants().iter().any(|x| is_hidden(cx, x)); - let mut missing_variants: Vec<_> = adt_def.variants().iter().filter(|x| !is_hidden(cx, x)).collect(); + let is_external = adt_def.did().as_local().is_none(); + let has_external_hidden = is_external && adt_def.variants().iter().any(|x| is_hidden(cx, x)); + let mut missing_variants: Vec<_> = adt_def + .variants() + .iter() + .filter(|x| !(is_external && is_hidden(cx, x))) + .collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -65,14 +70,14 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { _ => return, }; if arm.guard.is_none() { - missing_variants.retain(|e| e.ctor_def_id != Some(id)); + missing_variants.retain(|e| e.ctor_def_id() != Some(id)); } path }, PatKind::TupleStruct(path, patterns, ..) => { if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { - missing_variants.retain(|e| e.ctor_def_id != Some(id)); + missing_variants.retain(|e| e.ctor_def_id() != Some(id)); } } path @@ -122,18 +127,18 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { s }, variant.name, - match variant.ctor_kind { - CtorKind::Fn if variant.fields.len() == 1 => "(_)", - CtorKind::Fn => "(..)", - CtorKind::Const => "", - CtorKind::Fictive => "{ .. }", + match variant.ctor_kind() { + Some(CtorKind::Fn) if variant.fields.len() == 1 => "(_)", + Some(CtorKind::Fn) => "(..)", + Some(CtorKind::Const) => "", + None => "{ .. }", } ) }; match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_external_hidden => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -144,7 +149,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden { + let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 7d8171ead89e..7b15a307fecf 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -23,13 +23,13 @@ mod single_match; mod try_err; mod wild_in_or_pats; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs}; +use clippy_utils::{higher, in_constant, is_span_match}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; @@ -930,13 +930,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { - msrv: Option, + msrv: Msrv, infallible_destructuring_match_linted: bool, } impl Matches { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Matches::default() @@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { - if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) { match_same_arms::check(cx, arms); } @@ -1034,7 +1032,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { - if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { + if self.msrv.meets(msrvs::MATCHES_MACRO) { match_like_matches::check_if_let( cx, expr, diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 85269e533a06..f587c69f7302 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -83,8 +83,8 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t /// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that /// may have a surprising lifetime. -fn has_significant_drop_in_scrutinee<'tcx, 'a>( - cx: &'a LateContext<'tcx>, +fn has_significant_drop_in_scrutinee<'tcx>( + cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>, source: MatchSource, ) -> Option<(Vec, &'static str)> { @@ -226,7 +226,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { /// This will try to set the current suggestion (so it can be moved into the suggestions vec /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. - /// However, if we are at the the end of the chain, we want to accept whatever is there. (The + /// However, if we are at the end of the chain, we want to accept whatever is there. (The /// suggestion won't actually be output, but the diagnostic message will be output, so the user /// can determine the best way to handle the lint.) fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { @@ -377,7 +377,7 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> { } } -fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet { +fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet { let mut helper = ArmSigDropHelper::new(cx); for arm in arms { helper.visit_expr(arm.body); diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index d496107ffd6b..19b49c44d570 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{expr_block, snippet}; -use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs}; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::cmp::max; use rustc_errors::Applicability; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; +use rustc_span::sym; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -154,12 +153,12 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> } /// Returns `true` if the given type is an enum we know won't be expanded in the future -fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { +fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members - let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT]; + let candidates = [sym::Cow, sym::Option, sym::Result]; for candidate_ty in candidates { - if match_type(cx, ty, candidate_ty) { + if is_type_diagnostic_item(cx, ty, candidate_ty) { return true; } } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index c6cba81d8718..704c34c32bf7 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -107,7 +107,7 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); + if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); @@ -124,7 +124,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); + if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 0c4d9f100f7a..35024ec1224f 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } pub struct MemReplace { - msrv: Option, + msrv: Msrv, } impl MemReplace { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv, msrvs::MEM_TAKE) { + if self.msrv.meets(msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index cc26b0f7fa82..4720a6e6888b 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -41,7 +41,7 @@ pub(crate) trait BindInsteadOfMap { const GOOD_METHOD_NAME: &'static str; fn no_op_msg(cx: &LateContext<'_>) -> Option { - let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; + let variant_id = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM)?; let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}({})`, which is a no-op", @@ -52,7 +52,7 @@ pub(crate) trait BindInsteadOfMap { } fn lint_msg(cx: &LateContext<'_>) -> Option { - let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; + let variant_id = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM)?; let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", @@ -144,7 +144,7 @@ pub(crate) trait BindInsteadOfMap { fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { if_chain! { if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def(); - if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM); + if let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM); if adt.did() == cx.tcx.parent(vid); then {} else { return false; } } @@ -181,7 +181,7 @@ pub(crate) trait BindInsteadOfMap { fn is_variant(cx: &LateContext<'_>, res: Res) -> bool { if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) { + if let Some(variant_id) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM) { return cx.tcx.parent(id) == variant_id; } } diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs index fcfc25b523da..89aaad359d4a 100644 --- a/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; use super::BYTES_COUNT_TO_LEN; @@ -20,7 +19,7 @@ pub(super) fn check<'tcx>( if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); if cx.tcx.type_of(impl_id).is_str(); let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); - if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String); + if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String); then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 2e96346be977..d512cc4eeae1 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::BYTES_NTH; @@ -12,7 +11,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E let ty = cx.typeck_results().expr_ty(recv).peel_refs(); let caller_type = if ty.is_str() { "str" - } else if is_type_diagnostic_item(cx, ty, sym::String) { + } else if is_type_lang_item(cx, ty, LangItem::String) { "String" } else { return; diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index b3c2c7c9a2dc..0b3bf22743fa 100644 --- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,10 +1,13 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::source::{indent_of, reindent_multiline}; +use clippy_utils::ty::is_type_lang_item; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_hir::{Expr, ExprKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::{source_map::Spanned, symbol::sym, Span}; +use rustc_span::{source_map::Spanned, Span}; use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; @@ -15,6 +18,15 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, ) { + if let ExprKind::MethodCall(path_segment, ..) = recv.kind { + if matches!( + path_segment.ident.name.as_str(), + "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase" + ) { + return; + } + } + if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(method_id); @@ -26,15 +38,39 @@ pub(super) fn check<'tcx>( if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String); + if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String); then { - span_lint_and_help( + span_lint_and_then( cx, CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - call_span, + recv.span.to(call_span), "case-sensitive file extension comparison", - None, - "consider using a case-insensitive comparison instead", + |diag| { + diag.help("consider using a case-insensitive comparison instead"); + if let Some(mut recv_source) = snippet_opt(cx, recv.span) { + + if !cx.typeck_results().expr_ty(recv).is_ref() { + recv_source = format!("&{recv_source}"); + } + + let suggestion_source = reindent_multiline( + format!( + "std::path::Path::new({}) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))", + recv_source, ext_str.strip_prefix('.').unwrap()).into(), + true, + Some(indent_of(cx, call_span).unwrap_or(0) + 4) + ); + + diag.span_suggestion( + recv.span.to(call_span), + "use std::path::Path", + suggestion_source, + Applicability::MaybeIncorrect, + ); + } + } ); } } diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 7e808760663a..27a05337a290 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -9,8 +9,8 @@ use rustc_lint::LateContext; use rustc_lint::Lint; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, chain_methods: &[&str], lint: &'static Lint, diff --git a/clippy_lints/src/methods/chars_last_cmp.rs b/clippy_lints/src/methods/chars_last_cmp.rs index 07bbc5ca1bf4..2efff4c3c549 100644 --- a/clippy_lints/src/methods/chars_last_cmp.rs +++ b/clippy_lints/src/methods/chars_last_cmp.rs @@ -4,7 +4,7 @@ use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { true } else { diff --git a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs index c29ee0ec8c8c..5b8713f7d790 100644 --- a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -4,7 +4,7 @@ use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { true } else { diff --git a/clippy_lints/src/methods/chars_next_cmp.rs b/clippy_lints/src/methods/chars_next_cmp.rs index a6701d8830e7..b631fecab972 100644 --- a/clippy_lints/src/methods/chars_next_cmp.rs +++ b/clippy_lints/src/methods/chars_next_cmp.rs @@ -3,6 +3,6 @@ use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") } diff --git a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs index 28ede28e9358..caf21d3ff3bc 100644 --- a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -3,6 +3,6 @@ use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") } diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index 7c7938dd2e8b..3795c0ec2509 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_copy; use rustc_errors::Applicability; use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust}; +use rustc_middle::ty::{self, adjustment::Adjust, print::with_forced_trimmed_paths}; use rustc_span::symbol::{sym, Symbol}; use super::CLONE_DOUBLE_REF; @@ -47,10 +47,10 @@ pub(super) fn check( cx, CLONE_DOUBLE_REF, expr.span, - &format!( + &with_forced_trimmed_paths!(format!( "using `clone` on a double-reference; \ this will copy the reference of type `{ty}` instead of cloning the inner type" - ), + )), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; @@ -61,11 +61,11 @@ pub(super) fn check( } let refs = "&".repeat(n + 1); let derefs = "*".repeat(n); - let explicit = format!("<{refs}{ty}>::clone({snip})"); + let explicit = with_forced_trimmed_paths!(format!("<{refs}{ty}>::clone({snip})")); diag.span_suggestion( expr.span, "try dereferencing it", - format!("{refs}({derefs}{}).clone()", snip.deref()), + with_forced_trimmed_paths!(format!("{refs}({derefs}{}).clone()", snip.deref())), Applicability::MaybeIncorrect, ); diag.span_suggestion( @@ -129,7 +129,9 @@ pub(super) fn check( cx, CLONE_ON_COPY, expr.span, - &format!("using `clone` on type `{ty}` which implements the `Copy` trait"), + &with_forced_trimmed_paths!(format!( + "using `clone` on type `{ty}` which implements the `Copy` trait" + )), help, sugg, app, diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index e9aeab2d5b62..4e6ec61f6a83 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,25 +1,25 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => { + _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 501646863fe1..ac61b4377885 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _)) = method_call(parent) + && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -48,7 +48,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr(expr, |e| { - if let Some(("replace", _, [from, to], _)) = method_call(e) { + if let Some(("replace", _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); @@ -78,7 +78,7 @@ fn check_consecutive_replace_calls<'tcx>( .collect(); let app = Applicability::MachineApplicable; let earliest_replace_call = replace_methods.methods.front().unwrap(); - if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) { + if let Some((_, _, [..], span_lo, _)) = method_call(earliest_replace_call) { span_lint_and_sugg( cx, COLLAPSIBLE_STR_REPLACE, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 720d9a68c85e..ae03da0d3f9c 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,27 +1,27 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::has_debug_impl; -use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; pub(super) fn check( cx: &LateContext<'_>, _expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, - msrv: Option, expect_span: Span, err_span: Span, + msrv: &Msrv, ) { if_chain! { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if meets_msrv(msrv, msrvs::EXPECT_ERR); + if msrv.meets(msrvs::EXPECT_ERR); // Grabs the `Result` type let result_type = cx.typeck_results().expr_ty(recv); diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index d0cf411dfd34..a9189b31c571 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -33,7 +33,7 @@ pub(super) fn check<'tcx>( if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && { let arg_type = cx.typeck_results().expr_ty(receiver); let base_type = arg_type.peel_refs(); - *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::String) + *base_type.kind() == ty::Str || is_type_lang_item(cx, base_type, hir::LangItem::String) } { receiver } else { @@ -50,7 +50,7 @@ pub(super) fn check<'tcx>( // converted to string. fn requires_to_string(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { let arg_ty = cx.typeck_results().expr_ty(arg); - if is_type_diagnostic_item(cx, arg_ty, sym::String) { + if is_type_lang_item(cx, arg_ty, hir::LangItem::String) { return false; } if let ty::Ref(_, ty, ..) = arg_ty.kind() { diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index d59fefa1ddc0..cce8f797e98c 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_cfg_test; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,16 +18,16 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((EXPECT_USED, "an Option", "None", "")) + Some((EXPECT_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; let method = if is_err { "expect_err" } else { "expect" }; - if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } @@ -36,7 +36,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `{method}()` on `{kind}` value"), + &format!("used `{method}()` on {kind} value"), None, &format!("if this value is {none_prefix}`{none_value}`, it will panic"), ); diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 9719b2f1c512..fc80f2eeae01 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -17,7 +17,7 @@ use super::MANUAL_FILTER_MAP; use super::MANUAL_FIND_MAP; use super::OPTION_FILTER_MAP; -fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { +fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { match &expr.kind { hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, hir::ExprKind::Path(QPath::Resolved(_, segments)) => { @@ -30,12 +30,12 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy match closure_expr.kind { hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { if_chain! { - if ident.name == method_name; - if let hir::ExprKind::Path(path) = &receiver.kind; - if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); - then { - return arg_id == *local - } + if ident.name == method_name; + if let hir::ExprKind::Path(path) = &receiver.kind; + if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id); + then { + return arg_id == *local + } } false }, @@ -46,7 +46,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy } } -fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { +fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) } @@ -66,8 +66,8 @@ fn is_filter_some_map_unwrap( /// lint use of `filter().map()` or `find().map()` for `Iterators` #[allow(clippy::too_many_arguments)] -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_recv: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, @@ -92,92 +92,92 @@ pub(super) fn check<'tcx>( } if_chain! { - if is_trait_method(cx, map_recv, sym::Iterator); - - // filter(|x| ...is_some())... - if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; - // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { - (ref_pat, true) - } else { - (filter_param.pat, false) + if is_trait_method(cx, map_recv, sym::Iterator); + + // filter(|x| ...is_some())... + if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + + // .filter(..).map(|y| f(y).copied().unwrap()) + // ~~~~ + let map_arg_peeled = match map_arg.kind { + ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { + original_arg + }, + _ => map_arg, + }; + + // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap()) + let simple_equal = path_to_local_id(filter_arg, filter_param_id) + && path_to_local_id(map_arg_peeled, map_param_id); + + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) { - Some(true) + // let the filter closure arg and the map closure arg be equal + path_to_local_id(a_path, filter_param_id) + && path_to_local_id(b, map_param_id) + && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + }; + + if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled); + then { + let span = filter_span.with_hi(expr.span.hi()); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - - // ...map(|x| ...unwrap()) - if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - - // .filter(..).map(|y| f(y).copied().unwrap()) - // ~~~~ - let map_arg_peeled = match map_arg.kind { - ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => { - original_arg - }, - _ => map_arg, + ("filter", MANUAL_FILTER_MAP) }; + let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); + let (to_opt, deref) = if is_result { + (".ok()", String::new()) + } else { + let derefs = cx.typeck_results() + .expr_adjustments(map_arg) + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); - // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap()) - let simple_equal = path_to_local_id(filter_arg, filter_param_id) - && path_to_local_id(map_arg_peeled, map_param_id); - - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - path_to_local_id(a_path, filter_param_id) - && path_to_local_id(b, map_param_id) - && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + ("", "*".repeat(derefs)) }; - - if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled); - then { - let span = filter_span.with_hi(expr.span.hi()); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) - }; - let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`"); - let (to_opt, deref) = if is_result { - (".ok()", String::new()) - } else { - let derefs = cx.typeck_results() - .expr_adjustments(map_arg) - .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) - .count(); - - ("", "*".repeat(derefs)) - }; - let sugg = format!( - "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", - snippet(cx, map_arg.span, ".."), - ); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); - } + let sugg = format!( + "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})", + snippet(cx, map_arg.span, ".."), + ); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + } } } diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index ddf8a1f09b87..175e04f8ac06 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::FILTER_MAP_NEXT; @@ -14,10 +14,10 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) { + if !msrv.meets(msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 429cdc1918d7..06ecbce4e70e 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::peel_mid_ty_refs; +use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::{is_diag_item_method, is_diag_trait_item}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -19,6 +19,8 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv let (input_type, ref_count) = peel_mid_ty_refs(input_type); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())); if return_type == input_type; + if let Some(clone_trait) = cx.tcx.lang_items().clone_trait(); + if implements_trait(cx, return_type, clone_trait, &[]); then { let mut app = Applicability::MachineApplicable; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index ede3b8bb74e9..122088f4857c 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -12,8 +12,8 @@ use rustc_span::symbol::{sym, Symbol}; use super::INEFFICIENT_TO_STRING; /// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check<'tcx>( - cx: &LateContext<'tcx>, +pub fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, receiver: &hir::Expr<'_>, @@ -36,7 +36,7 @@ pub fn check<'tcx>( expr.span, &format!("calling `to_string` on `{arg_ty}`"), |diag| { - diag.help(&format!( + diag.help(format!( "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`" )); let mut applicability = Applicability::MachineApplicable; @@ -60,7 +60,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { return true; } - if is_type_diagnostic_item(cx, ty, sym::String) { + if is_type_lang_item(cx, ty, hir::LangItem::String) { return true; } diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 304024e80666..301aff5ae6ac 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -1,23 +1,22 @@ //! Lint for `c.is_digit(10)` use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::{ - consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, - source::snippet_with_applicability, + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability, }; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, self_arg: &'tcx Expr<'_>, radix: &'tcx Expr<'_>, - msrv: Option, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) { + if !msrv.meets(msrvs::IS_ASCII_DIGIT) { return; } diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 2244ebfb1292..c87f5daab6f2 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -6,7 +6,7 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; +use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::sym; @@ -30,9 +30,9 @@ pub(super) fn check<'tcx>( if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body); if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind; - let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) { - (key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value), - (PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key), + let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) { + (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value), + (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key), _ => return, }; @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind; if let [local_ident] = path.segments; - if local_ident.ident.as_str() == binded_ident.as_str(); + if local_ident.ident.as_str() == bound_ident.as_str(); then { span_lint_and_sugg( @@ -60,13 +60,23 @@ pub(super) fn check<'tcx>( applicability, ); } else { + let ref_annotation = if annotation.0 == ByRef::Yes { + "ref " + } else { + "" + }; + let mut_annotation = if annotation.1 == Mutability::Mut { + "mut " + } else { + "" + }; span_lint_and_sugg( cx, ITER_KV_MAP, expr.span, &format!("iterating on a map's {replacement_kind}s"), "try", - format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})", + format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})", snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)), applicability, ); diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 68d906c3ea39..c830958d5c80 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -10,7 +10,7 @@ use rustc_span::sym; use super::ITER_NTH_ZERO; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, expr, sym::Iterator); if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 4f73b3ec4224..70abe4891d98 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -25,7 +25,7 @@ impl IterType { } } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index 06a39c5997e2..b4210d875104 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( && let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id) && cx.tcx.trait_of_item(method_id) == Some(iter_id) && let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv) - && let Some(iter_assoc_ty) = get_associated_type(cx, cloned_recv_ty, iter_id, "Item") + && let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, "Item") && matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty)) { if needs_into_iter diff --git a/clippy_lints/src/methods/iter_skip_next.rs b/clippy_lints/src/methods/iter_skip_next.rs index beb772100aff..279175e20c37 100644 --- a/clippy_lints/src/methods/iter_skip_next.rs +++ b/clippy_lints/src/methods/iter_skip_next.rs @@ -29,7 +29,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr application = Applicability::Unspecified; diag.span_help( pat.span, - &format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), + format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")), ); } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index b80541b86479..a7284c644977 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -67,7 +67,7 @@ enum MinMax { Max, } -fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option { +fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { // `T::max_value()` `T::min_value()` inherent methods if_chain! { if let hir::ExprKind::Call(func, args) = &expr.kind; diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 8b6b8f1bf16c..a08f7254053f 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -36,14 +36,14 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { } } else { let ty = cx.typeck_results().expr_ty(e); - if is_type_diagnostic_item(cx, ty, sym::String) + if is_type_lang_item(cx, ty, LangItem::String) || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str)) || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String) + (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String) } } } @@ -58,11 +58,9 @@ pub(super) fn check( if_chain! { if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String); - if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); + if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id); if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); let ctxt = collect_expr.span.ctxt(); diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 7ce14ec080b1..52cc1e0464bf 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks}; +use clippy_utils::{is_diag_trait_item, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -9,19 +10,12 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_semver::RustcVersion; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; use super::MAP_CLONE; -pub(super) fn check<'tcx>( - cx: &LateContext<'_>, - e: &hir::Expr<'_>, - recv: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, - msrv: Option, -) { +pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); if cx.tcx.impl_of_method(method_id) @@ -97,10 +91,10 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } -fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option) { +fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) { let mut applicability = Applicability::MachineApplicable; - let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) { ("you are using an explicit closure for copying elements", "copied") } else { ("you are using an explicit closure for cloning elements", "cloned") diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index d420f144eea1..a0300d278709 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -11,18 +10,10 @@ use rustc_span::symbol::sym; use super::MAP_COLLECT_RESULT_UNIT; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - iter: &hir::Expr<'_>, - map_fn: &hir::Expr<'_>, - collect_recv: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); if_chain! { - // called on Iterator - if is_trait_method(cx, collect_recv, sym::Iterator); - // return of collect `Result<(),_>` - let collect_ret_ty = cx.typeck_results().expr_ty(expr); if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result); if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let Some(result_t) = substs.types().next(); diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index 1fb6617145e7..b773b3e423f4 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -6,7 +6,7 @@ use rustc_span::sym; use super::MAP_ERR_IGNORE; -pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result) diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 74fdead216b0..3122f72ee915 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use super::MAP_UNWRAP_OR; @@ -19,13 +18,13 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) { + if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) { return false; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 78c1b33ed97b..6a1575e182f0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -54,6 +54,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod mut_mutex_lock; +mod needless_collect; mod needless_option_as_deref; mod needless_option_take; mod no_effect_replace; @@ -69,6 +70,8 @@ mod path_buf_push_overwrite; mod range_zip_with_len; mod repeat_once; mod search_is_some; +mod seek_from_current; +mod seek_to_start_instead_of_rewind; mod single_char_add_str; mod single_char_insert_string; mod single_char_pattern; @@ -101,19 +104,16 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{ - contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty, -}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -158,9 +158,9 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); + /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l"); /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub COLLAPSIBLE_STR_REPLACE, perf, "collapse consecutive calls to str::replace (2 or more) into a single call" @@ -831,32 +831,30 @@ declare_clippy_lint! { /// etc. instead. /// /// ### Why is this bad? - /// The function will always be called and potentially - /// allocate an object acting as the default. + /// The function will always be called. This is only bad if it allocates or + /// does some non-trivial amount of work. /// /// ### Known problems - /// If the function has side-effects, not calling it will - /// change the semantic of the program, but you shouldn't rely on that anyway. + /// If the function has side-effects, not calling it will change the + /// semantic of the program, but you shouldn't rely on that. + /// + /// The lint also cannot figure out whether the function you call is + /// actually expensive to call or not. /// /// ### Example /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or(String::new()); + /// foo.unwrap_or(String::from("empty")); /// ``` /// /// Use instead: /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or_else(String::new); - /// - /// // or - /// - /// # let foo = Some(String::new()); - /// foo.unwrap_or_default(); + /// foo.unwrap_or_else(|| String::from("empty")); /// ``` #[clippy::version = "pre 1.29.0"] pub OR_FUN_CALL, - perf, + nursery, "using any `*or` method with a function call, which suggests `*or_else`" } @@ -1730,7 +1728,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). + /// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str). /// /// ### Why is this bad? /// Readability, this can be written more concisely as @@ -1820,6 +1818,7 @@ declare_clippy_lint! { /// - `or_else` to `or` /// - `get_or_insert_with` to `get_or_insert` /// - `ok_or_else` to `ok_or` + /// - `then` to `then_some` (for msrv >= 1.62.0) /// /// ### Why is this bad? /// Using eager evaluation is shorter and simpler in some cases. @@ -2096,8 +2095,7 @@ declare_clippy_lint! { /// let s = "Hello world!"; /// let cow = Cow::Borrowed(s); /// - /// let data = cow.into_owned(); - /// assert!(matches!(data, String)) + /// let _data: String = cow.into_owned(); /// ``` #[clippy::version = "1.65.0"] pub SUSPICIOUS_TO_OWNED, @@ -2428,7 +2426,7 @@ declare_clippy_lint! { /// ### Known problems /// /// The type of the resulting iterator might become incompatible with its usage - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub ITER_ON_SINGLE_ITEMS, nursery, "Iterator for array of length 1" @@ -2460,7 +2458,7 @@ declare_clippy_lint! { /// ### Known problems /// /// The type of the resulting iterator might become incompatible with its usage - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub ITER_ON_EMPTY_COLLECTIONS, nursery, "Iterator for empty array" @@ -3062,15 +3060,111 @@ declare_clippy_lint! { /// let map: HashMap = HashMap::new(); /// let values = map.values().collect::>(); /// ``` - #[clippy::version = "1.65.0"] + #[clippy::version = "1.66.0"] pub ITER_KV_MAP, complexity, "iterating on map using `iter` when `keys` or `values` would do" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks an argument of `seek` method of `Seek` trait + /// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead. + /// + /// ### Why is this bad? + /// + /// Readability. Use dedicated method. + /// + /// ### Example + /// + /// ```rust,no_run + /// use std::fs::File; + /// use std::io::{self, Write, Seek, SeekFrom}; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello")?; + /// eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?); + /// + /// Ok(()) + /// } + /// ``` + /// Use instead: + /// ```rust,no_run + /// use std::fs::File; + /// use std::io::{self, Write, Seek, SeekFrom}; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello")?; + /// eprintln!("Written {} bytes", f.stream_position()?); + /// + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub SEEK_FROM_CURRENT, + complexity, + "use dedicated method for seek from current position" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for jumps to the start of a stream that implements `Seek` + /// and uses the `seek` method providing `Start` as parameter. + /// + /// ### Why is this bad? + /// + /// Readability. There is a specific method that was implemented for + /// this exact scenario. + /// + /// ### Example + /// ```rust + /// # use std::io; + /// fn foo(t: &mut T) { + /// t.seek(io::SeekFrom::Start(0)); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::io; + /// fn foo(t: &mut T) { + /// t.rewind(); + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub SEEK_TO_START_INSTEAD_OF_REWIND, + complexity, + "jumping to the start of stream using `seek` method" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for functions collecting an iterator when collect + /// is not needed. + /// + /// ### Why is this bad? + /// `collect` causes the allocation of a new data structure, + /// when this allocation may not be needed. + /// + /// ### Example + /// ```rust + /// # let iterator = vec![1].into_iter(); + /// let len = iterator.clone().collect::>().len(); + /// // should be + /// let len = iterator.count(); + /// ``` + #[clippy::version = "1.30.0"] + pub NEEDLESS_COLLECT, + nursery, + "collecting an iterator when collect is not needed" +} + pub struct Methods { avoid_breaking_exported_api: bool, - msrv: Option, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, } @@ -3079,7 +3173,7 @@ impl Methods { #[must_use] pub fn new( avoid_breaking_exported_api: bool, - msrv: Option, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, ) -> Self { @@ -3192,16 +3286,19 @@ impl_lint_pass!(Methods => [ VEC_RESIZE_TO_ZERO, VERBOSE_FILE_READS, ITER_KV_MAP, + SEEK_FROM_CURRENT, + SEEK_TO_START_INSTEAD_OF_REWIND, + NEEDLESS_COLLECT, ]); /// Extracts a method call name, args, and `Span` of the method name. fn method_call<'tcx>( recv: &'tcx hir::Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> { - if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind { +) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> { + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span)); + return Some((name, receiver, args, path.ident.span, call_span)); } } None @@ -3229,7 +3326,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -3252,15 +3349,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let name = impl_item.ident.name.as_str(); let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir().expect_item(parent); - let self_ty = cx.tcx.type_of(item.def_id); + let self_ty = cx.tcx.type_of(item.owner_id); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind { - let method_sig = cx.tcx.fn_sig(impl_item.def_id); + let method_sig = cx.tcx.fn_sig(impl_item.owner_id); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); let first_arg_ty_opt = method_sig.inputs().iter().next().copied(); // if this impl block implements a trait, lint in trait definition instead - if !implements_trait && cx.access_levels.is_exported(impl_item.def_id.def_id) { + if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { // check missing trait implementations for method_config in &TRAIT_METHODS { if name == method_config.method_name @@ -3294,7 +3391,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if sig.decl.implicit_self.has_implicit_self() && !(self.avoid_breaking_exported_api - && cx.access_levels.is_exported(impl_item.def_id.def_id)) + && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id)) && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next() && let Some(first_arg_ty) = first_arg_ty_opt { @@ -3318,36 +3415,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id()); - // walk the return type and check for Self (this does not check associated types) - if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(ret_ty, self_adt) { - return; - } - } else if ret_ty.contains(self_ty) { + if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) { return; } - // if return type is impl trait, check the associated types - if let ty::Opaque(def_id, _) = *ret_ty.kind() { - // one of the associated types must be Self - for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { - let assoc_ty = match projection_predicate.term.unpack() { - ty::TermKind::Ty(ty) => ty, - ty::TermKind::Const(_c) => continue, - }; - // walk the associated type and check for Self - if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(assoc_ty, self_adt) { - return; - } - } else if assoc_ty.contains(self_ty) { - return; - } - } - } - } - if name == "new" && ret_ty != self_ty { span_lint( cx, @@ -3372,7 +3443,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()) + .self_ty() + .skip_binder(); wrong_self_convention::check( cx, item.ident.name.as_str(), @@ -3380,7 +3453,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { first_arg_ty, first_arg_span, false, - true + true, ); } } @@ -3389,7 +3462,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id()); - let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); + let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id()) + .self_ty() + .skip_binder(); if !ret_ty.contains(self_ty); then { @@ -3409,7 +3484,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((name, recv, args, span)) = method_call(expr) { + if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -3427,29 +3502,32 @@ impl Methods { ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); - }, - Some(("map", m_recv, [m_arg], _)) => { - map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); - }, - Some(("take", take_self_arg, [take_arg], _)) => { - if meets_msrv(self.msrv, msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); - } - }, - _ => {}, + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv), + ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + needless_collect::check(cx, span, expr, recv, call_span); + match method_call(recv) { + Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + iter_cloned_collect::check(cx, name, expr, recv2); + }, + Some(("map", m_recv, [m_arg], _, _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg); + }, + Some(("take", take_self_arg, [take_arg], _, _)) => { + if self.msrv.meets(msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, + _ => {}, + } }, ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, ("drain", [arg]) => { @@ -3461,8 +3539,8 @@ impl Methods { } }, ("expect", [_]) => match method_call(recv) { - Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3482,13 +3560,13 @@ impl Methods { flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), + Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), _ => {}, }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", _, [_], span2)) = method_call(recv) { + if let Some(("inspect", _, [_], span2, _)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -3501,19 +3579,19 @@ impl Methods { unit_hash::check(cx, expr, recv, arg); }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("iter" | "iter_mut" | "into_iter", []) => { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, ("join", [join_arg]) => { - if let Some(("collect", _, _, span)) = method_call(recv) { + if let Some(("collect", _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, ("last", []) | ("skip", [_]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3524,17 +3602,17 @@ impl Methods { }, (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { - map_clone::check(cx, expr, recv, m_arg, self.msrv); - if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) { + map_clone::check(cx, expr, recv, m_arg, &self.msrv); + if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } } else { map_err_ignore::check(cx, expr, m_arg); } - if let Some((name, recv2, args, span2)) = method_call(recv) { + if let Some((name, recv2, args, span2,_)) = method_call(recv) { match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv), + ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv), ("filter", [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, @@ -3551,11 +3629,11 @@ impl Methods { manual_ok_or::check(cx, expr, recv, def, map); }, ("next", []) => { - if let Some((name2, recv2, args2, _)) = method_call(recv) { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv), ("iter", []) => iter_next_slice::check(cx, expr, recv2), ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), ("skip_while", [_]) => skip_while_next::check(cx, expr), @@ -3564,10 +3642,10 @@ impl Methods { } }, ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), - Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), + Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), @@ -3595,13 +3673,24 @@ impl Methods { no_effect_replace::check(cx, expr, arg1, arg2); // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint - if name == "replace" && let Some(("replace", ..)) = method_call(recv) { + if self.msrv.meets(msrvs::PATTERN_TRAIT_CHAR_ARRAY) + && name == "replace" + && let Some(("replace", ..)) = method_call(recv) + { collapsible_str_replace::check(cx, expr, arg1, arg2); } }, ("resize", [count_arg, default_arg]) => { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, + ("seek", [arg]) => { + if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) { + seek_from_current::check(cx, expr, recv, arg); + } + if self.msrv.meets(msrvs::SEEK_REWIND) { + seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); + } + }, ("sort", []) => { stable_sort_primitive::check(cx, expr, recv); }, @@ -3614,7 +3703,7 @@ impl Methods { ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); + str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { @@ -3624,7 +3713,7 @@ impl Methods { }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [_arg]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3632,7 +3721,7 @@ impl Methods { }, ("take", []) => needless_option_take::check(cx, expr, recv), ("then", [arg]) => { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + if !self.msrv.meets(msrvs::BOOL_THEN_SOME) { return; } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); @@ -3647,13 +3736,13 @@ impl Methods { }, ("unwrap", []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _)) => { + Some(("get", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _)) => { + Some(("get_mut", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span)) => { + Some(("or", recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, @@ -3662,20 +3751,20 @@ impl Methods { }, ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests), ("unwrap_or", [u_arg]) => match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("map", m_recv, [m_arg], span)) => { + Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, - Some(("then_some", t_recv, [t_arg], _)) => { + Some(("then_some", t_recv, [t_arg], _, _)) => { obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); }, _ => {}, }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { - Some(("map", recv, [map_arg], _)) - if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, + Some(("map", recv, [map_arg], _, _)) + if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); @@ -3695,7 +3784,7 @@ impl Methods { } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) { + if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } @@ -3846,12 +3935,12 @@ impl SelfKind { return m == mutability && t == parent_ty; } - let trait_path = match mutability { - hir::Mutability::Not => &paths::ASREF_TRAIT, - hir::Mutability::Mut => &paths::ASMUT_TRAIT, + let trait_sym = match mutability { + hir::Mutability::Not => sym::AsRef, + hir::Mutability::Mut => sym::AsMut, }; - let Some(trait_def_id) = get_trait_def_id(cx, trait_path) else { + let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else { return false }; implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) @@ -3898,20 +3987,12 @@ impl OutType { (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), + (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Ref(_, _)), _ => false, } } } -fn is_bool(ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - matches!(path.res, Res::PrimTy(PrimTy::Bool)) - } else { - false - } -} - fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs similarity index 62% rename from clippy_lints/src/loops/needless_collect.rs rename to clippy_lints/src/methods/needless_collect.rs index 66f9e28596e8..f4d3ef3b7425 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -3,94 +3,99 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; -use if_chain::if_chain; +use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; +use clippy_utils::{ + can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, + CaptureKind, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind, +}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -use rustc_span::Span; +use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, Symbol}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; -pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - check_needless_collect_direct_usage(expr, cx); - check_needless_collect_indirect_usage(expr, cx); -} -fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if_chain! { - if let ExprKind::MethodCall(method, receiver, args, _) = expr.kind; - if let ExprKind::MethodCall(chain_method, ..) = receiver.kind; - if chain_method.ident.name == sym!(collect) && is_trait_method(cx, receiver, sym::Iterator); - then { - let ty = cx.typeck_results().expr_ty(receiver); - let mut applicability = Applicability::MaybeIncorrect; - let is_empty_sugg = "next().is_none()".to_string(); - let method_name = method.ident.name.as_str(); - let sugg = if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::LinkedList) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { - match method_name { - "len" => "count()".to_string(), - "is_empty" => is_empty_sugg, - "contains" => { - let contains_arg = snippet_with_applicability(cx, args[0].span, "??", &mut applicability); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{arg}| x == {pred})") - } - _ => return, - } - } - else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || - is_type_diagnostic_item(cx, ty, sym::HashMap) { - match method_name { - "is_empty" => is_empty_sugg, - _ => return, - } - } - else { - return; - }; - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - chain_method.ident.span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - applicability, - ); - } - } -} +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + name_span: Span, + collect_expr: &'tcx Expr<'_>, + iter_expr: &'tcx Expr<'tcx>, + call_span: Span, +) { + if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { + match parent { + Node::Expr(parent) => { + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { + let mut app = Applicability::MachineApplicable; + let name = name.ident.as_str(); + let collect_ty = cx.typeck_results().expr_ty(collect_expr); -fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if let ExprKind::Block(block, _) = expr.kind { - for stmt in block.stmts { - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(_, id, ..) = local.pat.kind; - if let Some(init_expr) = local.init; - if let ExprKind::MethodCall(method_name, iter_source, [], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - let ty = cx.typeck_results().expr_ty(init_expr); - if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || - is_type_diagnostic_item(cx, ty, sym::LinkedList); - let iter_ty = cx.typeck_results().expr_ty(iter_source); - if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)); - if let [iter_call] = &*iter_calls; - then { + let sugg: String = match name { + "len" => { + if let Some(adt) = collect_ty.ty_adt_def() + && matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap) + ) + { + "count()".into() + } else { + return; + } + }, + "is_empty" + if is_is_empty_sig(cx, parent.hir_id) + && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) => + { + "next().is_none()".into() + }, + "contains" => { + if is_contains_sig(cx, parent.hir_id, iter_expr) + && let Some(arg) = args.first() + { + let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind { + (arg.span, "") + } else { + (arg.span, "*") + }; + let snip = snippet_with_applicability(cx, span, "??", &mut app); + format!("any(|x| x == {prefix}{snip})") + } else { + return; + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_hi(parent.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + app, + ); + } + }, + Node::Local(l) => { + if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) + = l.pat.kind + && let ty = cx.typeck_results().expr_ty(collect_expr) + && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter() + .any(|item| is_type_diagnostic_item(cx, ty, item)) + && let iter_ty = cx.typeck_results().expr_ty(iter_expr) + && let Some(block) = get_enclosing_block(cx, l.hir_id) + && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)) + && let [iter_call] = &*iter_calls + { let mut used_count_visitor = UsedCountVisitor { cx, id, @@ -102,20 +107,20 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo } // Suggest replacing iter_call with iter_replacement, and removing stmt - let mut span = MultiSpan::from_span(method_name.ident.span); + let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, - init_expr.hir_id, + collect_expr.hir_id, span, NEEDLESS_COLLECT_MSG, |diag| { - let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx)); diag.multipart_suggestion( iter_call.get_suggestion_text(), vec![ - (stmt.span, String::new()), + (l.span, String::new()), (iter_call.span, iter_replacement) ], Applicability::MaybeIncorrect, @@ -123,11 +128,61 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo }, ); } - } + }, + _ => (), } } } +/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` +fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { + cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { + let sig = cx.tcx.fn_sig(id).skip_binder(); + sig.inputs().len() == 1 && sig.output().is_bool() + }) +} + +/// Checks if `::Item` is the same as `::Item` +fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool { + let item = Symbol::intern("Item"); + if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty]) + && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty]) + && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( + cx.param_env, + cx.tcx.mk_projection(into_iter_item_proj.def_id, into_iter_item_proj.substs) + ) + { + iter_item_ty == into_iter_item_ty + } else { + false + } +} + +/// Checks if the given method call matches the expected signature of +/// `([&[mut]] self, &::Item) -> bool` +fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool { + let typeck = cx.typeck_results(); + if let Some(id) = typeck.type_dependent_def_id(call_id) + && let sig = cx.tcx.fn_sig(id) + && sig.skip_binder().output().is_bool() + && let [_, search_ty] = *sig.skip_binder().inputs() + && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind() + && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(iter_item) = cx.tcx + .associated_items(iter_trait) + .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait) + && let substs = cx.tcx.mk_substs([GenericArg::from(typeck.expr_ty_adjusted(iter_expr))].into_iter()) + && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs) + && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) + { + item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + } else { + false + } +} + struct IterFunction { func: IterFunctionKind, span: Span, diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs index a76341855b6d..01655e860c43 100644 --- a/clippy_lints/src/methods/no_effect_replace.rs +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::SpanlessEq; use if_chain::if_chain; use rustc_ast::LitKind; -use rustc_hir::ExprKind; +use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::NO_EFFECT_REPLACE; @@ -16,7 +15,7 @@ pub(super) fn check<'tcx>( arg2: &'tcx rustc_hir::Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(expr).peel_refs(); - if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) { + if !(ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) { return; } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 6fb92d1c663c..3e33f9193374 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,27 +1,27 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks}; +use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::OPTION_AS_REF_DEREF; /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, is_mut: bool, - msrv: Option, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { + if !msrv.meets(msrvs::OPTION_AS_DEREF) { return; } @@ -32,8 +32,7 @@ pub(super) fn check<'tcx>( return; } - let deref_aliases: [&[&str]; 9] = [ - &paths::DEREF_TRAIT_METHOD, + let deref_aliases: [&[&str]; 8] = [ &paths::DEREF_MUT_TRAIT_METHOD, &paths::CSTRING_AS_C_STR, &paths::OS_STRING_AS_OS_STR, @@ -45,12 +44,14 @@ pub(super) fn check<'tcx>( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => cx - .qpath_res(expr_qpath, map_arg.hir_id) - .opt_def_id() - .map_or(false, |fun_def_id| { - deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) - }), + hir::ExprKind::Path(ref expr_qpath) => { + cx.qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) + || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }) + }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); @@ -68,7 +69,8 @@ pub(super) fn check<'tcx>( if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj; then { let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + cx.tcx.is_diagnostic_item(sym::deref_method, method_did) + || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) } else { false } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 30421a6dd5af..4c6328481e43 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, "))); } - diag.multipart_suggestion(&format!("use `{suggest}` instead"), suggestion, applicability); + diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability); }); } } @@ -97,7 +97,7 @@ struct UnwrapVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { self.identifiers.insert(ident(path)); walk_path(self, path); } @@ -116,7 +116,7 @@ struct MapExprVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { + fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) { if self.identifiers.contains(&ident(path)) { self.found_identifier = true; return; diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 6a35024d0361..4460f38fcc18 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; -use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths}; +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_trait_item, last_path_segment}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Symbol}; use std::borrow::Cow; use super::OR_FUN_CALL; @@ -83,16 +83,18 @@ pub(super) fn check<'tcx>( method_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, + // `Some` if fn has second argument + second_arg: Option<&hir::Expr<'_>>, span: Span, // None if lambda is required fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ - (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"), - (&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), - (&paths::RESULT, true, &["or", "unwrap_or"], "else"), + const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ + (sym::BTreeEntry, false, &["or_insert"], "with"), + (sym::HashMapEntry, false, &["or_insert"], "with"), + (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"), + (sym::Result, true, &["or", "unwrap_or"], "else"), ]; if_chain! { @@ -104,36 +106,45 @@ pub(super) fn check<'tcx>( let self_ty = cx.typeck_results().expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0)); + KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0)); if poss.contains(&name); then { - let macro_expanded_snipped; - let sugg: Cow<'_, str> = { + let sugg = { let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { (false, Some(fun_span)) => (fun_span, false), _ => (arg.span, true), }; - let snippet = { - let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); - if not_macro_argument_snippet == "vec![]" { - macro_expanded_snipped = snippet(cx, snippet_span, ".."); + + let format_span = |span: Span| { + let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, ".."); + let snip = if not_macro_argument_snippet == "vec![]" { + let macro_expanded_snipped = snippet(cx, snippet_span, ".."); match macro_expanded_snipped.strip_prefix("$crate::vec::") { - Some(stripped) => Cow::from(stripped), - None => macro_expanded_snipped + Some(stripped) => Cow::Owned(stripped.to_owned()), + None => macro_expanded_snipped, } - } - else { + } else { not_macro_argument_snippet - } + }; + + snip.to_string() }; - if use_lambda { + let snip = format_span(snippet_span); + let snip = if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snippet}").into() + format!("|{l_arg}| {snip}") } else { - snippet + snip + }; + + if let Some(f) = second_arg { + let f = format_span(f.span); + format!("{snip}, {f}") + } else { + snip } }; let span_replace_word = method_span.with_hi(span.hi()); @@ -150,8 +161,8 @@ pub(super) fn check<'tcx>( } } - if let [arg] = args { - let inner_arg = if let hir::ExprKind::Block( + let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| { + if let hir::ExprKind::Block( hir::Block { stmts: [], expr: Some(expr), @@ -163,19 +174,32 @@ pub(super) fn check<'tcx>( expr } else { arg - }; + } + }; + + if let [arg] = args { + let inner_arg = extract_inner_arg(arg); match inner_arg.kind { hir::ExprKind::Call(fun, or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) { let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span); + check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span); } }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, receiver, arg, expr.span, None); + check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None); }, _ => (), } } + + // `map_or` takes two arguments + if let [arg, lambda] = args { + let inner_arg = extract_inner_arg(arg); + if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind { + let fun_span = if or_args.is_empty() { Some(fun.span) } else { None }; + check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span); + } + } } diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs index 0a14f9216ab3..a345ec813ff5 100644 --- a/clippy_lints/src/methods/repeat_once.rs +++ b/clippy_lints/src/methods/repeat_once.rs @@ -1,11 +1,10 @@ use clippy_utils::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; -use rustc_span::sym; use super::REPEAT_ONCE; @@ -37,7 +36,7 @@ pub(super) fn check<'tcx>( format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), Applicability::MachineApplicable, ); - } else if is_type_diagnostic_item(cx, ty, sym::String) { + } else if is_type_lang_item(cx, ty, LangItem::String) { span_lint_and_sugg( cx, REPEAT_ONCE, diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 324c9c17b5a9..1c031ad6acba 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use clippy_utils::{is_trait_method, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -105,7 +105,7 @@ pub(super) fn check<'tcx>( else if search_method == "find" { let is_string_or_str_slice = |e| { let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); - if is_type_diagnostic_item(cx, self_ty, sym::String) { + if is_type_lang_item(cx, self_ty, hir::LangItem::String) { true } else { *self_ty.kind() == ty::Str diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs new file mode 100644 index 000000000000..361a3082f949 --- /dev/null +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -0,0 +1,48 @@ +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability, + ty::implements_trait, +}; + +use super::SEEK_FROM_CURRENT; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + + if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) { + if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) { + let mut applicability = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); + + span_lint_and_sugg( + cx, + SEEK_FROM_CURRENT, + expr.span, + "using `SeekFrom::Current` to start from current position", + "replace with", + format!("{snip}.stream_position()"), + applicability, + ); + } + } +} + +fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Call(f, args) = expr.kind && + let ExprKind::Path(ref path) = f.kind && + let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() && + match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) { + // check if argument of `SeekFrom::Current` is `0` + if args.len() == 1 && + let ExprKind::Lit(ref lit) = args[0].kind && + let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node { + return true + } + } + + false +} diff --git a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs new file mode 100644 index 000000000000..660b7049cce9 --- /dev/null +++ b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, is_expr_used_or_unified, match_def_path, paths}; +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::SEEK_TO_START_INSTEAD_OF_REWIND; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + recv: &'tcx Expr<'_>, + arg: &'tcx Expr<'_>, + name_span: Span, +) { + // Get receiver type + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + + if is_expr_used_or_unified(cx.tcx, expr) { + return; + } + + if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) && + implements_trait(cx, ty, seek_trait_id, &[]) && + let ExprKind::Call(func, args1) = arg.kind && + let ExprKind::Path(ref path) = func.kind && + let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && + match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) && + args1.len() == 1 && + let ExprKind::Lit(ref lit) = args1[0].kind && + let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + { + let method_call_span = expr.span.with_lo(name_span.lo()); + span_lint_and_then( + cx, + SEEK_TO_START_INSTEAD_OF_REWIND, + method_call_span, + "used `seek` to go to the start of the stream", + |diag| { + let app = Applicability::MachineApplicable; + + diag.span_suggestion(method_call_span, "replace with", "rewind()", app); + }, + ); + } +} diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 1acac59144ce..d00708e828ea 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; -use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; @@ -12,7 +13,6 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; @@ -24,7 +24,7 @@ pub(super) fn check( self_arg: &Expr<'_>, pat_arg: &Expr<'_>, count: u128, - msrv: Option, + msrv: &Msrv, ) { if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; @@ -34,7 +34,7 @@ pub(super) fn check( IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; - let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE); + let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE); match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), @@ -167,7 +167,7 @@ fn check_manual_split_once_indirect( }; diag.span_suggestion_verbose( local.span, - &format!("try `{r}split_once`"), + format!("try `{r}split_once`"), format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), app, ); diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 6974260f70db..f35d81cee8e9 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -1,26 +1,29 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::method_chain_args; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; use super::STRING_EXTEND_CHARS; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if !is_type_diagnostic_item(cx, obj_ty, sym::String) { + if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) { return; } if let Some(arglists) = method_chain_args(arg, &["chars"]) { let target = &arglists[0].0; let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { - "" - } else if is_type_diagnostic_item(cx, self_ty, sym::String) { + if matches!(target.kind, hir::ExprKind::Index(..)) { + "&" + } else { + "" + } + } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { "&" } else { return; diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 851cdf544550..2ac0786b37b1 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -8,7 +8,7 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; -pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, count_recv, sym::Iterator); let closure = expr_or_init(cx, map_arg); diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index 15c1c618c513..fe88fa41fd91 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, print::with_forced_trimmed_paths}; use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; @@ -24,7 +24,9 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - cx, SUSPICIOUS_TO_OWNED, expr.span, - &format!("this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"), + &with_forced_trimmed_paths!(format!( + "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" + )), "consider using, depending on intent", format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), app, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 1966a85f7a73..52a4ff7d1ae4 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -2,10 +2,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::{fn_def_id, get_parent_expr}; use rustc_errors::Applicability; -use rustc_hir::{def_id::DefId, Expr, ExprKind, LangItem}; +use rustc_hir::{def_id::DefId, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::{sym, Symbol}; @@ -54,7 +54,7 @@ pub fn check_for_loop_iter( if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator); let collection_ty = cx.typeck_results().expr_ty(collection); if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]); - if let Some(into_iter_item_ty) = get_associated_type(cx, collection_ty, into_iterator_trait_id, "Item"); + if let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item"); if iter_item_ty == into_iter_item_ty; if let Some(collection_snippet) = snippet_opt(cx, collection.span); @@ -100,5 +100,5 @@ pub fn check_for_loop_iter( /// Returns true if the named method is `IntoIterator::into_iter`. pub fn is_into_iter(cx: &LateContext<'_>, callee_def_id: DefId) -> bool { - cx.tcx.lang_items().require(LangItem::IntoIterIntoIter) == Ok(callee_def_id) + Some(callee_def_id) == cx.tcx.lang_items().into_iter_fn() } diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index 973b8a7e6bf6..087e1e4343b7 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -1,10 +1,10 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::{Ref, Slice}; -use rustc_span::{sym, Span}; +use rustc_span::Span; use super::UNNECESSARY_JOIN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( // the turbofish for collect is ::> if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind(); if let Slice(slice) = ref_type.kind(); - if is_type_diagnostic_item(cx, *slice, sym::String); + if is_type_lang_item(cx, *slice, LangItem::String); // the argument for join is "" if let ExprKind::Lit(spanned) = &join_arg.kind; if let LitKind::Str(symbol, _) = spanned.node; @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_JOIN, span.with_hi(expr.span.hi()), - r#"called `.collect>().join("")` on an iterator"#, + r#"called `.collect::>().join("")` on an iterator"#, "try using", "collect::()".to_owned(), applicability, diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 0e73459ad65f..47e2e744112c 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| { diag.span_suggestion( span, - &format!("use `{simplify_using}(..)` instead"), + format!("use `{simplify_using}(..)` instead"), format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), applicability, ); diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 9ab0d6141146..b812e81cb107 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -1,25 +1,22 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; -use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; -use rustc_hir_analysis::check::{FnCtxt, Inherited}; +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node}; +use rustc_hir_typeck::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::EarlyBinder; -use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; -use rustc_semver::RustcVersion; +use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; use rustc_span::{sym, Symbol}; use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; -use std::cmp::max; use super::UNNECESSARY_TO_OWNED; @@ -29,7 +26,7 @@ pub fn check<'tcx>( method_name: Symbol, receiver: &'tcx Expr<'_>, args: &'tcx [Expr<'_>], - msrv: Option, + msrv: &Msrv, ) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); @@ -146,7 +143,7 @@ fn check_addr_of_expr( if_chain! { if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); if implements_trait(cx, receiver_ty, deref_trait_id, &[]); - if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty); + if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty); then { if n_receiver_refs > 0 { span_lint_and_sugg( @@ -200,7 +197,7 @@ fn check_into_iter_call_arg( expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -215,7 +212,7 @@ fn check_into_iter_call_arg( if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" @@ -263,11 +260,22 @@ fn check_other_call_arg<'tcx>( if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id; let receiver_ty = cx.typeck_results().expr_ty(receiver); - if can_change_type(cx, maybe_arg, receiver_ty); // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // `Target = T`. - if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; - let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty))); + if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { + Some((n_refs, receiver_ty)) + } else if trait_predicate.def_id() != deref_trait_id { + Some((1, cx.tcx.mk_ref( + cx.tcx.lifetimes.re_erased, + ty::TypeAndMut { + ty: receiver_ty, + mutbl: Mutability::Not, + }, + ))) + } else { + None + }; + if can_change_type(cx, maybe_arg, receiver_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { span_lint_and_sugg( @@ -337,12 +345,12 @@ fn get_input_traits_and_projections<'tcx>( let mut projection_predicates = Vec::new(); for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() { match predicate.kind().skip_binder() { - PredicateKind::Trait(trait_predicate) => { + PredicateKind::Clause(Clause::Trait(trait_predicate)) => { if trait_predicate.trait_ref.self_ty() == input { trait_predicates.push(trait_predicate); } }, - PredicateKind::Projection(projection_predicate) => { + PredicateKind::Clause(Clause::Projection(projection_predicate)) => { if projection_predicate.projection_ty.self_ty() == input { projection_predicates.push(projection_predicate); } @@ -363,7 +371,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< && let output_ty = return_ty(cx, item.hir_id()) && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id()) && Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id()); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id); fn_ctxt.can_coerce(ty, output_ty) }) { if has_lifetime(output_ty) && has_lifetime(ty) { @@ -378,14 +386,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< Node::Expr(parent_expr) => { if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr) { - if cx.tcx.lang_items().require(LangItem::IntoFutureIntoFuture) == Ok(callee_def_id) { - return false; - } - let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) && let Some(param_ty) = fn_sig.inputs().get(arg_index) && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind() + // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021 + && (*param_index as usize) < call_substs.len() { if fn_sig .inputs() @@ -399,10 +405,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let mut trait_predicates = cx.tcx.param_env(callee_def_id) .caller_bounds().iter().filter(|predicate| { - if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == *param_ty { - true - } else { + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) + = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == *param_ty + { + true + } else { false } }); @@ -419,10 +427,8 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< if trait_predicates.any(|predicate| { let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst); - let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); - !cx.tcx - .infer_ctxt() - .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); + !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) }) { return false; } @@ -476,7 +482,7 @@ fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: D } /// Returns true if the named method is `ToString::to_string` and it's called on a type that -/// is string-like i.e. implements `AsRef` or `Deref`. +/// is string-like i.e. implements `AsRef` or `Deref`. fn is_to_string_on_string_like<'a>( cx: &LateContext<'_>, call_expr: &'a Expr<'a>, @@ -492,7 +498,7 @@ fn is_to_string_on_string_like<'a>( && let GenericArgKind::Type(ty) = generic_arg.unpack() && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) - && (implements_trait(cx, ty, deref_trait_id, &[cx.tcx.types.str_.into()]) || + && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) || implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) { true } else { diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index ee17f2d7889e..90983f249cd5 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_test_function, is_lint_allowed}; +use clippy_utils::{is_in_cfg_test, is_lint_allowed}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -18,16 +18,16 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((UNWRAP_USED, "an Option", "None", "")) + Some((UNWRAP_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; let method_suffix = if is_err { "_err" } else { "" }; - if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } @@ -45,7 +45,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `unwrap{method_suffix}()` on `{kind}` value"), + &format!("used `unwrap{method_suffix}()` on {kind} value"), None, &help, ); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 516dee20f8b1..9f4beb92b9d2 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -9,12 +9,14 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{ExpnKind, Span}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq}; +use clippy_utils::{ + get_parent_expr, in_constant, is_integer_literal, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq, +}; declare_clippy_lint! { /// ### What it does @@ -120,14 +122,28 @@ declare_clippy_lint! { "using `0 as *{const, mut} T`" } -declare_lint_pass!(MiscLints => [ +pub struct LintPass { + std_or_core: &'static str, +} +impl Default for LintPass { + fn default() -> Self { + Self { std_or_core: "std" } + } +} +impl_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, SHORT_CIRCUIT_STATEMENT, ZERO_PTR, ]); -impl<'tcx> LateLintPass<'tcx> for MiscLints { +impl<'tcx> LateLintPass<'tcx> for LintPass { + fn check_crate(&mut self, cx: &LateContext<'_>) { + if is_no_std_crate(cx) { + self.std_or_core = "core"; + } + } + fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -231,7 +247,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Cast(e, ty) = expr.kind { - check_cast(cx, expr.span, e, ty); + self.check_cast(cx, expr.span, e, ty); return; } if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) { @@ -310,26 +326,28 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool { } } -fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { - if_chain! { - if let TyKind::Ptr(ref mut_ty) = ty.kind; - if is_integer_literal(e, 0); - if !in_constant(cx, e.hir_id); - then { - let (msg, sugg_fn) = match mut_ty.mutbl { - Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"), - Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"), - }; +impl LintPass { + fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) { + if_chain! { + if let TyKind::Ptr(ref mut_ty) = ty.kind; + if is_integer_literal(e, 0); + if !in_constant(cx, e.hir_id); + then { + let (msg, sugg_fn) = match mut_ty.mutbl { + Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"), + Mutability::Not => ("`0 as *const _` detected", "ptr::null"), + }; - let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { - (format!("{sugg_fn}()"), Applicability::MachineApplicable) - } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { - (format!("{sugg_fn}::<{mut_ty_snip}>()"), Applicability::MachineApplicable) - } else { - // `MaybeIncorrect` as type inference may not work with the suggested code - (format!("{sugg_fn}()"), Applicability::MaybeIncorrect) - }; - span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); + let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind { + (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable) + } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) { + (format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable) + } else { + // `MaybeIncorrect` as type inference may not work with the suggested code + (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect) + }; + span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl); + } } } } diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs index 27e7f8505eb5..eda4376f200e 100644 --- a/clippy_lints/src/misc_early/literal_suffix.rs +++ b/clippy_lints/src/misc_early/literal_suffix.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::{SEPARATED_LITERAL_SUFFIX, UNSEPARATED_LITERAL_SUFFIX}; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &str, sugg_type: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffix: &str, sugg_type: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, SEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should not be separated by an underscore"), "remove the underscore", format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]), @@ -25,7 +25,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str, suffix: &s span_lint_and_sugg( cx, UNSEPARATED_LITERAL_SUFFIX, - lit.span, + lit_span, &format!("{sugg_type} type suffix should be separated by an underscore"), "add an underscore", format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]), diff --git a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs index 263ee1e945a2..ddb8b9173a53 100644 --- a/clippy_lints/src/misc_early/mixed_case_hex_literals.rs +++ b/clippy_lints/src/misc_early/mixed_case_hex_literals.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast::Lit; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::MIXED_CASE_HEX_LITERALS; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_snip: &str) { let Some(maybe_last_sep_idx) = lit_snip.len().checked_sub(suffix.len() + 1) else { return; // It's useless so shouldn't lint. }; @@ -23,7 +23,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, suffix: &str, lit_snip: &s span_lint( cx, MIXED_CASE_HEX_LITERALS, - lit.span, + lit_span, "inconsistent casing in hexadecimal literal", ); break; diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index c8227ca44505..78be6b9e23fa 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -9,7 +9,8 @@ mod zero_prefixed_literal; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind}; +use rustc_ast::token; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -374,42 +375,43 @@ impl EarlyLintPass for MiscEarlyLints { return; } - if let ExprKind::Lit(ref lit) = expr.kind { - MiscEarlyLints::check_lit(cx, lit); + if let ExprKind::Lit(lit) = expr.kind { + MiscEarlyLints::check_lit(cx, lit, expr.span); } double_neg::check(cx, expr); } } impl MiscEarlyLints { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // We test if first character in snippet is a number, because the snippet could be an expansion // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. // See for a regression. // FIXME: Find a better way to detect those cases. - let lit_snip = match snippet_opt(cx, lit.span) { + let lit_snip = match snippet_opt(cx, span) { Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; - if let LitKind::Int(value, lit_int_type) = lit.kind { + let lit_kind = LitKind::from_token_lit(lit); + if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind { let suffix = match lit_int_type { LitIntType::Signed(ty) => ty.name_str(), LitIntType::Unsigned(ty) => ty.name_str(), LitIntType::Unsuffixed => "", }; - literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); + literal_suffix::check(cx, span, &lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip); + mixed_case_hex_literals::check(cx, span, suffix, &lit_snip); } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { // nothing to do } else if value != 0 && lit_snip.starts_with('0') { - zero_prefixed_literal::check(cx, lit, &lit_snip); + zero_prefixed_literal::check(cx, span, &lit_snip); } - } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { + } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind { let suffix = float_ty.name_str(); - literal_suffix::check(cx, lit, &lit_snip, suffix, "float"); + literal_suffix::check(cx, span, &lit_snip, suffix, "float"); } } } diff --git a/clippy_lints/src/misc_early/zero_prefixed_literal.rs b/clippy_lints/src/misc_early/zero_prefixed_literal.rs index 4963bba82f2d..4f9578d1b257 100644 --- a/clippy_lints/src/misc_early/zero_prefixed_literal.rs +++ b/clippy_lints/src/misc_early/zero_prefixed_literal.rs @@ -1,29 +1,33 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::Lit; use rustc_errors::Applicability; use rustc_lint::EarlyContext; +use rustc_span::Span; use super::ZERO_PREFIXED_LITERAL; -pub(super) fn check(cx: &EarlyContext<'_>, lit: &Lit, lit_snip: &str) { +pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str) { + let trimmed_lit_snip = lit_snip.trim_start_matches(|c| c == '_' || c == '0'); span_lint_and_then( cx, ZERO_PREFIXED_LITERAL, - lit.span, + lit_span, "this is a decimal constant", |diag| { diag.span_suggestion( - lit.span, + lit_span, "if you mean to use a decimal constant, remove the `0` to avoid confusion", - lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(), - Applicability::MaybeIncorrect, - ); - diag.span_suggestion( - lit.span, - "if you mean to use an octal constant, use `0o`", - format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')), + trimmed_lit_snip.to_string(), Applicability::MaybeIncorrect, ); + // do not advise to use octal form if the literal cannot be expressed in base 8. + if !lit_snip.contains(|c| c == '8' || c == '9') { + diag.span_suggestion( + lit_span, + "if you mean to use an octal constant, use `0o`", + format!("0o{trimmed_lit_snip}"), + Applicability::MaybeIncorrect, + ); + } }, ); } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 71cc0d0a81cd..5bc04bc17fb4 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::ty::has_drop; -use clippy_utils::{ - fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method, -}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -11,7 +10,6 @@ use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -75,12 +73,12 @@ declare_clippy_lint! { impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); pub struct MissingConstForFn { - msrv: Option, + msrv: Msrv, } impl MissingConstForFn { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) { + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, err.as_ref()); } diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 641dbb7f7a74..9942e8115b7f 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -132,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { - let at_root = cx.tcx.local_parent(it.def_id.def_id) == CRATE_DEF_ID; + let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; if at_root { return; } @@ -156,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::Use(..) => return, }; - let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id()); + let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); let attrs = cx.tcx.hir().attrs(it.hir_id()); if !is_from_proc_macro(cx, it) { @@ -165,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { - let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id()); + let (article, desc) = cx.tcx.article_and_description(trait_item.owner_id.to_def_id()); let attrs = cx.tcx.hir().attrs(trait_item.hir_id()); if !is_from_proc_macro(cx, trait_item) { @@ -175,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. - if let Some(cid) = cx.tcx.associated_item(impl_item.def_id).impl_container(cx.tcx) { + if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { if cx.tcx.impl_trait_ref(cid).is_some() { return; } @@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { return; } - let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id()); + let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); let attrs = cx.tcx.hir().attrs(impl_item.hir_id()); if !is_from_proc_macro(cx, impl_item) { self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc); @@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - let attrs = cx.tcx.hir().attrs(v.id); + let attrs = cx.tcx.hir().attrs(v.hir_id); if !is_from_proc_macro(cx, v) { self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant"); } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 872679f25ab5..773174679dbd 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -59,42 +59,45 @@ impl LateLintPass<'_> for ImportRename { fn check_crate(&mut self, cx: &LateContext<'_>) { for Rename { path, rename } in &self.conf_renames { let segs = path.split("::").collect::>(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.renames.insert(id, Symbol::intern(rename)); } } } fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Use(path, UseKind::Single) = &item.kind; - if let Res::Def(_, id) = path.res; - if let Some(name) = self.renames.get(&id); - // Remove semicolon since it is not present for nested imports - let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); - if let Some(snip) = snippet_opt(cx, span_without_semi); - if let Some(import) = match snip.split_once(" as ") { - None => Some(snip.as_str()), - Some((import, rename)) => { - if rename.trim() == name.as_str() { - None - } else { - Some(import.trim()) + if let ItemKind::Use(path, UseKind::Single) = &item.kind { + for &res in &path.res { + if_chain! { + if let Res::Def(_, id) = res; + if let Some(name) = self.renames.get(&id); + // Remove semicolon since it is not present for nested imports + let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); + if let Some(snip) = snippet_opt(cx, span_without_semi); + if let Some(import) = match snip.split_once(" as ") { + None => Some(snip.as_str()), + Some((import, rename)) => { + if rename.trim() == name.as_str() { + None + } else { + Some(import.trim()) + } + }, + }; + then { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!( + "{import} as {name}", + ), + Applicability::MachineApplicable, + ); } - }, - }; - then { - span_lint_and_sugg( - cx, - MISSING_ENFORCED_IMPORT_RENAMES, - span_without_semi, - "this import should be renamed", - "try", - format!( - "{import} as {name}", - ), - Applicability::MachineApplicable, - ); + } } } } diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 655df5419ac6..5a459548153a 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { return; } - if !cx.access_levels.is_exported(it.def_id.def_id) { + if !cx.effective_visibilities.is_exported(it.owner_id.def_id) { return; } match it.kind { @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { match tit_.kind { hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, hir::TraitItemKind::Fn(..) => { - if cx.tcx.impl_defaultness(tit.id.def_id).has_value() { + if cx.tcx.impl_defaultness(tit.id.owner_id).has_value() { // trait method with default body needs inline in case // an impl is not provided let desc = "a default trait method"; @@ -142,24 +142,24 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { } // If the item being implemented is not exported, then we don't need #[inline] - if !cx.access_levels.is_exported(impl_item.def_id.def_id) { + if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { return; } let desc = match impl_item.kind { hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) => return, + hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, }; - let assoc_item = cx.tcx.associated_item(impl_item.def_id); + let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { TraitContainer => Some(container_id), - ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.def_id), + ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; if let Some(trait_def_id) = trait_def_id { - if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.def_id.def_id) { + if trait_def_id.is_local() && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { // If a trait is being implemented for an item, and the // trait is not exported, we don't need #[inline] return; diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs new file mode 100644 index 000000000000..e99081ad0620 --- /dev/null +++ b/clippy_lints/src/missing_trait_methods.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::span_is_local; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Impl, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::AssocItem; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks if a provided method is used implicitly by a trait + /// implementation. A usage example would be a wrapper where every method + /// should perform some operation before delegating to the inner type's + /// implemenation. + /// + /// This lint should typically be enabled on a specific trait `impl` item + /// rather than globally. + /// + /// ### Why is this bad? + /// Indicates that a method is missing. + /// + /// ### Example + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// } + /// ``` + /// Use instead: + /// ```rust + /// trait Trait { + /// fn required(); + /// + /// fn provided() {} + /// } + /// + /// # struct Type; + /// #[warn(clippy::missing_trait_methods)] + /// impl Trait for Type { + /// fn required() { /* ... */ } + /// + /// fn provided() { /* ... */ } + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MISSING_TRAIT_METHODS, + restriction, + "trait implementation uses default provided method" +} +declare_lint_pass!(MissingTraitMethods => [MISSING_TRAIT_METHODS]); + +impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id()) + && span_is_local(item.span) + && let ItemKind::Impl(Impl { + items, + of_trait: Some(trait_ref), + .. + }) = item.kind + && let Some(trait_id) = trait_ref.trait_def_id() + { + let mut provided: DefIdMap<&AssocItem> = cx + .tcx + .provided_trait_methods(trait_id) + .map(|assoc| (assoc.def_id, assoc)) + .collect(); + + for impl_item in *items { + if let Some(def_id) = impl_item.trait_item_def_id { + provided.remove(&def_id); + } + } + + cx.tcx.with_stable_hashing_context(|hcx| { + for assoc in provided.values_sorted(&hcx, true) { + let source_map = cx.tcx.sess.source_map(); + let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id)); + + span_lint_and_help( + cx, + MISSING_TRAIT_METHODS, + source_map.guess_head_span(item.span), + &format!("missing trait method provided by default: `{}`", assoc.name), + Some(definition_span), + "implement the method", + ); + } + }); + } + } +} diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 6752976348f6..f0be7771bb1a 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -186,7 +186,7 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) { let map = &vis.cx.tcx.hir(); let mut cur_id = vis.write_expr.hir_id; loop { - let parent_id = map.get_parent_node(cur_id); + let parent_id = map.parent_id(cur_id); if parent_id == cur_id { break; } @@ -218,7 +218,7 @@ enum StopEarly { Stop, } -fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { +fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { if expr.hir_id == vis.last_expr.hir_id { return StopEarly::KeepGoing; } @@ -265,7 +265,7 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) - StopEarly::KeepGoing } -fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { +fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs new file mode 100644 index 000000000000..191da3085be2 --- /dev/null +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -0,0 +1,186 @@ +use clippy_utils::{ + diagnostics::span_lint_and_then, + visitors::{for_each_expr_with_closures, Descend, Visitable}, +}; +use core::ops::ControlFlow::Continue; +use hir::{ + def::{DefKind, Res}, + BlockCheckMode, ExprKind, QPath, UnOp, Unsafety, +}; +use rustc_ast::Mutability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `unsafe` blocks that contain more than one unsafe operation. + /// + /// ### Why is this bad? + /// Combined with `undocumented_unsafe_blocks`, + /// this lint ensures that each unsafe operation must be independently justified. + /// Combined with `unused_unsafe`, this lint also ensures + /// elimination of unnecessary unsafe blocks through refactoring. + /// + /// ### Example + /// ```rust + /// /// Reads a `char` from the given pointer. + /// /// + /// /// # Safety + /// /// + /// /// `ptr` must point to four consecutive, initialized bytes which + /// /// form a valid `char` when interpreted in the native byte order. + /// fn read_char(ptr: *const u8) -> char { + /// // SAFETY: The caller has guaranteed that the value pointed + /// // to by `bytes` is a valid `char`. + /// unsafe { char::from_u32_unchecked(*ptr.cast::()) } + /// } + /// ``` + /// Use instead: + /// ```rust + /// /// Reads a `char` from the given pointer. + /// /// + /// /// # Safety + /// /// + /// /// - `ptr` must be 4-byte aligned, point to four consecutive + /// /// initialized bytes, and be valid for reads of 4 bytes. + /// /// - The bytes pointed to by `ptr` must represent a valid + /// /// `char` when interpreted in the native byte order. + /// fn read_char(ptr: *const u8) -> char { + /// // SAFETY: `ptr` is 4-byte aligned, points to four consecutive + /// // initialized bytes, and is valid for reads of 4 bytes. + /// let int_value = unsafe { *ptr.cast::() }; + /// + /// // SAFETY: The caller has guaranteed that the four bytes + /// // pointed to by `bytes` represent a valid `char`. + /// unsafe { char::from_u32_unchecked(int_value) } + /// } + /// ``` + #[clippy::version = "1.68.0"] + pub MULTIPLE_UNSAFE_OPS_PER_BLOCK, + restriction, + "more than one unsafe operation per `unsafe` block" +} +declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]); + +impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) || in_external_macro(cx.tcx.sess, block.span) { + return; + } + let mut unsafe_ops = vec![]; + collect_unsafe_exprs(cx, block, &mut unsafe_ops); + if unsafe_ops.len() > 1 { + span_lint_and_then( + cx, + MULTIPLE_UNSAFE_OPS_PER_BLOCK, + block.span, + &format!( + "this `unsafe` block contains {} unsafe operations, expected only one", + unsafe_ops.len() + ), + |diag| { + for (msg, span) in unsafe_ops { + diag.span_note(span, msg); + } + }, + ); + } + } +} + +fn collect_unsafe_exprs<'tcx>( + cx: &LateContext<'tcx>, + node: impl Visitable<'tcx>, + unsafe_ops: &mut Vec<(&'static str, Span)>, +) { + for_each_expr_with_closures(cx, node, |expr| { + match expr.kind { + ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), + + ExprKind::Field(e, _) => { + if cx.typeck_results().expr_ty(e).is_union() { + unsafe_ops.push(("union field access occurs here", expr.span)); + } + }, + + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def(DefKind::Static(Mutability::Mut), _), + .. + }, + )) => { + unsafe_ops.push(("access of a mutable static occurs here", expr.span)); + }, + + ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty_adjusted(e).is_unsafe_ptr() => { + unsafe_ops.push(("raw pointer dereference occurs here", expr.span)); + }, + + ExprKind::Call(path_expr, _) => match path_expr.kind { + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def(kind, def_id), + .. + }, + )) if kind.is_fn_like() => { + let sig = cx.tcx.bound_fn_sig(*def_id); + if sig.0.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe function call occurs here", expr.span)); + } + }, + + ExprKind::Path(QPath::TypeRelative(..)) => { + if let Some(sig) = cx + .typeck_results() + .type_dependent_def_id(path_expr.hir_id) + .map(|def_id| cx.tcx.bound_fn_sig(def_id)) + { + if sig.0.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe function call occurs here", expr.span)); + } + } + }, + + _ => {}, + }, + + ExprKind::MethodCall(..) => { + if let Some(sig) = cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .map(|def_id| cx.tcx.bound_fn_sig(def_id)) + { + if sig.0.unsafety() == Unsafety::Unsafe { + unsafe_ops.push(("unsafe method call occurs here", expr.span)); + } + } + }, + + ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { + if matches!( + lhs.kind, + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def(DefKind::Static(Mutability::Mut), _), + .. + } + )) + ) { + unsafe_ops.push(("modification of a mutable static occurs here", expr.span)); + collect_unsafe_exprs(cx, rhs, unsafe_ops); + return Continue(Descend::No); + } + }, + + _ => {}, + }; + + Continue::<(), _>(Descend::Yes) + }); +} diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 25d6ca83a94b..a651020ca656 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::trait_ref_of_method; +use clippy_utils::{def_path_def_ids, trait_ref_of_method}; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; use std::iter; @@ -78,26 +79,44 @@ declare_clippy_lint! { "Check for mutable `Map`/`Set` key type" } -declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); +#[derive(Clone)] +pub struct MutableKeyType { + ignore_interior_mutability: Vec, + ignore_mut_def_ids: FxHashSet, +} + +impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + self.ignore_mut_def_ids.clear(); + let mut path = Vec::new(); + for ty in &self.ignore_interior_mutability { + path.extend(ty.split("::")); + for id in def_path_def_ids(cx, &path[..]) { + self.ignore_mut_def_ids.insert(id); + } + path.clear(); + } + } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { - if trait_ref_of_method(cx, item.def_id.def_id).is_none() { - check_sig(cx, item.hir_id(), sig.decl); + if trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + self.check_sig(cx, item.hir_id(), sig.decl); } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } @@ -105,71 +124,81 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { if let hir::PatKind::Wild = local.pat.kind { return; } - check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat)); + self.check_ty_(cx, local.span, cx.typeck_results().pat_ty(local.pat)); } } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { - let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); - let fn_sig = cx.tcx.fn_sig(fn_def_id); - for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { - check_ty(cx, hir_ty.span, *ty); +impl MutableKeyType { + pub fn new(ignore_interior_mutability: Vec) -> Self { + Self { + ignore_interior_mutability, + ignore_mut_def_ids: FxHashSet::default(), + } } - check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); -} -// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased -// generics (because the compiler cannot ensure immutability for unknown types). -fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { - let ty = ty.peel_refs(); - if let Adt(def, substs) = ty.kind() { - let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) { - span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); + fn check_sig(&self, cx: &LateContext<'_>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { + let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); + let fn_sig = cx.tcx.fn_sig(fn_def_id); + for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { + self.check_ty_(cx, hir_ty.span, *ty); } + self.check_ty_(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); } -} -/// Determines if a type contains interior mutability which would affect its implementation of -/// [`Hash`] or [`Ord`]. -fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool { - match *ty.kind() { - Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span), - Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span), - Array(inner_ty, size) => { - size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) - && is_interior_mutable_type(cx, inner_ty, span) - }, - Tuple(fields) => fields.iter().any(|ty| is_interior_mutable_type(cx, ty, span)), - Adt(def, substs) => { - // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to - // that of their type parameters. Note: we don't include `HashSet` and `HashMap` - // because they have no impl for `Hash` or `Ord`. - let is_std_collection = [ - sym::Option, - sym::Result, - sym::LinkedList, - sym::Vec, - sym::VecDeque, - sym::BTreeMap, - sym::BTreeSet, - sym::Rc, - sym::Arc, - ] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - let is_box = Some(def.did()) == cx.tcx.lang_items().owned_box(); - if is_std_collection || is_box { - // The type is mutable if any of its type parameters are - substs.types().any(|ty| is_interior_mutable_type(cx, ty, span)) - } else { - !ty.has_escaping_bound_vars() - && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() - && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased + // generics (because the compiler cannot ensure immutability for unknown types). + fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { + let ty = ty.peel_refs(); + if let Adt(def, substs) = ty.kind() { + let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); + if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) { + span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); } - }, - _ => false, + } + } + + /// Determines if a type contains interior mutability which would affect its implementation of + /// [`Hash`] or [`Ord`]. + fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match *ty.kind() { + Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty), + Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty), + Array(inner_ty, size) => { + size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) + && self.is_interior_mutable_type(cx, inner_ty) + }, + Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)), + Adt(def, substs) => { + // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to + // that of their type parameters. Note: we don't include `HashSet` and `HashMap` + // because they have no impl for `Hash` or `Ord`. + let def_id = def.did(); + let is_std_collection = [ + sym::Option, + sym::Result, + sym::LinkedList, + sym::Vec, + sym::VecDeque, + sym::BTreeMap, + sym::BTreeSet, + sym::Rc, + sym::Arc, + ] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); + let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); + if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) { + // The type is mutable if any of its type parameters are + substs.types().any(|ty| self.is_interior_mutable_type(cx, ty)) + } else { + !ty.has_escaping_bound_vars() + && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.is_freeze(cx.tcx, cx.param_env) + } + }, + _ => false, + } } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index cb16f00047a3..64d8333a093b 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -68,13 +68,15 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - span_lint( - self.cx, - MUT_MUT, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { + if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) { + span_lint( + self.cx, + MUT_MUT, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); + } } } } @@ -84,7 +86,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { return; } - if let hir::TyKind::Rptr( + if let hir::TyKind::Ref( _, hir::MutTy { ty: pty, @@ -92,7 +94,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { }, ) = ty.kind { - if let hir::TyKind::Rptr( + if let hir::TyKind::Ref( _, hir::MutTy { mutbl: hir::Mutability::Mut, diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 09cb53331763..dc866ab6373b 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -1,6 +1,6 @@ //! Checks for uses of mutex where an atomic value could be used //! -//! This lint is **warn** by default +//! This lint is **allow** by default use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; @@ -20,6 +20,10 @@ declare_clippy_lint! { /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and /// faster. /// + /// On the other hand, `Mutex`es are, in general, easier to + /// verify correctness. An atomic does not behave the same as + /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s commentary for more details. + /// /// ### Known problems /// This lint cannot detect if the mutex is actually used /// for waiting before a critical section. @@ -39,8 +43,8 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, - nursery, - "using a mutex where an atomic value could be used instead" + restriction, + "using a mutex where an atomic value could be used instead." } declare_clippy_lint! { diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index f2ffac85bf40..5457eeec4eac 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -124,7 +124,7 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl); } }, - TyKind::Rptr(lifetime, mut_ty) => { + TyKind::Ref(lifetime, mut_ty) => { if_chain! { if let TyKind::Path(None, path) = &mut_ty.ty.kind; if let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind; diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 10c3ff026b6d..498e1408e52a 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -36,14 +36,14 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { - fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() { + fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { + if ref_pat.span.from_expansion() { // OK, simple enough, lints doesn't check in macro. return; } // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { let Node::Pat(pat) = node else { break }; if matches!(pat.kind, PatKind::Or(_)) { @@ -52,20 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { } // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return }; - match sub_pat.kind { + match pat.kind { // Check sub_pat got a `ref` keyword (excluding `ref mut`). PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { span_lint_and_then( cx, NEEDLESS_BORROWED_REFERENCE, - pat.span, + ref_pat.span, "this pattern takes a reference on something that is being dereferenced", |diag| { // `&ref ident` // ^^^^^ - let span = pat.span.until(ident.span); + let span = ref_pat.span.until(ident.span); diag.span_suggestion_verbose( span, "try removing the `&ref` part", @@ -84,41 +84,71 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { }), after, ) => { - let mut suggestions = Vec::new(); - - for element_pat in itertools::chain(before, after) { - if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { - // `&[..., ref ident, ...]` - // ^^^^ - let span = element_pat.span.until(ident.span); - suggestions.push((span, String::new())); - } else { - return; - } - } + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, + } + } +} - if !suggestions.is_empty() { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - pat.span, - "dereferencing a slice pattern where every element takes a reference", - |diag| { - // `&[...]` - // ^ - let span = pat.span.until(sub_pat.span); - suggestions.push((span, String::new())); +fn check_subpatterns<'tcx>( + cx: &LateContext<'tcx>, + message: &str, + ref_pat: &Pat<'_>, + pat: &Pat<'_>, + subpatterns: impl IntoIterator>, +) { + let mut suggestions = Vec::new(); - diag.multipart_suggestion( - "try removing the `&` and `ref` parts", - suggestions, - Applicability::MachineApplicable, - ); - }, - ); - } + for subpattern in subpatterns { + match subpattern.kind { + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + // `ref ident` + // ^^^^ + let span = subpattern.span.until(ident.span); + suggestions.push((span, String::new())); }, - _ => {}, + PatKind::Wild => {}, + _ => return, } } + + if !suggestions.is_empty() { + span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| { + // `&pat` + // ^ + let span = ref_pat.span.until(pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }); + } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 6f0e755466e5..38a75034cd31 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -287,7 +287,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; -fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) { +fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { // snip is the whole *help* message that appears after the warning. // message is the warning message. // expr is the expression which the lint warning message refers to. @@ -313,7 +313,7 @@ fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, ); } -fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); @@ -327,7 +327,7 @@ fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &' ) } -fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); // Region B @@ -361,7 +361,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: ) } -fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) { +fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { if_chain! { if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind; if let Some(last_stmt) = loop_block.stmts.last(); diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9d26e5900866..5a9387b34cc1 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -180,10 +180,13 @@ fn assignment_suggestions<'tcx>( let suggestions = assignments .iter() .flat_map(|assignment| { - [ - assignment.span.until(assignment.rhs_span), - assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), - ] + let mut spans = vec![assignment.span.until(assignment.rhs_span)]; + + if assignment.rhs_span.hi() != assignment.span.hi() { + spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); + } + + spans }) .map(|span| (span, String::new())) .collect::>(); @@ -281,7 +284,7 @@ fn check<'tcx>( diag.span_suggestion( assign.lhs_span, - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), let_snippet, Applicability::MachineApplicable, ); @@ -301,7 +304,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); @@ -332,7 +335,7 @@ fn check<'tcx>( diag.span_suggestion_verbose( usage.stmt.span.shrink_to_lo(), - &format!("declare `{binding_name}` here"), + format!("declare `{binding_name}` here"), format!("{let_snippet} = "), applicability, ); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 178c973981b1..8c9d4c5cfe66 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{ + implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item, +}; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -11,18 +13,18 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{ BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind, }; -use rustc_hir::{HirIdMap, HirIdSet}; -use rustc_hir_analysis::expr_use_visitor as euv; +use rustc_hir::{HirIdMap, HirIdSet, LangItem}; +use rustc_hir_typeck::expr_use_visitor as euv; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::kw; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_span::{sym, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::misc::can_type_implement_copy; +use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; use std::borrow::Cow; declare_clippy_lint! { @@ -98,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) @@ -124,7 +126,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .filter_map(|obligation| { // Note that we do not want to deal with qualified predicates here. match obligation.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Trait(pred)) if pred.def_id() != sized_trait => Some(pred), + Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => { + Some(pred) + }, _ => None, } }) @@ -138,10 +142,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .. } = { let mut ctx = MovedVariablesCtxt::default(); - cx.tcx.infer_ctxt().enter(|infcx| { - euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body); ctx }; @@ -186,8 +188,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if !is_self(arg); if !ty.is_mutable_ptr(); if !is_copy(cx, ty); - if ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env); - if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])); + if ty.is_sized(cx.tcx, cx.param_env); + if !allowed_traits.iter().any(|&t| implements_trait_with_env(cx.tcx, cx.param_env, ty, t, [None])); if !implements_borrow_trait; if !all_borrowable_trait; @@ -198,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { let sugg = |diag: &mut Diagnostic| { if let ty::Adt(def, ..) = ty.kind() { if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { - if can_type_implement_copy( + if type_allowed_to_implement_copy( cx.tcx, cx.param_env, ty, @@ -251,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { } } - if is_type_diagnostic_item(cx, ty, sym::String) { + if is_type_lang_item(cx, ty, LangItem::String) { if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) { diag.span_suggestion( @@ -342,11 +344,5 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read( - &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index a7e0e35787cf..a022fc156fca 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::implements_trait; -use clippy_utils::{self, get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind, 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::sym; declare_clippy_lint! { /// ### What it does @@ -47,18 +47,16 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if !in_external_macro(cx.sess(), expr.span); if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; if let ExprKind::Binary(ref op, left, _) = inner.kind; if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; then { - let ty = cx.typeck_results().expr_ty(left); let implements_ord = { - if let Some(id) = get_trait_def_id(cx, &paths::ORD) { + if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) { implements_trait(cx, ty, id, &[]) } else { return; @@ -67,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { let implements_partial_ord = { if let Some(id) = cx.tcx.lang_items().partial_ord_trait() { - implements_trait(cx, ty, id, &[]) + implements_trait(cx, ty, id, &[ty.into()]) } else { return; } @@ -81,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { "the use of negated comparison operators on partially ordered \ types produces code that is hard to read and refactor, please \ consider using the `partial_cmp` method instead, to make it \ - clear that the two values could be incomparable" + clear that the two values could be incomparable", ); } } diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 6017117e1ecc..54a3c82b713d 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // can't be implemented for unsafe new return; } - if cx.tcx.is_doc_hidden(impl_item.def_id.def_id) { + if cx.tcx.is_doc_hidden(impl_item.owner_id.def_id) { // shouldn't be implemented when it is hidden in docs return; } @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { if_chain! { if sig.decl.inputs.is_empty(); if name == sym::new; - if cx.access_levels.is_reachable(impl_item.def_id.def_id); + if cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id); let self_def_id = cx.tcx.hir().get_parent_item(id); let self_ty = cx.tcx.type_of(self_def_id); if self_ty == return_ty(cx, id); diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 819646bb6780..79c1ae4861e8 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::ops::Deref; @@ -159,8 +160,11 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; + let ctxt = stmt.span.ctxt(); + if expr.span.ctxt() == ctxt; if let Some(reduced) = reduce_expression(cx, expr); - if !&reduced.iter().any(|e| e.span.from_expansion()); + if !in_external_macro(cx.sess(), stmt.span); + if reduced.iter().all(|e| e.span.ctxt() == ctxt); then { if let ExprKind::Index(..) = &expr.kind { let snippet = if let (Some(arr), Some(func)) = diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index a6742824bc56..07fd321d69fc 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -20,7 +20,7 @@ use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, InnerSpan, Span, DUMMY_SP}; +use rustc_span::{sym, InnerSpan, Span}; // FIXME: this is a correctness problem but there's no suitable // warn-by-default category. @@ -136,7 +136,7 @@ fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // since it works when a pointer indirection involves (`Cell<*const T>`). // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; // but I'm not sure whether it's a decent way, if possible. - cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env) } fn is_value_unfrozen_raw<'tcx>( @@ -303,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); if let Some(of_assoc_item) = cx .tcx - .associated_item(impl_item.def_id) + .associated_item(impl_item.owner_id) .trait_item_def_id; if cx .tcx @@ -366,7 +366,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let mut dereferenced_expr = expr; let mut needs_check_adjustment = true; loop { - let parent_id = cx.tcx.hir().get_parent_node(cur_expr.hir_id); + let parent_id = cx.tcx.hir().parent_id(cur_expr.hir_id); if parent_id == cur_expr.hir_id { break; } diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index ddef7352de88..839c3a3815c2 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -89,8 +89,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { if let Some(trait_id) = trait_ref.trait_def_id(); if send_trait == trait_id; if hir_impl.polarity == ImplPolarity::Positive; - if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); - if let self_ty = ty_trait_ref.self_ty(); + if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); + if let self_ty = ty_trait_ref.subst_identity().self_ty(); if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind(); then { let mut non_send_fields = Vec::new(); diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index f380a5065827..7376ab0c846d 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -56,11 +56,11 @@ impl EarlyLintPass for OctalEscapes { return; } - if let ExprKind::Lit(lit) = &expr.kind { - if matches!(lit.token_lit.kind, LitKind::Str) { - check_lit(cx, &lit.token_lit, lit.span, true); - } else if matches!(lit.token_lit.kind, LitKind::ByteStr) { - check_lit(cx, &lit.token_lit, lit.span, false); + if let ExprKind::Lit(token_lit) = &expr.kind { + if matches!(token_lit.kind, LitKind::Str) { + check_lit(cx, token_lit, expr.span, true); + } else if matches!(token_lit.kind, LitKind::ByteStr) { + check_lit(cx, token_lit, expr.span, false); } } } @@ -125,7 +125,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { if is_string { "string" } else { "byte string" } ), |diag| { - diag.help(&format!( + diag.help(format!( "octal escapes are not supported, `\\0` is always a null {}", if is_string { "character" } else { "byte" } )); @@ -139,7 +139,7 @@ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { // suggestion 2: unambiguous null byte diag.span_suggestion( span, - &format!( + format!( "if the null {} is intended, disambiguate using", if is_string { "character" } else { "byte" } ), diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index d64a9cf71e17..8b77a5c99f76 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::subst::{EarlyBinder, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, ConstKind}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Ident}; @@ -227,25 +227,25 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions. // It can't be renamed, and it can't be removed without removing it from multiple functions. let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) { - Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0), + Some(Node::Item(i)) => (i.owner_id.to_def_id(), FnKind::Fn, 0), Some(Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(ref sig, _), - def_id, + owner_id, .. })) => ( - def_id.to_def_id(), + owner_id.to_def_id(), FnKind::TraitFn, usize::from(sig.decl.implicit_self.has_implicit_self()), ), Some(Node::ImplItem(&ImplItem { kind: ImplItemKind::Fn(ref sig, _), - def_id, + owner_id, .. })) => { #[allow(trivial_casts)] - if let Some(Node::Item(item)) = get_parent_node(cx.tcx, def_id.into()) - && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id) - && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id + if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into()) + && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::subst_identity) + && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id { ( trait_item_id, @@ -253,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { - (def_id.to_def_id(), FnKind::Fn, 0) + (owner_id.to_def_id(), FnKind::Fn, 0) } }, _ => return, diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8827daaa3ee7..cff82b875f11 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,25 +1,30 @@ use super::ARITHMETIC_SIDE_EFFECTS; -use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; +use clippy_utils::{ + consts::{constant, constant_simple}, + diagnostics::span_lint, + peel_hir_expr_refs, peel_hir_expr_unary, +}; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::{Span, Spanned}; -const HARD_CODED_ALLOWED: &[&str] = &[ - "&str", - "f32", - "f64", - "std::num::Saturating", - "std::num::Wrapping", - "std::string::String", +const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[ + ["f32", "f32"], + ["f64", "f64"], + ["std::num::Saturating", "std::num::Saturating"], + ["std::num::Wrapping", "std::num::Wrapping"], + ["std::string::String", "&str"], ]; +const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; #[derive(Debug)] pub struct ArithmeticSideEffects { - allowed: FxHashSet, + allowed_binary: FxHashMap>, + allowed_unary: FxHashSet, // Used to check whether expressions are constants, such as in enum discriminants and consts const_span: Option, expr_span: Option, @@ -29,37 +34,55 @@ impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); impl ArithmeticSideEffects { #[must_use] - pub fn new(mut allowed: FxHashSet) -> Self { - allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from)); + pub fn new(user_allowed_binary: Vec<[String; 2]>, user_allowed_unary: Vec) -> Self { + let mut allowed_binary: FxHashMap> = <_>::default(); + for [lhs, rhs] in user_allowed_binary.into_iter().chain( + HARD_CODED_ALLOWED_BINARY + .iter() + .copied() + .map(|[lhs, rhs]| [lhs.to_string(), rhs.to_string()]), + ) { + allowed_binary.entry(lhs).or_default().insert(rhs); + } + let allowed_unary = user_allowed_unary + .into_iter() + .chain(HARD_CODED_ALLOWED_UNARY.iter().copied().map(String::from)) + .collect(); Self { - allowed, + allowed_binary, + allowed_unary, const_span: None, expr_span: None, } } - /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a - /// non-constant environment that won't overflow. - fn has_valid_op(op: &Spanned, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Lit(ref lit) = expr.kind && - let ast::LitKind::Int(value, _) = lit.node - { - match (&op.node, value) { - (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, - (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) - | (hir::BinOpKind::Mul, 0 | 1) => true, - _ => false, + /// Checks if the lhs and the rhs types of a binary operation like "addition" or + /// "multiplication" are present in the inner set of allowed types. + fn has_allowed_binary(&self, lhs_ty: Ty<'_>, rhs_ty: Ty<'_>) -> bool { + let lhs_ty_string = lhs_ty.to_string(); + let lhs_ty_string_elem = lhs_ty_string.split('<').next().unwrap_or_default(); + let rhs_ty_string = rhs_ty.to_string(); + let rhs_ty_string_elem = rhs_ty_string.split('<').next().unwrap_or_default(); + if let Some(rhs_from_specific) = self.allowed_binary.get(lhs_ty_string_elem) + && { + let rhs_has_allowed_ty = rhs_from_specific.contains(rhs_ty_string_elem); + rhs_has_allowed_ty || rhs_from_specific.contains("*") } + { + true + } else if let Some(rhs_from_glob) = self.allowed_binary.get("*") { + rhs_from_glob.contains(rhs_ty_string_elem) } else { false } } - /// Checks if the given `expr` has any of the inner `allowed` elements. - fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { - self.allowed - .contains(ty.to_string().split('<').next().unwrap_or_default()) + /// Checks if the type of an unary operation like "negation" is present in the inner set of + /// allowed types. + fn has_allowed_unary(&self, ty: Ty<'_>) -> bool { + let ty_string = ty.to_string(); + let ty_string_elem = ty_string.split('<').next().unwrap_or_default(); + self.allowed_unary.contains(ty_string_elem) } // For example, 8i32 or &i64::MAX. @@ -74,15 +97,17 @@ impl ArithmeticSideEffects { self.expr_span = Some(expr.span); } - /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. - fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option> { - if matches!(expr.kind, hir::ExprKind::Lit(_)) { - return Some(LiteralIntegerTy::Value(expr)); + /// If `expr` is not a literal integer like `1`, returns `None`. + /// + /// Returns the absolute value of the expression, if this is an integer literal. + fn literal_integer(expr: &hir::Expr<'_>) -> Option { + let actual = peel_hir_expr_unary(expr).0; + if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node { + Some(n) } - if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { - return Some(LiteralIntegerTy::Ref(inn)); + else { + None } - None } /// Manages when the lint should be triggered. Operations in constant environments, hard coded @@ -101,26 +126,35 @@ impl ArithmeticSideEffects { if !matches!( op.node, hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul | hir::BinOpKind::Div + | hir::BinOpKind::Mul | hir::BinOpKind::Rem | hir::BinOpKind::Shl | hir::BinOpKind::Shr + | hir::BinOpKind::Sub ) { return; }; let lhs_ty = cx.typeck_results().expr_ty(lhs); let rhs_ty = cx.typeck_results().expr_ty(rhs); - let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty; - if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) { + if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { - match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { - (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), - (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, - (None, None) | (Some(_), Some(_)) => false, + let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) { + (None, None) => false, + (None, Some(n)) | (Some(n), None) => match (&op.node, n) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + }, + (Some(_), Some(_)) => { + matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) + }, } } else { false @@ -129,21 +163,45 @@ impl ArithmeticSideEffects { self.issue_lint(cx, expr); } } + + fn manage_unary_ops<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, + un_expr: &hir::Expr<'tcx>, + un_op: hir::UnOp, + ) { + let hir::UnOp::Neg = un_op else { return; }; + if constant(cx, cx.typeck_results(), un_expr).is_some() { + return; + } + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if self.has_allowed_unary(ty) { + return; + } + let actual_un_expr = peel_hir_expr_refs(un_expr).0; + if Self::literal_integer(actual_un_expr).is_some() { + return; + } + self.issue_lint(cx, expr); + } + + fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool { + self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) + } } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { - if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { + if self.should_skip_expr(expr) { return; } match &expr.kind { - hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { + hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op, lhs, rhs); }, - hir::ExprKind::Unary(hir::UnOp::Neg, _) => { - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - self.issue_lint(cx, expr); - } + hir::ExprKind::Unary(un_op, un_expr) => { + self.manage_unary_ops(cx, expr, un_expr, *un_op); }, _ => {}, } @@ -177,22 +235,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } - -/// Tells if an expression is a integer declared by value or by reference. -/// -/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather -/// than `hirExprKind::Addr`. -enum LiteralIntegerTy<'expr, 'tcx> { - /// For example, `&199` - Ref(&'expr hir::Expr<'tcx>), - /// For example, `1` or `i32::MAX` - Value(&'expr hir::Expr<'tcx>), -} - -impl<'expr, 'tcx> From> for &'expr hir::Expr<'tcx> { - fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { - match from { - LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, - } - } -} diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 26bca7c306a8..9bbf385fb599 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -8,7 +8,7 @@ use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; @@ -28,7 +28,7 @@ pub(super) fn check<'tcx>( let rty = cx.typeck_results().expr_ty(rhs); if_chain! { if let Some((_, lang_item)) = binop_traits(op.node); - if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item); + if let Some(trait_id) = cx.tcx.lang_items().get(lang_item); let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id; if trait_ref_of_method(cx, parent_fn) .map_or(true, |t| t.path.res.def_id() != trait_id); @@ -123,16 +123,15 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } @@ -156,15 +155,14 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index c9c777f1bd8d..24aeb82a37f3 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{match_any_def_paths, path_def_id, paths}; +use clippy_utils::{match_def_path, path_def_id, paths}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -49,13 +49,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path) - .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .map_or(false, |idx| match idx { - 0 => true, - 1 => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path_def_id(cx, path).map_or(false, |id| { + if match_def_path(cx, id, &paths::FROM_STR_METHOD) { + true + } else if cx.tcx.lang_items().from_fn() == Some(id) { + !is_copy(cx, typeck.expr_ty(expr)) + } else { + false + } + }) => { (arg, arg.span) }, diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index b48d6c4e2e2a..14a12da862ef 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{clip, unsext}; +use clippy_utils::{clip, peel_hir_expr_refs, unsext}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; use rustc_lint::LateContext; @@ -20,20 +20,76 @@ pub(crate) fn check<'tcx>( if !is_allowed(cx, op, left, right) { match op { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check_op(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + 0, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + 0, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + right, + 0, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Mul => { - check_op(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + 1, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + 1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, - BinOpKind::Div => check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded), + BinOpKind::Div => check_op( + cx, + right, + 1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ), BinOpKind::BitAnd => { - check_op(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right)); - check_op(cx, right, -1, expr.span, left.span, Parens::Unneeded); + check_op( + cx, + left, + -1, + expr.span, + peel_hir_expr_refs(right).0.span, + needs_parenthesis(cx, expr, right), + ); + check_op( + cx, + right, + -1, + expr.span, + peel_hir_expr_refs(left).0.span, + Parens::Unneeded, + ); }, BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs index ae805147f07a..015f6c14e761 100644 --- a/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -50,7 +50,7 @@ fn lint_misrefactored_assign_op( let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, - &format!( + format!( "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", op.as_str() ), diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index b8a20d5ebe9b..eba230da6c39 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -90,9 +90,6 @@ declare_clippy_lint! { /// use rust_decimal::Decimal; /// let _n = Decimal::MAX + Decimal::MAX; /// ``` - /// - /// ### Allowed types - /// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter. #[clippy::version = "1.64.0"] pub ARITHMETIC_SIDE_EFFECTS, restriction, diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index 1085e6089441..d7917e86a861 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -199,12 +199,12 @@ fn in_impl<'tcx>( } } -fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { +fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { if_chain! { if let ty::Adt(adt_def, _) = middle_ty.kind(); if let Some(local_did) = adt_def.did().as_local(); let item = cx.tcx.hir().expect_item(local_did); - let middle_ty_id = item.def_id.to_def_id(); + let middle_ty_id = item.owner_id.to_def_id(); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; if let Res::Def(_, hir_ty_id) = path.res; diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index d9ee031c9f97..377bddeaa5fe 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_direct_expn_of; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -37,8 +37,8 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if_chain! { - if let ExprKind::MethodCall(path_segment, receiver, _, _) = &expr.kind; - if matches!(path_segment.ident.name, sym::expect | sym::unwrap); + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap); if let ExprKind::Call(caller, _) = &receiver.kind; if is_direct_expn_of(caller.span, "option_env").is_some(); then { diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4eb42da1fed0..c5ea09590d3d 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -25,11 +25,11 @@ declare_clippy_lint! { /// Using the dedicated functions of the `Option` type is clearer and /// more concise than an `if let` expression. /// - /// ### Known problems - /// This lint uses a deliberately conservative metric for checking - /// if the inside of either body contains breaks or continues which will - /// cause it to not suggest a fix if either block contains a loop with - /// continues or breaks contained within the loop. + /// ### Notes + /// This lint uses a deliberately conservative metric for checking if the + /// inside of either body contains loop control expressions `break` or + /// `continue` (which cannot be used within closures). If these are found, + /// this lint will not be raised. /// /// ### Example /// ```rust @@ -213,11 +213,14 @@ fn try_convert_match<'tcx>( cx: &LateContext<'tcx>, arms: &[Arm<'tcx>], ) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { - if arms.len() == 2 { - return if is_none_or_err_arm(cx, &arms[1]) { - Some((arms[0].pat, arms[0].body, arms[1].body)) - } else if is_none_or_err_arm(cx, &arms[0]) { - Some((arms[1].pat, arms[1].body, arms[0].body)) + if let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + return if is_none_or_err_arm(cx, second_arm) { + Some((first_arm.pat, first_arm.body, second_arm.body)) + } else if is_none_or_err_arm(cx, first_arm) { + Some((second_arm.pat, second_arm.body, first_arm.body)) } else { None }; diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs new file mode 100644 index 000000000000..f60d9d65b120 --- /dev/null +++ b/clippy_lints/src/partial_pub_fields.rs @@ -0,0 +1,81 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks whether partial fields of a struct are public. + /// + /// Either make all fields of a type public, or make none of them public + /// + /// ### Why is this bad? + /// Most types should either be: + /// * Abstract data types: complex objects with opaque implementation which guard + /// interior invariants and expose intentionally limited API to the outside world. + /// * Data: relatively simple objects which group a bunch of related attributes together. + /// + /// ### Example + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// b: u8, + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub struct Color { + /// pub r: u8, + /// pub g: u8, + /// pub b: u8, + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub PARTIAL_PUB_FIELDS, + restriction, + "partial fields of a struct are public" +} +declare_lint_pass!(PartialPubFields => [PARTIAL_PUB_FIELDS]); + +impl EarlyLintPass for PartialPubFields { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + let ItemKind::Struct(ref st, _) = item.kind else { + return; + }; + + let mut fields = st.fields().iter(); + let Some(first_field) = fields.next() else { + // Empty struct. + return; + }; + let all_pub = first_field.vis.kind.is_pub(); + let all_priv = !all_pub; + + let msg = "mixed usage of pub and non-pub fields"; + + for field in fields { + if all_priv && field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using private field here", + ); + return; + } else if all_pub && !field.vis.kind.is_pub() { + span_lint_and_help( + cx, + PARTIAL_PUB_FIELDS, + field.vis.span, + msg, + None, + "consider using public field here", + ); + return; + } + } + } +} diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 09ac514d014e..5aa3c6f2f934 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -36,7 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind; - if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); + if !cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived); if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); if trait_ref.path.res.def_id() == eq_trait; then { diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 6810a2431758..456ded3fc026 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// if f.is_some() { "yay" } else { "nay" } /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub PARTIALEQ_TO_NONE, style, "Binary comparison to `Option::None` relies on `T: PartialEq`, which is unneeded" diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 45e98de10ace..2d21aaa4f7fd 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -139,7 +139,7 @@ impl<'tcx> PassByRefOrValue { } fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option) { - if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } @@ -184,16 +184,16 @@ impl<'tcx> PassByRefOrValue { if is_copy(cx, ty) && let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()) && size <= self.ref_min_size - && let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind + && let hir::TyKind::Ref(_, MutTy { ty: decl_ty, .. }) = input.kind { if let Some(typeck) = cx.maybe_typeck_results() { // Don't lint if an unsafe pointer is created. // TODO: Limit the check only to unsafe pointers to the argument (or part of the argument) // which escape the current function. - if typeck.node_types().iter().any(|(_, &ty)| ty.is_unsafe_ptr()) + if typeck.node_types().items().any(|(_, &ty)| ty.is_unsafe_ptr()) || typeck .adjustments() - .iter() + .items() .flat_map(|(_, a)| a) .any(|a| matches!(a.kind, Adjust::Pointer(PointerCast::UnsafeFnPointer))) { @@ -261,7 +261,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { - self.check_poly_fn(cx, item.def_id.def_id, method_sig.decl, None); + self.check_poly_fn(cx, item.owner_id.def_id, method_sig.decl, None); } } @@ -299,7 +299,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index a4d265111f9a..97b5a4ce3641 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -130,7 +130,7 @@ enum DerefPossible { Impossible, } -fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { +fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat); if let Some((span, mutability, level)) = maybe_mismatch { span_lint_and_help( @@ -163,7 +163,7 @@ enum Level { Lower, } -fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { +fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { let mut result = None; pat.walk(|p| { if result.is_some() { diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs new file mode 100644 index 000000000000..e7095ec191f7 --- /dev/null +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths; +use clippy_utils::ty::match_type; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`. + /// + /// ### Why is this bad? + /// On Unix platforms this results in the file being world writable, + /// equivalent to `chmod a+w `. + /// ### Example + /// ```rust + /// use std::fs::File; + /// let f = File::create("foo.txt").unwrap(); + /// let metadata = f.metadata().unwrap(); + /// let mut permissions = metadata.permissions(); + /// permissions.set_readonly(false); + /// ``` + #[clippy::version = "1.66.0"] + pub PERMISSIONS_SET_READONLY_FALSE, + suspicious, + "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" +} +declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALSE]); + +impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind + && match_type(cx, cx.typeck_results().expr_ty(receiver), &paths::PERMISSIONS) + && path.ident.name == sym!(set_readonly) + && let ExprKind::Lit(lit) = &arg.kind + && LitKind::Bool(false) == lit.node + { + span_lint_and_then( + cx, + PERMISSIONS_SET_READONLY_FALSE, + expr.span, + "call to `set_readonly` with argument `false`", + |diag| { + diag.note("on Unix platforms this results in the file being world writable"); + diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\ + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html"); + } + ); + } + } +} diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index e6e3ad05ad70..057b7e30642e 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; -use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, MethodCall, UnOp}; +use rustc_ast::token; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -109,18 +110,18 @@ impl EarlyLintPass for Precedence { let mut arg = operand; let mut all_odd = true; - while let ExprKind::MethodCall(path_segment, receiver, _, _) = &arg.kind { - let path_segment_str = path_segment.ident.name.as_str(); + while let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &arg.kind { + let seg_str = seg.ident.name.as_str(); all_odd &= ALLOWED_ODD_FUNCTIONS .iter() - .any(|odd_function| **odd_function == *path_segment_str); + .any(|odd_function| **odd_function == *seg_str); arg = receiver; } if_chain! { if !all_odd; if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + if let token::LitKind::Integer | token::LitKind::Float = &lit.kind; then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 2d80236327a1..262953042581 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -12,16 +12,20 @@ use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{ self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, - ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, - TraitItem, TraitItemKind, TyKind, Unsafety, + ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, + TyKind, Unsafety, }; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Binder, Clause, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::fmt; use std::iter; @@ -160,7 +164,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, - cx.tcx.fn_sig(item.def_id).skip_binder().inputs(), + cx.tcx.fn_sig(item.owner_id).skip_binder().inputs(), sig.decl.inputs, &[], ) @@ -184,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { let (item_id, sig, is_trait_item) = match parents.next() { Some((_, Node::Item(i))) => { if let ItemKind::Fn(sig, ..) = &i.kind { - (i.def_id, sig, false) + (i.owner_id, sig, false) } else { return; } @@ -196,14 +200,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } if let ImplItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig, false) + (i.owner_id, sig, false) } else { return; } }, Some((_, Node::TraitItem(i))) => { if let TraitItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig, true) + (i.owner_id, sig, true) } else { return; } @@ -339,21 +343,16 @@ impl PtrArg<'_> { } struct RefPrefix { - lt: LifetimeName, + lt: Lifetime, mutability: Mutability, } impl fmt::Display for RefPrefix { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use fmt::Write; f.write_char('&')?; - match self.lt { - LifetimeName::Param(_, ParamName::Plain(name)) => { - name.fmt(f)?; - f.write_char(' ')?; - }, - LifetimeName::Infer => f.write_str("'_ ")?, - LifetimeName::Static => f.write_str("'static ")?, - _ => (), + if !self.lt.is_anonymous() { + self.lt.ident.fmt(f)?; + f.write_char(' ')?; } f.write_str(self.mutability.prefix_str()) } @@ -384,6 +383,17 @@ enum DerefTy<'tcx> { Slice(Option, Ty<'tcx>), } impl<'tcx> DerefTy<'tcx> { + fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> { + match *self { + Self::Str => cx.tcx.types.str_, + Self::Path => cx.tcx.mk_adt( + cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()), + List::empty(), + ), + Self::Slice(_, ty) => cx.tcx.mk_slice(ty), + } + } + fn argless_str(&self) -> &'static str { match *self { Self::Str => "str", @@ -411,7 +421,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( if let ty::Ref(_, ty, mutability) = *ty.kind(); if let ty::Adt(adt, substs) = *ty.kind(); - if let TyKind::Rptr(lt, ref ty) = hir_ty.kind; + if let TyKind::Ref(lt, ref ty) = hir_ty.kind; if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind; // Check that the name as typed matches the actual name of the type. @@ -435,7 +445,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( substs.type_at(0), ), ), - Some(sym::String) => ( + _ if Some(adt.did()) == cx.tcx.lang_items().string() => ( [("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str, ), @@ -480,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( ty_name: name.ident.name, method_renames, ref_prefix: RefPrefix { - lt: lt.name, + lt: *lt, mutability, }, deref_ty, @@ -493,14 +503,14 @@ fn check_fn_args<'cx, 'tcx: 'cx>( fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) { if let FnRetTy::Return(ty) = sig.decl.output - && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) + && let Some((out, Mutability::Mut, _)) = get_ref_lm(ty) { let out_region = cx.tcx.named_region(out.hir_id); let args: Option> = sig .decl .inputs .iter() - .filter_map(get_rptr_lm) + .filter_map(get_ref_lm) .filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region) .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span)) .collect(); @@ -581,6 +591,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| { match *ty.skip_binder().peel_refs().kind() { + ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds), ty::Param(_) => true, ty::Adt(def, _) => def.did() == args.ty_did, _ => false, @@ -614,6 +625,9 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: }; match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() { + ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { + set_skip_flag(); + }, ty::Param(_) => { set_skip_flag(); }, @@ -665,8 +679,33 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: v.results } -fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { - if let TyKind::Rptr(lt, ref m) = ty.kind { +fn matches_preds<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + preds: &'tcx [ty::PolyExistentialPredicate<'tcx>], +) -> bool { + let infcx = cx.tcx.infer_ctxt().build(); + preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) { + ExistentialPredicate::Trait(p) => infcx + .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.substs.iter()), cx.param_env) + .must_apply_modulo_regions(), + ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new( + cx.tcx, + ObligationCause::dummy(), + cx.param_env, + cx.tcx + .mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection( + p.with_self_ty(cx.tcx, ty), + )))), + )), + ExistentialPredicate::AutoTrait(p) => infcx + .type_implements_trait(p, [ty], cx.param_env) + .must_apply_modulo_regions(), + }) +} + +fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { + if let TyKind::Ref(lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) } else { None diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 72dda67c72b2..47b8891e1230 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -105,17 +105,17 @@ fn expr_as_ptr_offset_call<'tcx>( } // Is the type of the expression a usize? -fn is_expr_ty_usize<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize } // Is the type of the expression a raw pointer? -fn is_expr_ty_raw_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.typeck_results().expr_ty(expr).is_unsafe_ptr() } -fn build_suggestion<'tcx>( - cx: &LateContext<'tcx>, +fn build_suggestion( + cx: &LateContext<'_>, method: Method, receiver_expr: &Expr<'_>, cast_lhs_expr: &Expr<'_>, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 328371fd602f..5269bbd1f1ac 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -94,7 +94,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex then { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); - let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx.at(caller.span), cx.param_env) && + let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { if eq_expr_value(cx, caller, peel_blocks(else_inner)) { @@ -189,6 +189,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) || is_res_lang_ctor(cx, res, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) + && if_else.is_none() }, _ => false, } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index c6fbb5e805ab..fc655fe2d0bb 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,16 +1,16 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use std::cmp::Ordering; @@ -103,7 +103,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for range expressions `x..y` where both `x` and `y` - /// are constant and `x` is greater or equal to `y`. + /// are constant and `x` is greater to `y`. Also triggers if `x` is equal to `y` when they are conditions to a `for` loop. /// /// ### Why is this bad? /// Empty ranges yield no values so iterating them is a no-op. @@ -161,12 +161,12 @@ declare_clippy_lint! { } pub struct Ranges { - msrv: Option, + msrv: Msrv, } impl Ranges { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind { - if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { + if self.msrv.meets(msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index aedbe08e3e46..c1677fb3da1c 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{def_id, Body, FnDecl, HirId}; +use rustc_hir::{def_id, Body, FnDecl, HirId, LangItem}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD) || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD) || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD) - && is_type_diagnostic_item(cx, arg_ty, sym::String)); + && is_type_lang_item(cx, arg_ty, LangItem::String)); let from_deref = !from_borrow && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 74eea6de4bbe..2a42e73488f1 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -69,10 +69,10 @@ impl EarlyLintPass for RedundantClosureCall { if_chain! { if let ast::ExprKind::Call(ref paren, _) = expr.kind; if let ast::ExprKind::Paren(ref closure) = paren.kind; - if let ast::ExprKind::Closure(_, _, ref r#async, _, ref decl, ref block, _) = closure.kind; + if let ast::ExprKind::Closure(box ast::Closure { ref asyncness, ref fn_decl, ref body, .. }) = closure.kind; then { let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(block); + visitor.visit_expr(body); if !visitor.found_return { span_lint_and_then( cx, @@ -80,13 +80,13 @@ impl EarlyLintPass for RedundantClosureCall { expr.span, "try not to call a closure in the expression where it is declared", |diag| { - if decl.inputs.is_empty() { - let app = Applicability::MachineApplicable; - let mut hint = Sugg::ast(cx, block, ".."); + if fn_decl.inputs.is_empty() { + let mut app = Applicability::MachineApplicable; + let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app); - if r#async.is_async() { + if asyncness.is_async() { // `async x` is a syntax error, so it becomes `async { x }` - if !matches!(block.kind, ast::ExprKind::Block(_, _)) { + if !matches!(body.kind, ast::ExprKind::Block(_, _)) { hint = hint.blockify(); } @@ -105,8 +105,8 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'a, 'tcx>( - cx: &'a LateContext<'tcx>, + fn count_closure_usage<'tcx>( + cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>, ) -> usize { diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 40b03068f6c7..61bff4a0e38d 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +36,12 @@ declare_clippy_lint! { } pub struct RedundantFieldNames { - msrv: Option, + msrv: Msrv, } impl RedundantFieldNames { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) { + if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 464f6827e1d5..c2a8db7df038 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -46,12 +46,12 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if_chain! { - if cx.tcx.visibility(item.def_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()); - if !cx.access_levels.is_exported(item.def_id.def_id) && self.is_exported.last() == Some(&false); + if cx.tcx.visibility(item.owner_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()); + if !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false); if is_not_macro_export(item); then { let span = item.span.with_hi(item.ident.span.hi()); - let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id()); + let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); span_lint_and_then( cx, REDUNDANT_PUB_CRATE, @@ -70,7 +70,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } if let ItemKind::Mod { .. } = item.kind { - self.is_exported.push(cx.access_levels.is_exported(item.def_id.def_id)); + self.is_exported + .push(cx.effective_visibilities.is_exported(item.owner_id.def_id)); } } @@ -83,7 +84,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { if let ItemKind::Use(path, _) = item.kind { - if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res { + if path + .res + .iter() + .all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) + { return false; } } else if let ItemKind::Macro(..) = item.kind { diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 60ba62c4a433..44bf824aa0e2 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +33,12 @@ declare_clippy_lint! { } pub struct RedundantStaticLifetimes { - msrv: Option, + msrv: Msrv, } impl RedundantStaticLifetimes { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -60,14 +59,14 @@ impl RedundantStaticLifetimes { } }, // This is what we are looking for ! - TyKind::Rptr(ref optional_lifetime, ref borrow_type) => { + TyKind::Ref(ref optional_lifetime, ref borrow_type) => { // Match the 'static lifetime if let Some(lifetime) = *optional_lifetime { match borrow_type.ty.kind { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime { let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{snip}"); + let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); span_lint_and_then( cx, REDUNDANT_STATIC_LIFETIMES, @@ -96,7 +95,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) { + if !self.msrv.meets(msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 42514f861be1..448a32b77c03 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -39,7 +39,7 @@ declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { if_chain! { - if let TyKind::Rptr(_, ref mut_ty) = ty.kind; + if let TyKind::Ref(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; if let TyKind::Path(ref qpath) = &mut_ty.ty.kind; let last = last_path_segment(qpath); @@ -52,7 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { GenericArg::Type(inner_ty) => Some(inner_ty), _ => None, }); - if let TyKind::Rptr(_, _) = inner_ty.kind; + if let TyKind::Ref(_, ref inner_mut_ty) = inner_ty.kind; + if inner_mut_ty.mutbl == Mutability::Not; then { span_lint_and_sugg( diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 1fda58fa54de..9e6c6c73d4fe 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -1,5 +1,8 @@ +use std::fmt::Display; + use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::source::snippet_opt; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; @@ -77,13 +80,45 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[must_use] -fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { - let offset = u32::from(offset); - let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset); - let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset); - assert!(start <= end); - Span::new(start, end, base.ctxt(), base.parent()) +fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescaped: &str, base: Span, offset: u8) { + let parts: Option<(_, _, &dyn Display)> = match &error { + regex_syntax::Error::Parse(e) => Some((e.span(), e.auxiliary_span(), e.kind())), + regex_syntax::Error::Translate(e) => Some((e.span(), None, e.kind())), + _ => None, + }; + + let convert_span = |regex_span: ®ex_syntax::ast::Span| { + let offset = u32::from(offset); + let start = base.lo() + BytePos(u32::try_from(regex_span.start.offset).expect("offset too large") + offset); + let end = base.lo() + BytePos(u32::try_from(regex_span.end.offset).expect("offset too large") + offset); + + Span::new(start, end, base.ctxt(), base.parent()) + }; + + if let Some((primary, auxiliary, kind)) = parts + && let Some(literal_snippet) = snippet_opt(cx, base) + && let Some(inner) = literal_snippet.get(offset as usize..) + // Only convert to native rustc spans if the parsed regex matches the + // source snippet exactly, to ensure the span offsets are correct + && inner.get(..unescaped.len()) == Some(unescaped) + { + let spans = if let Some(auxiliary) = auxiliary { + vec![convert_span(primary), convert_span(auxiliary)] + } else { + vec![convert_span(primary)] + }; + + span_lint(cx, INVALID_REGEX, spans, &format!("regex syntax error: {kind}")); + } else { + span_lint_and_help( + cx, + INVALID_REGEX, + base, + &error.to_string(), + None, + "consider using a raw string literal: `r\"..\"`", + ); + } } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { @@ -155,25 +190,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => lint_syntax_error(cx, &e, r, expr.span, offset), } } } else if let Some(r) = const_str(cx, expr) { @@ -183,25 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => span_lint(cx, INVALID_REGEX, expr.span, &e.to_string()), } } } diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index d320eea1c377..9f487dedb8cb 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -2,17 +2,17 @@ #[rustfmt::skip] pub static RENAMED_LINTS: &[(&str, &str)] = &[ + ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), ("clippy::blacklisted_name", "clippy::disallowed_names"), ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), ("clippy::box_vec", "clippy::box_collection"), ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), @@ -31,9 +31,13 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::drop_bounds", "drop_bounds"), + ("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::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), + ("clippy::let_underscore_drop", "let_underscore_drop"), ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), ("clippy::panic_params", "non_fmt_panics"), ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 16d702a3868d..b77faf7322bd 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -74,7 +74,7 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa if !in_external_macro(cx.sess(), span); if decl.implicit_self.has_implicit_self(); // We only show this warning for public exported methods. - if cx.access_levels.is_exported(fn_def); + if cx.effective_visibilities.is_exported(fn_def); // We don't want to emit this lint if the `#[must_use]` attribute is already there. if !cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::must_use)); if cx.tcx.visibility(fn_def.to_def_id()).is_public(); @@ -128,7 +128,7 @@ impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { if let TraitItemKind::Fn(ref sig, _) = item.kind { - check_method(cx, sig.decl, item.def_id.def_id, item.span, item.hir_id()); + check_method(cx, sig.decl, item.owner_id.def_id, item.span, item.hir_id()); } } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2b2a41d16011..42c51d7d0710 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,17 +1,18 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{fn_def_id, path_to_local_id}; +use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, LangItem, MatchSource, PatKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::{BytePos, Pos}; declare_clippy_lint! { /// ### What it does @@ -150,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, - _: Span, + sp: Span, _: HirId, ) { match kind { @@ -165,14 +166,14 @@ impl<'tcx> LateLintPass<'tcx> for Return { check_final_expr(cx, body.value, vec![], replacement); }, FnKind::ItemFn(..) | FnKind::Method(..) => { - check_block_return(cx, &body.value.kind, vec![]); + check_block_return(cx, &body.value.kind, sp, vec![]); }, } } } // if `expr` is a block, check if there are needless returns in it -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec) { +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { if let ExprKind::Block(block, _) = expr_kind { if let Some(block_expr) = block.expr { check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty); @@ -182,12 +183,14 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, check_final_expr(cx, expr, semi_spans, RetReplacement::Empty); }, StmtKind::Semi(semi_expr) => { - let mut semi_spans_and_this_one = semi_spans; - // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382` - if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) { - semi_spans_and_this_one.push(semicolon_span); - check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty); + // Remove ending semicolons and any whitespace ' ' in between. + // Without `return`, the suggestion might not compile if the semicolon is retained + if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { + let semi_span_to_remove = + span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); + semi_spans.push(semi_span_to_remove); } + check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty); }, _ => (), } @@ -206,23 +209,33 @@ fn check_final_expr<'tcx>( match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { - if cx.tcx.hir().attrs(expr.hir_id).is_empty() { - let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); - if !borrows { - emit_return_lint( - cx, - peeled_drop_expr.span, - semi_spans, - inner.as_ref().map(|i| i.span), - replacement, - ); - } + // if desugar of `do yeet`, don't lint + if let Some(inner_expr) = inner + && let ExprKind::Call(path_expr, _) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind + { + return; } + if !cx.tcx.hir().attrs(expr.hir_id).is_empty() { + return; + } + let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); + if borrows { + return; + } + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); }, ExprKind::If(_, then, else_clause_opt) => { - check_block_return(cx, &then.kind, semi_spans.clone()); + check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); if let Some(else_clause) = else_clause_opt { - check_block_return(cx, &else_clause.kind, semi_spans); + check_block_return(cx, &else_clause.kind, peeled_drop_expr.span, semi_spans); } }, // a match expr, check all arms @@ -235,7 +248,7 @@ fn check_final_expr<'tcx>( } }, // if it's a whole block, check it - other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans), + other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), } } @@ -284,8 +297,21 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { ControlFlow::Break(()) } else { - ControlFlow::Continue(Descend::from(!expr.span.from_expansion())) + ControlFlow::Continue(Descend::from(!e.span.from_expansion())) } }) .is_some() } + +// Go backwards while encountering whitespace and extend the given Span to that point. +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [' ', '\t', '\n']; + if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); + } + } + + sp +} diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index dead36e3bea8..f35bfb0b4ed8 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let mut map = FxHashMap::::default(); for id in cx.tcx.hir().items() { - if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl) + if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl) && let item = cx.tcx.hir().item(id) && let ItemKind::Impl(Impl { items, @@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { |diag| { diag.span_note( trait_method_span, - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); @@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { // iterate on trait_spans? diag.span_note( trait_spans[0], - &format!("existing `{method_name}` defined here"), + format!("existing `{method_name}` defined here"), ); }, ); diff --git a/clippy_lints/src/self_named_constructors.rs b/clippy_lints/src/self_named_constructors.rs index 1ac538f4c7c0..71b387c66a33 100644 --- a/clippy_lints/src/self_named_constructors.rs +++ b/clippy_lints/src/self_named_constructors.rs @@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let item = cx.tcx.hir().expect_item(parent); - let self_ty = cx.tcx.type_of(item.def_id); + let self_ty = cx.tcx.type_of(item.owner_id); let ret_ty = return_ty(cx, impl_item.hir_id()); // Do not check trait impls diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs new file mode 100644 index 000000000000..34a3e5ddf4f6 --- /dev/null +++ b/clippy_lints/src/semicolon_block.rs @@ -0,0 +1,137 @@ +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// + /// Suggests moving the semicolon after a block to the inside of the block, after its last + /// expression. + /// + /// ### Why is this bad? + /// + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine + /// and this lint suggests inside the block. + /// Take a look at `semicolon_outside_block` for the other alternative. + /// + /// ### Example + /// + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x) }; + /// ``` + /// Use instead: + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x); } + /// ``` + #[clippy::version = "1.68.0"] + pub SEMICOLON_INSIDE_BLOCK, + restriction, + "add a semicolon inside the block" +} +declare_clippy_lint! { + /// ### What it does + /// + /// Suggests moving the semicolon from a block's final expression outside of the block. + /// + /// ### Why is this bad? + /// + /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine + /// and this lint suggests outside the block. + /// Take a look at `semicolon_inside_block` for the other alternative. + /// + /// ### Example + /// + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x); } + /// ``` + /// Use instead: + /// ```rust + /// # fn f(_: u32) {} + /// # let x = 0; + /// unsafe { f(x) }; + /// ``` + #[clippy::version = "1.68.0"] + pub SEMICOLON_OUTSIDE_BLOCK, + restriction, + "add a semicolon outside the block" +} +declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]); + +impl LateLintPass<'_> for SemicolonBlock { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { + match stmt.kind { + StmtKind::Expr(Expr { + kind: ExprKind::Block(block, _), + .. + }) if !block.span.from_expansion() => { + let Block { + expr: None, + stmts: [.., stmt], + .. + } = block else { return }; + let &Stmt { + kind: StmtKind::Semi(expr), + span, + .. + } = stmt else { return }; + semicolon_outside_block(cx, block, expr, span); + }, + StmtKind::Semi(Expr { + kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _), + .. + }) if !block.span.from_expansion() => semicolon_inside_block(cx, block, tail, stmt.span), + _ => (), + } + } +} + +fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { + let insert_span = tail.span.source_callsite().shrink_to_hi(); + let remove_span = semi_span.with_lo(block.span.hi()); + + span_lint_and_then( + cx, + SEMICOLON_INSIDE_BLOCK, + semi_span, + "consider moving the `;` inside the block for consistent formatting", + |diag| { + multispan_sugg_with_applicability( + diag, + "put the `;` here", + Applicability::MachineApplicable, + [(remove_span, String::new()), (insert_span, ";".to_owned())], + ); + }, + ); +} + +fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) { + let insert_span = block.span.with_lo(block.span.hi()); + // account for macro calls + let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span); + let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi()); + + span_lint_and_then( + cx, + SEMICOLON_OUTSIDE_BLOCK, + block.span, + "consider moving the `;` outside the block for consistent formatting", + |diag| { + multispan_sugg_with_applicability( + diag, + "put the `;` here", + Applicability::MachineApplicable, + [(remove_span, String::new()), (insert_span, ";".to_owned())], + ); + }, + ); +} diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 66b79513032f..d46f6a6352c6 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { @@ -33,51 +34,32 @@ declare_clippy_lint! { "imports with single component path are redundant" } -declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +#[derive(Default)] +pub struct SingleComponentPathImports { + /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected + found: NodeMap>, +} + +struct SingleUse { + name: Symbol, + span: Span, + item_id: NodeId, + can_suggest: bool, +} + +impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if cx.sess().opts.edition < Edition::Edition2018 { return; } - check_mod(cx, &krate.items); - } -} -fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { - // keep track of imports reused with `self` keyword, - // such as `self::crypto_hash` in the example below - // ```rust,ignore - // use self::crypto_hash::{Algorithm, Hasher}; - // ``` - let mut imports_reused_with_self = Vec::new(); - - // keep track of single use statements - // such as `crypto_hash` in the example below - // ```rust,ignore - // use crypto_hash; - // ``` - let mut single_use_usages = Vec::new(); - - // keep track of macros defined in the module as we don't want it to trigger on this (#7106) - // ```rust,ignore - // macro_rules! foo { () => {} }; - // pub(crate) use foo; - // ``` - let mut macros = Vec::new(); - - for item in items { - track_uses( - cx, - item, - &mut imports_reused_with_self, - &mut single_use_usages, - &mut macros, - ); + self.check_mod(cx, &krate.items); } - for (name, span, can_suggest) in single_use_usages { - if !imports_reused_with_self.contains(&name) { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() { if can_suggest { span_lint_and_sugg( cx, @@ -102,74 +84,127 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { } } -fn track_uses( - cx: &EarlyContext<'_>, - item: &Item, - imports_reused_with_self: &mut Vec, - single_use_usages: &mut Vec<(Symbol, Span, bool)>, - macros: &mut Vec, -) { - if item.span.from_expansion() || item.vis.kind.is_pub() { - return; - } +impl SingleComponentPathImports { + fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P]) { + // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example + // below. Removing the `use crypto_hash;` would make this a compile error + // ``` + // use crypto_hash; + // + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); - match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { - check_mod(cx, items); - }, - ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { - macros.push(item.ident.name); - }, - ItemKind::Use(use_tree) => { - let segments = &use_tree.prefix.segments; - - // keep track of `use some_module;` usages - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, item.span, true)); - } - } - return; + // keep track of single use statements such as `crypto_hash` in the example below + // ``` + // use crypto_hash; + // ``` + let mut single_use_usages = Vec::new(); + + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ``` + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + + for item in items { + self.track_uses( + cx, + item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); + } + + for usage in single_use_usages { + if !imports_reused_with_self.contains(&usage.name) { + self.found.entry(usage.item_id).or_default().push(usage); } + } + } - if segments.is_empty() { - // keep track of `use {some_module, some_other_module};` usages - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { - let segments = &tree.0.prefix.segments; - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, tree.0.span, false)); - } - } + fn track_uses( + &mut self, + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec, + single_use_usages: &mut Vec, + macros: &mut Vec, + ) { + if item.span.from_expansion() || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + self.check_mod(cx, items); + }, + ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; + + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None) = use_tree.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: item.span, + item_id: item.id, + can_suggest: true, + }); } } + return; } - } else { - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); - return; - } - // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if segments.is_empty() { + // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); + if segments.len() == 1 { + if let UseTreeKind::Simple(None) = tree.0.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: false, + }); + } + } + } + } + } + } else { + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); + } } } } } - } - }, - _ => {}, + }, + _ => {}, + } } } diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs new file mode 100644 index 000000000000..3fcdb4288ce6 --- /dev/null +++ b/clippy_lints/src/size_of_ref.rs @@ -0,0 +1,73 @@ +use clippy_utils::{diagnostics::span_lint_and_help, path_def_id, ty::peel_mid_ty_refs}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for calls to `std::mem::size_of_val()` where the argument is + /// a reference to a reference. + /// + /// ### Why is this bad? + /// + /// Calling `size_of_val()` with a reference to a reference as the argument + /// yields the size of the reference-type, not the size of the value behind + /// the reference. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Note that `&self` as an argument is a `&&Foo`: Because `self` + /// // is already a reference, `&self` is a double-reference. + /// // The return value of `size_of_val()` therefor is the + /// // size of the reference-type, not the size of `self`. + /// std::mem::size_of_val(&self) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct Foo { + /// buffer: [u8], + /// } + /// + /// impl Foo { + /// fn size(&self) -> usize { + /// // Correct + /// std::mem::size_of_val(self) + /// } + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub SIZE_OF_REF, + suspicious, + "Argument to `std::mem::size_of_val()` is a double-reference, which is almost certainly unintended" +} +declare_lint_pass!(SizeOfRef => [SIZE_OF_REF]); + +impl LateLintPass<'_> for SizeOfRef { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Call(path, [arg]) = expr.kind + && let Some(def_id) = path_def_id(cx, path) + && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && peel_mid_ty_refs(arg_ty).1 > 1 + { + span_lint_and_help( + cx, + SIZE_OF_REF, + expr.span, + "argument to `std::mem::size_of_val()` is a reference to a reference", + None, + "dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type", + ); + } + } +} diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 760399231513..a2109038a057 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -168,7 +168,7 @@ impl SlowVectorInit { }; } - fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { + fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len"); span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index d356c99c8fc4..bc18cad6d381 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq}; use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; -use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, ExprKind::Index(target, _idx) => { let e_ty = cx.typeck_results().expr_ty(target).peel_refs(); - if matches!(e_ty.kind(), ty::Str) || is_type_diagnostic_item(cx, e_ty, sym::String) { + if matches!(e_ty.kind(), ty::Str) || is_type_lang_item(cx, e_ty, LangItem::String) { span_lint( cx, STRING_SLICE, @@ -205,7 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { } fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) + is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String) } fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { @@ -249,6 +249,7 @@ const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use rustc_ast::LitKind; @@ -316,18 +317,27 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT && !receiver.span.from_expansion() { - span_lint_and_sugg( - cx, - STRING_LIT_AS_BYTES, - e.span, - "calling `as_bytes()` on a string literal", - "consider using a byte string literal instead", - format!( - "b{}", - snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) - ), - applicability, - ); + if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e) + && let Node::Expr(parent) = parent + && let ExprKind::Match(scrutinee, ..) = parent.kind + && scrutinee.hir_id == id + { + // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces + // `&[u8]`. This change would prevent matching with different sized slices. + } else { + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `as_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}", + snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability) + ), + applicability, + ); + } } } } @@ -446,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; if path.ident.name == sym::to_string; let ty = cx.typeck_results().expr_ty(self_arg); - if is_type_diagnostic_item(cx, ty, sym::String); + if is_type_lang_item(cx, ty, LangItem::String); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index eef9bdc78494..e111c7d22915 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -580,9 +580,9 @@ fn ident_difference_expr_with_base_location( | (Await(_), Await(_)) | (Async(_, _, _), Async(_, _, _)) | (Block(_, _), Block(_, _)) - | (Closure(_, _, _, _, _, _, _), Closure(_, _, _, _, _, _, _)) + | (Closure(_), Closure(_)) | (Match(_, _), Match(_, _)) - | (Loop(_, _), Loop(_, _)) + | (Loop(_, _, _), Loop(_, _, _)) | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) | (While(_, _, _), While(_, _, _)) | (If(_, _, _), If(_, _, _)) @@ -593,7 +593,7 @@ fn ident_difference_expr_with_base_location( | (Unary(_, _), Unary(_, _)) | (Binary(_, _, _), Binary(_, _, _)) | (Tup(_), Tup(_)) - | (MethodCall(_, _, _, _), MethodCall(_, _, _, _)) + | (MethodCall(_), MethodCall(_)) | (Call(_, _), Call(_, _)) | (ConstBlock(_), ConstBlock(_)) | (Array(_), Array(_)) diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index b57b484bdc89..6271ea027314 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -60,8 +60,8 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { if_chain! { if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind; if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node); - if let Ok(binop_trait_id) = cx.tcx.lang_items().require(binop_trait_lang); - if let Ok(op_assign_trait_id) = cx.tcx.lang_items().require(op_assign_trait_lang); + if let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang); + if let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang); // Check for more than one binary operation in the implemented function // Linting when multiple operations are involved can result in false positives @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { (&OP_ASSIGN_TRAITS, SUSPICIOUS_OP_ASSIGN_IMPL), ] .iter() - .find(|&(ts, _)| ts.iter().any(|&t| Ok(trait_id) == cx.tcx.lang_items().require(t))); + .find(|&(ts, _)| ts.iter().any(|&t| Some(trait_id) == cx.tcx.lang_items().get(t))); if count_binops(body.value) == 1; then { span_lint( diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs new file mode 100644 index 000000000000..9c0dc8096d0d --- /dev/null +++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -0,0 +1,53 @@ +use clippy_utils::{numeric_literal::NumericLiteral, source::snippet_with_context}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal. + /// ### Why is this bad? + /// It's most probably a typo and may lead to unexpected behaviours. + /// ### Example + /// ```rust + /// let x = 3_i32 ^ 4_i32; + /// ``` + /// Use instead: + /// ```rust + /// let x = 3_i32.pow(4); + /// ``` + #[clippy::version = "1.67.0"] + pub SUSPICIOUS_XOR_USED_AS_POW, + restriction, + "XOR (`^`) operator possibly used as exponentiation operator" +} +declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]); + +impl LateLintPass<'_> for ConfusingXorAndPow { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if !in_external_macro(cx.sess(), expr.span) && + let ExprKind::Binary(op, left, right) = &expr.kind && + op.node == BinOpKind::BitXor && + left.span.ctxt() == right.span.ctxt() && + let ExprKind::Lit(lit_left) = &left.kind && + let ExprKind::Lit(lit_right) = &right.kind && + let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && + let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && + let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) && + let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) && + left_val.is_decimal() && + right_val.is_decimal() { + clippy_utils::diagnostics::span_lint_and_sugg( + cx, + SUSPICIOUS_XOR_USED_AS_POW, + expr.span, + "`^` is not the exponentiation operator", + "did you mean to write", + format!("{}.pow({})", left_val.format(), right_val.format()), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f46c21e12655..17e9cc5f6b7c 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; @@ -16,6 +16,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for manual swapping. /// + /// Note that the lint will not be emitted in const blocks, as the suggestion would not be applicable. + /// /// ### Why is this bad? /// The `std::mem::swap` function exposes the intent better /// without deinitializing or copying either variable. @@ -130,7 +132,7 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa applicability, ); if !is_xor_based { - diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?")); + diag.note(format!("or maybe you should use `{sugg}::mem::replace`?")); } }, ); @@ -138,6 +140,10 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { + if in_constant(cx, block.hir_id) { + return; + } + for w in block.stmts.windows(3) { if_chain! { // let t = foo(); @@ -208,7 +214,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { Applicability::MaybeIncorrect, ); diag.note( - &format!("or maybe you should use `{sugg}::mem::replace`?") + format!("or maybe you should use `{sugg}::mem::replace`?") ); } }); diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 58cc057a39ed..63b326048a48 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{HirId, Item, ItemKind}; +use clippy_utils::has_repr_attr; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Const; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { None, &format!( "consider annotating `{}` with `#[repr(C)]` or another `repr` attribute", - cx.tcx.def_path_str(item.def_id.to_def_id()) + cx.tcx.def_path_str(item.owner_id.to_def_id()) ), ); } @@ -72,7 +72,3 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_ } } } - -fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) -} diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 424a6e9264e4..c0d290b5adc4 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -3,6 +3,7 @@ mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; mod transmute_int_to_float; +mod transmute_null_to_fn; mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; mod transmute_ptr_to_ref; @@ -16,10 +17,10 @@ mod utils; mod wrong_transmute; use clippy_utils::in_constant; +use clippy_utils::msrvs::Msrv; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; @@ -409,8 +410,36 @@ declare_clippy_lint! { "transmutes from a null pointer to a reference, which is undefined behavior" } +declare_clippy_lint! { + /// ### What it does + /// Checks for null function pointer creation through transmute. + /// + /// ### Why is this bad? + /// Creating a null function pointer is undefined behavior. + /// + /// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization + /// + /// ### Known problems + /// Not all cases can be detected at the moment of this writing. + /// For example, variables which hold a null pointer and are then fed to a `transmute` + /// call, aren't detectable yet. + /// + /// ### Example + /// ```rust + /// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null::<()>() ) }; + /// ``` + /// Use instead: + /// ```rust + /// let null_fn: Option = None; + /// ``` + #[clippy::version = "1.67.0"] + pub TRANSMUTE_NULL_TO_FN, + correctness, + "transmute results in a null function pointer, which is undefined behavior" +} + pub struct Transmute { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, @@ -428,10 +457,11 @@ impl_lint_pass!(Transmute => [ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, TRANSMUTING_NULL, + TRANSMUTE_NULL_TO_FN, ]); impl Transmute { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -449,7 +479,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { // - char conversions (https://github.com/rust-lang/rust/issues/89259) let const_context = in_constant(cx, e.hir_id); - let from_ty = cx.typeck_results().expr_ty_adjusted(arg); + let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { + [] => (cx.typeck_results().expr_ty(arg), false), + [.., a] => (a.target, true), + }; // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. let to_ty = cx.typeck_results().expr_ty(e); @@ -461,7 +494,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) + | transmute_null_to_fn::check(cx, e, arg, to_ty) + | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) @@ -475,7 +509,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); if !linted { - transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg); + transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); } } } diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs new file mode 100644 index 000000000000..e75d7f6bf1d5 --- /dev/null +++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs @@ -0,0 +1,64 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{is_integer_literal, is_path_diagnostic_item}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::symbol::sym; + +use super::TRANSMUTE_NULL_TO_FN; + +fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { + span_lint_and_then( + cx, + TRANSMUTE_NULL_TO_FN, + expr.span, + "transmuting a known null pointer into a function pointer", + |diag| { + diag.span_label(expr.span, "this transmute results in undefined behavior"); + diag.help( + "try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value" + ); + }, + ); +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { + if !to_ty.is_fn() { + return false; + } + + match arg.kind { + // Catching: + // transmute over constants that resolve to `null`. + ExprKind::Path(ref _qpath) + if matches!(constant(cx, cx.typeck_results(), arg), Some((Constant::RawPtr(0), _))) => + { + lint_expr(cx, expr); + true + }, + + // Catching: + // `std::mem::transmute(0 as *const i32)` + ExprKind::Cast(inner_expr, _cast_ty) if is_integer_literal(inner_expr, 0) => { + lint_expr(cx, expr); + true + }, + + // Catching: + // `std::mem::transmute(std::ptr::null::())` + ExprKind::Call(func1, []) if is_path_diagnostic_item(cx, func1, sym::ptr_null) => { + lint_expr(cx, expr); + true + }, + + _ => { + // FIXME: + // Also catch transmutations of variables which are known nulls. + // To do this, MIR const propagation seems to be the better tool. + // Whenever MIR const prop routines are more developed, this will + // become available. As of this writing (25/03/19) it is not yet. + false + }, + } +} diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 12d0b866e1c9..54ac04df1c12 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -1,12 +1,12 @@ use super::TRANSMUTE_PTR_TO_REF; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, msrvs, sugg}; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitable}; -use rustc_semver::RustcVersion; /// Checks for `transmute_ptr_to_ref` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, path: &'tcx Path<'_>, - msrv: Option, + msrv: &Msrv, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => { @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>( } } else if from_ptr_ty.ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) @@ -71,7 +71,7 @@ pub(super) fn check<'tcx>( /// Gets the type `Bar` in `…::transmute`. fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> { if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)? - && let TyKind::Rptr(_, ty) = &ty.kind + && let TyKind::Ref(_, ty) = &ty.kind { Some(ty.ty) } else { diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 1c99a02e6c71..af0242348ac2 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -5,7 +5,6 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; -use rustc_span::DUMMY_SP; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( @@ -78,7 +77,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -92,7 +91,7 @@ pub(super) fn check<'tcx>( &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -120,16 +119,16 @@ pub(super) fn check<'tcx>( ), |diag| { if let Some(same_adt_did) = same_adt_did { - diag.note(&format!( + diag.note(format!( "two instances of the same generic type (`{}`) may have different layouts", cx.tcx.item_name(same_adt_did) )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } } }, @@ -147,7 +146,7 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); + diag.note(format!("the contained type `{from_ty}` has an undefined layout")); } }, ); @@ -164,7 +163,7 @@ pub(super) fn check<'tcx>( &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); + diag.note(format!("the contained type `{to_ty}` has an undefined layout")); } }, ); @@ -210,12 +209,12 @@ fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: T continue; }, (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _) - if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) => + if !unsized_ty.is_sized(cx.tcx, cx.param_env) => { (true, false) }, (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))) - if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) => + if !unsized_ty.is_sized(cx.tcx, cx.param_env) => { (false, true) }, diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 6b444922a7cc..8530b43243fa 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,11 +1,11 @@ -use super::utils::can_be_expressed_as_pointer_cast; +use super::utils::check_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{cast::CastKind, Ty}; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -13,24 +13,40 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, + from_ty_adjusted: bool, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) { - span_lint_and_then( - cx, - TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - e.span, - &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(&to_ty.to_string()).to_string(); - diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); - } - }, - ); - true - } else { - false - } + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; + let mut app = Applicability::MachineApplicable; + let sugg = match check_cast(cx, e, from_ty, to_ty) { + Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => { + Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) + .as_ty(to_ty.to_string()) + .to_string() + }, + Some(PtrAddrCast) if !from_ty_adjusted => Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) + .as_ty(to_ty.to_string()) + .to_string(), + + // The only adjustments here would be ref-to-ptr and unsize coercions. The result of an unsize coercions can't + // be transmuted to a usize. For ref-to-ptr coercions, borrows need to be cast to a pointer before being cast to + // a usize. + Some(PtrAddrCast) => format!( + "{} as {to_ty}", + Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app).as_ty(from_ty) + ), + _ => return false, + }; + + span_lint_and_sugg( + cx, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + e.span, + &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), + "try", + sugg, + app, + ); + true } diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs index 19ce5ae72c24..1e407fc4138c 100644 --- a/clippy_lints/src/transmute/transmuting_null.rs +++ b/clippy_lints/src/transmute/transmuting_null.rs @@ -18,8 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if let ExprKind::Path(ref _qpath) = arg.kind && - let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) && - x == 0 + let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg) { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); return true; diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index f919bbd5afca..871c3fadbba7 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -61,7 +61,7 @@ pub(super) fn check<'tcx>( "transmute from an integer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified); + diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified); } }, ); diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index b567d92230bb..cddaf9450eab 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,8 +1,6 @@ +use rustc_hir as hir; use rustc_hir::Expr; -use rustc_hir_analysis::check::{ - cast::{self, CastCheckResult}, - FnCtxt, Inherited, -}; +use rustc_hir_typeck::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; @@ -22,33 +20,21 @@ pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx } } -/// Check if the type conversion can be expressed as a pointer cast, instead of -/// a transmute. In certain cases, including some invalid casts from array -/// references to pointers, this may cause additional errors to be emitted and/or -/// ICE error messages. This function will panic if that occurs. -pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> bool { - use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; - matches!( - check_cast(cx, e, from_ty, to_ty), - Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) - ) -} - /// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. -fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { +pub(super) fn check_cast<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option { let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; Inherited::build(cx.tcx, local_def_id).enter(|inherited| { - let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id); + let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id); // If we already have errors, we can't be sure we can pointer cast. assert!( @@ -56,10 +42,15 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> "Newly created FnCtxt contained errors" ); - if let CastCheckResult::Deferred(check) = cast::check_cast( - &fn_ctxt, e, from_ty, to_ty, + if let Ok(check) = cast::CastCheck::new( + &fn_ctxt, + e, + from_ty, + to_ty, // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, DUMMY_SP, + DUMMY_SP, + DUMMY_SP, + hir::Constness::NotConst, ) { let res = check.do_check(&fn_ctxt); diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 9c6629958401..65dfe7637ea9 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -31,10 +31,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m return false; } - let ltopt = if lt.name.is_anonymous() { + let ltopt = if lt.is_anonymous() { String::new() } else { - format!("{} ", lt.name.ident().as_str()) + format!("{} ", lt.ident.as_str()) }; if mut_ty.mutbl == Mutability::Mut { diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 08020ce66381..43665a922d44 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -37,18 +37,26 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let param = qpath_generic_tys(qpath).next()?; let id = path_def_id(cx, param)?; - cx.tcx.get_diagnostic_name(id).filter(|&name| { - matches!( - name, - sym::HashMap - | sym::String - | sym::Vec - | sym::HashSet - | sym::VecDeque - | sym::LinkedList - | sym::BTreeMap - | sym::BTreeSet - | sym::BinaryHeap - ) - }) + cx.tcx + .get_diagnostic_name(id) + .filter(|&name| { + matches!( + name, + sym::HashMap + | sym::Vec + | sym::HashSet + | sym::VecDeque + | sym::LinkedList + | sym::BTreeMap + | sym::BTreeSet + | sym::BinaryHeap + ) + }) + .or_else(|| { + cx.tcx + .lang_items() + .string() + .filter(|did| id == *did) + .map(|_| sym::String) + }) } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 79c31efb9fcd..229478b7ce3c 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -127,7 +127,7 @@ declare_clippy_lint! { /// `Vec` or a `VecDeque` (formerly called `RingBuf`). /// /// ### Why is this bad? - /// Gankro says: + /// Gankra says: /// /// > The TL;DR of `LinkedList` is that it's built on a massive amount of /// pointers and indirection. @@ -319,7 +319,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { false }; - let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id)); + let is_exported = cx.effective_visibilities.is_exported(cx.tcx.hir().local_def_id(id)); self.check_fn_decl( cx, @@ -333,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let is_exported = cx.access_levels.is_exported(item.def_id.def_id); + let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty( @@ -374,12 +374,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to // make type alias declaration in trait simpler, see #1013 - ImplItemKind::Fn(..) | ImplItemKind::TyAlias(..) => (), + ImplItemKind::Fn(..) | ImplItemKind::Type(..) => (), } } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id)); + let is_exported = cx + .effective_visibilities + .is_exported(cx.tcx.hir().local_def_id(field.hir_id)); self.check_ty( cx, @@ -392,7 +394,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) { - let is_exported = cx.access_levels.is_exported(item.def_id.def_id); + let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); let context = CheckTyContext { is_exported, @@ -537,7 +539,7 @@ impl Types { QPath::LangItem(..) => {}, } }, - TyKind::Rptr(lt, ref mut_ty) => { + TyKind::Ref(lt, ref mut_ty) => { context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { self.check_ty(cx, mut_ty.ty, context); diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index fa567b9b2d24..855137b14d84 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -91,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { let ty = qpath_generic_tys(qpath).next()?; let id = path_def_id(cx, ty)?; - let path = match cx.tcx.get_diagnostic_name(id)? { - sym::String => "str", - sym::OsString => "std::ffi::OsStr", - sym::PathBuf => "std::path::Path", + let path = match cx.tcx.get_diagnostic_name(id) { + Some(sym::OsString) => "std::ffi::OsStr", + Some(sym::PathBuf) => "std::path::Path", + _ if Some(id) == cx.tcx.lang_items().string() => "str", _ => return None, }; Some(path) diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 92d2c48a5898..f9b9a66b5fa4 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -5,11 +5,13 @@ use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; +use rustc_middle::ty::TypeVisitable; use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { @@ -21,7 +23,6 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_then( cx, @@ -30,7 +31,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); - diag.note(&format!( + diag.note(format!( "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); }, @@ -51,19 +52,19 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ return false }; let inner_span = match qpath_generic_tys(inner_qpath).next() { - Some(ty) => { + Some(hir_ty) => { // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use // here because `mod.rs` guarantees this lint is only run on types outside of bodies and // is not run on locals. - if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) { return false; } - ty.span + hir_ty.span }, None => return false, }; if inner_sym == outer_sym { - let mut applicability = Applicability::MaybeIncorrect; let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability); span_lint_and_then( cx, @@ -77,7 +78,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ format!("{outer_sym}<{generic_snippet}>"), applicability, ); - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); }, @@ -90,10 +91,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ hir_ty.span, &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"), |diag| { - diag.note(&format!( + diag.note(format!( "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation" )); - diag.help(&format!( + diag.help(format!( "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`" )); }, diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 5ca4023aa5c1..0aa50c99c169 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -44,7 +44,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { let (add_score, sub_nest) = match ty.kind { // _, &x and *x have only small overhead; don't mess with nesting level - TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0), + TyKind::Infer | TyKind::Ptr(..) | TyKind::Ref(..) => (1, 0), // the "normal" components of a type: named types, arrays/tuples TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), diff --git a/clippy_lints/src/types/utils.rs b/clippy_lints/src/types/utils.rs index 0fa75f8f0a9b..7f43b7841ff3 100644 --- a/clippy_lints/src/types/utils.rs +++ b/clippy_lints/src/types/utils.rs @@ -13,7 +13,7 @@ pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) GenericArg::Type(ty) => Some(ty), _ => None, }); - if let TyKind::Rptr(..) = ty.kind; + if let TyKind::Ref(..) = ty.kind; then { return Some(ty.span); } diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 6c329d8cdf19..7a3c7cd8a99f 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -40,9 +40,9 @@ pub(super) fn check( }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); if !ty_ty.has_escaping_bound_vars(); - if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); + if ty_ty.is_sized(cx.tcx, cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); - if ty_ty_size <= box_size_threshold; + if ty_ty_size < box_size_threshold; then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index d2e675a783ea..2920684ade33 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,6 +1,10 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::walk_span_to_context; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{get_parent_node, is_lint_allowed}; +use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; @@ -59,16 +63,45 @@ declare_clippy_lint! { restriction, "creating an unsafe block without explaining why it is safe" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `// SAFETY: ` comments on safe code. + /// + /// ### Why is this bad? + /// Safe code has no safety requirements, so there is no need to + /// describe safety invariants. + /// + /// ### Example + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// // SAFETY: references are guaranteed to be non-null. + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + /// Use instead: + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + #[clippy::version = "1.67.0"] + pub UNNECESSARY_SAFETY_COMMENT, + restriction, + "annotating safe code with a safety comment" +} -declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]); -impl LateLintPass<'_> for UndocumentedUnsafeBlocks { - fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { +impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) && !is_unsafe_from_proc_macro(cx, block.span) - && !block_has_safety_comment(cx, block) + && !block_has_safety_comment(cx, block.span) + && !block_parents_have_safety_comment(cx, block.hir_id) { let source_map = cx.tcx.sess.source_map(); let span = if source_map.is_multiline(block.span) { @@ -86,35 +119,187 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { "consider adding a safety comment on the preceding line", ); } + + if let Some(tail) = block.expr + && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) + && !in_external_macro(cx.tcx.sess, tail.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) + { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + tail.span, + "expression has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } } - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Impl(imple) = item.kind - && imple.unsafety == hir::Unsafety::Unsafe - && !in_external_macro(cx.tcx.sess, item.span) - && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) - && !is_unsafe_from_proc_macro(cx, item.span) - && !item_has_safety_comment(cx, item) + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { + let ( + hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) + | hir::StmtKind::Expr(expr) + | hir::StmtKind::Semi(expr) + ) = stmt.kind else { return }; + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) + && !in_external_macro(cx.tcx.sess, stmt.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + stmt.span, + "statement has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + let mk_spans = |pos: BytePos| { let source_map = cx.tcx.sess.source_map(); + let span = Span::new(pos, pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); let span = if source_map.is_multiline(item.span) { source_map.span_until_char(item.span, '\n') } else { item.span }; + (span, help_span) + }; - span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe impl missing a safety comment", - None, - "consider adding a safety comment on the preceding line", - ); + let item_has_safety_comment = item_has_safety_comment(cx, item); + match (&item.kind, item_has_safety_comment) { + // lint unsafe impl without safety comment + (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { + if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) + && !is_unsafe_from_proc_macro(cx, item.span) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(item.span) { + source_map.span_until_char(item.span, '\n') + } else { + item.span + }; + + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); + } + }, + // lint safe impl with unnecessary safety comment + (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + "impl has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + }, + (hir::ItemKind::Impl(_), _) => {}, + // const and static items only need a safety comment if their body is an unsafe block, lint otherwise + (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { + let body = cx.tcx.hir().body(body); + if !matches!( + body.value.kind, hir::ExprKind::Block(block, _) + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + } + }, + // Aside from unsafe impls and consts/statics with an unsafe block, items in general + // do not have safety invariants that need to be documented, so lint those. + (_, HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + }, + _ => (), } } } +fn expr_has_unnecessary_safety_comment<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + comment_pos: BytePos, +) -> Option { + if cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, ref node)| { + matches!( + node, + Node::Block(&Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }), + ) + }) { + return None; + } + + // this should roughly be the reverse of `block_parents_have_safety_comment` + if for_each_expr_with_closures(cx, expr, |expr| match expr.kind { + hir::ExprKind::Block( + Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }, + _, + ) => ControlFlow::Break(()), + // statements will be handled by check_stmt itself again + hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() + { + return None; + } + + let source_map = cx.tcx.sess.source_map(); + let span = Span::new(comment_pos, comment_pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); + + Some(help_span) +} + fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let file_pos = source_map.lookup_byte_offset(span.lo()); @@ -126,8 +311,41 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { .map_or(true, |src| !src.starts_with("unsafe")) } +// Checks if any parent {expression, statement, block, local, const, static} +// has a safety comment +fn block_parents_have_safety_comment(cx: &LateContext<'_>, id: hir::HirId) -> bool { + if let Some(node) = get_parent_node(cx.tcx, id) { + return match node { + Node::Expr(expr) => !is_branchy(expr) && span_in_body_has_safety_comment(cx, expr.span), + Node::Stmt(hir::Stmt { + kind: + hir::StmtKind::Local(hir::Local { span, .. }) + | hir::StmtKind::Expr(hir::Expr { span, .. }) + | hir::StmtKind::Semi(hir::Expr { span, .. }), + .. + }) + | Node::Local(hir::Local { span, .. }) + | Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + span, + .. + }) => span_in_body_has_safety_comment(cx, *span), + _ => false, + }; + } + false +} + +/// Checks if an expression is "branchy", e.g. loop, match/if/etc. +fn is_branchy(expr: &hir::Expr<'_>) -> bool { + matches!( + expr.kind, + hir::ExprKind::If(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) + ) +} + /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -136,85 +354,134 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> boo // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. - span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span) + matches!( + span_from_macro_expansion_has_safety_comment(cx, span), + HasSafetyComment::Yes(_) + ) || span_in_body_has_safety_comment(cx, span) +} + +enum HasSafetyComment { + Yes(BytePos), + No, + Maybe, } /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { - if span_from_macro_expansion_has_safety_comment(cx, item.span) { - return true; +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, item.span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, } - if item.span.ctxt() == SyntaxContext::root() { - if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { - let comment_start = match parent_node { - Node::Crate(parent_mod) => { - comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) - }, - Node::Item(parent_item) => { - if let ItemKind::Mod(parent_mod) = &parent_item.kind { - comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item) - } else { - // Doesn't support impls in this position. Pretend a comment was found. - return true; - } - }, - Node::Stmt(stmt) => { - if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { - match stmt_parent { - Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), - _ => { - // Doesn't support impls in this position. Pretend a comment was found. - return true; - }, - } - } else { - // Problem getting the parent node. Pretend a comment was found. - return true; - } - }, - _ => { + if item.span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } + if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + let comment_start = match parent_node { + Node::Crate(parent_mod) => { + comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) + }, + Node::Item(parent_item) => { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) + } else { // Doesn't support impls in this position. Pretend a comment was found. - return true; - }, - }; + return HasSafetyComment::Maybe; + } + }, + Node::Stmt(stmt) => { + if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) { + walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo) + } else { + // Problem getting the parent node. Pretend a comment was found. + return HasSafetyComment::Maybe; + } + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return HasSafetyComment::Maybe; + }, + }; - let source_map = cx.sess().source_map(); - if let Some(comment_start) = comment_start - && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) - && let Ok(comment_start_line) = source_map.lookup_line(comment_start) - && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() - { - unsafe_line.sf.lines(|lines| { - comment_start_line.line < unsafe_line.line && text_has_safety_comment( + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( src, &lines[comment_start_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), - ) - }) - } else { - // Problem getting source text. Pretend a comment was found. - true - } - } else { - // No parent node. Pretend a comment was found. - true + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } + }); } - } else { - false } + HasSafetyComment::Maybe } -fn comment_start_before_impl_in_mod( +/// Checks if the lines immediately preceding the item contain a safety comment. +#[allow(clippy::collapsible_match)] +fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, + } + + if span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } + + if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { + let comment_start = match parent_node { + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), + _ => return HasSafetyComment::Maybe, + }; + + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } + }); + } + } + HasSafetyComment::Maybe +} + +fn comment_start_before_item_in_mod( cx: &LateContext<'_>, parent_mod: &hir::Mod<'_>, parent_mod_span: Span, - imple: &hir::Item<'_>, + item: &hir::Item<'_>, ) -> Option { parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { - if *item_id == imple.item_id() { + if *item_id == item.item_id() { if idx == 0 { // mod A { /* comment */ unsafe impl T {} ... } // ^------------------------------------------^ returns the start of this span @@ -236,11 +503,11 @@ fn comment_start_before_impl_in_mod( }) } -fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { - false + HasSafetyComment::Maybe } else { // From a macro expansion. Get the text from the start of the macro declaration to start of the // unsafe block. @@ -252,15 +519,22 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && let Some(src) = unsafe_line.sf.src.as_deref() { unsafe_line.sf.lines(|lines| { - macro_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } else { + HasSafetyComment::No + } }) } else { // Problem getting source text. Pretend a comment was found. - true + HasSafetyComment::Maybe } } } @@ -299,7 +573,7 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { src, &lines[body_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), - ) + ).is_some() }) } else { // Problem getting source text. Pretend a comment was found. @@ -311,30 +585,34 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } /// Checks if the given text has a safety comment for the immediately proceeding line. -fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool { +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option { let mut lines = line_starts .array_windows::<2>() .rev() .map_while(|[start, end]| { let start = start.to_usize() - offset; let end = end.to_usize() - offset; - src.get(start..end).map(|text| (start, text.trim_start())) + let text = src.get(start..end)?; + let trimmed = text.trim_start(); + Some((start + (text.len() - trimmed.len()), trimmed)) }) .filter(|(_, text)| !text.is_empty()); let Some((line_start, line)) = lines.next() else { - return false; + return None; }; // Check for a sequence of line comments. if line.starts_with("//") { - let mut line = line; + let (mut line, mut line_start) = (line, line_start); loop { if line.to_ascii_uppercase().contains("SAFETY:") { - return true; + return Some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { - Some((_, x)) if x.starts_with("//") => line = x, - _ => return false, + Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s), + _ => return None, } } } @@ -343,16 +621,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); + let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset]; let mut tokens = tokenize(src); - return src[..tokens.next().unwrap().len as usize] + return (src[..tokens.next().unwrap().len as usize] .to_ascii_uppercase() .contains("SAFETY:") - && tokens.all(|t| t.kind == TokenKind::Whitespace); + && tokens.all(|t| t.kind == TokenKind::Whitespace)) + .then_some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { Some(x) => (line_start, line) = x, - None => return false, + None => return None, } } } diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index 57aff5367dd1..a138a4baa9b3 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::{get_trait_def_id, paths}; use if_chain::if_chain; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; +use rustc_middle::ty::{Clause, GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Span}; +use rustc_span::{sym, BytePos, Span}; declare_clippy_lint! { /// ### What it does @@ -46,7 +45,7 @@ fn get_trait_predicates_for_trait_id<'tcx>( let mut preds = Vec::new(); for (pred, _) in generics.predicates { if_chain! { - if let PredicateKind::Trait(poly_trait_pred) = pred.kind().skip_binder(); + if let PredicateKind::Clause(Clause::Trait(poly_trait_pred)) = pred.kind().skip_binder(); let trait_pred = cx.tcx.erase_late_bound_regions(pred.kind().rebind(poly_trait_pred)); if let Some(trait_def_id) = trait_id; if trait_def_id == trait_pred.trait_ref.def_id; @@ -64,7 +63,7 @@ fn get_projection_pred<'tcx>( trait_pred: TraitPredicate<'tcx>, ) -> Option> { generics.predicates.iter().find_map(|(proj_pred, _)| { - if let ty::PredicateKind::Projection(pred) = proj_pred.kind().skip_binder() { + if let ty::PredicateKind::Clause(Clause::Projection(pred)) = proj_pred.kind().skip_binder() { let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred.kind().rebind(pred)); if projection_pred.projection_ty.substs == trait_pred.trait_ref.substs { return Some(projection_pred); @@ -80,7 +79,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve let fn_sig = cx.tcx.fn_sig(def_id); let generics = cx.tcx.predicates_of(def_id); let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); - let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord)); let partial_ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error @@ -99,11 +98,15 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve if trait_pred.self_ty() == inp; if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); then { - if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) { + if ord_preds + .iter() + .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) + { args_to_check.push((i, "Ord".to_string())); - } else if partial_ord_preds.iter().any(|pord| { - pord.self_ty() == return_ty_pred.term.ty().unwrap() - }) { + } else if partial_ord_preds + .iter() + .any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap()) + { args_to_check.push((i, "PartialOrd".to_string())); } } diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index f6d3fb00f4ee..dd120599c04e 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { return; } let map = &cx.tcx.hir(); - let opt_parent_node = map.find(map.get_parent_node(expr.hir_id)); + let opt_parent_node = map.find_parent(expr.hir_id); if_chain! { if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node; if is_questionmark_desugar_marked_call(parent_expr); @@ -129,7 +129,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( - &format!("use {singular}unit literal{plural} instead"), + format!("use {singular}unit literal{plural} instead"), args_to_recover .iter() .map(|arg| (arg.span, "()".to_string())) @@ -142,7 +142,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp let it_or_them = if plural { "them" } else { "it" }; db.span_suggestion( expr.span, - &format!( + format!( "{or}move the expression{empty_or_s} in front of the call and replace {it_or_them} with the unit literal `()`" ), sugg, @@ -192,7 +192,7 @@ fn fmt_stmts_and_call( let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); // expr is not in a block statement or result expression position, wrap in a block - let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id)); + let parent_node = cx.tcx.hir().find_parent(call_expr.hir_id); if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { let block_indent = call_expr_indent + 4; stmts_and_call_snippet = diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 016aacbf9da3..9f207d32fcff 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; @@ -7,7 +7,6 @@ use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -55,13 +54,13 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { ); } else { if_chain! { - if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(fun_def_id); + if Some(fun_def_id) == cx.tcx.lang_items().from_fn(); if let [.., last_arg] = args; if let ExprKind::Lit(spanned) = &last_arg.kind; if let LitKind::Str(symbol, _) = spanned.node; if symbol.is_empty(); let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); - if is_type_diagnostic_item(cx, inner_expr_type, sym::String); + if is_type_lang_item(cx, inner_expr_type, LangItem::String); then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs index bc0dd263d88a..397633f533b2 100644 --- a/clippy_lints/src/unnecessary_self_imports.rs +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -57,7 +57,7 @@ impl EarlyLintPass for UnnecessarySelfImports { format!( "{}{};", last_segment.ident, - if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {alias}") } else { String::new() }, + if let UseTreeKind::Simple(Some(alias)) = self_tree.kind { format!(" as {alias}") } else { String::new() }, ), Applicability::MaybeIncorrect, ); diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 7211e6864f3a..84ec0d0fb1cf 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { match fn_kind { FnKind::ItemFn(..) | FnKind::Method(..) => { let def_id = cx.tcx.hir().local_def_id(hir_id); - if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) { + if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } }, @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } // Abort if the method is implementing a trait or of it a trait method. - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { if matches!( item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..) diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index b305dae76084..7355260ae4af 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -2,14 +2,14 @@ use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{meets_msrv, msrvs, over}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::over; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; @@ -45,14 +45,13 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -#[derive(Clone, Copy)] pub struct UnnestedOrPatterns { - msrv: Option, + msrv: Msrv, } impl UnnestedOrPatterns { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } @@ -292,7 +291,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) /// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern /// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. fn extend_with_struct_pat( - qself1: &Option, + qself1: &Option>, path1: &ast::Path, fps1: &mut [ast::PatField], rest1: bool, diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 32cd46812014..7ee785804f0a 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -39,7 +39,7 @@ impl EarlyLintPass for UnsafeNameRemoval { fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { match use_tree.kind { - UseTreeKind::Simple(Some(new_name), ..) => { + UseTreeKind::Simple(Some(new_name)) => { let old_name = use_tree .prefix .segments @@ -48,9 +48,9 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { .ident; unsafe_to_safe_check(old_name, new_name, cx, span); }, - UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {}, + UseTreeKind::Simple(None) | UseTreeKind::Glob => {}, UseTreeKind::Nested(ref nested_use_tree) => { - for &(ref use_tree, _) in nested_use_tree { + for (use_tree, _) in nested_use_tree { check_use_tree(use_tree, cx, span); } }, diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index bf487c7ca20c..3538bef6e061 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, IsAsync, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { span: Span, hir_id: HirId, ) { - if !span.from_expansion() && fn_kind.asyncness() == IsAsync::Async { + if !span.from_expansion() && fn_kind.asyncness().is_async() { let mut visitor = AsyncFnVisitor { cx, found_await: false }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), hir_id); if !visitor.found_await { diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 92053cec59fc..0e526c216bee 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() { - "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { + "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => { check_map_error(cx, arg_0, expr); }, _ => (), diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index f1cebf0f9923..4ee16d9a5e4a 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -3,7 +3,6 @@ use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators}; use rustc_ast::Mutability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::lang_items::LangItem; use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; @@ -37,7 +36,7 @@ declare_clippy_lint! { /// // ... /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub UNUSED_PEEKABLE, nursery, "creating a peekable iterator without using any of its methods" @@ -132,11 +131,11 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { // If the Peekable is passed to a function, stop ExprKind::Call(_, args) => { if let Some(func_did) = fn_def_id(self.cx, expr) - && let Ok(into_iter_did) = self + && let Some(into_iter_did) = self .cx .tcx .lang_items() - .require(LangItem::IntoIterIntoIter) + .into_iter_fn() && func_did == into_iter_did { // Probably a for loop desugar, stop searching diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 3164937293b6..097568cd1f70 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use clippy_utils::source::snippet; +use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -29,23 +30,16 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { - if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind +fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> { + if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") - && let ExprKind::Lit(spanned) = &receiver.kind - && let LitKind::Float(symbol, ty) = spanned.kind { - let f = symbol.as_str().parse::().unwrap(); - let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { - ty.name_str() - } else { - "" - }; - if f.fract() == 0.0 { - Some((method_name, f_str)) - } else { - None - } + && let ExprKind::Lit(token_lit) = &receiver.kind + && token_lit.is_semantic_float() + && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::() { + (f.fract() == 0.0).then(|| + (method_name, snippet(cx, receiver.span, "..").to_string()) + ) } else { None } @@ -53,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { impl EarlyLintPass for UnusedRounding { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let Some((method_name, float)) = is_useless_rounding(expr) { + if let Some((method_name, float)) = is_useless_rounding(cx, expr) { span_lint_and_sugg( cx, UNUSED_ROUNDING, diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 713fe06bad43..f864c520302e 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,9 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::visitors::is_local_used; use if_chain::if_chain; -use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; +use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -56,15 +58,30 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { } let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id; let parent_item = cx.tcx.hir().expect_item(parent); - let assoc_item = cx.tcx.associated_item(impl_item.def_id); + let assoc_item = cx.tcx.associated_item(impl_item.owner_id); + let contains_todo = |cx, body: &'_ Body<'_>| -> bool { + clippy_utils::visitors::for_each_expr(body.value, |e| { + if let Some(macro_call) = root_macro_call_first_node(cx, e) { + if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } else { + ControlFlow::Continue(()) + } + }) + .is_some() + }; if_chain! { if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind; if assoc_item.fn_has_self_parameter; if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; - if !cx.access_levels.is_exported(impl_item.def_id.def_id) || !self.avoid_breaking_exported_api; + if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) || !self.avoid_breaking_exported_api; let body = cx.tcx.hir().body(*body_id); if let [self_param, ..] = body.params; if !is_local_used(cx, body, self_param.pat.hir_id); + if !contains_todo(cx, body); then { span_lint_and_help( cx, @@ -72,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { self_param.span, "unused `self` argument", None, - "consider refactoring to a associated function", + "consider refactoring to an associated function", ); } } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index cd1d90e860b9..cad8da18c2fb 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::{ast, visit::FnKind, ClosureBinder}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -43,6 +42,11 @@ impl EarlyLintPass for UnusedUnit { if let ast::TyKind::Tup(ref vals) = ty.kind; if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); then { + // implicit types in closure signatures are forbidden when `for<...>` is present + if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + return; + } + lint_unneeded_unit_return(cx, ty, span); } } diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index a69719b127b2..f3611d174340 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { if let ImplItemKind::Fn(_, body_id) = impl_item.kind { let body = cx.tcx.hir().body(body_id); - let typeck = cx.tcx.typeck(impl_item.def_id.def_id); + let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); let mut result = Vec::new(); let _: Option = for_each_expr(body.value, |e| { // check for `expect` diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 654ea306793b..1d2d3eb12e11 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -105,7 +105,7 @@ impl LateLintPass<'_> for UpperCaseAcronyms { fn check_item(&mut self, cx: &LateContext<'_>, it: &Item<'_>) { // do not lint public items or in macros if in_external_macro(cx.sess(), it.span) - || (self.avoid_breaking_exported_api && cx.access_levels.is_exported(it.def_id.def_id)) + || (self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(it.owner_id.def_id)) { return; } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 65f1b5462081..6ae9d9d63538 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::same_type_and_consts; -use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -14,7 +15,6 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -30,7 +30,6 @@ declare_clippy_lint! { /// /// ### Known problems /// - Unaddressed false negative in fn bodies of trait implementations - /// - False positive with associated types in traits (#4140) /// /// ### Example /// ```rust @@ -58,13 +57,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct UseSelf { - msrv: Option, + msrv: Msrv, stack: Vec, } impl UseSelf { #[must_use] - pub fn new(msrv: Option) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Self::default() @@ -103,10 +102,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) }); + if !item.span.from_expansion(); if !is_from_proc_macro(cx, item); // expensive, should be last check then { StackItem::Check { - impl_id: item.def_id.def_id, + impl_id: item.owner_id.def_id, in_body: 0, types_to_skip: std::iter::once(self_ty.hir_id).collect(), } @@ -137,13 +137,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { then { // `self_ty` is the semantic self type of `impl for `. This cannot be // `Self`. - let self_ty = impl_trait_ref.self_ty(); + let self_ty = impl_trait_ref.subst_identity().self_ty(); // `trait_method_sig` is the signature of the function, how it is declared in the // trait, not in the impl of the trait. let trait_method = cx .tcx - .associated_item(impl_item.def_id) + .associated_item(impl_item.owner_id) .trait_item_def_id .expect("impl method matches a trait method"); let trait_method_sig = cx.tcx.fn_sig(trait_method); @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { if !hir_ty.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, in_body, @@ -228,30 +228,19 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } } match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res { - Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => (), - Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path), - _ => span_lint(cx, path.span), - }, - // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) + ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), ExprKind::Call(fun, _) => { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { - if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res { - match ctor_of { - CtorOf::Variant => lint_path_to_variant(cx, path), - CtorOf::Struct => span_lint(cx, path.span), - } - } + check_path(cx, path); } }, - // unit enum variants (`Enum::A`) - ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path), + ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), _ => (), } } @@ -259,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { if_chain! { if !pat.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); // get the path from the pattern if let PatKind::Path(QPath::Resolved(_, path)) @@ -267,15 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id); then { - match path.res { - Res::Def(DefKind::Ctor(ctor_of, _), ..) => match ctor_of { - CtorOf::Variant => lint_path_to_variant(cx, path), - CtorOf::Struct => span_lint(cx, path.span), - }, - Res::Def(DefKind::Variant, ..) => lint_path_to_variant(cx, path), - Res::Def(DefKind::Struct, ..) => span_lint(cx, path.span), - _ => () - } + check_path(cx, path); } } } @@ -313,6 +294,16 @@ fn span_lint(cx: &LateContext<'_>, span: Span) { ); } +fn check_path(cx: &LateContext<'_>, path: &Path<'_>) { + match path.res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _) | DefKind::Variant, ..) => { + lint_path_to_variant(cx, path); + }, + Res::Def(DefKind::Ctor(CtorOf::Struct, _) | DefKind::Struct, ..) => span_lint(cx, path.span), + _ => (), + } +} + fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { if let [.., self_seg, _variant] = path.segments { let span = path diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1f69db1cbca4..a95e7b613746 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; -use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -81,16 +81,24 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if is_trait_method(cx, e, sym::IntoIterator) && name.ident.name == sym::into_iter { - if let Some(parent_expr) = get_parent_expr(cx, e) { - if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind { - if parent_name.ident.name != sym::into_iter { - return; - } - } + if get_parent_expr(cx, e).is_some() && + let Some(id) = path_to_local(recv) && + let Node::Pat(pat) = cx.tcx.hir().get(id) && + let PatKind::Binding(ann, ..) = pat.kind && + ann != BindingAnnotation::MUT + { + // Do not remove .into_iter() applied to a non-mutable local variable used in + // a larger expression context as it would differ in mutability. + return; } + let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + + // If the types are identical then .into_iter() can be removed, unless the type + // implements Copy, in which case .into_iter() returns a copy of the receiver and + // cannot be safely omitted. + if same_type_and_consts(a, b) && !is_copy(cx, b) { let sugg = snippet(cx, recv.span, "").into_owned(); span_lint_and_sugg( cx, @@ -153,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } if_chain! { - if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(def_id); + if Some(def_id) == cx.tcx.lang_items().from_fn(); if same_type_and_consts(a, b); then { diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 0c052d86eda4..bd7daf0773ca 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -299,7 +299,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }; kind!("Float(_, {float_ty})"); }, - LitKind::ByteStr(ref vec) => { + LitKind::ByteStr(ref vec, _) => { bind!(self, vec); kind!("ByteStr(ref {vec})"); chain!(self, "let [{:?}] = **{vec}", vec.value); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 668123e4d6e3..1d78c7cfae0d 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -53,11 +53,11 @@ impl DisallowedPath { path } - pub fn reason(&self) -> Option<&str> { + pub fn reason(&self) -> Option { match self { Self::WithReason { reason: Some(reason), .. - } => Some(reason), + } => Some(format!("{reason} (from clippy.toml)")), _ => None, } } @@ -205,15 +205,55 @@ macro_rules! define_Conf { } define_Conf! { - /// Lint: Arithmetic. + /// Lint: ARITHMETIC_SIDE_EFFECTS. /// - /// Suppress checking of the passed type names. + /// Suppress checking of the passed type names in all types of operations. + /// + /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] + /// ``` + /// + /// #### Noteworthy + /// + /// A type, say `SomeType`, listed in this configuration has the same behavior of + /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. (arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet = <_>::default()), + /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// + /// Suppress checking of the passed type pair names in binary operations like addition or + /// multiplication. + /// + /// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless + /// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. + /// + /// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as + /// `["AnotherType", "SomeType"]`. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] + /// ``` + (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()), + /// Lint: ARITHMETIC_SIDE_EFFECTS. + /// + /// Suppress checking of the passed type names in unary operations like "negation" (`-`). + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] + /// ``` + (arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet = <_>::default()), /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN. /// /// The minimum rust version that the project supports (msrv: Option = None), @@ -283,7 +323,7 @@ define_Conf! { /// /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit: Option = None), - /// Lint: LARGE_TYPE_PASS_BY_MOVE. + /// Lint: LARGE_TYPES_PASSED_BY_VALUE. /// /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. (pass_by_value_size_limit: u64 = 256), @@ -294,7 +334,7 @@ define_Conf! { /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. /// /// The maximum allowed size for arrays on the stack - (array_size_threshold: u64 = 512_000), + (array_size_threshold: u128 = 512_000), /// Lint: VEC_BOX. /// /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed @@ -335,6 +375,12 @@ define_Conf! { /// /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other (upper_case_acronyms_aggressive: bool = false), + /// Lint: MANUAL_LET_ELSE. + /// + /// Whether the matches should be considered by the lint, and whether there should + /// be filtering for common types. + (matches_for_let_else: crate::manual_let_else::MatchLintBehaviour = + crate::manual_let_else::MatchLintBehaviour::WellKnownTypes), /// Lint: _CARGO_COMMON_METADATA. /// /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. @@ -365,7 +411,7 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE + /// Lint: AWAIT_HOLDING_INVALID_TYPE. (await_holding_invalid_types: Vec = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// @@ -373,23 +419,48 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test functions + /// Whether `expect` should be allowed within `#[cfg(test)]` (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// - /// Whether `unwrap` should be allowed in test functions + /// Whether `unwrap` should be allowed in test cfg (allow_unwrap_in_tests: bool = false), /// Lint: DBG_MACRO. /// /// Whether `dbg!` should be allowed in test functions (allow_dbg_in_tests: bool = false), - /// Lint: RESULT_LARGE_ERR + /// Lint: PRINT_STDOUT, PRINT_STDERR. + /// + /// Whether print macros (ex. `println!`) should be allowed in test functions + (allow_print_in_tests: bool = false), + /// Lint: RESULT_LARGE_ERR. /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), + /// Lint: MUTABLE_KEY_TYPE. + /// + /// A list of paths to types that should be treated like `Arc`, i.e. ignored but + /// for the generic parameters for determining interior mutability + (ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()])), + /// Lint: UNINLINED_FORMAT_ARGS. + /// + /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + (allow_mixed_uninlined_format_args: bool = true), + /// Lint: INDEXING_SLICING. + /// + /// Whether to suppress a restriction lint in constant code. In same + /// cases the restructured operation might not be unavoidable, as the + /// suggested counterparts are unavailable in constant code. This + /// configuration will cause restriction lints to trigger even + /// if no suggestion can be made. + (suppress_restriction_lint_in_const: bool = false), } /// Search for the configuration file. +/// +/// # Errors +/// +/// Returns any unexpected filesystem error encountered when searching for the config file pub fn lookup_conf_file() -> io::Result> { /// Possible filename to search for. const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0d908bf2a83e..71f6c9909ddd 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,1560 +1,12 @@ -use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; -use clippy_utils::consts::{constant_simple, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::ty::match_type; -use clippy_utils::{ - def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths, - match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq, -}; -use if_chain::if_chain; -use rustc_ast as ast; -use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId}; -use rustc_ast::visit::FnKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{ - BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, - TyKind, UnOp, -}; -use rustc_hir_analysis::hir_ty_to_ty; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; -use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{ - self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty, -}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, BytePos, Span}; - -use std::borrow::{Borrow, Cow}; -use std::str; - -#[cfg(feature = "internal")] +pub mod clippy_lints_internal; +pub mod collapsible_calls; +pub mod compiler_lint_functions; +pub mod if_chain_style; +pub mod interning_defined_symbol; +pub mod invalid_paths; +pub mod lint_without_lint_pass; pub mod metadata_collector; - -declare_clippy_lint! { - /// ### What it does - /// Checks for various things we like to keep tidy in clippy. - /// - /// ### Why is this bad? - /// We like to pretend we're an example of tidy code. - /// - /// ### Example - /// Wrong ordering of the util::paths constants. - pub CLIPPY_LINTS_INTERNAL, - internal, - "various things that will negatively affect your clippy experience" -} - -declare_clippy_lint! { - /// ### What it does - /// Ensures every lint is associated to a `LintPass`. - /// - /// ### Why is this bad? - /// The compiler only knows lints via a `LintPass`. Without - /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not - /// know the name of the lint. - /// - /// ### Known problems - /// Only checks for lints associated using the - /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub LINT_1, ... } - /// declare_lint! { pub LINT_2, ... } - /// declare_lint! { pub FORGOTTEN_LINT, ... } - /// // ... - /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); - /// // missing FORGOTTEN_LINT - /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` - /// variant of the function. - /// - /// ### Why is this bad? - /// The `utils::*` variants also add a link to the Clippy documentation to the - /// warning/error messages. - /// - /// ### Example - /// ```rust,ignore - /// cx.span_lint(LINT_NAME, "message"); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::span_lint(cx, LINT_NAME, "message"); - /// ``` - pub COMPILER_LINT_FUNCTIONS, - internal, - "usage of the lint functions of the compiler instead of the utils::* variant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `cx.outer().expn_data()` and suggests to use - /// the `cx.outer_expn_data()` - /// - /// ### Why is this bad? - /// `cx.outer_expn_data()` is faster and more concise. - /// - /// ### Example - /// ```rust,ignore - /// expr.span.ctxt().outer().expn_data() - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// expr.span.ctxt().outer_expn_data() - /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" -} - -declare_clippy_lint! { - /// ### What it does - /// Not an actual lint. This lint is only meant for testing our customized internal compiler - /// error message by calling `panic`. - /// - /// ### Why is this bad? - /// ICE in large quantities can damage your teeth - /// - /// ### Example - /// ```rust,ignore - /// 🍦🍦🍦🍦🍦 - /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated lint without an updated description, - /// i.e. `default lint description`. - /// - /// ### Why is this bad? - /// Indicates that the lint is not finished. - /// - /// ### Example - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } - /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" -} - -declare_clippy_lint! { - /// ### What it does - /// Lints `span_lint_and_then` function calls, where the - /// closure argument has only one statement and that statement is a method - /// call to `span_suggestion`, `span_help`, `span_note` (using the same - /// span), `help` or `note`. - /// - /// These usages of `span_lint_and_then` should be replaced with one of the - /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or - /// `span_lint_and_note`. - /// - /// ### Why is this bad? - /// Using the wrapper `span_lint_and_*` functions, is more - /// convenient, readable and less error prone. - /// - /// ### Example - /// ```rust,ignore - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_suggestion( - /// expr.span, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_help(expr.span, help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.help(help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_note(expr.span, note_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.note(note_msg); - /// }); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// span_lint_and_sugg( - /// cx, - /// TEST_LINT, - /// expr.span, - /// lint_msg, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); - /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. - /// - /// ### Why is this bad? - /// The path for an item is subject to change and is less efficient to look up than a - /// diagnostic item or a `LangItem`. - /// - /// ### Example - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) - /// ``` - pub UNNECESSARY_DEF_PATH, - internal, - "using a def path when a diagnostic item or a `LangItem` is available" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks the paths module for invalid paths. - /// - /// ### Why is this bad? - /// It indicates a bug in the code. - /// - /// ### Example - /// None. - pub INVALID_PATHS, - internal, - "invalid path" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// ### Why is this bad? - /// It's faster and easier to use the symbol constant. - /// - /// ### Example - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for unnecessary conversion from Symbol to a string. - /// - /// ### Why is this bad? - /// It's faster use symbols directly instead of strings. - /// - /// ### Example - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -declare_clippy_lint! { - /// Finds unidiomatic usage of `if_chain!` - pub IF_CHAIN_STYLE, - internal, - "non-idiomatic `if_chain!` usage" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for invalid `clippy::version` attributes. - /// - /// Valid values are: - /// * "pre 1.29.0" - /// * any valid semantic version - pub INVALID_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found an invalid `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for declared clippy lints without the `clippy::version` attribute. - /// - pub MISSING_CLIPPY_VERSION_ATTRIBUTE, - internal, - "found clippy lint without `clippy::version` attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. - /// - pub MISSING_MSRV_ATTR_IMPL, - internal, - "checking if all necessary steps were taken when adding a MSRV to a lint" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for cases of an auto-generated deprecated lint without an updated reason, - /// i.e. `"default deprecation note"`. - /// - /// ### Why is this bad? - /// Indicates that the documentation is incomplete. - /// - /// ### Example - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// TODO - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "default deprecation note" - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// declare_deprecated_lint! { - /// /// ### What it does - /// /// Nothing. This lint has been deprecated. - /// /// - /// /// ### Deprecation reason - /// /// This lint has been replaced by `cooler_lint` - /// #[clippy::version = "1.63.0"] - /// pub COOL_LINT, - /// "this lint has been replaced by `cooler_lint`" - /// } - /// ``` - pub DEFAULT_DEPRECATION_REASON, - internal, - "found 'default deprecation note' in a deprecated lint declaration" -} - -declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); - -impl EarlyLintPass for ClippyLintsInternal { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { - if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { - let mut last_name: Option<&str> = None; - for item in items { - let name = item.ident.as_str(); - if let Some(last_name) = last_name { - if *last_name > *name { - span_lint( - cx, - CLIPPY_LINTS_INTERNAL, - item.span, - "this constant should be before the previous constant due to lexical \ - ordering", - ); - } - } - last_name = Some(name); - } - } - } - } - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct LintWithoutLintPass { - declared_lints: FxHashMap, - registered_lints: FxHashSet, -} - -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); - -impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) - || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) - { - return; - } - - if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { - let is_lint_ref_ty = is_lint_ref_type(cx, ty); - if is_deprecated_lint(cx, ty) || is_lint_ref_ty { - check_invalid_clippy_version_attribute(cx, item); - - let expr = &cx.tcx.hir().body(body_id).value; - let fields; - if is_lint_ref_ty { - if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind - && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { - fields = struct_fields; - } else { - return; - } - } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { - fields = struct_fields; - } else { - return; - } - - let field = fields - .iter() - .find(|f| f.ident.as_str() == "desc") - .expect("lints must have a description field"); - - if let ExprKind::Lit(Spanned { - node: LitKind::Str(ref sym, _), - .. - }) = field.expr.kind - { - let sym_str = sym.as_str(); - if is_lint_ref_ty { - if sym_str == "default lint description" { - span_lint( - cx, - DEFAULT_LINT, - item.span, - &format!("the lint `{}` has the default lint description", item.ident.name), - ); - } - - self.declared_lints.insert(item.ident.name, item.span); - } else if sym_str == "default deprecation note" { - span_lint( - cx, - DEFAULT_DEPRECATION_REASON, - item.span, - &format!("the lint `{}` has the default deprecation reason", item.ident.name), - ); - } - } - } - } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { - if !matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "impl_lint_pass" | "declare_lint_pass" - ) { - return; - } - if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, - items: impl_item_refs, - .. - }) = item.kind - { - let mut collector = LintCollector { - output: &mut self.registered_lints, - cx, - }; - let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id(), - ), - ); - collector.visit_expr(cx.tcx.hir().body(body_id).value); - } - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { - return; - } - - for (lint_name, &lint_span) in &self.declared_lints { - // When using the `declare_tool_lint!` macro, the original `lint_span`'s - // file points to "". - // `compiletest-rs` thinks that's an error in a different file and - // just ignores it. This causes the test in compile-fail/lint_pass - // not able to capture the error. - // Therefore, we need to climb the macro expansion tree and find the - // actual span that invoked `declare_tool_lint!`: - let lint_span = lint_span.ctxt().outer_expn_data().call_site; - - if !self.registered_lints.contains(lint_name) { - span_lint( - cx, - LINT_WITHOUT_LINT_PASS, - lint_span, - &format!("the lint `{lint_name}` is not added to any `LintPass`"), - ); - } - } - } -} - -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Rptr( - _, - MutTy { - ty: inner, - mutbl: Mutability::Not, - }, - ) = ty.kind - { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } - } - - false -} - -fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { - if let Some(value) = extract_clippy_version_value(cx, item) { - // The `sym!` macro doesn't work as it only expects a single token. - // It's better to keep it this way and have a direct `Symbol::intern` call here. - if value == Symbol::intern("pre 1.29.0") { - return; - } - - if RustcVersion::parse(value.as_str()).is_err() { - span_lint_and_help( - cx, - INVALID_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this item has an invalid `clippy::version` attribute", - None, - "please use a valid semantic version, see `doc/adding_lints.md`", - ); - } - } else { - span_lint_and_help( - cx, - MISSING_CLIPPY_VERSION_ATTRIBUTE, - item.span, - "this lint is missing the `clippy::version` attribute or version value", - None, - "please use a `clippy::version` attribute, see `doc/adding_lints.md`", - ); - } -} - -/// This function extracts the version value of a `clippy::version` attribute if the given value has -/// one -fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - attrs.iter().find_map(|attr| { - if_chain! { - // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; - if tool_name.ident.name == sym::clippy; - if attr_name.ident.name == sym::version; - if let Some(version) = attr.value_str(); - then { - Some(version) - } else { - None - } - } - }) -} - -struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { - type NestedFilter = nested_filter::All; - - fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { - if path.segments.len() == 1 { - self.output.insert(path.segments[0].ident.name); - } - } - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } -} - -#[derive(Clone, Default)] -pub struct CompilerLintFunctions { - map: FxHashMap<&'static str, &'static str>, -} - -impl CompilerLintFunctions { - #[must_use] - pub fn new() -> Self { - let mut map = FxHashMap::default(); - map.insert("span_lint", "utils::span_lint"); - map.insert("struct_span_lint", "utils::span_lint"); - map.insert("lint", "utils::span_lint"); - map.insert("span_lint_note", "utils::span_lint_and_note"); - map.insert("span_lint_help", "utils::span_lint_and_help"); - Self { map } - } -} - -impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); - -impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; - let fn_name = path.ident; - if let Some(sugg) = self.map.get(fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) - || match_type(cx, ty, &paths::LATE_CONTEXT); - then { - span_lint_and_help( - cx, - COMPILER_LINT_FUNCTIONS, - path.ident.span, - "usage of a compiler lint function", - None, - &format!("please use the Clippy variant of this function: `{sugg}`"), - ); - } - } - } -} - -declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); - -impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { - return; - } - - let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let (self_arg, args)= arg_lists[1]; - if args.is_empty(); - let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); - then { - span_lint_and_sugg( - cx, - OUTER_EXPN_EXPN_DATA, - spans[1].with_hi(expr.span.hi()), - "usage of `outer_expn().expn_data()`", - "try", - "outer_expn_data()".to_string(), - Applicability::MachineApplicable, - ); - } - } - } -} - -declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); - -impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); - } -} - -fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { - match fn_kind { - FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", - FnKind::Closure(..) => false, - } -} - -declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); - -impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, and_then_args) = expr.kind; - if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); - if and_then_args.len() == 5; - if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(body); - let only_expr = peel_blocks_with_stmt(body.value); - if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; - if let ExprKind::Path(..) = recv.kind; - then { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); - }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); - }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - } - "note" => { - let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - } - _ => (), - } - } - } - } -} - -struct AndThenSnippets<'a> { - cx: Cow<'a, str>, - lint: Cow<'a, str>, - span: Cow<'a, str>, - msg: Cow<'a, str>, -} - -fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { - let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); - let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); - let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); - let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); - - AndThenSnippets { - cx: cx_snippet, - lint: lint_snippet, - span: span_snippet, - msg: msg_snippet, - } -} - -struct SpanSuggestionSnippets<'a> { - help: Cow<'a, str>, - sugg: Cow<'a, str>, - applicability: Cow<'a, str>, -} - -fn span_suggestion_snippets<'a, 'hir>( - cx: &LateContext<'_>, - span_call_args: &'hir [Expr<'hir>], -) -> SpanSuggestionSnippets<'a> { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); - let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); - - SpanSuggestionSnippets { - help: help_snippet, - sugg: sugg_snippet, - applicability: applicability_snippet, - } -} - -fn suggest_suggestion( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - span_suggestion_snippets: &SpanSuggestionSnippets<'_>, -) { - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - span_suggestion_snippets.help, - span_suggestion_snippets.sugg, - span_suggestion_snippets.applicability - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_help( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - help: &str, - with_span: bool, -) { - let option_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_help({}, {}, {}, {}, {}, {help})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_note( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - note: &str, - with_span: bool, -) { - let note_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", - and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, - ), - Applicability::MachineApplicable, - ); -} - -declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); - -#[allow(clippy::too_many_lines)] -impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - enum Item { - LangItem(Symbol), - DiagnosticItem(Symbol), - } - static PATHS: &[&[&str]] = &[ - &["clippy_utils", "match_def_path"], - &["clippy_utils", "match_trait_method"], - &["clippy_utils", "ty", "match_type"], - &["clippy_utils", "is_expr_path_def_path"], - ]; - - if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind; - if let ExprKind::Path(path) = &func.kind; - if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if let Some(which_path) = match_any_def_paths(cx, id, PATHS); - let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; - // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, item_arg); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = def_path_res(cx, &segments[..], None).opt_def_id(); - then { - // def_path_res will match field names before anything else, but for this we want to match - // inherent functions first. - let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx.def_key(def_id).parent - .and_then(|parent_idx| - cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter() - .find_map(|impl_id| cx.tcx.associated_items(*impl_id) - .find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - ) - ) - .map_or(def_id, |item| item.def_id) - } else { - def_id - }; - - // Check if the target item is a diagnostic item or LangItem. - let (msg, item) = if let Some(item_name) - = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) - { - ( - "use of a def path to a diagnostic item", - Item::DiagnosticItem(*item_name), - ) - } else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) { - let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id(); - let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name; - ( - "use of a def path to a `LangItem`", - Item::LangItem(item_name), - ) - } else { - return; - }; - - let has_ctor = match cx.tcx.def_kind(def_id) { - DefKind::Struct => { - let variant = cx.tcx.adt_def(def_id).non_enum_variant(); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - DefKind::Variant => { - let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); - variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) - } - _ => false, - }; - - let mut app = Applicability::MachineApplicable; - let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); - let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); - let (sugg, with_note) = match (which_path, item) { - // match_def_path - (0, Item::DiagnosticItem(item)) => - (format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor), - (0, Item::LangItem(item)) => ( - format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"), - has_ctor - ), - // match_trait_method - (1, Item::DiagnosticItem(item)) => - (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false), - // match_type - (2, Item::DiagnosticItem(item)) => - (format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (2, Item::LangItem(item)) => - (format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false), - // is_expr_path_def_path - (3, Item::DiagnosticItem(item)) if has_ctor => ( - format!( - "is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})", - ), - false, - ), - (3, Item::LangItem(item)) if has_ctor => ( - format!( - "is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})", - ), - false, - ), - (3, Item::DiagnosticItem(item)) => - (format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false), - (3, Item::LangItem(item)) => ( - format!( - "path_res({cx_snip}, {def_snip}).opt_def_id()\ - .map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))", - ), - false, - ), - _ => return, - }; - - span_lint_and_then( - cx, - UNNECESSARY_DEF_PATH, - expr.span, - msg, - |diag| { - diag.span_suggestion(expr.span, "try", sugg, app); - if with_note { - diag.help( - "if this `DefId` came from a constructor expression or pattern then the \ - parent `DefId` should be used instead" - ); - } - }, - ); - } - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - match peel_hir_expr_refs(expr).0.kind { - ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { - path_to_matched_type(cx, init) - } else { - None - } - }, - Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( - cx, - cx.tcx.eval_static_initializer(def_id).ok()?.inner(), - cx.tcx.type_of(def_id), - ), - Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { - ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { - read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) - }, - _ => None, - }, - _ => None, - }, - ExprKind::Array(exprs) => exprs - .iter() - .map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some((*sym.as_str()).to_owned()); - } - } - - None - }) - .collect(), - _ => None, - } -} - -fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { - let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().values().next()?; - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - (alloc.inner(), ty) - } else { - return None; - } - } else { - (alloc, ty) - }; - - if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() - && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() - && ty.is_str() - { - alloc - .provenance() - .values() - .map(|&alloc| { - if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { - let alloc = alloc.inner(); - str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) - .ok().map(ToOwned::to_owned) - } else { - None - } - }) - .collect() - } else { - None - } -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path, None) != Res::Err { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - // This list isn't complete, but good enough for our current list of paths. - let incoherent_impls = [ - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32), - SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64), - SimplifiedTypeGen::SliceSimplifiedType, - SimplifiedTypeGen::StrSimplifiedType, - ] - .iter() - .flat_map(|&ty| cx.tcx.incoherent_impls(ty)); - for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) { - let lang_item_path = cx.get_def_path(*item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - if matches!( - cx.tcx.def_kind(*item_def_id), - DefKind::Mod | DefKind::Enum | DefKind::Trait - ) { - for child in cx.tcx.module_children(*item_def_id) { - if child.ident.name == *item { - return true; - } - } - } else { - for child in cx.tcx.associated_item_def_ids(*item_def_id) { - if cx.tcx.item_name(*child) == *item { - return true; - } - } - } - } - } - } - - false -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id()); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); - let body = cx.tcx.hir().body(body_id); - let typeck_results = cx.tcx.typeck_body(body_id); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); - let path: Vec<&str> = path.iter().map(|x| { - if let Constant::Str(s) = x { - s.as_str() - } else { - // We checked the type of the constant above - unreachable!() - } - }).collect(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, INVALID_PATHS, item.span, "invalid path"); - } - } - } -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { - for item in cx.tcx.module_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg]) = &expr.kind; - if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); - if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); - if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - let value = Symbol::intern(&arg).as_u32(); - if let Some(&def_id) = self.symbol_map.get(&value); - then { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } - } - if let ExprKind::Binary(op, left, right) = expr.kind { - if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } - } -} - -impl InterningDefinedSymbol { - fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[ - &paths::SYMBOL_AS_STR, - &paths::SYMBOL_TO_IDENT_STRING, - &paths::TO_STRING_METHOD, - ]; - let call = if_chain! { - if let ExprKind::AddrOf(_, _, e) = expr.kind; - if let ExprKind::Unary(UnOp::Deref, e) = e.kind; - then { e } else { expr } - }; - if_chain! { - // is a method call - if let ExprKind::MethodCall(_, item, [], _) = call.kind; - if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); - let ty = cx.typeck_results().expr_ty(item); - // ...on either an Ident or a Symbol - if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { - Some(false) - } else if match_type(cx, ty, &paths::IDENT) { - Some(true) - } else { - None - }; - // ...which converts it to a string - let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); - then { - let is_to_owned = path.last().unwrap().ends_with("string"); - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - } - // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} - -declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); - -impl<'tcx> LateLintPass<'tcx> for IfChainStyle { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - let (local, after, if_chain_span) = if_chain! { - if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; - if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); - then { (local, after, if_chain_span) } else { return } - }; - if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be above the `if_chain!`", - ); - } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { - span_lint( - cx, - IF_CHAIN_STYLE, - if_chain_local_span(cx, local, if_chain_span), - "`let` expression should be inside `then { .. }`", - ); - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { - (cond, then, r#else.is_some()) - } else { - return; - }; - let ExprKind::Block(then_block, _) = then.kind else { return }; - let if_chain_span = is_expn_of(expr.span, "if_chain"); - if !els { - check_nested_if_chains(cx, expr, then_block, if_chain_span); - } - let Some(if_chain_span) = if_chain_span else { return }; - // check for `if a && b;` - if_chain! { - if let ExprKind::Binary(op, _, _) = cond.kind; - if op.node == BinOpKind::And; - if cx.sess().source_map().is_multiline(cond.span); - then { - span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); - } - } - if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) - && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) - { - span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); - } - } -} - -fn check_nested_if_chains( - cx: &LateContext<'_>, - if_expr: &Expr<'_>, - then_block: &Block<'_>, - if_chain_span: Option, -) { - #[rustfmt::skip] - let (head, tail) = match *then_block { - Block { stmts, expr: Some(tail), .. } => (stmts, tail), - Block { - stmts: &[ - ref head @ .., - Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } - ], - .. - } => (head, tail), - _ => return, - }; - if_chain! { - if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); - let sm = cx.sess().source_map(); - if head - .iter() - .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); - if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); - then {} else { return } - } - let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { - (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), - (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), - (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), - _ => return, - }; - span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { - let (span, msg) = match head { - [] => return, - [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), - [a, .., b] => ( - a.span.to(b.span), - "these `let` statements can also be in the `if_chain!`", - ), - }; - diag.span_help(span, msg); - }); -} - -fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { - cx.tcx - .hir() - .parent_iter(hir_id) - .find(|(_, node)| { - #[rustfmt::skip] - !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) - }) - .map_or(false, |(id, _)| { - is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) - }) -} - -/// Checks a trailing slice of statements and expression of a `Block` to see if they are part -/// of the `then {..}` portion of an `if_chain!` -fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { - let span = if let [stmt, ..] = stmts { - stmt.span - } else if let Some(expr) = expr { - expr.span - } else { - // empty `then {}` - return true; - }; - is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) -} - -/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. -fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { - let mut span = local.pat.span; - if let Some(init) = local.init { - span = span.to(init.span); - } - span.adjust(if_chain_span.ctxt().outer_expn()); - let sm = cx.sess().source_map(); - let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); - let span = sm.span_extend_to_next_char(span, ';', false); - Span::new( - span.lo() - BytePos(3), - span.hi() + BytePos(1), - span.ctxt(), - span.parent(), - ) -} - -declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); - -impl LateLintPass<'_> for MsrvAttrImpl { - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if_chain! { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: Some(lint_pass_trait_ref), - self_ty, - items, - .. - }) = &item.kind; - if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); - let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); - if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); - let self_ty = hir_ty_to_ty(cx.tcx, self_ty); - if let ty::Adt(self_ty_def, _) = self_ty.kind(); - if self_ty_def.is_struct(); - if self_ty_def.all_fields().any(|f| { - cx.tcx - .type_of(f.did) - .walk() - .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) - }); - if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); - then { - let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; - let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; - let span = cx.sess().source_map().span_through_char(item.span, '{'); - span_lint_and_sugg( - cx, - MISSING_MSRV_ATTR_IMPL, - span, - &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), - &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), - format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), - Applicability::MachineApplicable, - ); - } - } - } -} +pub mod msrv_attr_impl; +pub mod outer_expn_data_pass; +pub mod produce_ice; +pub mod unnecessary_def_path; diff --git a/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs new file mode 100644 index 000000000000..da9514dd15ee --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/clippy_lints_internal.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Crate, ItemKind, ModKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for various things we like to keep tidy in clippy. + /// + /// ### Why is this bad? + /// We like to pretend we're an example of tidy code. + /// + /// ### Example + /// Wrong ordering of the util::paths constants. + pub CLIPPY_LINTS_INTERNAL, + internal, + "various things that will negatively affect your clippy experience" +} + +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); + +impl EarlyLintPass for ClippyLintsInternal { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind { + if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") { + if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind { + let mut last_name: Option<&str> = None; + for item in items { + let name = item.ident.as_str(); + if let Some(last_name) = last_name { + if *last_name > *name { + span_lint( + cx, + CLIPPY_LINTS_INTERNAL, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + } + last_name = Some(name); + } + } + } + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs new file mode 100644 index 000000000000..d7666b77f6e9 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -0,0 +1,245 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::{Closure, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::borrow::{Borrow, Cow}; + +declare_clippy_lint! { + /// ### What it does + /// Lints `span_lint_and_then` function calls, where the + /// closure argument has only one statement and that statement is a method + /// call to `span_suggestion`, `span_help`, `span_note` (using the same + /// span), `help` or `note`. + /// + /// These usages of `span_lint_and_then` should be replaced with one of the + /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or + /// `span_lint_and_note`. + /// + /// ### Why is this bad? + /// Using the wrapper `span_lint_and_*` functions, is more + /// convenient, readable and less error prone. + /// + /// ### Example + /// ```rust,ignore + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_suggestion( + /// expr.span, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_help(expr.span, help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.help(help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_note(expr.span, note_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.note(note_msg); + /// }); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// span_lint_and_sugg( + /// cx, + /// TEST_LINT, + /// expr.span, + /// lint_msg, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + /// ``` + pub COLLAPSIBLE_SPAN_LINT_CALLS, + internal, + "found collapsible `span_lint_and_then` calls" +} + +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(func, and_then_args) = expr.kind; + if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); + if and_then_args.len() == 5; + if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind; + let body = cx.tcx.hir().body(body); + let only_expr = peel_blocks_with_stmt(body.value); + if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind; + if let ExprKind::Path(..) = recv.kind; + then { + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + match ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + suggest_suggestion( + cx, + expr, + &and_then_snippets, + &span_suggestion_snippets(cx, span_call_args), + ); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + }, + "note" => { + let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + }, + _ => (), + } + } + } + } +} + +struct AndThenSnippets<'a> { + cx: Cow<'a, str>, + lint: Cow<'a, str>, + span: Cow<'a, str>, + msg: Cow<'a, str>, +} + +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { + let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); + let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); + let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); + let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); + + AndThenSnippets { + cx: cx_snippet, + lint: lint_snippet, + span: span_snippet, + msg: msg_snippet, + } +} + +struct SpanSuggestionSnippets<'a> { + help: Cow<'a, str>, + sugg: Cow<'a, str>, + applicability: Cow<'a, str>, +} + +fn span_suggestion_snippets<'a, 'hir>( + cx: &LateContext<'_>, + span_call_args: &'hir [Expr<'hir>], +) -> SpanSuggestionSnippets<'a> { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + let sugg_snippet = snippet(cx, span_call_args[2].span, ".."); + let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable"); + + SpanSuggestionSnippets { + help: help_snippet, + sugg: sugg_snippet, + applicability: applicability_snippet, + } +} + +fn suggest_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + span_suggestion_snippets: &SpanSuggestionSnippets<'_>, +) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + span_suggestion_snippets.help, + span_suggestion_snippets.sugg, + span_suggestion_snippets.applicability + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_help( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + help: &str, + with_span: bool, +) { + let option_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_help({}, {}, {}, {}, {}, {help})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span, + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_note( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + note: &str, + with_span: bool, +) { + let note_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_note({}, {}, {}, {}, {note_span}, {note})", + and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, + ), + Applicability::MachineApplicable, + ); +} diff --git a/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs new file mode 100644 index 000000000000..cacd05262a21 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` + /// variant of the function. + /// + /// ### Why is this bad? + /// The `utils::*` variants also add a link to the Clippy documentation to the + /// warning/error messages. + /// + /// ### Example + /// ```rust,ignore + /// cx.span_lint(LINT_NAME, "message"); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::span_lint(cx, LINT_NAME, "message"); + /// ``` + pub COMPILER_LINT_FUNCTIONS, + internal, + "usage of the lint functions of the compiler instead of the utils::* variant" +} + +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); + +#[derive(Clone, Default)] +pub struct CompilerLintFunctions { + map: FxHashMap<&'static str, &'static str>, +} + +impl CompilerLintFunctions { + #[must_use] + pub fn new() -> Self { + let mut map = FxHashMap::default(); + map.insert("span_lint", "utils::span_lint"); + map.insert("struct_span_lint", "utils::span_lint"); + map.insert("lint", "utils::span_lint"); + map.insert("span_lint_note", "utils::span_lint_and_note"); + map.insert("span_lint_help", "utils::span_lint_and_help"); + Self { map } + } +} + +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind; + let fn_name = path.ident; + if let Some(sugg) = self.map.get(fn_name.as_str()); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); + then { + span_lint_and_help( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + None, + &format!("please use the Clippy variant of this function: `{sugg}`"), + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/if_chain_style.rs b/clippy_lints/src/utils/internal_lints/if_chain_style.rs new file mode 100644 index 000000000000..883a5c08e5c1 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/if_chain_style.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::{higher, is_else_clause, is_expn_of}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); + +impl<'tcx> LateLintPass<'tcx> for IfChainStyle { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let (local, after, if_chain_span) = if_chain! { + if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; + if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); + then { (local, after, if_chain_span) } else { return } + }; + if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be above the `if_chain!`", + ); + } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be inside `then { .. }`", + ); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { + (cond, then, r#else.is_some()) + } else { + return; + }; + let ExprKind::Block(then_block, _) = then.kind else { return }; + let if_chain_span = is_expn_of(expr.span, "if_chain"); + if !els { + check_nested_if_chains(cx, expr, then_block, if_chain_span); + } + let Some(if_chain_span) = if_chain_span else { return }; + // check for `if a && b;` + if_chain! { + if let ExprKind::Binary(op, _, _) = cond.kind; + if op.node == BinOpKind::And; + if cx.sess().source_map().is_multiline(cond.span); + then { + span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); + } + } + if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) + && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) + { + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`"); + } + } +} + +fn check_nested_if_chains( + cx: &LateContext<'_>, + if_expr: &Expr<'_>, + then_block: &Block<'_>, + if_chain_span: Option, +) { + #[rustfmt::skip] + let (head, tail) = match *then_block { + Block { stmts, expr: Some(tail), .. } => (stmts, tail), + Block { + stmts: &[ + ref head @ .., + Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } + ], + .. + } => (head, tail), + _ => return, + }; + if_chain! { + if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); + let sm = cx.sess().source_map(); + if head + .iter() + .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); + if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); + then { + } else { + return; + } + } + let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { + (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), + (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), + (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), + _ => return, + }; + span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { + let (span, msg) = match head { + [] => return, + [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), + [a, .., b] => ( + a.span.to(b.span), + "these `let` statements can also be in the `if_chain!`", + ), + }; + diag.span_help(span, msg); + }); +} + +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { + cx.tcx + .hir() + .parent_iter(hir_id) + .find(|(_, node)| { + #[rustfmt::skip] + !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) + }) + .map_or(false, |(id, _)| { + is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) + }) +} + +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part +/// of the `then {..}` portion of an `if_chain!` +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { + let span = if let [stmt, ..] = stmts { + stmt.span + } else if let Some(expr) = expr { + expr.span + } else { + // empty `then {}` + return true; + }; + is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) +} + +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { + let mut span = local.pat.span; + if let Some(init) = local.init { + span = span.to(init.span); + } + span.adjust(if_chain_span.ctxt().outer_expn()); + let sm = cx.sess().source_map(); + let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new( + span.lo() - BytePos(3), + span.hi() + BytePos(1), + span.ctxt(), + span.parent(), + ) +} diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs new file mode 100644 index 000000000000..4b33d492a0e4 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -0,0 +1,239 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty::{self}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; + +use std::borrow::Cow; + +declare_clippy_lint! { + /// ### What it does + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// ### Why is this bad? + /// It's faster and easier to use the symbol constant. + /// + /// ### Example + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for unnecessary conversion from Symbol to a string. + /// + /// ### Why is this bad? + /// It's faster use symbols directly instead of strings. + /// + /// ### Example + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + for def_id in def_path_def_ids(cx, module) { + for item in cx.tcx.module_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(&def_id) = self.symbol_map.get(&value); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + cx.tcx.def_path_str(def_id), + Applicability::MachineApplicable, + ); + } + } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::Deref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, item, [], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs new file mode 100644 index 000000000000..9afe02c1e47d --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -0,0 +1,108 @@ +use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::def_path_res; +use clippy_utils::diagnostics::span_lint; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::Item; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, fast_reject::SimplifiedType, FloatTy}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks the paths module for invalid paths. + /// + /// ### Why is this bad? + /// It indicates a bug in the code. + /// + /// ### Example + /// None. + pub INVALID_PATHS, + internal, + "invalid path" +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id()); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value); + let path: Vec<&str> = path + .iter() + .map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }) + .collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, INVALID_PATHS, item.span, "invalid path"); + } + } + } +} + +// This is not a complete resolver for paths. It works on all the paths currently used in the paths +// module. That's all it does and all it needs to do. +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { + if !def_path_res(cx, path).is_empty() { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + // This list isn't complete, but good enough for our current list of paths. + let incoherent_impls = [ + SimplifiedType::FloatSimplifiedType(FloatTy::F32), + SimplifiedType::FloatSimplifiedType(FloatTy::F64), + SimplifiedType::SliceSimplifiedType, + SimplifiedType::StrSimplifiedType, + ] + .iter() + .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied()); + for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) { + let lang_item_path = cx.get_def_path(item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + if matches!( + cx.tcx.def_kind(item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } + } + } + } + } + } + + false +} diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs new file mode 100644 index 000000000000..4c3b1b131fd4 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -0,0 +1,342 @@ +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::{is_lint_allowed, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast as ast; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::hir_id::CRATE_HIR_ID; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Ensures every lint is associated to a `LintPass`. + /// + /// ### Why is this bad? + /// The compiler only knows lints via a `LintPass`. Without + /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not + /// know the name of the lint. + /// + /// ### Known problems + /// Only checks for lints associated using the + /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub LINT_1, ... } + /// declare_lint! { pub LINT_2, ... } + /// declare_lint! { pub FORGOTTEN_LINT, ... } + /// // ... + /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); + /// // missing FORGOTTEN_LINT + /// ``` + pub LINT_WITHOUT_LINT_PASS, + internal, + "declaring a lint without associating it in a LintPass" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated lint without an updated description, + /// i.e. `default lint description`. + /// + /// ### Why is this bad? + /// Indicates that the lint is not finished. + /// + /// ### Example + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } + /// ``` + pub DEFAULT_LINT, + internal, + "found 'default lint description' in a lint declaration" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for invalid `clippy::version` attributes. + /// + /// Valid values are: + /// * "pre 1.29.0" + /// * any valid semantic version + pub INVALID_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found an invalid `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for declared clippy lints without the `clippy::version` attribute. + /// + pub MISSING_CLIPPY_VERSION_ATTRIBUTE, + internal, + "found clippy lint without `clippy::version` attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for cases of an auto-generated deprecated lint without an updated reason, + /// i.e. `"default deprecation note"`. + /// + /// ### Why is this bad? + /// Indicates that the documentation is incomplete. + /// + /// ### Example + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// TODO + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "default deprecation note" + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// declare_deprecated_lint! { + /// /// ### What it does + /// /// Nothing. This lint has been deprecated. + /// /// + /// /// ### Deprecation reason + /// /// This lint has been replaced by `cooler_lint` + /// #[clippy::version = "1.63.0"] + /// pub COOL_LINT, + /// "this lint has been replaced by `cooler_lint`" + /// } + /// ``` + pub DEFAULT_DEPRECATION_REASON, + internal, + "found 'default deprecation note' in a deprecated lint declaration" +} + +#[derive(Clone, Debug, Default)] +pub struct LintWithoutLintPass { + declared_lints: FxHashMap, + registered_lints: FxHashSet, +} + +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]); + +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) + || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id()) + { + return; + } + + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { + let is_lint_ref_ty = is_lint_ref_type(cx, ty); + if is_deprecated_lint(cx, ty) || is_lint_ref_ty { + check_invalid_clippy_version_attribute(cx, item); + + let expr = &cx.tcx.hir().body(body_id).value; + let fields; + if is_lint_ref_ty { + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind + && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind { + fields = struct_fields; + } else { + return; + } + } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind { + fields = struct_fields; + } else { + return; + } + + let field = fields + .iter() + .find(|f| f.ident.as_str() == "desc") + .expect("lints must have a description field"); + + if let ExprKind::Lit(Spanned { + node: LitKind::Str(ref sym, _), + .. + }) = field.expr.kind + { + let sym_str = sym.as_str(); + if is_lint_ref_ty { + if sym_str == "default lint description" { + span_lint( + cx, + DEFAULT_LINT, + item.span, + &format!("the lint `{}` has the default lint description", item.ident.name), + ); + } + + self.declared_lints.insert(item.ident.name, item.span); + } else if sym_str == "default deprecation note" { + span_lint( + cx, + DEFAULT_DEPRECATION_REASON, + item.span, + &format!("the lint `{}` has the default deprecation reason", item.ident.name), + ); + } + } + } + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } + if let hir::ItemKind::Impl(hir::Impl { + of_trait: None, + items: impl_item_refs, + .. + }) = item.kind + { + let mut collector = LintCollector { + output: &mut self.registered_lints, + cx, + }; + let body_id = cx.tcx.hir().body_owned_by( + cx.tcx.hir().local_def_id( + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .hir_id(), + ), + ); + collector.visit_expr(cx.tcx.hir().body(body_id).value); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) { + return; + } + + for (lint_name, &lint_span) in &self.declared_lints { + // When using the `declare_tool_lint!` macro, the original `lint_span`'s + // file points to "". + // `compiletest-rs` thinks that's an error in a different file and + // just ignores it. This causes the test in compile-fail/lint_pass + // not able to capture the error. + // Therefore, we need to climb the macro expansion tree and find the + // actual span that invoked `declare_tool_lint!`: + let lint_span = lint_span.ctxt().outer_expn_data().call_site; + + if !self.registered_lints.contains(lint_name) { + span_lint( + cx, + LINT_WITHOUT_LINT_PASS, + lint_span, + &format!("the lint `{lint_name}` is not added to any `LintPass`"), + ); + } + } + } +} + +pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { + if let TyKind::Ref( + _, + MutTy { + ty: inner, + mutbl: Mutability::Not, + }, + ) = ty.kind + { + if let TyKind::Path(ref path) = inner.kind { + if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { + return match_def_path(cx, def_id, &paths::LINT); + } + } + } + + false +} + +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) { + if let Some(value) = extract_clippy_version_value(cx, item) { + // The `sym!` macro doesn't work as it only expects a single token. + // It's better to keep it this way and have a direct `Symbol::intern` call here. + if value == Symbol::intern("pre 1.29.0") { + return; + } + + if RustcVersion::parse(value.as_str()).is_err() { + span_lint_and_help( + cx, + INVALID_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this item has an invalid `clippy::version` attribute", + None, + "please use a valid semantic version, see `doc/adding_lints.md`", + ); + } + } else { + span_lint_and_help( + cx, + MISSING_CLIPPY_VERSION_ATTRIBUTE, + item.span, + "this lint is missing the `clippy::version` attribute or version value", + None, + "please use a `clippy::version` attribute, see `doc/adding_lints.md`", + ); + } +} + +/// This function extracts the version value of a `clippy::version` attribute if the given value has +/// one +pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + attrs.iter().find_map(|attr| { + if_chain! { + // Identify attribute + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; + if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; + if tool_name.ident.name == sym::clippy; + if attr_name.ident.name == sym::version; + if let Some(version) = attr.value_str(); + then { Some(version) } else { None } + } + }) +} + +struct LintCollector<'a, 'tcx> { + output: &'a mut FxHashSet, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { + type NestedFilter = nested_filter::All; + + fn visit_path(&mut self, path: &Path<'_>, _: HirId) { + if path.segments.len() == 1 { + self.output.insert(path.segments[0].ident.name); + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c84191bb0103..b1b5164ffb3e 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -8,12 +8,13 @@ //! a simple mistake) use crate::renamed_lints::RENAMED_LINTS; -use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; +use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{match_type, walk_ptrs_ty_depth}; use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths}; use if_chain::if_chain; +use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ @@ -34,8 +35,10 @@ use std::path::Path; use std::path::PathBuf; use std::process::Command; -/// This is the output file of the lint collector. -const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; +/// This is the json output file of the lint collector. +const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; +/// This is the markdown output file of the lint collector. +const MARKDOWN_OUTPUT_FILE: &str = "../book/src/lint_configuration.md"; /// These lints are excluded from the export. const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like @@ -176,6 +179,23 @@ This lint has the following configuration variables: ) }) } + + fn configs_to_markdown(&self, map_fn: fn(&ClippyConfiguration) -> String) -> String { + self.config + .iter() + .filter(|config| config.deprecation_reason.is_none()) + .filter(|config| !config.lints.is_empty()) + .map(map_fn) + .join("\n") + } + + fn get_markdown_docs(&self) -> String { + format!( + "## Lint Configuration Options\n|
Option
| Default Value |\n|--|--|\n{}\n\n{}\n", + self.configs_to_markdown(ClippyConfiguration::to_markdown_table_entry), + self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph), + ) + } } impl Drop for MetadataCollector { @@ -199,12 +219,37 @@ impl Drop for MetadataCollector { collect_renames(&mut lints); - // Outputting - if Path::new(OUTPUT_FILE).exists() { - fs::remove_file(OUTPUT_FILE).unwrap(); + // Outputting json + if Path::new(JSON_OUTPUT_FILE).exists() { + fs::remove_file(JSON_OUTPUT_FILE).unwrap(); } - let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); + let mut file = OpenOptions::new() + .write(true) + .create(true) + .open(JSON_OUTPUT_FILE) + .unwrap(); writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); + + // Outputting markdown + if Path::new(MARKDOWN_OUTPUT_FILE).exists() { + fs::remove_file(MARKDOWN_OUTPUT_FILE).unwrap(); + } + let mut file = OpenOptions::new() + .write(true) + .create(true) + .open(MARKDOWN_OUTPUT_FILE) + .unwrap(); + writeln!( + file, + " + +{}", + self.get_markdown_docs(), + ) + .unwrap(); } } @@ -505,6 +550,28 @@ impl ClippyConfiguration { deprecation_reason, } } + + fn to_markdown_paragraph(&self) -> String { + format!( + "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n", + self.name, + self.doc + .lines() + .map(|line| line.strip_prefix(" ").unwrap_or(line)) + .join("\n"), + self.default, + self.config_type, + self.lints + .iter() + .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) + .map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) + .join("\n"), + ) + } + + fn to_markdown_table_entry(&self) -> String { + format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default) + } } fn collect_configs() -> Vec { @@ -532,7 +599,11 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { // Extract lints doc_comment.make_ascii_lowercase(); - let lints: Vec = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect(); + let lints: Vec = doc_comment + .split_off(DOC_START.len()) + .split(", ") + .map(str::to_string) + .collect(); // Format documentation correctly // split off leading `.` from lint name list and indent for correct formatting @@ -554,8 +625,8 @@ impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { writeln!( f, - "* `{}`: `{}`: {} (defaults to `{}`)", - self.name, self.config_type, self.doc, self.default + "* `{}`: `{}`(defaults to `{}`): {}", + self.name, self.config_type, self.default, self.doc ) } } @@ -1015,7 +1086,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { self.cx.tcx.hir() } - fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'hir>, _id: hir::HirId) { for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() { if match_path(path, enum_value) { self.add_new_index(index); @@ -1054,7 +1125,7 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); - match map.find(map.get_parent_node(hir_id)) { + match map.find_parent(hir_id) { Some(hir::Node::Local(local)) => Some(local), Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs new file mode 100644 index 000000000000..9876a8a765cc --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::match_type; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir_analysis::hir_ty_to_ty; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{self, subst::GenericArgKind}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV. + /// + pub MISSING_MSRV_ATTR_IMPL, + internal, + "checking if all necessary steps were taken when adding a MSRV to a lint" +} + +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]); + +impl LateLintPass<'_> for MsrvAttrImpl { + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if_chain! { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: Some(lint_pass_trait_ref), + self_ty, + items, + .. + }) = &item.kind; + if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id(); + let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS); + if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS); + let self_ty = hir_ty_to_ty(cx.tcx, self_ty); + if let ty::Adt(self_ty_def, _) = self_ty.kind(); + if self_ty_def.is_struct(); + if self_ty_def.all_fields().any(|f| { + cx.tcx + .type_of(f.did) + .walk() + .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) + .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) + }); + if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); + then { + let context = if is_late_pass { "LateContext" } else { "EarlyContext" }; + let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" }; + let span = cx.sess().source_map().span_through_char(item.span, '{'); + span_lint_and_sugg( + cx, + MISSING_MSRV_ATTR_IMPL, + span, + &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"), + &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"), + format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs new file mode 100644 index 000000000000..2b13fad80665 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/outer_expn_data_pass.rs @@ -0,0 +1,62 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::match_type; +use clippy_utils::{is_lint_allowed, method_calls, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `cx.outer().expn_data()` and suggests to use + /// the `cx.outer_expn_data()` + /// + /// ### Why is this bad? + /// `cx.outer_expn_data()` is faster and more concise. + /// + /// ### Example + /// ```rust,ignore + /// expr.span.ctxt().outer().expn_data() + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// expr.span.ctxt().outer_expn_data() + /// ``` + pub OUTER_EXPN_EXPN_DATA, + internal, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" +} + +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); + +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { + return; + } + + let (method_names, arg_lists, spans) = method_calls(expr, 2); + let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect(); + if_chain! { + if let ["expn_data", "outer_expn"] = method_names.as_slice(); + let (self_arg, args) = arg_lists[1]; + if args.is_empty(); + let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); + then { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/internal_lints/produce_ice.rs b/clippy_lints/src/utils/internal_lints/produce_ice.rs new file mode 100644 index 000000000000..5899b94e16ba --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/produce_ice.rs @@ -0,0 +1,37 @@ +use rustc_ast::ast::NodeId; +use rustc_ast::visit::FnKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Not an actual lint. This lint is only meant for testing our customized internal compiler + /// error message by calling `panic`. + /// + /// ### Why is this bad? + /// ICE in large quantities can damage your teeth + /// + /// ### Example + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?"); + } +} + +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", + FnKind::Closure(..) => false, + } +} diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs new file mode 100644 index 000000000000..7144363637a0 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -0,0 +1,303 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; +use rustc_middle::ty::{self, DefIdTree, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::Symbol; +use rustc_span::Span; + +use std::str; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used. + /// + /// ### Why is this bad? + /// The path for an item is subject to change and is less efficient to look up than a + /// diagnostic item or a `LangItem`. + /// + /// ### Example + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) + /// ``` + pub UNNECESSARY_DEF_PATH, + internal, + "using a def path when a diagnostic item or a `LangItem` is available" +} + +impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); + +#[derive(Default)] +pub struct UnnecessaryDefPath { + array_def_ids: FxHashSet<(DefId, Span)>, + linted_def_ids: FxHashSet, +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) { + return; + } + + match expr.kind { + ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span), + ExprKind::Array(elements) => self.check_array(cx, elements, expr.span), + _ => {}, + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(def_id, span) in &self.array_def_ids { + if self.linted_def_ids.contains(&def_id) { + continue; + } + + let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) { + ("diagnostic item", format!("sym::{sym}")) + } else if let Some(sym) = get_lang_item_name(cx, def_id) { + ("language item", format!("LangItem::{sym}")) + } else { + continue; + }; + + span_lint_and_help( + cx, + UNNECESSARY_DEF_PATH, + span, + &format!("hardcoded path to a {msg}"), + None, + &format!("convert all references to use `{sugg}`"), + ); + } + } +} + +impl UnnecessaryDefPath { + #[allow(clippy::too_many_lines)] + fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) { + enum Item { + LangItem(&'static str), + DiagnosticItem(Symbol), + } + static PATHS: &[&[&str]] = &[ + &["clippy_utils", "match_def_path"], + &["clippy_utils", "match_trait_method"], + &["clippy_utils", "ty", "match_type"], + &["clippy_utils", "is_expr_path_def_path"], + ]; + + if_chain! { + if let [cx_arg, def_arg, args @ ..] = args; + if let ExprKind::Path(path) = &func.kind; + if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if let Some(which_path) = match_any_def_paths(cx, id, PATHS); + let item_arg = if which_path == 4 { &args[1] } else { &args[0] }; + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, item_arg); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next(); + then { + // Check if the target item is a diagnostic item or LangItem. + #[rustfmt::skip] + let (msg, item) = if let Some(item_name) + = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id) + { + ( + "use of a def path to a diagnostic item", + Item::DiagnosticItem(*item_name), + ) + } else if let Some(item_name) = get_lang_item_name(cx, def_id) { + ( + "use of a def path to a `LangItem`", + Item::LangItem(item_name), + ) + } else { + return; + }; + + let has_ctor = match cx.tcx.def_kind(def_id) { + DefKind::Struct => { + let variant = cx.tcx.adt_def(def_id).non_enum_variant(); + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + DefKind::Variant => { + let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id); + variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public()) + }, + _ => false, + }; + + let mut app = Applicability::MachineApplicable; + let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app); + let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app); + let (sugg, with_note) = match (which_path, item) { + // match_def_path + (0, Item::DiagnosticItem(item)) => ( + format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), + has_ctor, + ), + (0, Item::LangItem(item)) => ( + format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"), + has_ctor, + ), + // match_trait_method + (1, Item::DiagnosticItem(item)) => { + (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false) + }, + // match_type + (2, Item::DiagnosticItem(item)) => ( + format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (2, Item::LangItem(item)) => ( + format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), + false, + ), + // is_expr_path_def_path + (3, Item::DiagnosticItem(item)) if has_ctor => ( + format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",), + false, + ), + (3, Item::LangItem(item)) if has_ctor => ( + format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",), + false, + ), + (3, Item::DiagnosticItem(item)) => ( + format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), + false, + ), + (3, Item::LangItem(item)) => ( + format!( + "path_res({cx_snip}, {def_snip}).opt_def_id()\ + .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))", + ), + false, + ), + _ => return, + }; + + span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| { + diag.span_suggestion(span, "try", sugg, app); + if with_note { + diag.help( + "if this `DefId` came from a constructor expression or pattern then the \ + parent `DefId` should be used instead", + ); + } + }); + + self.linted_def_ids.insert(def_id); + } + } + } + + fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { + let Some(path) = path_from_array(elements) else { return }; + + for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::>()) { + self.array_def_ids.insert((def_id, span)); + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + match peel_hir_expr_refs(expr).0.kind { + ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().parent_id(hir_id); + if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) { + path_to_matched_type(cx, init) + } else { + None + } + }, + Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + cx, + cx.tcx.eval_static_initializer(def_id).ok()?.inner(), + cx.tcx.type_of(def_id), + ), + Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? { + ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => { + read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id)) + }, + _ => None, + }, + _ => None, + }, + ExprKind::Array(exprs) => path_from_array(exprs), + _ => None, + } +} + +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option> { + let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { + let &alloc = alloc.provenance().ptrs().values().next()?; + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + (alloc.inner(), ty) + } else { + return None; + } + } else { + (alloc, ty) + }; + + if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind() + && let ty::Ref(_, ty, Mutability::Not) = *ty.kind() + && ty.is_str() + { + alloc + .provenance() + .ptrs() + .values() + .map(|&alloc| { + if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { + let alloc = alloc.inner(); + str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())) + .ok().map(ToOwned::to_owned) + } else { + None + } + }) + .collect() + } else { + None + } +} + +fn path_from_array(exprs: &[Expr<'_>]) -> Option> { + exprs + .iter() + .map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some((*sym.as_str()).to_owned()); + } + } + + None + }) + .collect() +} + +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { + Some(lang_item.variant_name()) + } else { + None + } +} diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 301eed9a1fbf..e4d1ee195c4d 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -120,14 +120,14 @@ impl LateLintPass<'_> for WildcardImports { if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_add(1); } - let module = cx.tcx.parent_module_from_def_id(item.def_id.def_id); - if cx.tcx.visibility(item.def_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { + let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id); + if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) { return; } if_chain! { if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if self.warn_on_all || !self.check_exceptions(item, use_path.segments); - let used_imports = cx.tcx.names_imported_by_glob_use(item.def_id.def_id); + let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id); if !used_imports.is_empty(); // Already handled by `unused_imports` then { let mut applicability = Applicability::MachineApplicable; @@ -176,7 +176,8 @@ impl LateLintPass<'_> for WildcardImports { format!("{import_source_snippet}::{imports_string}") }; - let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res { + // Glob imports always have a single resolution. + let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { (ENUM_GLOB_USE, "usage of wildcard import for enum variants") } else { (WILDCARD_IMPORTS, "usage of wildcard import") diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 36574198f917..df3350388817 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; @@ -232,6 +233,16 @@ declare_clippy_lint! { #[derive(Default)] pub struct Write { in_debug_impl: bool, + allow_print_in_tests: bool, +} + +impl Write { + pub fn new(allow_print_in_tests: bool) -> Self { + Self { + allow_print_in_tests, + ..Default::default() + } + } } impl_lint_pass!(Write => [ @@ -271,13 +282,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { .as_ref() .map_or(false, |crate_name| crate_name == "build_script_build"); + let allowed_in_tests = self.allow_print_in_tests + && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)); match diag_name { - sym::print_macro | sym::println_macro => { + sym::print_macro | sym::println_macro if !allowed_in_tests => { if !is_build_script { span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`")); } }, - sym::eprint_macro | sym::eprintln_macro => { + sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => { span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`")); }, sym::write_macro | sym::writeln_macro => {}, @@ -364,7 +377,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c // print!("\n"), write!(f, "\n") diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (format_string_span, String::new())], Applicability::MachineApplicable, ); @@ -375,7 +388,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); diag.multipart_suggestion( - &format!("use `{name}ln!` instead"), + format!("use `{name}ln!` instead"), vec![(name_span, format!("{name}ln")), (newline_span, String::new())], Applicability::MachineApplicable, ); diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 83fee7bb39c2..173469f6cdc7 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.66" +version = "0.1.69" edition = "2021" publish = false diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 493991f30e87..9d0263e93be7 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -75,11 +75,11 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool { && over(&l.attrs, &r.attrs, eq_attr) } -pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { +pub fn eq_qself(l: &P, r: &P) -> bool { l.position == r.position && eq_ty(&l.ty, &r.ty) } -pub fn eq_maybe_qself(l: &Option, r: &Option) -> bool { +pub fn eq_maybe_qself(l: &Option>, r: &Option>) -> bool { match (l, r) { (Some(l), Some(r)) => eq_qself(l, r), (None, None) => true, @@ -147,12 +147,23 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), - (MethodCall(lc, ls, la, _), MethodCall(rc, rs, ra, _)) => { - eq_path_seg(lc, rc) && eq_expr(ls, rs) && over(la, ra, |l, r| eq_expr(l, r)) - }, + ( + MethodCall(box ast::MethodCall { + seg: ls, + receiver: lr, + args: la, + .. + }), + MethodCall(box ast::MethodCall { + seg: rs, + receiver: rr, + args: ra, + .. + }), + ) => eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)), (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), - (Lit(l), Lit(r)) => l.kind == r.kind, + (Lit(l), Lit(r)) => l == r, (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re), (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), @@ -160,7 +171,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) }, - (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt), + (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll, rl) && eq_block(lt, rt), (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), (TryBlock(l), TryBlock(r)) => eq_block(l, r), (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), @@ -170,7 +181,26 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), - (Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => { + ( + Closure(box ast::Closure { + binder: lb, + capture_clause: lc, + asyncness: la, + movability: lm, + fn_decl: lf, + body: le, + .. + }), + Closure(box ast::Closure { + binder: rb, + capture_clause: rc, + asyncness: ra, + movability: rm, + fn_decl: rf, + body: re, + .. + }), + ) => { eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() @@ -366,7 +396,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind)) }, (MacCall(l), MacCall(r)) => eq_mac_call(l, r), - (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body), + (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_delim_args(&l.body, &r.body), _ => false, } } @@ -438,14 +468,14 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) }, ( - TyAlias(box ast::TyAlias { + Type(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt, .. }), - TyAlias(box ast::TyAlias { + Type(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, @@ -536,7 +566,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { use UseTreeKind::*; match (l, r) { (Glob, Glob) => true, - (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)), + (Simple(l), Simple(r)) => both(l, r, |l, r| eq_id(*l, *r)), (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)), _ => false, } @@ -595,7 +625,7 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool { (Slice(l), Slice(r)) => eq_ty(l, r), (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value), (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty), - (Rptr(ll, l), Rptr(rl, r)) => { + (Ref(ll, l), Ref(rl, r)) => { both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty) }, (BareFn(l), BareFn(r)) => { @@ -687,7 +717,7 @@ pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool { } pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool { - eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args) + eq_path(&l.path, &r.path) && eq_delim_args(&l.args, &r.args) } pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { @@ -695,18 +725,22 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { l.style == r.style && match (&l.kind, &r.kind) { (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2, - (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_mac_args(&l.item.args, &r.item.args), + (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args), _ => false, } } -pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { - use MacArgs::*; +pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool { + use AttrArgs::*; match (l, r) { (Empty, Empty) => true, - (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts), - (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re), - (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind, + (Delimited(la), Delimited(ra)) => eq_delim_args(la, ra), + (Eq(_, AttrArgsEq::Ast(le)), Eq(_, AttrArgsEq::Ast(re))) => eq_expr(le, re), + (Eq(_, AttrArgsEq::Hir(ll)), Eq(_, AttrArgsEq::Hir(rl))) => ll.kind == rl.kind, _ => false, } } + +pub fn eq_delim_args(l: &DelimArgs, r: &DelimArgs) -> bool { + l.delim == r.delim && l.tokens.eq_unspanned(&r.tokens) +} diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index cd8575c90e86..7987a233bdc1 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -125,19 +125,19 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' } } -pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option { - let mut unique_attr = None; +pub fn get_unique_attr<'a>( + sess: &'a Session, + attrs: &'a [ast::Attribute], + name: &'static str, +) -> Option<&'a ast::Attribute> { + let mut unique_attr: Option<&ast::Attribute> = None; for attr in get_attr(sess, attrs, name) { - match attr.style { - ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), - ast::AttrStyle::Inner => { - sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) - .span_note(unique_attr.as_ref().unwrap().span, "first definition found here") - .emit(); - }, - ast::AttrStyle::Outer => { - sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute")); - }, + if let Some(duplicate) = unique_attr { + sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times")) + .span_note(duplicate.span, "first definition found here") + .emit(); + } else { + unique_attr = Some(attr); } } unique_attr diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 7a8d4e8068ed..43f0df145f0e 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -69,7 +69,9 @@ fn lit_search_pat(lit: &LitKind) -> (Pat, Pat) { LitKind::Str(_, StrStyle::Cooked) => (Pat::Str("\""), Pat::Str("\"")), LitKind::Str(_, StrStyle::Raw(0)) => (Pat::Str("r"), Pat::Str("\"")), LitKind::Str(_, StrStyle::Raw(_)) => (Pat::Str("r#"), Pat::Str("#")), - LitKind::ByteStr(_) => (Pat::Str("b\""), Pat::Str("\"")), + LitKind::ByteStr(_, StrStyle::Cooked) => (Pat::Str("b\""), Pat::Str("\"")), + LitKind::ByteStr(_, StrStyle::Raw(0)) => (Pat::Str("br\""), Pat::Str("\"")), + LitKind::ByteStr(_, StrStyle::Raw(_)) => (Pat::Str("br#\""), Pat::Str("#")), LitKind::Byte(_) => (Pat::Str("b'"), Pat::Str("'")), LitKind::Char(_) => (Pat::Str("'"), Pat::Str("'")), LitKind::Int(_, LitIntType::Signed(IntTy::Isize)) => (Pat::Num, Pat::Str("isize")), @@ -220,7 +222,7 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) { fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { let (start_pat, end_pat) = match &item.kind { ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), - ImplItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")), + ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")), ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), }; if item.vis_span.is_empty() { diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 07e4ef6a2fef..a67bd8d46006 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -51,8 +51,8 @@ pub enum Constant { impl PartialEq for Constant { fn eq(&self, other: &Self) -> bool { match (self, other) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs, - (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r, + (Self::Str(ls), Self::Str(rs)) => ls == rs, + (Self::Binary(l), Self::Binary(r)) => l == r, (&Self::Char(l), &Self::Char(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r, (&Self::F64(l), &Self::F64(r)) => { @@ -69,8 +69,8 @@ impl PartialEq for Constant { }, (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => ls == rs && lv == rv, + (Self::Ref(lb), Self::Ref(rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -126,8 +126,8 @@ impl Hash for Constant { impl Constant { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option { match (left, right) { - (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), - (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), + (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)), + (Self::Char(l), Self::Char(r)) => Some(l.cmp(r)), (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() { ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))), ty::Uint(_) => Some(l.cmp(&r)), @@ -135,8 +135,8 @@ impl Constant { }, (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), - (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)), - (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() { + (Self::Bool(l), Self::Bool(r)) => Some(l.cmp(r)), + (Self::Tuple(l), Self::Tuple(r)) if l.len() == r.len() => match *cmp_type.kind() { ty::Tuple(tys) if tys.len() == l.len() => l .iter() .zip(r) @@ -146,17 +146,16 @@ impl Constant { .unwrap_or_else(|| Some(l.len().cmp(&r.len()))), _ => None, }, - (&Self::Vec(ref l), &Self::Vec(ref r)) => { - let cmp_type = match *cmp_type.kind() { - ty::Array(ty, _) | ty::Slice(ty) => ty, - _ => return None, + (Self::Vec(l), Self::Vec(r)) => { + let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else { + return None }; iter::zip(l, r) .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) .find(|r| r.map_or(true, |o| o != Ordering::Equal)) .unwrap_or_else(|| Some(l.len().cmp(&r.len()))) }, - (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => { + (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => { match Self::partial_cmp( tcx, match *cmp_type.kind() { @@ -170,7 +169,7 @@ impl Constant { x => x, } }, - (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp( + (Self::Ref(lb), Self::Ref(rb)) => Self::partial_cmp( tcx, match *cmp_type.kind() { ty::Ref(_, ty, _) => ty, @@ -211,7 +210,7 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)), + LitKind::ByteStr(ref s, _) => Constant::Binary(Lrc::clone(s)), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { @@ -401,10 +400,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { use self::Constant::{Int, F32, F64}; match *o { Int(value) => { - let ity = match *ty.kind() { - ty::Int(ity) => ity, - _ => return None, - }; + let ty::Int(ity) = *ty.kind() else { return None }; // sign extend let value = sext(self.lcx.tcx, value, ity); let value = value.checked_neg()?; @@ -624,12 +620,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( int.try_into().expect("invalid f64 bit representation"), ))), - ty::RawPtr(type_and_mut) => { - if let ty::Uint(_) = type_and_mut.ty.kind() { - return Some(Constant::RawPtr(int.assert_bits(int.size()))); - } - None - }, + ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), // FIXME: implement other conversions. _ => None, } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 78f93755b72d..812f6fe71a0a 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -17,7 +17,7 @@ use std::env; fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) { if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() { if let Some(lint) = lint.name_lower().strip_prefix("clippy::") { - diag.help(&format!( + diag.help(format!( "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions @@ -72,8 +72,8 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( - cx: &'a T, +pub fn span_lint_and_help( + cx: &T, lint: &'static Lint, span: impl Into, msg: &str, @@ -114,8 +114,8 @@ pub fn span_lint_and_help<'a, T: LintContext>( /// 10 | forget(&SomeStruct); /// | ^^^^^^^^^^^ /// ``` -pub fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, +pub fn span_lint_and_note( + cx: &T, lint: &'static Lint, span: impl Into, msg: &str, @@ -192,8 +192,8 @@ pub fn span_lint_hir_and_then( /// = note: `-D fold-any` implied by `-D warnings` /// ``` #[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))] -pub fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, +pub fn span_lint_and_sugg( + cx: &T, lint: &'static Lint, sp: Span, msg: &str, diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index 95b3e651e2b5..96711936968b 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -73,7 +73,7 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: .flat_map(|v| v.fields.iter()) .any(|x| matches!(cx.tcx.type_of(x.did).peel_refs().kind(), ty::Param(_))) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { - PredicateKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, + PredicateKind::Clause(ty::Clause::Trait(pred)) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, _ => true, }) && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) @@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } } +fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res { + cx.typeck_results() + .expr_ty(e) + .has_significant_drop(cx.tcx, cx.param_env) + } else { + false + } +} + #[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { @@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, args, ) => match self.cx.qpath_res(path, hir_id) { - Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => { - if self - .cx - .typeck_results() - .expr_ty(e) - .has_significant_drop(self.cx.tcx, self.cx.param_env) - { + res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => { + if res_has_significant_drop(res, self.cx, e) { self.eagerness = ForceNoChange; return; } @@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS self.eagerness |= NoChange; return; }, + ExprKind::Path(ref path) => { + if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + self.eagerness = ForceNoChange; + return; + } + }, ExprKind::MethodCall(name, ..) => { self.eagerness |= self .cx @@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS | ExprKind::Match(..) | ExprKind::Closure { .. } | ExprKind::Field(..) - | ExprKind::Path(_) | ExprKind::AddrOf(..) | ExprKind::Struct(..) | ExprKind::Repeat(..) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index cf24ec8b67b9..2bbe1a19b625 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -7,8 +7,8 @@ use rustc_hir::def::Res; use rustc_hir::HirIdMap; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, - GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, - PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, + GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, + PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; @@ -113,7 +113,7 @@ impl HirEqInterExpr<'_, '_, '_> { } } - // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that + // eq_pat adds the HirIds to the locals map. We therefore call it last to make sure that // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) @@ -131,13 +131,10 @@ impl HirEqInterExpr<'_, '_, '_> { ([], None, [], None) => { // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro // expanded to nothing, or the cfg attribute was used. - let (left, right) = match ( + let (Some(left), Some(right)) = ( snippet_opt(self.inner.cx, left.span), snippet_opt(self.inner.cx, right.span), - ) { - (Some(left), Some(right)) => (left, right), - _ => return true, - }; + ) else { return true }; let mut left_pos = 0; let left = tokenize(&left) .map(|t| { @@ -269,7 +266,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Let(l), &ExprKind::Let(r)) => { self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) }, - (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, + (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node, (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) }, @@ -294,8 +291,8 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => { self.eq_expr(le, re) && self.eq_array_length(ll, rl) }, - (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)), - (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r), + (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)), + (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r), (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => { self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) @@ -340,7 +337,7 @@ impl HirEqInterExpr<'_, '_, '_> { } fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool { - left.name == right.name + left.res == right.res } fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool { @@ -365,7 +362,7 @@ impl HirEqInterExpr<'_, '_, '_> { } eq }, - (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), + (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { @@ -432,13 +429,11 @@ impl HirEqInterExpr<'_, '_, '_> { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), - (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { - l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty) - }, - (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { + (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty), + (TyKind::Ref(_, l_rmut), TyKind::Ref(_, r_rmut)) => { l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, - (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), + (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), (&TyKind::Infer, &TyKind::Infer) => true, _ => false, @@ -930,16 +925,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } pub fn hash_lifetime(&mut self, lifetime: &Lifetime) { - std::mem::discriminant(&lifetime.name).hash(&mut self.s); - if let LifetimeName::Param(param_id, ref name) = lifetime.name { - std::mem::discriminant(name).hash(&mut self.s); + lifetime.ident.name.hash(&mut self.s); + std::mem::discriminant(&lifetime.res).hash(&mut self.s); + if let LifetimeName::Param(param_id) = lifetime.res { param_id.hash(&mut self.s); - match name { - ParamName::Plain(ref ident) => { - ident.name.hash(&mut self.s); - }, - ParamName::Fresh | ParamName::Error => {}, - } } } @@ -961,7 +950,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); }, - TyKind::Rptr(lifetime, ref mut_ty) => { + TyKind::Ref(lifetime, ref mut_ty) => { self.hash_lifetime(lifetime); self.hash_ty(mut_ty.ty); mut_ty.mutbl.hash(&mut self.s); @@ -1033,6 +1022,14 @@ pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 { h.finish() } +pub fn is_bool(ty: &Ty<'_>) -> bool { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + matches!(path.res, Res::PrimTy(PrimTy::Bool)) + } else { + false + } +} + pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { let mut h = SpanlessHash::new(cx); h.hash_expr(e); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index dbe75b43cb24..e675b134e5a1 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -22,9 +22,12 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; extern crate rustc_data_structures; +// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. +#[allow(unused_extern_crates)] +extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; -extern crate rustc_hir_analysis; +extern crate rustc_hir_typeck; extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; @@ -66,7 +69,7 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, + both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash, }; use core::ops::ControlFlow; @@ -80,17 +83,16 @@ use rustc_ast::ast::{self, LitKind}; use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr, - ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, - Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, - TraitRef, TyKind, UnOp, + self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, + Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local, + MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -98,7 +100,7 @@ use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{ +use rustc_middle::ty::fast_reject::SimplifiedType::{ ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType, PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, }; @@ -106,50 +108,30 @@ use rustc_middle::ty::{ layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture, }; use rustc_middle::ty::{FloatTy, IntTy, UintTy}; -use rustc_semver::RustcVersion; -use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::source_map::original_sp; use rustc_span::source_map::SourceMap; use rustc_span::sym; -use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::Span; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param}; use crate::visitors::for_each_expr; -pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { - if let Ok(version) = RustcVersion::parse(msrv) { - return Some(version); - } else if let Some(sess) = sess { - if let Some(span) = span { - sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); - } - } - None -} - -pub fn meets_msrv(msrv: Option, lint_msrv: RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv.meets(lint_msrv)) -} +use rustc_middle::hir::nested_filter; #[macro_export] macro_rules! extract_msrv_attr { ($context:ident) => { fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { let sess = rustc_lint::LintContext::sess(cx); - match $crate::get_unique_inner_attr(sess, attrs, "msrv") { - Some(msrv_attr) => { - if let Some(msrv) = msrv_attr.value_str() { - self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); - } else { - sess.span_err(msrv_attr.span, "bad clippy attribute"); - } - }, - _ => (), - } + self.msrv.enter_lint_attrs(sess, attrs); + } + + fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { + let sess = rustc_lint::LintContext::sess(cx); + self.msrv.exit_lint_attrs(sess, attrs); } }; } @@ -197,7 +179,7 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option< if_chain! { if let Some(Node::Pat(pat)) = hir.find(hir_id); if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)); - let parent = hir.get_parent_node(hir_id); + let parent = hir.parent_id(hir_id); if let Some(Node::Local(local)) = hir.find(parent); then { return local.init; @@ -219,7 +201,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { let parent_id = cx.tcx.hir().get_parent_item(id).def_id; match cx.tcx.hir().get_by_def_id(parent_id) { Node::Item(&Item { - kind: ItemKind::Const(..) | ItemKind::Static(..), + kind: ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Enum(..), .. }) | Node::TraitItem(&TraitItem { @@ -247,7 +229,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool { /// For example, use this to check whether a function call or a pattern is `Some(..)`. pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { if let Res::Def(DefKind::Ctor(..), id) = res - && let Ok(lang_id) = cx.tcx.lang_items().require(lang_item) + && let Some(lang_id) = cx.tcx.lang_items().get(lang_item) && let Some(id) = cx.tcx.opt_parent(id) { id == lang_id @@ -303,7 +285,7 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> _ => did, }; - cx.tcx.lang_items().require(item).map_or(false, |id| id == did) + cx.tcx.lang_items().get(item) == Some(did) } pub fn is_unit_expr(expr: &Expr<'_>) -> bool { @@ -434,6 +416,12 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[ path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments)) } +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if +/// it matches the given lang item. +pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool { + path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id)) +} + /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if /// it matches the given diagnostic item. pub fn is_path_diagnostic_item<'tcx>( @@ -525,125 +513,177 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { - let single = |ty| tcx.incoherent_impls(ty).iter().copied(); - let empty = || [].iter().copied(); - match name { - "bool" => single(BoolSimplifiedType), - "char" => single(CharSimplifiedType), - "str" => single(StrSimplifiedType), - "array" => single(ArraySimplifiedType), - "slice" => single(SliceSimplifiedType), +fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator + 'tcx { + let ty = match name { + "bool" => BoolSimplifiedType, + "char" => CharSimplifiedType, + "str" => StrSimplifiedType, + "array" => ArraySimplifiedType, + "slice" => SliceSimplifiedType, // FIXME: rustdoc documents these two using just `pointer`. // // Maybe this is something we should do here too. - "const_ptr" => single(PtrSimplifiedType(Mutability::Not)), - "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)), - "isize" => single(IntSimplifiedType(IntTy::Isize)), - "i8" => single(IntSimplifiedType(IntTy::I8)), - "i16" => single(IntSimplifiedType(IntTy::I16)), - "i32" => single(IntSimplifiedType(IntTy::I32)), - "i64" => single(IntSimplifiedType(IntTy::I64)), - "i128" => single(IntSimplifiedType(IntTy::I128)), - "usize" => single(UintSimplifiedType(UintTy::Usize)), - "u8" => single(UintSimplifiedType(UintTy::U8)), - "u16" => single(UintSimplifiedType(UintTy::U16)), - "u32" => single(UintSimplifiedType(UintTy::U32)), - "u64" => single(UintSimplifiedType(UintTy::U64)), - "u128" => single(UintSimplifiedType(UintTy::U128)), - "f32" => single(FloatSimplifiedType(FloatTy::F32)), - "f64" => single(FloatSimplifiedType(FloatTy::F64)), - _ => empty(), - } -} - -/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate -/// between `std::vec` the module and `std::vec` the macro -/// -/// This function is expensive and should be used sparingly. -pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option) -> Res { - fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local())) - .map(|child| child.res.expect_non_local()), - DefKind::Impl => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)), - DefKind::Struct | DefKind::Union => tcx - .adt_def(def_id) - .non_enum_variant() - .fields - .iter() - .find(|f| f.name.as_str() == name) - .map(|f| Res::Def(DefKind::Field, f.did)), - _ => None, + "const_ptr" => PtrSimplifiedType(Mutability::Not), + "mut_ptr" => PtrSimplifiedType(Mutability::Mut), + "isize" => IntSimplifiedType(IntTy::Isize), + "i8" => IntSimplifiedType(IntTy::I8), + "i16" => IntSimplifiedType(IntTy::I16), + "i32" => IntSimplifiedType(IntTy::I32), + "i64" => IntSimplifiedType(IntTy::I64), + "i128" => IntSimplifiedType(IntTy::I128), + "usize" => UintSimplifiedType(UintTy::Usize), + "u8" => UintSimplifiedType(UintTy::U8), + "u16" => UintSimplifiedType(UintTy::U16), + "u32" => UintSimplifiedType(UintTy::U32), + "u64" => UintSimplifiedType(UintTy::U64), + "u128" => UintSimplifiedType(UintTy::U128), + "f32" => FloatSimplifiedType(FloatTy::F32), + "f64" => FloatSimplifiedType(FloatTy::F64), + _ => return [].iter().copied(), + }; + + tcx.incoherent_impls(ty).iter().copied() +} + +fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx + .module_children(def_id) + .iter() + .filter(|item| item.ident.name == name) + .map(|child| child.res.expect_non_local()) + .collect(), + DefKind::Impl => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) + .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) + .collect(), + _ => Vec::new(), + } +} + +fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec { + let hir = tcx.hir(); + + let root_mod; + let item_kind = match hir.find_by_def_id(local_id) { + Some(Node::Crate(r#mod)) => { + root_mod = ItemKind::Mod(r#mod); + &root_mod + }, + Some(Node::Item(item)) => &item.kind, + _ => return Vec::new(), + }; + + let res = |ident: Ident, owner_id: OwnerId| { + if ident.name == name { + let def_id = owner_id.to_def_id(); + Some(Res::Def(tcx.def_kind(def_id), def_id)) + } else { + None } + }; + + match item_kind { + ItemKind::Mod(r#mod) => r#mod + .item_ids + .iter() + .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id)) + .collect(), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) + .collect(), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) + .collect(), + _ => Vec::new(), } +} - fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option { +fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { + if let Some(local_id) = def_id.as_local() { + local_item_children_by_name(tcx, local_id, name) + } else { + non_local_item_children_by_name(tcx, def_id, name) + } +} + +/// Resolves a def path like `std::vec::Vec`. +/// +/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. +/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. +/// +/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec` +/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. +/// +/// This function is expensive and should be used sparingly. +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec { + fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ { tcx.crates(()) .iter() .copied() - .find(|&num| tcx.crate_name(num).as_str() == name) + .filter(move |&num| tcx.crate_name(num) == name) .map(CrateNum::as_def_id) } - let (base, path) = match *path { + let tcx = cx.tcx; + + let (base, mut path) = match *path { [primitive] => { - return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy); + return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; }, [base, ref path @ ..] => (base, path), - _ => return Res::Err, + _ => return Vec::new(), }; - let tcx = cx.tcx; - let starts = find_primitive(tcx, base) - .chain(find_crate(tcx, base)) + + let base_sym = Symbol::intern(base); + + let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym { + Some(LOCAL_CRATE.as_def_id()) + } else { + None + }; + + let starts = find_primitive_impls(tcx, base) + .chain(find_crates(tcx, base_sym)) + .chain(local_crate) .map(|id| Res::Def(tcx.def_kind(id), id)); - for first in starts { - let last = path - .iter() - .copied() - .enumerate() - // for each segment, find the child item - .try_fold(first, |res, (idx, segment)| { - let matches_ns = |res: Res| { - // If at the last segment in the path, respect the namespace hint - if idx == path.len() - 1 { - match namespace_hint { - Some(ns) => res.matches_ns(ns), - None => true, - } - } else { - res.matches_ns(Namespace::TypeNS) - } - }; - - let def_id = res.def_id(); - if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) { - Some(item) - } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { - // it is not a child item so check inherent impl items - tcx.inherent_impls(def_id) - .iter() - .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns)) - } else { - None - } - }); + let mut resolutions: Vec = starts.collect(); - if let Some(last) = last { - return last; - } + while let [segment, rest @ ..] = path { + path = rest; + let segment = Symbol::intern(segment); + + resolutions = resolutions + .into_iter() + .filter_map(|res| res.opt_def_id()) + .flat_map(|def_id| { + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + let inherent_impl_children = tcx + .inherent_impls(def_id) + .iter() + .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); + + let direct_children = item_children_by_name(tcx, def_id, segment); + + inherent_impl_children.chain(direct_children) + }) + .collect(); } - Res::Err + resolutions +} + +/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. +pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator { + def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id()) } /// Convenience function to get the `DefId` of a trait by path. @@ -651,10 +691,10 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option< /// /// This function is expensive and should be used sparingly. pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option { - match def_path_res(cx, path, Some(Namespace::TypeNS)) { + def_path_res(cx, path).into_iter().find_map(|res| match res { Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), _ => None, - } + }) } /// Gets the `hir::TraitRef` of the trait the given method is implemented for. @@ -760,7 +800,6 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - /// constructor from the std library fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { let std_types_symbols = &[ - sym::String, sym::Vec, sym::VecDeque, sym::LinkedList, @@ -775,9 +814,9 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< if method.ident.name == sym::new { if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { - return std_types_symbols - .iter() - .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did())); + return std_types_symbols.iter().any(|&symbol| { + cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() + }); } } } @@ -834,7 +873,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: & ExprKind::Lit(hir::Lit { node: LitKind::Str(ref sym, _), .. - }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String), + }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String), ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec), ExprKind::Repeat(_, ArrayLen::Body(len)) => { if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind && @@ -954,7 +993,7 @@ impl std::ops::BitOrAssign for CaptureKind { /// Note as this will walk up to parent expressions until the capture can be determined it should /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or /// function argument (other than a receiver). -pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { +pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { let mut capture = CaptureKind::Ref(Mutability::Not); pat.each_binding_or_first(&mut |_, id, span, _| match cx @@ -1219,22 +1258,33 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } } -pub struct ContainsName { +pub struct ContainsName<'a, 'tcx> { + pub cx: &'a LateContext<'tcx>, pub name: Symbol, pub result: bool, } -impl<'tcx> Visitor<'tcx> for ContainsName { +impl<'a, 'tcx> Visitor<'tcx> for ContainsName<'a, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + fn visit_name(&mut self, name: Symbol) { if self.name == name { self.result = true; } } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } } /// Checks if an `Expr` contains a certain name. -pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool { - let mut cn = ContainsName { name, result: false }; +pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { + let mut cn = ContainsName { + name, + result: false, + cx, + }; cn.visit_expr(expr); cn.result } @@ -1251,26 +1301,9 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { .is_some() } -/// Extends the span to the beginning of the spans line, incl. whitespaces. -/// -/// ```rust -/// let x = (); -/// // ^^ -/// // will be converted to -/// let x = (); -/// // ^^^^^^^^^^^^^^ -/// ``` -fn line_span(cx: &T, span: Span) -> Span { - let span = original_sp(span, DUMMY_SP); - let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); - let line_no = source_map_and_line.line; - let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); - span.with_lo(line_start) -} - /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { - tcx.hir().parent_iter(id).next().map(|(_, node)| node) + tcx.hir().find_parent(id) } /// Gets the parent expression, if any –- this is useful to constrain a lint. @@ -1287,6 +1320,7 @@ pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) } } +/// Gets the enclosing block, if any. pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { let map = &cx.tcx.hir(); let enclosing_node = map @@ -1740,6 +1774,10 @@ pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool { attrs.iter().any(|attr| attr.has_name(symbol)) } +pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { + has_attr(cx.tcx.hir().attrs(hir_id), sym::repr) +} + pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool { let map = &tcx.hir(); let mut prev_enclosing_node = None; @@ -1766,6 +1804,7 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool /// ```rust,ignore /// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX); /// ``` +/// This function is deprecated. Use [`match_function_call_with_def_id`]. pub fn match_function_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -1783,6 +1822,22 @@ pub fn match_function_call<'tcx>( None } +pub fn match_function_call_with_def_id<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + fun_def_id: DefId, +) -> Option<&'tcx [Expr<'tcx>]> { + if_chain! { + if let ExprKind::Call(fun, args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id); + then { + return Some(args); + } + }; + None +} + /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if /// any. /// @@ -1795,7 +1850,7 @@ pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) } /// Checks if the given `DefId` matches the path. -pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { +pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool { // We should probably move to Symbols in Clippy as well rather than interning every time. let path = cx.get_def_path(did); syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied()) @@ -1844,7 +1899,11 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, /// Checks if the given function kind is an async function. pub fn is_async_fn(kind: FnKind<'_>) -> bool { - matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async) + match kind { + FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async, + FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async, + FnKind::Closure => false, + } } /// Peels away all the compiler generated code surrounding the body of an async function, @@ -2033,7 +2092,7 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { /// } /// ``` pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) { matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })) } else { false @@ -2091,9 +2150,9 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } } -/// Returns Option where String is a textual representation of the type encapsulated in the -/// slice iff the given expression is a slice of primitives (as defined in the -/// `is_recursively_primitive_type` function) and None otherwise. +/// Returns `Option` where String is a textual representation of the type encapsulated in +/// the slice iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function) and `None` otherwise. pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); let expr_kind = expr_type.kind(); @@ -2202,6 +2261,18 @@ pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<' (e, count - remaining) } +/// Peels off all unary operators of an expression. Returns the underlying expression and the number +/// of operators removed. +pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { + let mut count: usize = 0; + let mut curr_expr = expr; + while let ExprKind::Unary(_, local_expr) = curr_expr.kind { + count = count.wrapping_add(1); + curr_expr = local_expr; + } + (curr_expr, count) +} + /// Peels off all references on the expression. Returns the underlying expression and the number of /// references removed. pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { @@ -2222,7 +2293,7 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) let mut count = 0; loop { match &ty.kind { - TyKind::Rptr(_, ref_ty) => { + TyKind::Ref(_, ref_ty) => { ty = ref_ty.ty; count += 1; }, @@ -2264,7 +2335,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol Entry::Vacant(entry) => { let mut names = Vec::new(); for id in tcx.hir().module_items(module) { - if matches!(tcx.def_kind(id.def_id), DefKind::Const) + if matches!(tcx.def_kind(id.owner_id), DefKind::Const) && let item = tcx.hir().item(id) && let ItemKind::Const(ty, _body) = item.kind { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { @@ -2420,6 +2491,10 @@ pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { comments_buf.join("\n") } +pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { + sm.span_take_while(span, |&ch| ch == ' ' || ch == ';') +} + macro_rules! op_utils { ($($name:ident $assign:ident)*) => { /// Binary operation traits like `LangItem::Add` diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 5a63c290a315..a8f8da67b517 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -1,6 +1,5 @@ #![allow(clippy::similar_names)] // `expr` and `expn` -use crate::is_path_diagnostic_item; use crate::source::snippet_opt; use crate::visitors::{for_each_expr, Descend}; @@ -8,7 +7,7 @@ use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; use rustc_ast::ast::LitKind; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath}; +use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind}; use rustc_lexer::unescape::unescape_literal; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use rustc_lint::LateContext; @@ -199,15 +198,21 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option< pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool { let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; matches!( - name.as_str(), - "core_panic_macro" - | "std_panic_macro" - | "core_panic_2015_macro" - | "std_panic_2015_macro" - | "core_panic_2021_macro" + name, + sym::core_panic_macro + | sym::std_panic_macro + | sym::core_panic_2015_macro + | sym::std_panic_2015_macro + | sym::core_panic_2021_macro ) } +/// Is `def_id` of `assert!` or `debug_assert!` +pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool { + let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false }; + matches!(name, sym::assert_macro | sym::debug_assert_macro) +} + pub enum PanicExpn<'a> { /// No arguments - `panic!()` Empty, @@ -433,8 +438,7 @@ impl<'tcx> FormatArgsValues<'tcx> { // ArgumentV1::from_usize() if let ExprKind::Call(callee, [val]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind - && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind - && path.segments.last().unwrap().ident.name == sym::ArgumentV1 + && let TyKind::Path(QPath::LangItem(LangItem::FormatArgument, _, _)) = ty.kind { let val_idx = if val.span.ctxt() == expr.span.ctxt() && let ExprKind::Field(_, field) = val.kind @@ -480,20 +484,6 @@ struct ParamPosition { impl<'tcx> Visitor<'tcx> for ParamPosition { fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) { - fn parse_count(expr: &Expr<'_>) -> Option { - // ::core::fmt::rt::v1::Count::Param(1usize), - if let ExprKind::Call(ctor, [val]) = expr.kind - && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind - && path.segments.last()?.ident.name == sym::Param - && let ExprKind::Lit(lit) = &val.kind - && let LitKind::Int(pos, _) = lit.node - { - Some(pos as usize) - } else { - None - } - } - match field.ident.name { sym::position => { if let ExprKind::Lit(lit) = &field.expr.kind @@ -513,15 +503,41 @@ impl<'tcx> Visitor<'tcx> for ParamPosition { } } +fn parse_count(expr: &Expr<'_>) -> Option { + // <::core::fmt::rt::v1::Count>::Param(1usize), + if let ExprKind::Call(ctor, [val]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(_, path)) = ctor.kind + && path.ident.name == sym::Param + && let ExprKind::Lit(lit) = &val.kind + && let LitKind::Int(pos, _) = lit.node + { + Some(pos as usize) + } else { + None + } +} + /// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)` fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option + 'tcx> { if let ExprKind::AddrOf(.., array) = fmt_arg.kind && let ExprKind::Array(specs) = array.kind { Some(specs.iter().map(|spec| { - let mut position = ParamPosition::default(); - position.visit_expr(spec); - position + if let ExprKind::Call(f, args) = spec.kind + && let ExprKind::Path(QPath::TypeRelative(ty, f)) = f.kind + && let TyKind::Path(QPath::LangItem(LangItem::FormatPlaceholder, _, _)) = ty.kind + && f.ident.name == sym::new + && let [position, _fill, _align, _flags, precision, width] = args + && let ExprKind::Lit(position) = &position.kind + && let LitKind::Int(position, _) = position.node { + ParamPosition { + value: position as usize, + width: parse_count(width), + precision: parse_count(precision), + } + } else { + ParamPosition::default() + } })) } else { None @@ -627,7 +643,7 @@ pub enum Count<'tcx> { /// `FormatParamKind::Numbered`. Param(FormatParam<'tcx>), /// Not specified. - Implied, + Implied(Option), } impl<'tcx> Count<'tcx> { @@ -638,8 +654,10 @@ impl<'tcx> Count<'tcx> { inner: Option, values: &FormatArgsValues<'tcx>, ) -> Option { + let span = inner.map(|inner| span_from_inner(values.format_string_span, inner)); + Some(match count { - rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), + rpf::Count::CountIs(val) => Self::Is(val, span?), rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new( FormatParamKind::Named(Symbol::intern(name)), usage, @@ -661,12 +679,12 @@ impl<'tcx> Count<'tcx> { inner?, values, )?), - rpf::Count::CountImplied => Self::Implied, + rpf::Count::CountImplied => Self::Implied(span), }) } pub fn is_implied(self) -> bool { - matches!(self, Count::Implied) + matches!(self, Count::Implied(_)) } pub fn param(self) -> Option> { @@ -675,6 +693,14 @@ impl<'tcx> Count<'tcx> { _ => None, } } + + pub fn span(self) -> Option { + match self { + Count::Is(_, span) => Some(span), + Count::Param(param) => Some(param.span), + Count::Implied(span) => span, + } + } } /// Specification for the formatting of an argument in the format string. See @@ -738,8 +764,13 @@ impl<'tcx> FormatSpec<'tcx> { /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`, /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}` pub fn is_default(&self) -> bool { - self.r#trait == sym::Display - && self.width.is_implied() + self.r#trait == sym::Display && self.is_default_for_trait() + } + + /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, + /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` + pub fn is_default_for_trait(&self) -> bool { + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.flags == 0 @@ -757,6 +788,22 @@ pub struct FormatArg<'tcx> { pub span: Span, } +impl<'tcx> FormatArg<'tcx> { + /// Span of the `:` and format specifiers + /// + /// ```ignore + /// format!("{:.}"), format!("{foo:.}") + /// ^^ ^^ + /// ``` + pub fn format_span(&self) -> Span { + let base = self.span.data(); + + // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing + // brace `{...|}` + Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent) + } +} + /// A parsed `format_args!` expansion. #[derive(Debug)] pub struct FormatArgsExpn<'tcx> { @@ -853,7 +900,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg) if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind - && is_path_diagnostic_item(cx, ty, sym::Arguments) + && let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted") { let format_string = FormatString::new(cx, pieces)?; diff --git a/clippy_utils/src/mir/maybe_storage_live.rs b/clippy_utils/src/mir/maybe_storage_live.rs deleted file mode 100644 index d262b335d99d..000000000000 --- a/clippy_utils/src/mir/maybe_storage_live.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir; -use rustc_mir_dataflow::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis}; - -/// Determines liveness of each local purely based on `StorageLive`/`Dead`. -#[derive(Copy, Clone)] -pub(super) struct MaybeStorageLive; - -impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive { - type Domain = BitSet; - const NAME: &'static str = "maybe_storage_live"; - - fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - // bottom = dead - BitSet::new_empty(body.local_decls.len()) - } - - fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) { - for arg in body.args_iter() { - state.insert(arg); - } - } -} - -impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive { - type Idx = mir::Local; - - fn statement_effect(&self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: mir::Location) { - match stmt.kind { - mir::StatementKind::StorageLive(l) => trans.gen(l), - mir::StatementKind::StorageDead(l) => trans.kill(l), - _ => (), - } - } - - fn terminator_effect( - &self, - _trans: &mut impl GenKill, - _terminator: &mir::Terminator<'tcx>, - _loc: mir::Location, - ) { - } - - fn call_return_effect( - &self, - _trans: &mut impl GenKill, - _block: mir::BasicBlock, - _return_places: CallReturnPlaces<'_, 'tcx>, - ) { - // Nothing to do when a call returns successfully - } -} diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index c8aa6f3e595d..26c0015e87e0 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -5,8 +5,6 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::TyCtxt; -mod maybe_storage_live; - mod possible_borrower; pub use possible_borrower::PossibleBorrowerMap; @@ -121,8 +119,7 @@ pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option { }) } -/// Returns a vector of `mir::Location` where `local` is assigned. Each statement referred to has -/// kind `StatementKind::Assign`. +/// Returns a vector of `mir::Location` where `local` is assigned. pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec { let mut locations = Vec::new(); for (block, data) in mir.basic_blocks.iter_enumerated() { diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 25717bf3d2fe..9adae7733894 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -1,14 +1,12 @@ -use super::{ - maybe_storage_live::MaybeStorageLive, possible_origin::PossibleOriginVisitor, - transitive_relation::TransitiveRelation, -}; +use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation}; use crate::ty::is_copy; use rustc_data_structures::fx::FxHashMap; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_lint::LateContext; use rustc_middle::mir::{self, visit::Visitor as _, Mutability}; use rustc_middle::ty::{self, visit::TypeVisitor}; -use rustc_mir_dataflow::{Analysis, ResultsCursor}; +use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor}; +use std::borrow::Cow; use std::ops::ControlFlow; /// Collects the possible borrowers of each local. @@ -39,7 +37,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { fn into_map( self, cx: &'a LateContext<'tcx>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, ) -> PossibleBorrowerMap<'b, 'tcx> { let mut map = FxHashMap::default(); for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) { @@ -170,7 +168,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { pub struct PossibleBorrowerMap<'b, 'tcx> { /// Mapping `Local -> its possible borrowers` pub map: FxHashMap>, - maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive>, + maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>, // Caches to avoid allocation of `BitSet` on every query pub bitset: (BitSet, BitSet), } @@ -182,7 +180,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive + let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len()))) .into_engine(cx.tcx, mir) .pass_name("redundant_clone") .iterate_to_fixpoint() diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 8b843732a236..dbf9f3b621d7 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -1,4 +1,11 @@ +use std::sync::OnceLock; + +use rustc_ast::Attribute; use rustc_semver::RustcVersion; +use rustc_session::Session; +use rustc_span::Span; + +use crate::attrs::get_unique_attr; macro_rules! msrv_aliases { ($($major:literal,$minor:literal,$patch:literal { @@ -12,13 +19,15 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,62,0 { BOOL_THEN_SOME } - 1,58,0 { FORMAT_ARGS_CAPTURE } + 1,65,0 { LET_ELSE } + 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } + 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } + 1,55,0 { SEEK_REWIND } 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } - 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS } + 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } 1,50,0 { BOOL_THEN, CLAMP } - 1,47,0 { TAU } + 1,47,0 { TAU, IS_ASCII_DIGIT_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2 } @@ -38,3 +47,97 @@ msrv_aliases! { 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } } + +fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = RustcVersion::parse(msrv) { + return Some(version); + } else if let Some(sess) = sess { + if let Some(span) = span { + sess.span_err(span, format!("`{msrv}` is not a valid Rust version")); + } + } + None +} + +/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]` +#[derive(Debug, Clone, Default)] +pub struct Msrv { + stack: Vec, +} + +impl Msrv { + fn new(initial: Option) -> Self { + Self { + stack: Vec::from_iter(initial), + } + } + + fn read_inner(conf_msrv: &Option, sess: &Session) -> Self { + let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") + .ok() + .and_then(|v| parse_msrv(&v, None, None)); + let clippy_msrv = conf_msrv.as_ref().and_then(|s| { + parse_msrv(s, None, None).or_else(|| { + sess.err(format!( + "error reading Clippy's configuration file. `{s}` is not a valid Rust version" + )); + None + }) + }); + + // if both files have an msrv, let's compare them and emit a warning if they differ + if let Some(cargo_msrv) = cargo_msrv + && let Some(clippy_msrv) = clippy_msrv + && clippy_msrv != cargo_msrv + { + sess.warn(format!( + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" + )); + } + + Self::new(clippy_msrv.or(cargo_msrv)) + } + + /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version` + /// field in `Cargo.toml` + /// + /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the + /// `register_{late,early}_pass` callbacks + pub fn read(conf_msrv: &Option, sess: &Session) -> &'static Self { + static PARSED: OnceLock = OnceLock::new(); + + PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess)) + } + + pub fn current(&self) -> Option { + self.stack.last().copied() + } + + pub fn meets(&self, required: RustcVersion) -> bool { + self.current().map_or(true, |version| version.meets(required)) + } + + fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option { + if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") { + if let Some(msrv) = msrv_attr.value_str() { + return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span)); + } + + sess.span_err(msrv_attr.span, "bad clippy attribute"); + } + + None + } + + pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) { + if let Some(version) = Self::parse_attr(sess, attrs) { + self.stack.push(version); + } + } + + pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) { + if Self::parse_attr(sess, attrs).is_some() { + self.stack.pop(); + } + } +} diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 80098d9766c6..42bdfd4827f1 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use std::iter; #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -46,10 +46,6 @@ pub struct NumericLiteral<'a> { } impl<'a> NumericLiteral<'a> { - pub fn from_lit(src: &'a str, lit: &Lit) -> Option> { - NumericLiteral::from_lit_kind(src, &lit.kind) - } - pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { let unsigned_src = src.strip_prefix('-').map_or(src, |s| s); if lit_kind.is_numeric() @@ -69,12 +65,13 @@ impl<'a> NumericLiteral<'a> { #[must_use] pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { + let unsigned_lit = lit.trim_start_matches('-'); // Determine delimiter for radix prefix, if present, and radix. - let radix = if lit.starts_with("0x") { + let radix = if unsigned_lit.starts_with("0x") { Radix::Hexadecimal - } else if lit.starts_with("0b") { + } else if unsigned_lit.starts_with("0b") { Radix::Binary - } else if lit.starts_with("0o") { + } else if unsigned_lit.starts_with("0o") { Radix::Octal } else { Radix::Decimal diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 78adc4536543..95eebab75677 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -16,25 +16,16 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ #[cfg(feature = "internal")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; -pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"]; -pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"]; pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; -pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; -pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; -pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; -pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; -/// Preferably use the diagnostic item `sym::deref_method` where possible -pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; -pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; #[cfg(feature = "internal")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; #[cfg(feature = "internal")] @@ -42,30 +33,21 @@ pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"] pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "", "EPSILON"]; -pub const FILE: [&str; 3] = ["std", "fs", "File"]; -pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; -pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; -pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; -pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; #[cfg(feature = "internal")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal")] pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; -pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; -pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; -pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -76,13 +58,9 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; -pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; +#[cfg(feature = "internal")] +pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; -/// Preferably use the diagnostic item `sym::Option` where possible -pub const OPTION: [&str; 3] = ["core", "option", "Option"]; -pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"]; -pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"]; -pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; @@ -94,9 +72,6 @@ pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekab pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; #[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; -pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; -pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"]; -pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"]; pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; @@ -125,14 +100,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -/// Preferably use the diagnostic item `sym::Result` where possible -pub const RESULT: [&str; 3] = ["core", "result", "Result"]; -pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; -pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; -#[cfg(feature = "internal")] -pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"]; -pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; -pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; @@ -145,6 +112,9 @@ pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; +pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"]; +pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"]; +pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 5a0721486e33..e5d7da682813 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -3,6 +3,7 @@ // of terminologies might not be relevant in the context of Clippy. Note that its behavior might // differ from the time of `rustc` even if the name stays the same. +use crate::msrvs::Msrv; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ @@ -18,25 +19,28 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option) -> McfResult { +pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { let predicates = tcx.predicates_of(current); for (predicate, _) in predicates.predicates { match predicate.kind().skip_binder() { - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) + ty::PredicateKind::Clause( + ty::Clause::RegionOutlives(_) + | ty::Clause::TypeOutlives(_) + | ty::Clause::Projection(_) + | ty::Clause::Trait(..), + ) | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::Projection(_) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Trait(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"), + ty::PredicateKind::Ambiguous => panic!("ambiguous predicate on function: {predicate:#?}"), } } match predicates.parent { @@ -78,7 +82,7 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { ty::Ref(_, _, hir::Mutability::Mut) => { return Err((span, "mutable references in const fn are unstable".into())); }, - ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), + ty::Alias(ty::Opaque, ..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::FnPtr(..) => { return Err((span, "function pointers in const fn are unstable".into())); }, @@ -129,7 +133,12 @@ fn check_rvalue<'tcx>( | Rvalue::Use(operand) | Rvalue::Cast( CastKind::PointerFromExposedAddress - | CastKind::Misc + | CastKind::IntToInt + | CastKind::FloatToInt + | CastKind::IntToFloat + | CastKind::FloatToFloat + | CastKind::FnPtrToPtr + | CastKind::PtrToPtr | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _, @@ -271,11 +280,11 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B Ok(()) } -fn check_terminator<'a, 'tcx>( +fn check_terminator<'tcx>( tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, + body: &Body<'tcx>, terminator: &Terminator<'tcx>, - msrv: Option, + msrv: &Msrv, ) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { @@ -292,11 +301,7 @@ fn check_terminator<'a, 'tcx>( check_operand(tcx, value, span, body) }, - TerminatorKind::SwitchInt { - discr, - switch_ty: _, - targets: _, - } => check_operand(tcx, discr, span, body), + TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { @@ -359,7 +364,7 @@ fn check_terminator<'a, 'tcx>( } } -fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bool { +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { @@ -378,15 +383,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bo let since = rustc_span::Symbol::intern(short_version); - crate::meets_msrv( - msrv, - RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { - panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") - }), - ) + msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| { + panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}") + })) } else { // Unstable const fn with the feature enabled. - msrv.is_none() + msrv.current().is_none() } }) } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index d28bd92d708b..cd5dcfdaca34 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -2,13 +2,13 @@ #![allow(clippy::module_name_repetitions)] -use crate::line_span; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; +use rustc_session::Session; use rustc_span::hygiene; -use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext}; +use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use std::borrow::Cow; /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. @@ -55,6 +55,23 @@ fn first_char_in_first_line(cx: &T, span: Span) -> Option(cx: &T, span: Span) -> Span { + let span = original_sp(span, DUMMY_SP); + let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap(); + let line_no = source_map_and_line.line; + let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]); + span.with_lo(line_start) +} + /// Returns the indentation of the line of a span /// /// ```rust,ignore @@ -188,11 +205,20 @@ pub fn snippet_with_applicability<'a, T: LintContext>( span: Span, default: &'a str, applicability: &mut Applicability, +) -> Cow<'a, str> { + snippet_with_applicability_sess(cx.sess(), span, default, applicability) +} + +fn snippet_with_applicability_sess<'a>( + sess: &Session, + span: Span, + default: &'a str, + applicability: &mut Applicability, ) -> Cow<'a, str> { if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - snippet_opt(cx, span).map_or_else( + snippet_opt_sess(sess, span).map_or_else( || { if *applicability == Applicability::MachineApplicable { *applicability = Applicability::HasPlaceholders; @@ -210,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(cx: &T, span: Span) -> Option { - cx.sess().source_map().span_to_snippet(span).ok() +pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option { + snippet_opt_sess(cx.sess(), span) +} + +fn snippet_opt_sess(sess: &Session, span: Span) -> Option { + sess.source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -261,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>( /// Same as `snippet_block`, but adapts the applicability level by the rules of /// `snippet_with_applicability`. -pub fn snippet_block_with_applicability<'a, T: LintContext>( - cx: &T, +pub fn snippet_block_with_applicability<'a>( + cx: &impl LintContext, span: Span, default: &'a str, indent_relative_to: Option, @@ -283,7 +313,17 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// /// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context<'a>( - cx: &LateContext<'_>, + cx: &impl LintContext, + span: Span, + outer: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, +) -> (Cow<'a, str>, bool) { + snippet_with_context_sess(cx.sess(), span, outer, default, applicability) +} + +fn snippet_with_context_sess<'a>( + sess: &Session, span: Span, outer: SyntaxContext, default: &'a str, @@ -302,7 +342,7 @@ pub fn snippet_with_context<'a>( ); ( - snippet_with_applicability(cx, span, default, applicability), + snippet_with_applicability_sess(sess, span, default, applicability), is_macro_call, ) } diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index f25bced0c2b3..2d1044af17e8 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -12,7 +12,7 @@ use rustc_ast_pretty::pprust::token_kind_to_string; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::hir::place::ProjectionKind; @@ -176,25 +176,27 @@ impl<'a> Sugg<'a> { } /// Prepare a suggestion from an expression. - pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { + pub fn ast( + cx: &EarlyContext<'_>, + expr: &ast::Expr, + default: &'a str, + ctxt: SyntaxContext, + app: &mut Applicability, + ) -> Self { use rustc_ast::ast::RangeLimits; - let snippet_without_expansion = |cx, span: Span, default| { - if span.from_expansion() { - snippet_with_macro_callsite(cx, span, default) - } else { - snippet(cx, span, default) - } - }; - match expr.kind { + _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::AddrOf(..) | ast::ExprKind::Box(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) | ast::ExprKind::Let(..) | ast::ExprKind::Unary(..) - | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)), + | ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) { + (snip, false) => Sugg::MaybeParen(snip), + (snip, true) => Sugg::NonParen(snip), + }, ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Break(..) @@ -207,6 +209,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::InlineAsm(..) | ast::ExprKind::ConstBlock(..) | ast::ExprKind::Lit(..) + | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::Loop(..) | ast::ExprKind::MacCall(..) | ast::ExprKind::MethodCall(..) @@ -216,6 +219,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) | ast::ExprKind::Yeet(..) + | ast::ExprKind::FormatArgs(..) | ast::ExprKind::Struct(..) | ast::ExprKind::Try(..) | ast::ExprKind::TryBlock(..) @@ -223,45 +227,49 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) - | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)), + | ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, - lhs.as_ref() - .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), - rhs.as_ref() - .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), + lhs.as_ref().map_or("".into(), |lhs| { + snippet_with_context(cx, lhs.span, ctxt, default, app).0 + }), + rhs.as_ref().map_or("".into(), |rhs| { + snippet_with_context(cx, rhs.span, ctxt, default, app).0 + }), ), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( AssocOp::DotDotEq, - lhs.as_ref() - .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)), - rhs.as_ref() - .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)), + lhs.as_ref().map_or("".into(), |lhs| { + snippet_with_context(cx, lhs.span, ctxt, default, app).0 + }), + rhs.as_ref().map_or("".into(), |rhs| { + snippet_with_context(cx, rhs.span, ctxt, default, app).0 + }), ), ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( AssocOp::Assign, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( astbinop2assignop(op), - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, rhs.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, rhs.span, ctxt, default, app).0, ), ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp( AssocOp::As, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, ty.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, ty.span, ctxt, default, app).0, ), ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( AssocOp::Colon, - snippet_without_expansion(cx, lhs.span, default), - snippet_without_expansion(cx, ty.span, default), + snippet_with_context(cx, lhs.span, ctxt, default, app).0, + snippet_with_context(cx, ty.span, ctxt, default, app).0, ), } } @@ -771,8 +779,7 @@ impl DiagnosticExt for rustc_errors::Diagnostic { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) { let mut remove_span = item; - let hi = cx.sess().source_map().next_point(remove_span).hi(); - let fmpos = cx.sess().source_map().lookup_byte_offset(hi); + let fmpos = cx.sess().source_map().lookup_byte_offset(remove_span.hi()); if let Some(ref src) = fmpos.sf.src { let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n'); @@ -801,14 +808,14 @@ pub struct DerefClosure { /// Returns `None` if no such use cases have been triggered in closure body /// /// note: this only works on single line immutable closures with exactly one input parameter. -pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option { +pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option { if let hir::ExprKind::Closure(&Closure { fn_decl, body, .. }) = closure.kind { let closure_body = cx.tcx.hir().body(body); // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`) // a type annotation is present if param `kind` is different from `TyKind::Infer` - let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind + let closure_arg_is_type_annotated_double_ref = if let TyKind::Ref(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind { - matches!(ty.kind, TyKind::Rptr(_, MutTy { .. })) + matches!(ty.kind, TyKind::Ref(_, MutTy { .. })) } else { false }; @@ -823,10 +830,9 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<' }; let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id); - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) - .consume_body(closure_body); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results()) + .consume_body(closure_body); if !visitor.suggestion_start.is_empty() { return Some(DerefClosure { @@ -1056,13 +1062,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read( - &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } #[cfg(test)] diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 934470bd135b..f35ef56d1643 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -9,26 +9,30 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{ + type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, + TyCtxtInferExt, +}; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy, - Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr, + self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, + PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, + VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Size, VariantIdx}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::query::normalize::AtExt; +use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use std::iter; use crate::{match_def_path, path_res, paths}; -// Checks if the given type implements copy. +/// Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env) + ty.is_copy_modulo_regions(cx.tcx, cx.param_env) } /// This checks whether a given type is known to implement Debug. @@ -59,29 +63,80 @@ pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { }) } +/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt +/// constructor of the same type. +/// +/// This method also recurses into opaque type predicates, so call it with `impl Trait` and `U` +/// will also return `true`. +pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool { + fn contains_ty_adt_constructor_opaque_inner<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + needle: Ty<'tcx>, + seen: &mut FxHashSet, + ) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => { + if inner_ty == needle { + return true; + } + + if inner_ty.ty_adt_def() == needle.ty_adt_def() { + return true; + } + + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() { + if !seen.insert(def_id) { + return false; + } + + for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { + match predicate.kind().skip_binder() { + // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through + // and check substituions to find `U`. + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { + if trait_predicate + .trait_ref + .substs + .types() + .skip(1) // Skip the implicit `Self` generic parameter + .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) + { + return true; + } + }, + // For `impl Trait`, it will register a predicate of `::Assoc = U`, + // so we check the term for `U`. + ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => { + if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() { + if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { + return true; + } + }; + }, + _ => (), + } + } + } + + false + }, + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + } + + // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not + // visited twice. + let mut seen = FxHashSet::default(); + contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen) +} + /// Resolves `::Item` for `T` /// Do not invoke without first verifying that the type implements `Iterator` pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { cx.tcx .get_diagnostic_item(sym::Iterator) - .and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item")) -} - -/// Returns the associated type `name` for `ty` as an implementation of `trait_id`. -/// Do not invoke without first verifying that the type implements the trait. -pub fn get_associated_type<'tcx>( - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - trait_id: DefId, - name: &str, -) -> Option> { - cx.tcx - .associated_items(trait_id) - .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) - .and_then(|assoc| { - let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[])); - cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok() - }) + .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item")) } /// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type @@ -153,7 +208,13 @@ pub fn implements_trait<'tcx>( trait_id: DefId, ty_params: &[GenericArg<'tcx>], ) -> bool { - implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params) + implements_trait_with_env( + cx.tcx, + cx.param_env, + ty, + trait_id, + ty_params.iter().map(|&arg| Some(arg)), + ) } /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. @@ -162,7 +223,7 @@ pub fn implements_trait_with_env<'tcx>( param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, trait_id: DefId, - ty_params: &[GenericArg<'tcx>], + ty_params: impl IntoIterator>>, ) -> bool { // Clippy shouldn't have infer types assert!(!ty.needs_infer()); @@ -171,12 +232,19 @@ pub fn implements_trait_with_env<'tcx>( if ty.has_escaping_bound_vars() { return false; } - let ty_params = tcx.mk_substs(ty_params.iter()); - tcx.infer_ctxt().enter(|infcx| { - infcx - .type_implements_trait(trait_id, ty, ty_params, param_env) - .must_apply_modulo_regions() - }) + let infcx = tcx.infer_ctxt().build(); + let orig = TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }; + let ty_params = tcx.mk_substs( + ty_params + .into_iter() + .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())), + ); + infcx + .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env) + .must_apply_modulo_regions() } /// Checks whether this type implements `Drop`. @@ -198,9 +266,9 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { is_must_use_ty(cx, *ty) }, ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(def_id, _) => { + ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { - if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; } @@ -242,27 +310,26 @@ fn is_normalizable_helper<'tcx>( } // prevent recursive loops, false-negative is better than endless loop leading to stack overflow cache.insert(ty, false); - let result = cx.tcx.infer_ctxt().enter(|infcx| { - let cause = rustc_middle::traits::ObligationCause::dummy(); - if infcx.at(&cause, param_env).normalize(ty).is_ok() { - match ty.kind() { - ty::Adt(def, substs) => def.variants().iter().all(|variant| { - variant - .fields - .iter() - .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) - }), - _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(inner_ty) if inner_ty != ty => { - is_normalizable_helper(cx, param_env, inner_ty, cache) - }, - _ => true, // if inner_ty == ty, we've already checked it - }), - } - } else { - false + let infcx = cx.tcx.infer_ctxt().build(); + let cause = rustc_middle::traits::ObligationCause::dummy(); + let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() { + match ty.kind() { + ty::Adt(def, substs) => def.variants().iter().all(|variant| { + variant + .fields + .iter() + .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + }), + _ => ty.walk().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(inner_ty) if inner_ty != ty => { + is_normalizable_helper(cx, param_env, inner_ty, cache) + }, + _ => true, // if inner_ty == ty, we've already checked it + }), } - }); + } else { + false + }; cache.insert(ty, result); result } @@ -320,11 +387,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb /// Returns `false` if the `LangItem` is not defined. pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { match ty.kind() { - ty::Adt(adt, _) => cx - .tcx - .lang_items() - .require(lang_item) - .map_or(false, |li| li == adt.did()), + ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()), _ => false, } } @@ -433,7 +496,7 @@ pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bo /// Returns the base type for HIR references and pointers. pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), + TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), _ => ty, } } @@ -584,7 +647,12 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))), - ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)), + ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => sig_from_bounds( + cx, + ty, + cx.tcx.item_bounds(def_id).subst(cx.tcx, substs), + cx.tcx.opt_parent(def_id), + ), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), ty::Dynamic(bounds, _, _) => { let lang_items = cx.tcx.lang_items(); @@ -603,7 +671,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option None, } }, - ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) { + ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) { Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty), _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)), }, @@ -624,7 +692,7 @@ fn sig_from_bounds<'tcx>( for pred in predicates { match pred.kind().skip_binder() { - PredicateKind::Trait(p) + PredicateKind::Clause(ty::Clause::Trait(p)) if (lang_items.fn_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) @@ -637,9 +705,8 @@ fn sig_from_bounds<'tcx>( } inputs = Some(i); }, - PredicateKind::Projection(p) - if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() - && p.projection_ty.self_ty() == ty => + PredicateKind::Clause(ty::Clause::Projection(p)) + if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty => { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? @@ -654,26 +721,23 @@ fn sig_from_bounds<'tcx>( inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id)) } -fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option> { +fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option> { let mut inputs = None; let mut output = None; let lang_items = cx.tcx.lang_items(); - for pred in cx + for (pred, _) in cx .tcx - .bound_explicit_item_bounds(ty.item_def_id) - .transpose_iter() - .map(|x| x.map_bound(|(p, _)| p)) + .bound_explicit_item_bounds(ty.def_id) + .subst_iter_copied(cx.tcx, ty.substs) { - match pred.0.kind().skip_binder() { - PredicateKind::Trait(p) + match pred.kind().skip_binder() { + PredicateKind::Clause(ty::Clause::Trait(p)) if (lang_items.fn_trait() == Some(p.def_id()) || lang_items.fn_mut_trait() == Some(p.def_id()) || lang_items.fn_once_trait() == Some(p.def_id())) => { - let i = pred - .map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1))) - .subst(cx.tcx, ty.substs); + let i = pred.kind().rebind(p.trait_ref.substs.type_at(1)); if inputs.map_or(false, |inputs| inputs != i) { // Multiple different fn trait impls. Is this even allowed? @@ -681,15 +745,14 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O } inputs = Some(i); }, - PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => { + PredicateKind::Clause(ty::Clause::Projection(p)) + if Some(p.projection_ty.def_id) == lang_items.fn_once_output() => + { if output.is_some() { // Multiple different fn trait impls. Is this even allowed? return None; } - output = Some( - pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap())) - .subst(cx.tcx, ty.substs), - ); + output = pred.kind().rebind(p.term.ty()).transpose(); }, _ => (), } @@ -713,7 +776,7 @@ impl core::ops::Add for EnumValue { } } -/// Attempts to read the given constant as though it were an an enum value. +/// Attempts to read the given constant as though it were an enum value. #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { @@ -794,6 +857,42 @@ pub fn for_each_top_level_late_bound_region( ty.visit_with(&mut V { index: 0, f }) } +pub struct AdtVariantInfo { + pub ind: usize, + pub size: u64, + + /// (ind, size) + pub fields_size: Vec<(usize, u64)>, +} + +impl AdtVariantInfo { + /// Returns ADT variants ordered by size + pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List>) -> Vec { + let mut variants_size = adt + .variants() + .iter() + .enumerate() + .map(|(i, variant)| { + let mut fields_size = variant + .fields + .iter() + .enumerate() + .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst)))) + .collect::>(); + fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size))); + + Self { + ind: i, + size: fields_size.iter().map(|(_, size)| size).sum(), + fields_size, + } + }) + .collect::>(); + variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); + variants_size + } +} + /// Gets the struct or enum variant from the given `Res` pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> { match res { @@ -823,7 +922,7 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc predicates .iter() .try_fold(false, |found, p| { - if let PredicateKind::Trait(p) = p.kind().skip_binder() + if let PredicateKind::Clause(ty::Clause::Trait(p)) = p.kind().skip_binder() && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() && ty.index == self_ty.index { @@ -888,3 +987,124 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { (Err(_), _) => 0, } } + +/// Makes the projection type for the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to exist, and as such, will only return +/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions +/// enabled this will check that the named associated type exists, the correct number of +/// substitutions are given, and that the correct kinds of substitutions are given (lifetime, +/// constant or type). This will not check if type normalization would succeed. +pub fn make_projection<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>( + tcx: TyCtxt<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: SubstsRef<'tcx>, + ) -> Option> { + let Some(assoc_item) = tcx + .associated_items(container_id) + .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id) + else { + debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`"); + return None; + }; + #[cfg(debug_assertions)] + { + let generics = tcx.generics_of(assoc_item.def_id); + let generic_count = generics.parent_count + generics.params.len(); + let params = generics + .parent + .map_or([].as_slice(), |id| &*tcx.generics_of(id).params) + .iter() + .chain(&generics.params) + .map(|x| &x.kind); + + debug_assert!( + generic_count == substs.len(), + "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\ + note: the expected parameters are: {:#?}\n\ + the given arguments are: `{substs:#?}`", + assoc_item.def_id, + substs.len(), + params.map(ty::GenericParamDefKind::descr).collect::>(), + ); + + if let Some((idx, (param, arg))) = params + .clone() + .zip(substs.iter().map(GenericArg::unpack)) + .enumerate() + .find(|(_, (param, arg))| { + !matches!( + (param, arg), + (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) + ) + }) + { + debug_assert!( + false, + "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\ + note: the expected parameters are {:#?}\n\ + the given arguments are {substs:#?}", + param.descr(), + params.map(ty::GenericParamDefKind::descr).collect::>() + ); + } + } + + Some(tcx.mk_alias_ty(assoc_item.def_id, substs)) + } + helper( + tcx, + container_id, + assoc_ty, + tcx.mk_substs(substs.into_iter().map(Into::into)), + ) +} + +/// Normalizes the named associated type in the given impl or trait impl. +/// +/// This function is for associated types which are "known" to be valid with the given +/// substitutions, and as such, will only return `None` when debug assertions are disabled in order +/// to prevent ICE's. With debug assertions enabled this will check that that type normalization +/// succeeds as well as everything checked by `make_projection`. +pub fn make_normalized_projection<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + container_id: DefId, + assoc_ty: Symbol, + substs: impl IntoIterator>>, +) -> Option> { + fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option> { + #[cfg(debug_assertions)] + if let Some((i, subst)) = ty + .substs + .iter() + .enumerate() + .find(|(_, subst)| subst.has_late_bound_regions()) + { + debug_assert!( + false, + "substs contain late-bound region at index `{i}` which can't be normalized.\n\ + use `TyCtxt::erase_late_bound_regions`\n\ + note: subst is `{subst:#?}`", + ); + return None; + } + match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.def_id, ty.substs)) { + Ok(ty) => Some(ty), + Err(e) => { + debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}"); + None + }, + } + } + helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?) +} diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index b5ec3fef3e0b..ab3976a13bdb 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; use rustc_hir::{Expr, ExprKind, HirId, Node}; -use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -18,16 +18,15 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> used_mutably: HirIdSet::default(), skip: false, }; - cx.tcx.infer_ctxt().enter(|infcx| { - ExprUseVisitor::new( - &mut delegate, - &infcx, - expr.hir_id.owner.def_id, - cx.param_env, - cx.typeck_results(), - ) - .walk_expr(expr); - }); + let infcx = cx.tcx.infer_ctxt().build(); + ExprUseVisitor::new( + &mut delegate, + &infcx, + expr.hir_id.owner.def_id, + cx.param_env, + cx.typeck_results(), + ) + .walk_expr(expr); if delegate.skip { return None; @@ -74,13 +73,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate { self.update(cmt); } - fn fake_read( - &mut self, - _: &rustc_hir_analysis::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } pub struct ParamBindingIdCollector { @@ -135,7 +128,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { } } - fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { if let hir::def::Res::Local(id) = path.res { if self.binding_ids.contains(&id) { self.usage_found = true; diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index d4294f18fd50..14c01a60b4c3 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -170,22 +170,22 @@ where cb: F, } - struct WithStmtGuarg<'a, F> { + struct WithStmtGuard<'a, F> { val: &'a mut RetFinder, prev_in_stmt: bool, } impl RetFinder { - fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> { let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); - WithStmtGuarg { + WithStmtGuard { val: self, prev_in_stmt, } } } - impl std::ops::Deref for WithStmtGuarg<'_, F> { + impl std::ops::Deref for WithStmtGuard<'_, F> { type Target = RetFinder; fn deref(&self) -> &Self::Target { @@ -193,13 +193,13 @@ where } } - impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + impl std::ops::DerefMut for WithStmtGuard<'_, F> { fn deref_mut(&mut self) -> &mut Self::Target { self.val } } - impl Drop for WithStmtGuarg<'_, F> { + impl Drop for WithStmtGuard<'_, F> { fn drop(&mut self) { self.val.in_stmt = self.prev_in_stmt; } @@ -724,3 +724,14 @@ pub fn for_each_local_assignment<'tcx, B>( ControlFlow::Continue(()) } } + +pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool { + for_each_expr(expr, |e| { + if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml new file mode 100644 index 000000000000..80eee368178e --- /dev/null +++ b/declare_clippy_lint/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "declare_clippy_lint" +version = "0.1.69" +edition = "2021" +publish = false + +[lib] +proc-macro = true + +[dependencies] +itertools = "0.10.1" +quote = "1.0.21" +syn = "1.0.100" + +[features] +deny-warnings = [] diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs new file mode 100644 index 000000000000..26210556d652 --- /dev/null +++ b/declare_clippy_lint/src/lib.rs @@ -0,0 +1,175 @@ +#![feature(let_chains)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +// warn on lints, that are included in `rust-lang/rust`s bootstrap +#![warn(rust_2018_idioms, unused_lifetimes)] + +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::{parse_macro_input, Attribute, Error, Ident, Lit, LitStr, Meta, Result, Token}; + +fn parse_attr(path: [&'static str; LEN], attr: &Attribute) -> Option { + if let Meta::NameValue(name_value) = attr.parse_meta().ok()? { + let path_idents = name_value.path.segments.iter().map(|segment| &segment.ident); + + if itertools::equal(path_idents, path) + && let Lit::Str(lit) = name_value.lit + { + return Some(lit); + } + } + + None +} + +struct ClippyLint { + attrs: Vec, + explanation: String, + name: Ident, + category: Ident, + description: LitStr, +} + +impl Parse for ClippyLint { + fn parse(input: ParseStream<'_>) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let mut in_code = false; + let mut explanation = String::new(); + let mut version = None; + for attr in &attrs { + if let Some(lit) = parse_attr(["doc"], attr) { + let value = lit.value(); + let line = value.strip_prefix(' ').unwrap_or(&value); + + if line.starts_with("```") { + explanation += "```\n"; + in_code = !in_code; + } else if !(in_code && line.starts_with("# ")) { + explanation += line; + explanation.push('\n'); + } + } else if let Some(lit) = parse_attr(["clippy", "version"], attr) { + if let Some(duplicate) = version.replace(lit) { + return Err(Error::new_spanned(duplicate, "duplicate clippy::version")); + } + } else { + return Err(Error::new_spanned(attr, "unexpected attribute")); + } + } + + input.parse::()?; + let name = input.parse()?; + input.parse::()?; + + let category = input.parse()?; + input.parse::()?; + + let description = input.parse()?; + + Ok(Self { + attrs, + explanation, + name, + category, + description, + }) + } +} + +/// Macro used to declare a Clippy lint. +/// +/// Every lint declaration consists of 4 parts: +/// +/// 1. The documentation, which is used for the website and `cargo clippy --explain` +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or +/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. +/// 4. The `description` that contains a short explanation on what's wrong with code where the +/// lint is triggered. +/// +/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are +/// enabled by default. As said in the README.md of this repository, if the lint level mapping +/// changes, please update README.md. +/// +/// # Example +/// +/// ``` +/// use rustc_session::declare_tool_lint; +/// +/// declare_clippy_lint! { +/// /// ### What it does +/// /// Checks for ... (describe what the lint matches). +/// /// +/// /// ### Why is this bad? +/// /// Supply the reason for linting the code. +/// /// +/// /// ### Example +/// /// ```rust +/// /// Insert a short example of code that triggers the lint +/// /// ``` +/// /// +/// /// Use instead: +/// /// ```rust +/// /// Insert a short example of improved code that doesn't trigger the lint +/// /// ``` +/// #[clippy::version = "1.65.0"] +/// pub LINT_NAME, +/// pedantic, +/// "description" +/// } +/// ``` +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints +#[proc_macro] +pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { + let ClippyLint { + attrs, + explanation, + name, + category, + description, + } = parse_macro_input!(input as ClippyLint); + + let mut category = category.to_string(); + + let level = format_ident!( + "{}", + match category.as_str() { + "correctness" => "Deny", + "style" | "suspicious" | "complexity" | "perf" | "internal_warn" => "Warn", + "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow", + _ => panic!("unknown category {category}"), + }, + ); + + let info = if category == "internal_warn" { + None + } else { + let info_name = format_ident!("{name}_INFO"); + + (&mut category[0..1]).make_ascii_uppercase(); + let category_variant = format_ident!("{category}"); + + Some(quote! { + pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo { + lint: &#name, + category: crate::LintCategory::#category_variant, + explanation: #explanation, + }; + }) + }; + + let output = quote! { + declare_tool_lint! { + #(#attrs)* + pub clippy::#name, + #level, + #description, + report_in_external_macro: true + } + + #info + }; + + TokenStream::from(output) +} diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index de31c16b819e..653121af54dc 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -10,8 +10,8 @@ edition = "2021" publish = false [dependencies] -cargo_metadata = "0.14" -clap = "3.2" +cargo_metadata = "0.15.3" +clap = "4.1.4" crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index b344db634f61..b8824024e6c7 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -73,8 +73,7 @@ impl LintcheckConfig { let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { clap_config .get_one::("crates-toml") - .map(|s| &**s) - .unwrap_or("lintcheck/lintcheck_crates.toml") + .map_or("lintcheck/lintcheck_crates.toml", |s| &**s) .into() }); @@ -97,7 +96,7 @@ impl LintcheckConfig { Some(&0) => { // automatic choice // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize + rayon::current_num_threads() / 2 }, Some(&threads) => threads, // no -j passed, use a single thread diff --git a/lintcheck/src/driver.rs b/lintcheck/src/driver.rs index 63221bab32d3..47724a2fedb0 100644 --- a/lintcheck/src/driver.rs +++ b/lintcheck/src/driver.rs @@ -5,7 +5,7 @@ use std::net::TcpStream; use std::process::{self, Command, Stdio}; use std::{env, mem}; -/// 1. Sends [DriverInfo] to the [crate::recursive::LintcheckServer] running on `addr` +/// 1. Sends [`DriverInfo`] to the [`crate::recursive::LintcheckServer`] running on `addr` /// 2. Receives [bool] from the server, if `false` returns `None` /// 3. Otherwise sends the stderr of running `clippy-driver` to the server fn run_clippy(addr: &str) -> Option { diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 95b20d7f0242..bd49f0960726 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -116,12 +116,13 @@ impl ClippyWarning { let span = diag.spans.into_iter().find(|span| span.is_primary)?; - let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { - Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), - Err(_) => format!( - "target/lintcheck/sources/{}-{}/{}", - crate_name, crate_version, span.file_name - ), + let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + format!("$CARGO_HOME/{}", stripped.display()) + } else { + format!( + "target/lintcheck/sources/{crate_name}-{crate_version}/{}", + span.file_name + ) }; Some(Self { @@ -144,16 +145,17 @@ impl ClippyWarning { } let mut output = String::from("| "); - let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); + let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { - format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) + format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message) } } } +#[allow(clippy::result_large_err)] fn get(path: &str) -> Result { const MAX_RETRIES: u8 = 4; let mut retries = 0; @@ -161,11 +163,11 @@ fn get(path: &str) -> Result { match ureq::get(path).call() { Ok(res) => return Ok(res), Err(e) if retries >= MAX_RETRIES => return Err(e), - Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e), + Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"), Err(e) => return Err(e), } - eprintln!("retrying in {} seconds...", retries); - thread::sleep(Duration::from_secs(retries as u64)); + eprintln!("retrying in {retries} seconds..."); + thread::sleep(Duration::from_secs(u64::from(retries))); retries += 1; } } @@ -181,11 +183,11 @@ impl CrateSource { let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); // url to download the crate from crates.io - let url = format!("https://crates.io/api/v1/crates/{}/{}/download", name, version); - println!("Downloading and extracting {} {} from {}", name, version, url); + let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); + println!("Downloading and extracting {name} {version} from {url}"); create_dirs(&krate_download_dir, &extract_dir); - let krate_file_path = krate_download_dir.join(format!("{}-{}.crate.tar.gz", name, version)); + let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); // don't download/extract if we already have done so if !krate_file_path.is_file() { // create a file path to download and write the crate data into @@ -205,7 +207,7 @@ impl CrateSource { Crate { version: version.clone(), name: name.clone(), - path: extract_dir.join(format!("{}-{}/", name, version)), + path: extract_dir.join(format!("{name}-{version}/")), options: options.clone(), } }, @@ -218,12 +220,12 @@ impl CrateSource { let repo_path = { let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); // add a -git suffix in case we have the same crate from crates.io and a git repo - repo_path.push(format!("{}-git", name)); + repo_path.push(format!("{name}-git")); repo_path }; // clone the repo if we have not done so if !repo_path.is_dir() { - println!("Cloning {} and checking out {}", url, commit); + println!("Cloning {url} and checking out {commit}"); if !Command::new("git") .arg("clone") .arg(url) @@ -232,7 +234,7 @@ impl CrateSource { .expect("Failed to clone git repo!") .success() { - eprintln!("Failed to clone {} into {}", url, repo_path.display()) + eprintln!("Failed to clone {url} into {}", repo_path.display()); } } // check out the commit/branch/whatever @@ -245,7 +247,7 @@ impl CrateSource { .expect("Failed to check out commit") .success() { - eprintln!("Failed to checkout {} of repo at {}", commit, repo_path.display()) + eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display()); } Crate { @@ -256,22 +258,22 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { + fn is_cache_dir(entry: &DirEntry) -> bool { + std::fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory // as a result of this filter. let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); if dest_crate_root.exists() { - println!("Deleting existing directory at {:?}", dest_crate_root); + println!("Deleting existing directory at {dest_crate_root:?}"); std::fs::remove_dir_all(&dest_crate_root).unwrap(); } - println!("Copying {:?} to {:?}", path, dest_crate_root); - - fn is_cache_dir(entry: &DirEntry) -> bool { - std::fs::read(entry.path().join("CACHEDIR.TAG")) - .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) - .unwrap_or(false) - } + println!("Copying {path:?} to {dest_crate_root:?}"); for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { let entry = entry.unwrap(); @@ -301,6 +303,7 @@ impl CrateSource { impl Crate { /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy /// issued + #[allow(clippy::too_many_arguments)] fn run_clippy_lints( &self, cargo_clippy_path: &Path, @@ -319,13 +322,13 @@ impl Crate { if config.max_jobs == 1 { println!( - "{}/{} {}% Linting {} {}", - index, total_crates_to_lint, perc, &self.name, &self.version + "{index}/{total_crates_to_lint} {perc}% Linting {} {}", + &self.name, &self.version ); } else { println!( - "{}/{} {}% Linting {} {} in target dir {:?}", - index, total_crates_to_lint, perc, &self.name, &self.version, thread_index + "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}", + &self.name, &self.version ); } @@ -345,14 +348,14 @@ impl Crate { clippy_args.push(opt); } } else { - clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]) + clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]); } if lint_filter.is_empty() { clippy_args.push("--cap-lints=warn"); } else { clippy_args.push("--cap-lints=allow"); - clippy_args.extend(lint_filter.iter().map(|filter| filter.as_str())) + clippy_args.extend(lint_filter.iter().map(std::string::String::as_str)); } if let Some(server) = server { @@ -389,17 +392,13 @@ impl Crate { let all_output = Command::new(&cargo_clippy_path) // use the looping index to create individual target dirs - .env( - "CARGO_TARGET_DIR", - shared_target_dir.join(format!("_{:?}", thread_index)), - ) + .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}"))) .args(&cargo_clippy_args) .current_dir(&self.path) .output() .unwrap_or_else(|error| { panic!( - "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n", - error, + "Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n", &cargo_clippy_path.display(), &self.path.display() ); @@ -422,8 +421,8 @@ impl Crate { { let subcrate = &stderr[63..]; println!( - "ERROR: failed to apply some suggetion to {} / to (sub)crate {}", - self.name, subcrate + "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}", + self.name ); } // fast path, we don't need the warnings anyway @@ -459,14 +458,14 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { let toml_content: String = std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display())); let crate_list: SourceList = - toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{}", toml_path.display(), e)); + toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display())); // parse the hashmap of the toml file into a list of crates let tomlcrates: Vec = crate_list.crates.into_values().collect(); // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate => // multiple Cratesources) let mut crate_sources = Vec::new(); - tomlcrates.into_iter().for_each(|tk| { + for tk in tomlcrates { if let Some(ref path) = tk.path { crate_sources.push(CrateSource::Path { name: tk.name.clone(), @@ -475,13 +474,13 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { }); } else if let Some(ref versions) = tk.versions { // if we have multiple versions, save each one - versions.iter().for_each(|ver| { + for ver in versions.iter() { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), version: ver.to_string(), options: tk.options.clone(), }); - }) + } } else if tk.git_url.is_some() && tk.git_hash.is_some() { // otherwise, we should have a git source crate_sources.push(CrateSource::Git { @@ -498,16 +497,19 @@ fn read_crates(toml_path: &Path) -> (Vec, RecursiveOptions) { if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() { - eprintln!("tomlkrate: {:?}", tk); - if tk.git_hash.is_some() != tk.git_url.is_some() { - panic!("Error: Encountered TomlCrate with only one of git_hash and git_url!"); - } - if tk.path.is_some() && (tk.git_hash.is_some() || tk.versions.is_some()) { - panic!("Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"); - } + eprintln!("tomlkrate: {tk:?}"); + assert_eq!( + tk.git_hash.is_some(), + tk.git_url.is_some(), + "Error: Encountered TomlCrate with only one of git_hash and git_url!" + ); + assert!( + tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()), + "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields" + ); unreachable!("Failed to translate TomlCrate into CrateSource!"); } - }); + } // sort the crates crate_sources.sort(); @@ -526,13 +528,13 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); // sort by "000{count} {clippy::lintname}" // to not have a lint with 200 and 2 warnings take the same spot - stats.sort_by_key(|(lint, count)| format!("{:0>4}, {}", count, lint)); + stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}")); let mut header = String::from("| lint | count |\n"); header.push_str("| -------------------------------------------------- | ----- |\n"); let stats_string = stats .iter() - .map(|(lint, count)| format!("| {:<50} | {:>4} |\n", lint, count)) + .map(|(lint, count)| format!("| {lint:<50} | {count:>4} |\n")) .fold(header, |mut table, line| { table.push_str(&line); table @@ -541,34 +543,7 @@ fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, (stats_string, counter) } -/// check if the latest modification of the logfile is older than the modification date of the -/// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck -fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool { - if !lintcheck_logs_path.exists() { - return true; - } - - let clippy_modified: std::time::SystemTime = { - let [cargo, driver] = paths.map(|p| { - std::fs::metadata(p) - .expect("failed to get metadata of file") - .modified() - .expect("failed to get modification date") - }); - // the oldest modification of either of the binaries - std::cmp::max(cargo, driver) - }; - - let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path) - .expect("failed to get metadata of file") - .modified() - .expect("failed to get modification date"); - - // time is represented in seconds since X - // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck - logs_modified < clippy_modified -} - +#[allow(clippy::too_many_lines)] fn main() { // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive` if let Ok(addr) = env::var("LINTCHECK_SERVER") { @@ -590,23 +565,6 @@ fn main() { let cargo_clippy_path = fs::canonicalize(format!("target/debug/cargo-clippy{EXE_SUFFIX}")).unwrap(); let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap(); - // if the clippy bin is newer than our logs, throw away target dirs to force clippy to - // refresh the logs - if lintcheck_needs_rerun( - &config.lintcheck_results_path, - [&cargo_clippy_path, &clippy_driver_path], - ) { - let shared_target_dir = "target/lintcheck/shared_target_dir"; - // if we get an Err here, the shared target dir probably does simply not exist - if let Ok(metadata) = std::fs::metadata(shared_target_dir) { - if metadata.is_dir() { - println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir..."); - std::fs::remove_dir_all(shared_target_dir) - .expect("failed to remove target/lintcheck/shared_target_dir"); - } - } - } - // assert that clippy is found assert!( cargo_clippy_path.is_file(), @@ -730,8 +688,8 @@ fn main() { } write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); - for (cratename, msg) in ices.iter() { - let _ = write!(text, "{}: '{}'", cratename, msg); + for (cratename, msg) in &ices { + let _ = write!(text, "{cratename}: '{msg}'"); } println!("Writing logs to {}", config.lintcheck_results_path.display()); @@ -783,10 +741,10 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us let mut new_stats_deduped = new_stats; // remove duplicates from both hashmaps - same_in_both_hashmaps.iter().for_each(|(k, v)| { + for (k, v) in &same_in_both_hashmaps { assert!(old_stats_deduped.remove(k) == Some(*v)); assert!(new_stats_deduped.remove(k) == Some(*v)); - }); + } println!("\nStats:"); @@ -795,7 +753,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .iter() .filter(|(new_key, _)| old_stats_deduped.get::(new_key).is_none()) .for_each(|(new_key, new_value)| { - println!("{} 0 => {}", new_key, new_value); + println!("{new_key} 0 => {new_value}"); }); // list all changed counts (key is in both maps but value differs) @@ -804,7 +762,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .filter(|(new_key, _new_val)| old_stats_deduped.get::(new_key).is_some()) .for_each(|(new_key, new_val)| { let old_val = old_stats_deduped.get::(new_key).unwrap(); - println!("{} {} => {}", new_key, old_val, new_val); + println!("{new_key} {old_val} => {new_val}"); }); // list all gone counts (key is in old status but not in new stats) @@ -813,7 +771,7 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none()) .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key)) .for_each(|(old_key, old_value)| { - println!("{} {} => 0", old_key, old_value); + println!("{old_key} {old_value} => 0"); }); } @@ -824,19 +782,21 @@ fn print_stats(old_stats: HashMap, new_stats: HashMap<&String, us /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create lintcheck target dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create lintcheck target dir" + ); }); std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate download dir"); - } + assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); }); std::fs::create_dir(extract_dir).unwrap_or_else(|err| { - if err.kind() != ErrorKind::AlreadyExists { - panic!("cannot create crate extraction dir"); - } + assert_eq!( + err.kind(), + ErrorKind::AlreadyExists, + "cannot create crate extraction dir" + ); }); } diff --git a/lintcheck/src/recursive.rs b/lintcheck/src/recursive.rs index 67dcfc2b199c..49072e65192f 100644 --- a/lintcheck/src/recursive.rs +++ b/lintcheck/src/recursive.rs @@ -1,7 +1,7 @@ //! In `--recursive` mode we set the `lintcheck` binary as the `RUSTC_WRAPPER` of `cargo check`, -//! this allows [crate::driver] to be run for every dependency. The driver connects to -//! [LintcheckServer] to ask if it should be skipped, and if not sends the stderr of running clippy -//! on the crate to the server +//! this allows [`crate::driver`] to be run for every dependency. The driver connects to +//! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running +//! clippy on the crate to the server use crate::ClippyWarning; use crate::RecursiveOptions; @@ -109,8 +109,8 @@ impl LintcheckServer { Self { local_addr, - sender, receiver, + sender, } } diff --git a/rust-toolchain b/rust-toolchain index 49b13cb54e71..4e7fc565a322 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-10-06" -components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] +channel = "nightly-2023-01-27" +components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/rustc_tools_util/CHANGELOG.md b/rustc_tools_util/CHANGELOG.md new file mode 100644 index 000000000000..1b351da2e7bc --- /dev/null +++ b/rustc_tools_util/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## Version 0.3.0 + +* Added `setup_version_info!();` macro for automated scripts. +* `get_version_info!()` no longer requires the user to import `rustc_tools_util::VersionInfo` and `std::env` diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml index 89c3d6aaa89e..877049ae7d0e 100644 --- a/rustc_tools_util/Cargo.toml +++ b/rustc_tools_util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_tools_util" -version = "0.2.1" +version = "0.3.0" description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/rustc_tools_util/README.md b/rustc_tools_util/README.md index e947f9c7e66e..eefc661f9635 100644 --- a/rustc_tools_util/README.md +++ b/rustc_tools_util/README.md @@ -13,43 +13,39 @@ build = "build.rs" List rustc_tools_util as regular AND build dependency. ````toml [dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" [build-dependencies] -rustc_tools_util = "0.2.1" +rustc_tools_util = "0.3.0" ```` In `build.rs`, generate the data in your `main()` -````rust + +```rust fn main() { - println!( - "cargo:rustc-env=GIT_HASH={}", - rustc_tools_util::get_commit_hash().unwrap_or_default() - ); - println!( - "cargo:rustc-env=COMMIT_DATE={}", - rustc_tools_util::get_commit_date().unwrap_or_default() - ); - println!( - "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", - rustc_tools_util::get_channel().unwrap_or_default() - ); + rustc_tools_util::setup_version_info!(); } - -```` +``` Use the version information in your main.rs -````rust -use rustc_tools_util::*; +```rust fn show_version() { let version_info = rustc_tools_util::get_version_info!(); println!("{}", version_info); } -```` +``` + This gives the following output in clippy: -`clippy 0.0.212 (a416c5e 2018-12-14)` +`clippy 0.1.66 (a28f3c8 2022-11-20)` + +## Repository + +This project is part of the rust-lang/rust-clippy repository. The source code +can be found under `./rustc_tools_util/`. +The changelog for `rustc_tools_util` is available under: +[`rustc_tools_util/CHANGELOG.md`](https://github.com/rust-lang/rust-clippy/blob/master/rustc_tools_util/CHANGELOG.md) ## License diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 01d25c53126f..4c1d8c3733df 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -1,20 +1,20 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] -use std::env; - +/// This macro creates the version string during compilation from the +/// current environment #[macro_export] macro_rules! get_version_info { () => {{ - let major = env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(); - let minor = env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(); - let patch = env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(); - let crate_name = String::from(env!("CARGO_PKG_NAME")); + let major = std::env!("CARGO_PKG_VERSION_MAJOR").parse::().unwrap(); + let minor = std::env!("CARGO_PKG_VERSION_MINOR").parse::().unwrap(); + let patch = std::env!("CARGO_PKG_VERSION_PATCH").parse::().unwrap(); + let crate_name = String::from(std::env!("CARGO_PKG_NAME")); - let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string); - let commit_hash = option_env!("GIT_HASH").map(str::to_string); - let commit_date = option_env!("COMMIT_DATE").map(str::to_string); + let host_compiler = std::option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string); + let commit_hash = std::option_env!("GIT_HASH").map(str::to_string); + let commit_date = std::option_env!("COMMIT_DATE").map(str::to_string); - VersionInfo { + $crate::VersionInfo { major, minor, patch, @@ -26,6 +26,24 @@ macro_rules! get_version_info { }}; } +/// This macro can be used in `build.rs` to automatically set the needed +/// environment values, namely `GIT_HASH`, `COMMIT_DATE` and +/// `RUSTC_RELEASE_CHANNEL` +#[macro_export] +macro_rules! setup_version_info { + () => {{ + println!( + "cargo:rustc-env=GIT_HASH={}", + $crate::get_commit_hash().unwrap_or_default() + ); + println!( + "cargo:rustc-env=COMMIT_DATE={}", + $crate::get_commit_date().unwrap_or_default() + ); + println!("cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", $crate::get_channel()); + }}; +} + // some code taken and adapted from RLS and cargo pub struct VersionInfo { pub major: u8, @@ -101,7 +119,7 @@ pub fn get_commit_date() -> Option { #[must_use] pub fn get_channel() -> String { - match env::var("CFG_RELEASE_CHANNEL") { + match std::env::var("CFG_RELEASE_CHANNEL") { Ok(channel) => channel, Err(_) => { // if that failed, try to ask rustc -V, do some parsing and find out @@ -136,8 +154,8 @@ mod test { fn test_struct_local() { let vi = get_version_info!(); assert_eq!(vi.major, 0); - assert_eq!(vi.minor, 2); - assert_eq!(vi.patch, 1); + assert_eq!(vi.minor, 3); + assert_eq!(vi.patch, 0); assert_eq!(vi.crate_name, "rustc_tools_util"); // hard to make positive tests for these since they will always change assert!(vi.commit_hash.is_none()); @@ -147,7 +165,7 @@ mod test { #[test] fn test_display_local() { let vi = get_version_info!(); - assert_eq!(vi.to_string(), "rustc_tools_util 0.2.1"); + assert_eq!(vi.to_string(), "rustc_tools_util 0.3.0"); } #[test] @@ -156,7 +174,7 @@ mod test { let s = format!("{vi:?}"); assert_eq!( s, - "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }" + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 3, patch: 0 }" ); } } diff --git a/src/docs.rs b/src/docs.rs deleted file mode 100644 index 41c31f91bca5..000000000000 --- a/src/docs.rs +++ /dev/null @@ -1,604 +0,0 @@ -// autogenerated. Please look at /clippy_dev/src/update_lints.rs - -macro_rules! include_lint { - ($file_name: expr) => { - include_str!($file_name) - }; -} - -macro_rules! docs { - ($($lint_name: expr,)*) => { - pub fn explain(lint: &str) { - println!("{}", match lint { - $( - $lint_name => include_lint!(concat!("docs/", concat!($lint_name, ".txt"))), - )* - _ => "unknown lint", - }) - } - } -} - -docs! { - "absurd_extreme_comparisons", - "alloc_instead_of_core", - "allow_attributes_without_reason", - "almost_complete_letter_range", - "almost_swapped", - "approx_constant", - "arithmetic_side_effects", - "as_conversions", - "as_ptr_cast_mut", - "as_underscore", - "assertions_on_constants", - "assertions_on_result_states", - "assign_op_pattern", - "async_yields_async", - "await_holding_invalid_type", - "await_holding_lock", - "await_holding_refcell_ref", - "bad_bit_mask", - "bind_instead_of_map", - "blanket_clippy_restriction_lints", - "blocks_in_if_conditions", - "bool_assert_comparison", - "bool_comparison", - "bool_to_int_with_if", - "borrow_as_ptr", - "borrow_deref_ref", - "borrow_interior_mutable_const", - "borrowed_box", - "box_collection", - "box_default", - "boxed_local", - "branches_sharing_code", - "builtin_type_shadow", - "bytes_count_to_len", - "bytes_nth", - "cargo_common_metadata", - "case_sensitive_file_extension_comparisons", - "cast_abs_to_unsigned", - "cast_enum_constructor", - "cast_enum_truncation", - "cast_lossless", - "cast_nan_to_int", - "cast_possible_truncation", - "cast_possible_wrap", - "cast_precision_loss", - "cast_ptr_alignment", - "cast_ref_to_mut", - "cast_sign_loss", - "cast_slice_different_sizes", - "cast_slice_from_raw_parts", - "char_lit_as_u8", - "chars_last_cmp", - "chars_next_cmp", - "checked_conversions", - "clone_double_ref", - "clone_on_copy", - "clone_on_ref_ptr", - "cloned_instead_of_copied", - "cmp_nan", - "cmp_null", - "cmp_owned", - "cognitive_complexity", - "collapsible_else_if", - "collapsible_if", - "collapsible_match", - "collapsible_str_replace", - "comparison_chain", - "comparison_to_empty", - "copy_iterator", - "crate_in_macro_def", - "create_dir", - "crosspointer_transmute", - "dbg_macro", - "debug_assert_with_mut_call", - "decimal_literal_representation", - "declare_interior_mutable_const", - "default_instead_of_iter_empty", - "default_numeric_fallback", - "default_trait_access", - "default_union_representation", - "deprecated_cfg_attr", - "deprecated_semver", - "deref_addrof", - "deref_by_slicing", - "derivable_impls", - "derive_hash_xor_eq", - "derive_ord_xor_partial_ord", - "derive_partial_eq_without_eq", - "disallowed_macros", - "disallowed_methods", - "disallowed_names", - "disallowed_script_idents", - "disallowed_types", - "diverging_sub_expression", - "doc_link_with_quotes", - "doc_markdown", - "double_comparisons", - "double_must_use", - "double_neg", - "double_parens", - "drop_copy", - "drop_non_drop", - "drop_ref", - "duplicate_mod", - "duplicate_underscore_argument", - "duration_subsec", - "else_if_without_else", - "empty_drop", - "empty_enum", - "empty_line_after_outer_attr", - "empty_loop", - "empty_structs_with_brackets", - "enum_clike_unportable_variant", - "enum_glob_use", - "enum_variant_names", - "eq_op", - "equatable_if_let", - "erasing_op", - "err_expect", - "excessive_precision", - "exhaustive_enums", - "exhaustive_structs", - "exit", - "expect_fun_call", - "expect_used", - "expl_impl_clone_on_copy", - "explicit_auto_deref", - "explicit_counter_loop", - "explicit_deref_methods", - "explicit_into_iter_loop", - "explicit_iter_loop", - "explicit_write", - "extend_with_drain", - "extra_unused_lifetimes", - "fallible_impl_from", - "field_reassign_with_default", - "filetype_is_file", - "filter_map_identity", - "filter_map_next", - "filter_next", - "flat_map_identity", - "flat_map_option", - "float_arithmetic", - "float_cmp", - "float_cmp_const", - "float_equality_without_abs", - "fn_address_comparisons", - "fn_params_excessive_bools", - "fn_to_numeric_cast", - "fn_to_numeric_cast_any", - "fn_to_numeric_cast_with_truncation", - "for_kv_map", - "for_loops_over_fallibles", - "forget_copy", - "forget_non_drop", - "forget_ref", - "format_in_format_args", - "format_push_string", - "from_iter_instead_of_collect", - "from_over_into", - "from_str_radix_10", - "future_not_send", - "get_first", - "get_last_with_len", - "get_unwrap", - "identity_op", - "if_let_mutex", - "if_not_else", - "if_same_then_else", - "if_then_some_else_none", - "ifs_same_cond", - "implicit_clone", - "implicit_hasher", - "implicit_return", - "implicit_saturating_add", - "implicit_saturating_sub", - "imprecise_flops", - "inconsistent_digit_grouping", - "inconsistent_struct_constructor", - "index_refutable_slice", - "indexing_slicing", - "ineffective_bit_mask", - "inefficient_to_string", - "infallible_destructuring_match", - "infinite_iter", - "inherent_to_string", - "inherent_to_string_shadow_display", - "init_numbered_fields", - "inline_always", - "inline_asm_x86_att_syntax", - "inline_asm_x86_intel_syntax", - "inline_fn_without_body", - "inspect_for_each", - "int_plus_one", - "integer_arithmetic", - "integer_division", - "into_iter_on_ref", - "invalid_null_ptr_usage", - "invalid_regex", - "invalid_upcast_comparisons", - "invalid_utf8_in_unchecked", - "invisible_characters", - "is_digit_ascii_radix", - "items_after_statements", - "iter_cloned_collect", - "iter_count", - "iter_kv_map", - "iter_next_loop", - "iter_next_slice", - "iter_not_returning_iterator", - "iter_nth", - "iter_nth_zero", - "iter_on_empty_collections", - "iter_on_single_items", - "iter_overeager_cloned", - "iter_skip_next", - "iter_with_drain", - "iterator_step_by_zero", - "just_underscores_and_digits", - "large_const_arrays", - "large_digit_groups", - "large_enum_variant", - "large_include_file", - "large_stack_arrays", - "large_types_passed_by_value", - "len_without_is_empty", - "len_zero", - "let_and_return", - "let_underscore_drop", - "let_underscore_lock", - "let_underscore_must_use", - "let_unit_value", - "linkedlist", - "lossy_float_literal", - "macro_use_imports", - "main_recursion", - "manual_assert", - "manual_async_fn", - "manual_bits", - "manual_clamp", - "manual_filter", - "manual_filter_map", - "manual_find", - "manual_find_map", - "manual_flatten", - "manual_instant_elapsed", - "manual_map", - "manual_memcpy", - "manual_non_exhaustive", - "manual_ok_or", - "manual_range_contains", - "manual_rem_euclid", - "manual_retain", - "manual_saturating_arithmetic", - "manual_split_once", - "manual_str_repeat", - "manual_string_new", - "manual_strip", - "manual_swap", - "manual_unwrap_or", - "many_single_char_names", - "map_clone", - "map_collect_result_unit", - "map_entry", - "map_err_ignore", - "map_flatten", - "map_identity", - "map_unwrap_or", - "match_as_ref", - "match_bool", - "match_like_matches_macro", - "match_on_vec_items", - "match_overlapping_arm", - "match_ref_pats", - "match_result_ok", - "match_same_arms", - "match_single_binding", - "match_str_case_mismatch", - "match_wild_err_arm", - "match_wildcard_for_single_variants", - "maybe_infinite_iter", - "mem_forget", - "mem_replace_option_with_none", - "mem_replace_with_default", - "mem_replace_with_uninit", - "min_max", - "mismatched_target_os", - "mismatching_type_param_order", - "misrefactored_assign_op", - "missing_const_for_fn", - "missing_docs_in_private_items", - "missing_enforced_import_renames", - "missing_errors_doc", - "missing_inline_in_public_items", - "missing_panics_doc", - "missing_safety_doc", - "missing_spin_loop", - "mistyped_literal_suffixes", - "mixed_case_hex_literals", - "mixed_read_write_in_expression", - "mod_module_files", - "module_inception", - "module_name_repetitions", - "modulo_arithmetic", - "modulo_one", - "multi_assignments", - "multiple_crate_versions", - "multiple_inherent_impl", - "must_use_candidate", - "must_use_unit", - "mut_from_ref", - "mut_mut", - "mut_mutex_lock", - "mut_range_bound", - "mutable_key_type", - "mutex_atomic", - "mutex_integer", - "naive_bytecount", - "needless_arbitrary_self_type", - "needless_bitwise_bool", - "needless_bool", - "needless_borrow", - "needless_borrowed_reference", - "needless_collect", - "needless_continue", - "needless_doctest_main", - "needless_for_each", - "needless_late_init", - "needless_lifetimes", - "needless_match", - "needless_option_as_deref", - "needless_option_take", - "needless_parens_on_range_literals", - "needless_pass_by_value", - "needless_question_mark", - "needless_range_loop", - "needless_return", - "needless_splitn", - "needless_update", - "neg_cmp_op_on_partial_ord", - "neg_multiply", - "negative_feature_names", - "never_loop", - "new_ret_no_self", - "new_without_default", - "no_effect", - "no_effect_replace", - "no_effect_underscore_binding", - "non_ascii_literal", - "non_octal_unix_permissions", - "non_send_fields_in_send_ty", - "nonminimal_bool", - "nonsensical_open_options", - "nonstandard_macro_braces", - "not_unsafe_ptr_arg_deref", - "obfuscated_if_else", - "octal_escapes", - "ok_expect", - "only_used_in_recursion", - "op_ref", - "option_as_ref_deref", - "option_env_unwrap", - "option_filter_map", - "option_if_let_else", - "option_map_or_none", - "option_map_unit_fn", - "option_option", - "or_fun_call", - "or_then_unwrap", - "out_of_bounds_indexing", - "overflow_check_conditional", - "overly_complex_bool_expr", - "panic", - "panic_in_result_fn", - "panicking_unwrap", - "partialeq_ne_impl", - "partialeq_to_none", - "path_buf_push_overwrite", - "pattern_type_mismatch", - "possible_missing_comma", - "precedence", - "print_in_format_impl", - "print_literal", - "print_stderr", - "print_stdout", - "print_with_newline", - "println_empty_string", - "ptr_arg", - "ptr_as_ptr", - "ptr_eq", - "ptr_offset_with_cast", - "pub_use", - "question_mark", - "range_minus_one", - "range_plus_one", - "range_zip_with_len", - "rc_buffer", - "rc_clone_in_vec_init", - "rc_mutex", - "read_zero_byte_vec", - "recursive_format_impl", - "redundant_allocation", - "redundant_clone", - "redundant_closure", - "redundant_closure_call", - "redundant_closure_for_method_calls", - "redundant_else", - "redundant_feature_names", - "redundant_field_names", - "redundant_pattern", - "redundant_pattern_matching", - "redundant_pub_crate", - "redundant_slicing", - "redundant_static_lifetimes", - "ref_binding_to_reference", - "ref_option_ref", - "repeat_once", - "rest_pat_in_fully_bound_structs", - "result_large_err", - "result_map_or_into_option", - "result_map_unit_fn", - "result_unit_err", - "return_self_not_must_use", - "reversed_empty_ranges", - "same_functions_in_if_condition", - "same_item_push", - "same_name_method", - "search_is_some", - "self_assignment", - "self_named_constructors", - "self_named_module_files", - "semicolon_if_nothing_returned", - "separated_literal_suffix", - "serde_api_misuse", - "shadow_reuse", - "shadow_same", - "shadow_unrelated", - "short_circuit_statement", - "should_implement_trait", - "significant_drop_in_scrutinee", - "similar_names", - "single_char_add_str", - "single_char_lifetime_names", - "single_char_pattern", - "single_component_path_imports", - "single_element_loop", - "single_match", - "single_match_else", - "size_of_in_element_count", - "skip_while_next", - "slow_vector_initialization", - "stable_sort_primitive", - "std_instead_of_alloc", - "std_instead_of_core", - "str_to_string", - "string_add", - "string_add_assign", - "string_extend_chars", - "string_from_utf8_as_bytes", - "string_lit_as_bytes", - "string_slice", - "string_to_string", - "strlen_on_c_strings", - "struct_excessive_bools", - "suboptimal_flops", - "suspicious_arithmetic_impl", - "suspicious_assignment_formatting", - "suspicious_else_formatting", - "suspicious_map", - "suspicious_op_assign_impl", - "suspicious_operation_groupings", - "suspicious_splitn", - "suspicious_to_owned", - "suspicious_unary_op_formatting", - "swap_ptr_to_ref", - "tabs_in_doc_comments", - "temporary_assignment", - "to_digit_is_some", - "to_string_in_format_args", - "todo", - "too_many_arguments", - "too_many_lines", - "toplevel_ref_arg", - "trailing_empty_array", - "trait_duplication_in_bounds", - "transmute_bytes_to_str", - "transmute_float_to_int", - "transmute_int_to_bool", - "transmute_int_to_char", - "transmute_int_to_float", - "transmute_num_to_bytes", - "transmute_ptr_to_ptr", - "transmute_ptr_to_ref", - "transmute_undefined_repr", - "transmutes_expressible_as_ptr_casts", - "transmuting_null", - "trim_split_whitespace", - "trivial_regex", - "trivially_copy_pass_by_ref", - "try_err", - "type_complexity", - "type_repetition_in_bounds", - "undocumented_unsafe_blocks", - "undropped_manually_drops", - "unicode_not_nfc", - "unimplemented", - "uninit_assumed_init", - "uninit_vec", - "uninlined_format_args", - "unit_arg", - "unit_cmp", - "unit_hash", - "unit_return_expecting_ord", - "unnecessary_cast", - "unnecessary_filter_map", - "unnecessary_find_map", - "unnecessary_fold", - "unnecessary_join", - "unnecessary_lazy_evaluations", - "unnecessary_mut_passed", - "unnecessary_operation", - "unnecessary_owned_empty_strings", - "unnecessary_self_imports", - "unnecessary_sort_by", - "unnecessary_to_owned", - "unnecessary_unwrap", - "unnecessary_wraps", - "unneeded_field_pattern", - "unneeded_wildcard_pattern", - "unnested_or_patterns", - "unreachable", - "unreadable_literal", - "unsafe_derive_deserialize", - "unsafe_removed_from_name", - "unseparated_literal_suffix", - "unsound_collection_transmute", - "unused_async", - "unused_io_amount", - "unused_peekable", - "unused_rounding", - "unused_self", - "unused_unit", - "unusual_byte_groupings", - "unwrap_in_result", - "unwrap_or_else_default", - "unwrap_used", - "upper_case_acronyms", - "use_debug", - "use_self", - "used_underscore_binding", - "useless_asref", - "useless_attribute", - "useless_conversion", - "useless_format", - "useless_let_if_seq", - "useless_transmute", - "useless_vec", - "vec_box", - "vec_init_then_push", - "vec_resize_to_zero", - "verbose_bit_mask", - "verbose_file_reads", - "vtable_address_comparisons", - "while_immutable_condition", - "while_let_loop", - "while_let_on_iterator", - "wildcard_dependencies", - "wildcard_enum_match_arm", - "wildcard_imports", - "wildcard_in_or_patterns", - "write_literal", - "write_with_newline", - "writeln_empty_string", - "wrong_self_convention", - "wrong_transmute", - "zero_divided_by_zero", - "zero_prefixed_literal", - "zero_ptr", - "zero_sized_map_values", - "zst_offset", - -} diff --git a/src/docs/absurd_extreme_comparisons.txt b/src/docs/absurd_extreme_comparisons.txt deleted file mode 100644 index 590bee28aa23..000000000000 --- a/src/docs/absurd_extreme_comparisons.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for comparisons where one side of the relation is -either the minimum or maximum value for its type and warns if it involves a -case that is always true or always false. Only integer and boolean types are -checked. - -### Why is this bad? -An expression like `min <= x` may misleadingly imply -that it is possible for `x` to be less than the minimum. Expressions like -`max < x` are probably mistakes. - -### Known problems -For `usize` the size of the current compile target will -be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such -a comparison to detect target pointer width will trigger this lint. One can -use `mem::sizeof` and compare its value or conditional compilation -attributes -like `#[cfg(target_pointer_width = "64")] ..` instead. - -### Example -``` -let vec: Vec = Vec::new(); -if vec.len() <= 0 {} -if 100 > i32::MAX {} -``` \ No newline at end of file diff --git a/src/docs/alloc_instead_of_core.txt b/src/docs/alloc_instead_of_core.txt deleted file mode 100644 index 488a36e9276c..000000000000 --- a/src/docs/alloc_instead_of_core.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does - -Finds items imported through `alloc` when available through `core`. - -### Why is this bad? - -Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are -imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint -is also useful for crates migrating to become `no_std` compatible. - -### Example -``` -use alloc::slice::from_ref; -``` -Use instead: -``` -use core::slice::from_ref; -``` \ No newline at end of file diff --git a/src/docs/allow_attributes_without_reason.txt b/src/docs/allow_attributes_without_reason.txt deleted file mode 100644 index fcc4f49b08b3..000000000000 --- a/src/docs/allow_attributes_without_reason.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for attributes that allow lints without a reason. - -(This requires the `lint_reasons` feature) - -### Why is this bad? -Allowing a lint should always have a reason. This reason should be documented to -ensure that others understand the reasoning - -### Example -``` -#![feature(lint_reasons)] - -#![allow(clippy::some_lint)] -``` - -Use instead: -``` -#![feature(lint_reasons)] - -#![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] -``` \ No newline at end of file diff --git a/src/docs/almost_complete_letter_range.txt b/src/docs/almost_complete_letter_range.txt deleted file mode 100644 index 01cbaf9eae25..000000000000 --- a/src/docs/almost_complete_letter_range.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for ranges which almost include the entire range of letters from 'a' to 'z', but -don't because they're a half open range. - -### Why is this bad? -This (`'a'..'z'`) is almost certainly a typo meant to include all letters. - -### Example -``` -let _ = 'a'..'z'; -``` -Use instead: -``` -let _ = 'a'..='z'; -``` \ No newline at end of file diff --git a/src/docs/almost_swapped.txt b/src/docs/almost_swapped.txt deleted file mode 100644 index cd10a8d5409b..000000000000 --- a/src/docs/almost_swapped.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `foo = bar; bar = foo` sequences. - -### Why is this bad? -This looks like a failed attempt to swap. - -### Example -``` -a = b; -b = a; -``` -If swapping is intended, use `swap()` instead: -``` -std::mem::swap(&mut a, &mut b); -``` \ No newline at end of file diff --git a/src/docs/approx_constant.txt b/src/docs/approx_constant.txt deleted file mode 100644 index 393fa4b5ef7e..000000000000 --- a/src/docs/approx_constant.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for floating point literals that approximate -constants which are defined in -[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants) -or -[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants), -respectively, suggesting to use the predefined constant. - -### Why is this bad? -Usually, the definition in the standard library is more -precise than what people come up with. If you find that your definition is -actually more precise, please [file a Rust -issue](https://github.com/rust-lang/rust/issues). - -### Example -``` -let x = 3.14; -let y = 1_f64 / x; -``` -Use instead: -``` -let x = std::f32::consts::PI; -let y = std::f64::consts::FRAC_1_PI; -``` \ No newline at end of file diff --git a/src/docs/arithmetic_side_effects.txt b/src/docs/arithmetic_side_effects.txt deleted file mode 100644 index 4ae8bce88ad5..000000000000 --- a/src/docs/arithmetic_side_effects.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks any kind of arithmetic operation of any type. - -Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust -Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), -or can panic (`/`, `%`). - -Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant -environments, allowed types and non-constant operations that won't overflow are ignored. - -### Why is this bad? -For integers, overflow will trigger a panic in debug builds or wrap the result in -release mode; division by zero will cause a panic in either mode. As a result, it is -desirable to explicitly call checked, wrapping or saturating arithmetic methods. - -#### Example -``` -// `n` can be any number, including `i32::MAX`. -fn foo(n: i32) -> i32 { - n + 1 -} -``` - -Third-party types can also overflow or present unwanted side-effects. - -#### Example -``` -use rust_decimal::Decimal; -let _n = Decimal::MAX + Decimal::MAX; -``` - -### Allowed types -Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter. \ No newline at end of file diff --git a/src/docs/as_conversions.txt b/src/docs/as_conversions.txt deleted file mode 100644 index 4af479bd8111..000000000000 --- a/src/docs/as_conversions.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for usage of `as` conversions. - -Note that this lint is specialized in linting *every single* use of `as` -regardless of whether good alternatives exist or not. -If you want more precise lints for `as`, please consider using these separate lints: -`unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`, -`fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`. -There is a good explanation the reason why this lint should work in this way and how it is useful -[in this issue](https://github.com/rust-lang/rust-clippy/issues/5122). - -### Why is this bad? -`as` conversions will perform many kinds of -conversions, including silently lossy conversions and dangerous coercions. -There are cases when it makes sense to use `as`, so the lint is -Allow by default. - -### Example -``` -let a: u32; -... -f(a as u16); -``` - -Use instead: -``` -f(a.try_into()?); - -// or - -f(a.try_into().expect("Unexpected u16 overflow in f")); -``` \ No newline at end of file diff --git a/src/docs/as_ptr_cast_mut.txt b/src/docs/as_ptr_cast_mut.txt deleted file mode 100644 index 228dde996bb2..000000000000 --- a/src/docs/as_ptr_cast_mut.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer - -### Why is this bad? -Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior -mutability is used, making it unlikely that having it as a mutable pointer is correct. - -### Example -``` -let string = String::with_capacity(1); -let ptr = string.as_ptr() as *mut u8; -unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR -``` -Use instead: -``` -let mut string = String::with_capacity(1); -let ptr = string.as_mut_ptr(); -unsafe { ptr.write(4) }; -``` \ No newline at end of file diff --git a/src/docs/as_underscore.txt b/src/docs/as_underscore.txt deleted file mode 100644 index 2d9b0c358936..000000000000 --- a/src/docs/as_underscore.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Check for the usage of `as _` conversion using inferred type. - -### Why is this bad? -The conversion might include lossy conversion and dangerous cast that might go -undetected due to the type being inferred. - -The lint is allowed by default as using `_` is less wordy than always specifying the type. - -### Example -``` -fn foo(n: usize) {} -let n: u16 = 256; -foo(n as _); -``` -Use instead: -``` -fn foo(n: usize) {} -let n: u16 = 256; -foo(n as usize); -``` \ No newline at end of file diff --git a/src/docs/assertions_on_constants.txt b/src/docs/assertions_on_constants.txt deleted file mode 100644 index 270c1e3b639d..000000000000 --- a/src/docs/assertions_on_constants.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for `assert!(true)` and `assert!(false)` calls. - -### Why is this bad? -Will be optimized out by the compiler or should probably be replaced by a -`panic!()` or `unreachable!()` - -### Example -``` -assert!(false) -assert!(true) -const B: bool = false; -assert!(B) -``` \ No newline at end of file diff --git a/src/docs/assertions_on_result_states.txt b/src/docs/assertions_on_result_states.txt deleted file mode 100644 index 0889084fd3ad..000000000000 --- a/src/docs/assertions_on_result_states.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls. - -### Why is this bad? -An assertion failure cannot output an useful message of the error. - -### Known problems -The suggested replacement decreases the readability of code and log output. - -### Example -``` -assert!(r.is_ok()); -assert!(r.is_err()); -``` \ No newline at end of file diff --git a/src/docs/assign_op_pattern.txt b/src/docs/assign_op_pattern.txt deleted file mode 100644 index f355c0cc18d3..000000000000 --- a/src/docs/assign_op_pattern.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for `a = a op b` or `a = b commutative_op a` -patterns. - -### Why is this bad? -These can be written as the shorter `a op= b`. - -### Known problems -While forbidden by the spec, `OpAssign` traits may have -implementations that differ from the regular `Op` impl. - -### Example -``` -let mut a = 5; -let b = 0; -// ... - -a = a + b; -``` - -Use instead: -``` -let mut a = 5; -let b = 0; -// ... - -a += b; -``` \ No newline at end of file diff --git a/src/docs/async_yields_async.txt b/src/docs/async_yields_async.txt deleted file mode 100644 index a40de6d2e473..000000000000 --- a/src/docs/async_yields_async.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for async blocks that yield values of types -that can themselves be awaited. - -### Why is this bad? -An await is likely missing. - -### Example -``` -async fn foo() {} - -fn bar() { - let x = async { - foo() - }; -} -``` - -Use instead: -``` -async fn foo() {} - -fn bar() { - let x = async { - foo().await - }; -} -``` \ No newline at end of file diff --git a/src/docs/await_holding_invalid_type.txt b/src/docs/await_holding_invalid_type.txt deleted file mode 100644 index e9c768772ff6..000000000000 --- a/src/docs/await_holding_invalid_type.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Allows users to configure types which should not be held across `await` -suspension points. - -### Why is this bad? -There are some types which are perfectly "safe" to be used concurrently -from a memory access perspective but will cause bugs at runtime if they -are held in such a way. - -### Example - -``` -await-holding-invalid-types = [ - # You can specify a type name - "CustomLockType", - # You can (optionally) specify a reason - { path = "OtherCustomLockType", reason = "Relies on a thread local" } -] -``` - -``` -struct CustomLockType; -struct OtherCustomLockType; -async fn foo() { - let _x = CustomLockType; - let _y = OtherCustomLockType; - baz().await; // Lint violation -} -``` \ No newline at end of file diff --git a/src/docs/await_holding_lock.txt b/src/docs/await_holding_lock.txt deleted file mode 100644 index 0f450a11160c..000000000000 --- a/src/docs/await_holding_lock.txt +++ /dev/null @@ -1,51 +0,0 @@ -### What it does -Checks for calls to await while holding a non-async-aware MutexGuard. - -### Why is this bad? -The Mutex types found in std::sync and parking_lot -are not designed to operate in an async context across await points. - -There are two potential solutions. One is to use an async-aware Mutex -type. Many asynchronous foundation crates provide such a Mutex type. The -other solution is to ensure the mutex is unlocked before calling await, -either by introducing a scope or an explicit call to Drop::drop. - -### Known problems -Will report false positive for explicitly dropped guards -([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is -to wrap the `.lock()` call in a block instead of explicitly dropping the guard. - -### Example -``` -async fn foo(x: &Mutex) { - let mut guard = x.lock().unwrap(); - *guard += 1; - baz().await; -} - -async fn bar(x: &Mutex) { - let mut guard = x.lock().unwrap(); - *guard += 1; - drop(guard); // explicit drop - baz().await; -} -``` - -Use instead: -``` -async fn foo(x: &Mutex) { - { - let mut guard = x.lock().unwrap(); - *guard += 1; - } - baz().await; -} - -async fn bar(x: &Mutex) { - { - let mut guard = x.lock().unwrap(); - *guard += 1; - } // guard dropped here at end of scope - baz().await; -} -``` \ No newline at end of file diff --git a/src/docs/await_holding_refcell_ref.txt b/src/docs/await_holding_refcell_ref.txt deleted file mode 100644 index 226a261b9cc5..000000000000 --- a/src/docs/await_holding_refcell_ref.txt +++ /dev/null @@ -1,47 +0,0 @@ -### What it does -Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`. - -### Why is this bad? -`RefCell` refs only check for exclusive mutable access -at runtime. Holding onto a `RefCell` ref across an `await` suspension point -risks panics from a mutable ref shared while other refs are outstanding. - -### Known problems -Will report false positive for explicitly dropped refs -([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is -to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref. - -### Example -``` -async fn foo(x: &RefCell) { - let mut y = x.borrow_mut(); - *y += 1; - baz().await; -} - -async fn bar(x: &RefCell) { - let mut y = x.borrow_mut(); - *y += 1; - drop(y); // explicit drop - baz().await; -} -``` - -Use instead: -``` -async fn foo(x: &RefCell) { - { - let mut y = x.borrow_mut(); - *y += 1; - } - baz().await; -} - -async fn bar(x: &RefCell) { - { - let mut y = x.borrow_mut(); - *y += 1; - } // y dropped here at end of scope - baz().await; -} -``` \ No newline at end of file diff --git a/src/docs/bad_bit_mask.txt b/src/docs/bad_bit_mask.txt deleted file mode 100644 index d40024ee5620..000000000000 --- a/src/docs/bad_bit_mask.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for incompatible bit masks in comparisons. - -The formula for detecting if an expression of the type `_ m - c` (where `` is one of {`&`, `|`} and `` is one of -{`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following -table: - -|Comparison |Bit Op|Example |is always|Formula | -|------------|------|-------------|---------|----------------------| -|`==` or `!=`| `&` |`x & 2 == 3` |`false` |`c & m != c` | -|`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` | -|`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` | -|`==` or `!=`| `\|` |`x \| 1 == 0`|`false` |`c \| m != c` | -|`<` or `>=`| `\|` |`x \| 1 < 1` |`false` |`m >= c` | -|`<=` or `>` | `\|` |`x \| 1 > 0` |`true` |`m > c` | - -### Why is this bad? -If the bits that the comparison cares about are always -set to zero or one by the bit mask, the comparison is constant `true` or -`false` (depending on mask, compared value, and operators). - -So the code is actively misleading, and the only reason someone would write -this intentionally is to win an underhanded Rust contest or create a -test-case for this lint. - -### Example -``` -if (x & 1 == 2) { } -``` \ No newline at end of file diff --git a/src/docs/bind_instead_of_map.txt b/src/docs/bind_instead_of_map.txt deleted file mode 100644 index 148575803d38..000000000000 --- a/src/docs/bind_instead_of_map.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or -`_.or_else(|x| Err(y))`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.map(|x| y)` or `_.map_err(|x| y)`. - -### Example -``` -let _ = opt().and_then(|s| Some(s.len())); -let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) }); -let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) }); -``` - -The correct use would be: - -``` -let _ = opt().map(|s| s.len()); -let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 }); -let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 }); -``` \ No newline at end of file diff --git a/src/docs/blanket_clippy_restriction_lints.txt b/src/docs/blanket_clippy_restriction_lints.txt deleted file mode 100644 index 28a4ebf7169b..000000000000 --- a/src/docs/blanket_clippy_restriction_lints.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. - -### Why is this bad? -Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust. -These lints should only be enabled on a lint-by-lint basis and with careful consideration. - -### Example -``` -#![deny(clippy::restriction)] -``` - -Use instead: -``` -#![deny(clippy::as_conversions)] -``` \ No newline at end of file diff --git a/src/docs/blocks_in_if_conditions.txt b/src/docs/blocks_in_if_conditions.txt deleted file mode 100644 index 3afa14853fd2..000000000000 --- a/src/docs/blocks_in_if_conditions.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `if` conditions that use blocks containing an -expression, statements or conditions that use closures with blocks. - -### Why is this bad? -Style, using blocks in the condition makes it hard to read. - -### Examples -``` -if { true } { /* ... */ } - -if { let x = somefunc(); x } { /* ... */ } -``` - -Use instead: -``` -if true { /* ... */ } - -let res = { let x = somefunc(); x }; -if res { /* ... */ } -``` \ No newline at end of file diff --git a/src/docs/bool_assert_comparison.txt b/src/docs/bool_assert_comparison.txt deleted file mode 100644 index df7ca00cc2ba..000000000000 --- a/src/docs/bool_assert_comparison.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns about boolean comparisons in assert-like macros. - -### Why is this bad? -It is shorter to use the equivalent. - -### Example -``` -assert_eq!("a".is_empty(), false); -assert_ne!("a".is_empty(), true); -``` - -Use instead: -``` -assert!(!"a".is_empty()); -``` \ No newline at end of file diff --git a/src/docs/bool_comparison.txt b/src/docs/bool_comparison.txt deleted file mode 100644 index 0996f60cec44..000000000000 --- a/src/docs/bool_comparison.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for expressions of the form `x == true`, -`x != true` and order comparisons such as `x < true` (or vice versa) and -suggest using the variable directly. - -### Why is this bad? -Unnecessary code. - -### Example -``` -if x == true {} -if y == false {} -``` -use `x` directly: -``` -if x {} -if !y {} -``` \ No newline at end of file diff --git a/src/docs/bool_to_int_with_if.txt b/src/docs/bool_to_int_with_if.txt deleted file mode 100644 index 63535b454c9f..000000000000 --- a/src/docs/bool_to_int_with_if.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Instead of using an if statement to convert a bool to an int, -this lint suggests using a `from()` function or an `as` coercion. - -### Why is this bad? -Coercion or `from()` is idiomatic way to convert bool to a number. -Both methods are guaranteed to return 1 for true, and 0 for false. - -See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E - -### Example -``` -if condition { - 1_i64 -} else { - 0 -}; -``` -Use instead: -``` -i64::from(condition); -``` -or -``` -condition as i64; -``` \ No newline at end of file diff --git a/src/docs/borrow_as_ptr.txt b/src/docs/borrow_as_ptr.txt deleted file mode 100644 index 0be865abd578..000000000000 --- a/src/docs/borrow_as_ptr.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the usage of `&expr as *const T` or -`&mut expr as *mut T`, and suggest using `ptr::addr_of` or -`ptr::addr_of_mut` instead. - -### Why is this bad? -This would improve readability and avoid creating a reference -that points to an uninitialized value or unaligned place. -Read the `ptr::addr_of` docs for more information. - -### Example -``` -let val = 1; -let p = &val as *const i32; - -let mut val_mut = 1; -let p_mut = &mut val_mut as *mut i32; -``` -Use instead: -``` -let val = 1; -let p = std::ptr::addr_of!(val); - -let mut val_mut = 1; -let p_mut = std::ptr::addr_of_mut!(val_mut); -``` \ No newline at end of file diff --git a/src/docs/borrow_deref_ref.txt b/src/docs/borrow_deref_ref.txt deleted file mode 100644 index 352480d3f26a..000000000000 --- a/src/docs/borrow_deref_ref.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for `&*(&T)`. - -### Why is this bad? -Dereferencing and then borrowing a reference value has no effect in most cases. - -### Known problems -False negative on such code: -``` -let x = &12; -let addr_x = &x as *const _ as usize; -let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered. - // But if we fix it, assert will fail. -assert_ne!(addr_x, addr_y); -``` - -### Example -``` -let s = &String::new(); - -let a: &String = &* s; -``` - -Use instead: -``` -let a: &String = s; -``` \ No newline at end of file diff --git a/src/docs/borrow_interior_mutable_const.txt b/src/docs/borrow_interior_mutable_const.txt deleted file mode 100644 index e55b6a77e666..000000000000 --- a/src/docs/borrow_interior_mutable_const.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Checks if `const` items which is interior mutable (e.g., -contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. - -### Why is this bad? -Consts are copied everywhere they are referenced, i.e., -every time you refer to the const a fresh instance of the `Cell` or `Mutex` -or `AtomicXxxx` will be created, which defeats the whole purpose of using -these types in the first place. - -The `const` value should be stored inside a `static` item. - -### Known problems -When an enum has variants with interior mutability, use of its non -interior mutable variants can generate false positives. See issue -[#3962](https://github.com/rust-lang/rust-clippy/issues/3962) - -Types that have underlying or potential interior mutability trigger the lint whether -the interior mutable field is used or not. See issues -[#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and -[#3825](https://github.com/rust-lang/rust-clippy/issues/3825) - -### Example -``` -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); - -CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged -assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct -``` - -Use instead: -``` -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); - -static STATIC_ATOM: AtomicUsize = CONST_ATOM; -STATIC_ATOM.store(9, SeqCst); -assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance -``` \ No newline at end of file diff --git a/src/docs/borrowed_box.txt b/src/docs/borrowed_box.txt deleted file mode 100644 index d7089be662a5..000000000000 --- a/src/docs/borrowed_box.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for use of `&Box` anywhere in the code. -Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - -### Why is this bad? -A `&Box` parameter requires the function caller to box `T` first before passing it to a function. -Using `&T` defines a concrete type for the parameter and generalizes the function, this would also -auto-deref to `&T` at the function call site if passed a `&Box`. - -### Example -``` -fn foo(bar: &Box) { ... } -``` - -Better: - -``` -fn foo(bar: &T) { ... } -``` \ No newline at end of file diff --git a/src/docs/box_collection.txt b/src/docs/box_collection.txt deleted file mode 100644 index 053f24c46281..000000000000 --- a/src/docs/box_collection.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for use of `Box` where T is a collection such as Vec anywhere in the code. -Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - -### Why is this bad? -Collections already keeps their contents in a separate area on -the heap. So if you `Box` them, you just add another level of indirection -without any benefit whatsoever. - -### Example -``` -struct X { - values: Box>, -} -``` - -Better: - -``` -struct X { - values: Vec, -} -``` \ No newline at end of file diff --git a/src/docs/box_default.txt b/src/docs/box_default.txt deleted file mode 100644 index 1c670c773337..000000000000 --- a/src/docs/box_default.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -checks for `Box::new(T::default())`, which is better written as -`Box::::default()`. - -### Why is this bad? -First, it's more complex, involving two calls instead of one. -Second, `Box::default()` can be faster -[in certain cases](https://nnethercote.github.io/perf-book/standard-library-types.html#box). - -### Example -``` -let x: Box = Box::new(Default::default()); -``` -Use instead: -``` -let x: Box = Box::default(); -``` \ No newline at end of file diff --git a/src/docs/boxed_local.txt b/src/docs/boxed_local.txt deleted file mode 100644 index 8b1febf1455f..000000000000 --- a/src/docs/boxed_local.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usage of `Box` where an unboxed `T` would -work fine. - -### Why is this bad? -This is an unnecessary allocation, and bad for -performance. It is only necessary to allocate if you wish to move the box -into something. - -### Example -``` -fn foo(x: Box) {} -``` - -Use instead: -``` -fn foo(x: u32) {} -``` \ No newline at end of file diff --git a/src/docs/branches_sharing_code.txt b/src/docs/branches_sharing_code.txt deleted file mode 100644 index 79be6124798a..000000000000 --- a/src/docs/branches_sharing_code.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks if the `if` and `else` block contain shared code that can be -moved out of the blocks. - -### Why is this bad? -Duplicate code is less maintainable. - -### Known problems -* The lint doesn't check if the moved expressions modify values that are being used in - the if condition. The suggestion can in that case modify the behavior of the program. - See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452) - -### Example -``` -let foo = if … { - println!("Hello World"); - 13 -} else { - println!("Hello World"); - 42 -}; -``` - -Use instead: -``` -println!("Hello World"); -let foo = if … { - 13 -} else { - 42 -}; -``` \ No newline at end of file diff --git a/src/docs/builtin_type_shadow.txt b/src/docs/builtin_type_shadow.txt deleted file mode 100644 index 15b1c9df7baa..000000000000 --- a/src/docs/builtin_type_shadow.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Warns if a generic shadows a built-in type. - -### Why is this bad? -This gives surprising type errors. - -### Example - -``` -impl Foo { - fn impl_func(&self) -> u32 { - 42 - } -} -``` \ No newline at end of file diff --git a/src/docs/bytes_count_to_len.txt b/src/docs/bytes_count_to_len.txt deleted file mode 100644 index ca7bf9a38da8..000000000000 --- a/src/docs/bytes_count_to_len.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -It checks for `str::bytes().count()` and suggests replacing it with -`str::len()`. - -### Why is this bad? -`str::bytes().count()` is longer and may not be as performant as using -`str::len()`. - -### Example -``` -"hello".bytes().count(); -String::from("hello").bytes().count(); -``` -Use instead: -``` -"hello".len(); -String::from("hello").len(); -``` \ No newline at end of file diff --git a/src/docs/bytes_nth.txt b/src/docs/bytes_nth.txt deleted file mode 100644 index 260de343353d..000000000000 --- a/src/docs/bytes_nth.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for the use of `.bytes().nth()`. - -### Why is this bad? -`.as_bytes().get()` is more efficient and more -readable. - -### Example -``` -"Hello".bytes().nth(3); -``` - -Use instead: -``` -"Hello".as_bytes().get(3); -``` \ No newline at end of file diff --git a/src/docs/cargo_common_metadata.txt b/src/docs/cargo_common_metadata.txt deleted file mode 100644 index 1998647a9274..000000000000 --- a/src/docs/cargo_common_metadata.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks to see if all common metadata is defined in -`Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata - -### Why is this bad? -It will be more difficult for users to discover the -purpose of the crate, and key information related to it. - -### Example -``` -[package] -name = "clippy" -version = "0.0.212" -repository = "https://github.com/rust-lang/rust-clippy" -readme = "README.md" -license = "MIT OR Apache-2.0" -keywords = ["clippy", "lint", "plugin"] -categories = ["development-tools", "development-tools::cargo-plugins"] -``` - -Should include a description field like: - -``` -[package] -name = "clippy" -version = "0.0.212" -description = "A bunch of helpful lints to avoid common pitfalls in Rust" -repository = "https://github.com/rust-lang/rust-clippy" -readme = "README.md" -license = "MIT OR Apache-2.0" -keywords = ["clippy", "lint", "plugin"] -categories = ["development-tools", "development-tools::cargo-plugins"] -``` \ No newline at end of file diff --git a/src/docs/case_sensitive_file_extension_comparisons.txt b/src/docs/case_sensitive_file_extension_comparisons.txt deleted file mode 100644 index 8e6e18ed4e23..000000000000 --- a/src/docs/case_sensitive_file_extension_comparisons.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for calls to `ends_with` with possible file extensions -and suggests to use a case-insensitive approach instead. - -### Why is this bad? -`ends_with` is case-sensitive and may not detect files with a valid extension. - -### Example -``` -fn is_rust_file(filename: &str) -> bool { - filename.ends_with(".rs") -} -``` -Use instead: -``` -fn is_rust_file(filename: &str) -> bool { - let filename = std::path::Path::new(filename); - filename.extension() - .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) -} -``` \ No newline at end of file diff --git a/src/docs/cast_abs_to_unsigned.txt b/src/docs/cast_abs_to_unsigned.txt deleted file mode 100644 index c5d8ee034ce5..000000000000 --- a/src/docs/cast_abs_to_unsigned.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for uses of the `abs()` method that cast the result to unsigned. - -### Why is this bad? -The `unsigned_abs()` method avoids panic when called on the MIN value. - -### Example -``` -let x: i32 = -42; -let y: u32 = x.abs() as u32; -``` -Use instead: -``` -let x: i32 = -42; -let y: u32 = x.unsigned_abs(); -``` \ No newline at end of file diff --git a/src/docs/cast_enum_constructor.txt b/src/docs/cast_enum_constructor.txt deleted file mode 100644 index 675c03a42bc2..000000000000 --- a/src/docs/cast_enum_constructor.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for casts from an enum tuple constructor to an integer. - -### Why is this bad? -The cast is easily confused with casting a c-like enum value to an integer. - -### Example -``` -enum E { X(i32) }; -let _ = E::X as usize; -``` \ No newline at end of file diff --git a/src/docs/cast_enum_truncation.txt b/src/docs/cast_enum_truncation.txt deleted file mode 100644 index abe32a8296da..000000000000 --- a/src/docs/cast_enum_truncation.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for casts from an enum type to an integral type which will definitely truncate the -value. - -### Why is this bad? -The resulting integral value will not match the value of the variant it came from. - -### Example -``` -enum E { X = 256 }; -let _ = E::X as u8; -``` \ No newline at end of file diff --git a/src/docs/cast_lossless.txt b/src/docs/cast_lossless.txt deleted file mode 100644 index c3a61dd470fc..000000000000 --- a/src/docs/cast_lossless.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for casts between numerical types that may -be replaced by safe conversion functions. - -### Why is this bad? -Rust's `as` keyword will perform many kinds of -conversions, including silently lossy conversions. Conversion functions such -as `i32::from` will only perform lossless conversions. Using the conversion -functions prevents conversions from turning into silent lossy conversions if -the types of the input expressions ever change, and make it easier for -people reading the code to know that the conversion is lossless. - -### Example -``` -fn as_u64(x: u8) -> u64 { - x as u64 -} -``` - -Using `::from` would look like this: - -``` -fn as_u64(x: u8) -> u64 { - u64::from(x) -} -``` \ No newline at end of file diff --git a/src/docs/cast_nan_to_int.txt b/src/docs/cast_nan_to_int.txt deleted file mode 100644 index 122f5da0c921..000000000000 --- a/src/docs/cast_nan_to_int.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for a known NaN float being cast to an integer - -### Why is this bad? -NaNs are cast into zero, so one could simply use this and make the -code more readable. The lint could also hint at a programmer error. - -### Example -``` -let _: (0.0_f32 / 0.0) as u64; -``` -Use instead: -``` -let _: = 0_u64; -``` \ No newline at end of file diff --git a/src/docs/cast_possible_truncation.txt b/src/docs/cast_possible_truncation.txt deleted file mode 100644 index 0b164848cc7c..000000000000 --- a/src/docs/cast_possible_truncation.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for casts between numerical types that may -truncate large values. This is expected behavior, so the cast is `Allow` by -default. - -### Why is this bad? -In some problem domains, it is good practice to avoid -truncation. This lint can be activated to help assess where additional -checks could be beneficial. - -### Example -``` -fn as_u8(x: u64) -> u8 { - x as u8 -} -``` \ No newline at end of file diff --git a/src/docs/cast_possible_wrap.txt b/src/docs/cast_possible_wrap.txt deleted file mode 100644 index f883fc9cfb99..000000000000 --- a/src/docs/cast_possible_wrap.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for casts from an unsigned type to a signed type of -the same size. Performing such a cast is a 'no-op' for the compiler, -i.e., nothing is changed at the bit level, and the binary representation of -the value is reinterpreted. This can cause wrapping if the value is too big -for the target signed type. However, the cast works as defined, so this lint -is `Allow` by default. - -### Why is this bad? -While such a cast is not bad in itself, the results can -be surprising when this is not the intended behavior, as demonstrated by the -example below. - -### Example -``` -u32::MAX as i32; // will yield a value of `-1` -``` \ No newline at end of file diff --git a/src/docs/cast_precision_loss.txt b/src/docs/cast_precision_loss.txt deleted file mode 100644 index f915d9f8a6d0..000000000000 --- a/src/docs/cast_precision_loss.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for casts from any numerical to a float type where -the receiving type cannot store all values from the original type without -rounding errors. This possible rounding is to be expected, so this lint is -`Allow` by default. - -Basically, this warns on casting any integer with 32 or more bits to `f32` -or any 64-bit integer to `f64`. - -### Why is this bad? -It's not bad at all. But in some applications it can be -helpful to know where precision loss can take place. This lint can help find -those places in the code. - -### Example -``` -let x = u64::MAX; -x as f64; -``` \ No newline at end of file diff --git a/src/docs/cast_ptr_alignment.txt b/src/docs/cast_ptr_alignment.txt deleted file mode 100644 index 6a6d4dcaa2ae..000000000000 --- a/src/docs/cast_ptr_alignment.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for casts, using `as` or `pointer::cast`, -from a less-strictly-aligned pointer to a more-strictly-aligned pointer - -### Why is this bad? -Dereferencing the resulting pointer may be undefined -behavior. - -### Known problems -Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar -on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like -u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis. - -### Example -``` -let _ = (&1u8 as *const u8) as *const u16; -let _ = (&mut 1u8 as *mut u8) as *mut u16; - -(&1u8 as *const u8).cast::(); -(&mut 1u8 as *mut u8).cast::(); -``` \ No newline at end of file diff --git a/src/docs/cast_ref_to_mut.txt b/src/docs/cast_ref_to_mut.txt deleted file mode 100644 index fb5b4dbb62d8..000000000000 --- a/src/docs/cast_ref_to_mut.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for casts of `&T` to `&mut T` anywhere in the code. - -### Why is this bad? -It’s basically guaranteed to be undefined behavior. -`UnsafeCell` is the only way to obtain aliasable data that is considered -mutable. - -### Example -``` -fn x(r: &i32) { - unsafe { - *(r as *const _ as *mut _) += 1; - } -} -``` - -Instead consider using interior mutability types. - -``` -use std::cell::UnsafeCell; - -fn x(r: &UnsafeCell) { - unsafe { - *r.get() += 1; - } -} -``` \ No newline at end of file diff --git a/src/docs/cast_sign_loss.txt b/src/docs/cast_sign_loss.txt deleted file mode 100644 index d64fe1b07f46..000000000000 --- a/src/docs/cast_sign_loss.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for casts from a signed to an unsigned numerical -type. In this case, negative values wrap around to large positive values, -which can be quite surprising in practice. However, as the cast works as -defined, this lint is `Allow` by default. - -### Why is this bad? -Possibly surprising results. You can activate this lint -as a one-time check to see where numerical wrapping can arise. - -### Example -``` -let y: i8 = -1; -y as u128; // will return 18446744073709551615 -``` \ No newline at end of file diff --git a/src/docs/cast_slice_different_sizes.txt b/src/docs/cast_slice_different_sizes.txt deleted file mode 100644 index c01ef0ba92c0..000000000000 --- a/src/docs/cast_slice_different_sizes.txt +++ /dev/null @@ -1,38 +0,0 @@ -### What it does -Checks for `as` casts between raw pointers to slices with differently sized elements. - -### Why is this bad? -The produced raw pointer to a slice does not update its length metadata. The produced -pointer will point to a different number of bytes than the original pointer because the -length metadata of a raw slice pointer is in elements rather than bytes. -Producing a slice reference from the raw pointer will either create a slice with -less data (which can be surprising) or create a slice with more data and cause Undefined Behavior. - -### Example -// Missing data -``` -let a = [1_i32, 2, 3, 4]; -let p = &a as *const [i32] as *const [u8]; -unsafe { - println!("{:?}", &*p); -} -``` -// Undefined Behavior (note: also potential alignment issues) -``` -let a = [1_u8, 2, 3, 4]; -let p = &a as *const [u8] as *const [u32]; -unsafe { - println!("{:?}", &*p); -} -``` -Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length -``` -let a = [1_i32, 2, 3, 4]; -let old_ptr = &a as *const [i32]; -// The data pointer is cast to a pointer to the target `u8` not `[u8]` -// The length comes from the known length of 4 i32s times the 4 bytes per i32 -let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16); -unsafe { - println!("{:?}", &*new_ptr); -} -``` \ No newline at end of file diff --git a/src/docs/cast_slice_from_raw_parts.txt b/src/docs/cast_slice_from_raw_parts.txt deleted file mode 100644 index b58c739766aa..000000000000 --- a/src/docs/cast_slice_from_raw_parts.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for a raw slice being cast to a slice pointer - -### Why is this bad? -This can result in multiple `&mut` references to the same location when only a pointer is -required. -`ptr::slice_from_raw_parts` is a safe alternative that doesn't require -the same [safety requirements] to be upheld. - -### Example -``` -let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _; -let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _; -``` -Use instead: -``` -let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len); -let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); -``` -[safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety \ No newline at end of file diff --git a/src/docs/char_lit_as_u8.txt b/src/docs/char_lit_as_u8.txt deleted file mode 100644 index 00d60b9a451b..000000000000 --- a/src/docs/char_lit_as_u8.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for expressions where a character literal is cast -to `u8` and suggests using a byte literal instead. - -### Why is this bad? -In general, casting values to smaller types is -error-prone and should be avoided where possible. In the particular case of -converting a character literal to u8, it is easy to avoid by just using a -byte literal instead. As an added bonus, `b'a'` is even slightly shorter -than `'a' as u8`. - -### Example -``` -'x' as u8 -``` - -A better version, using the byte literal: - -``` -b'x' -``` \ No newline at end of file diff --git a/src/docs/chars_last_cmp.txt b/src/docs/chars_last_cmp.txt deleted file mode 100644 index 4c1d8838973a..000000000000 --- a/src/docs/chars_last_cmp.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usage of `_.chars().last()` or -`_.chars().next_back()` on a `str` to check if it ends with a given char. - -### Why is this bad? -Readability, this can be written more concisely as -`_.ends_with(_)`. - -### Example -``` -name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); -``` - -Use instead: -``` -name.ends_with('_') || name.ends_with('-'); -``` \ No newline at end of file diff --git a/src/docs/chars_next_cmp.txt b/src/docs/chars_next_cmp.txt deleted file mode 100644 index 77cbce2de00f..000000000000 --- a/src/docs/chars_next_cmp.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `.chars().next()` on a `str` to check -if it starts with a given char. - -### Why is this bad? -Readability, this can be written more concisely as -`_.starts_with(_)`. - -### Example -``` -let name = "foo"; -if name.chars().next() == Some('_') {}; -``` - -Use instead: -``` -let name = "foo"; -if name.starts_with('_') {}; -``` \ No newline at end of file diff --git a/src/docs/checked_conversions.txt b/src/docs/checked_conversions.txt deleted file mode 100644 index 536b01294ee1..000000000000 --- a/src/docs/checked_conversions.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for explicit bounds checking when casting. - -### Why is this bad? -Reduces the readability of statements & is error prone. - -### Example -``` -foo <= i32::MAX as u32; -``` - -Use instead: -``` -i32::try_from(foo).is_ok(); -``` \ No newline at end of file diff --git a/src/docs/clone_double_ref.txt b/src/docs/clone_double_ref.txt deleted file mode 100644 index 2729635bd246..000000000000 --- a/src/docs/clone_double_ref.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `.clone()` on an `&&T`. - -### Why is this bad? -Cloning an `&&T` copies the inner `&T`, instead of -cloning the underlying `T`. - -### Example -``` -fn main() { - let x = vec![1]; - let y = &&x; - let z = y.clone(); - println!("{:p} {:p}", *y, z); // prints out the same pointer -} -``` \ No newline at end of file diff --git a/src/docs/clone_on_copy.txt b/src/docs/clone_on_copy.txt deleted file mode 100644 index 99a0bdb4c4ac..000000000000 --- a/src/docs/clone_on_copy.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for usage of `.clone()` on a `Copy` type. - -### Why is this bad? -The only reason `Copy` types implement `Clone` is for -generics, not for using the `clone` method on a concrete type. - -### Example -``` -42u64.clone(); -``` \ No newline at end of file diff --git a/src/docs/clone_on_ref_ptr.txt b/src/docs/clone_on_ref_ptr.txt deleted file mode 100644 index 2d83f8fefc12..000000000000 --- a/src/docs/clone_on_ref_ptr.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usage of `.clone()` on a ref-counted pointer, -(`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified -function syntax instead (e.g., `Rc::clone(foo)`). - -### Why is this bad? -Calling '.clone()' on an Rc, Arc, or Weak -can obscure the fact that only the pointer is being cloned, not the underlying -data. - -### Example -``` -let x = Rc::new(1); - -x.clone(); -``` - -Use instead: -``` -Rc::clone(&x); -``` \ No newline at end of file diff --git a/src/docs/cloned_instead_of_copied.txt b/src/docs/cloned_instead_of_copied.txt deleted file mode 100644 index 2f2014d5fd29..000000000000 --- a/src/docs/cloned_instead_of_copied.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usages of `cloned()` on an `Iterator` or `Option` where -`copied()` could be used instead. - -### Why is this bad? -`copied()` is better because it guarantees that the type being cloned -implements `Copy`. - -### Example -``` -[1, 2, 3].iter().cloned(); -``` -Use instead: -``` -[1, 2, 3].iter().copied(); -``` \ No newline at end of file diff --git a/src/docs/cmp_nan.txt b/src/docs/cmp_nan.txt deleted file mode 100644 index e2ad04d93235..000000000000 --- a/src/docs/cmp_nan.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for comparisons to NaN. - -### Why is this bad? -NaN does not compare meaningfully to anything – not -even itself – so those comparisons are simply wrong. - -### Example -``` -if x == f32::NAN { } -``` - -Use instead: -``` -if x.is_nan() { } -``` \ No newline at end of file diff --git a/src/docs/cmp_null.txt b/src/docs/cmp_null.txt deleted file mode 100644 index 02fd15124f03..000000000000 --- a/src/docs/cmp_null.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -This lint checks for equality comparisons with `ptr::null` - -### Why is this bad? -It's easier and more readable to use the inherent -`.is_null()` -method instead - -### Example -``` -use std::ptr; - -if x == ptr::null { - // .. -} -``` - -Use instead: -``` -if x.is_null() { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/cmp_owned.txt b/src/docs/cmp_owned.txt deleted file mode 100644 index f8d4956ff1d4..000000000000 --- a/src/docs/cmp_owned.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for conversions to owned values just for the sake -of a comparison. - -### Why is this bad? -The comparison can operate on a reference, so creating -an owned value effectively throws it away directly afterwards, which is -needlessly consuming code and heap space. - -### Example -``` -if x.to_owned() == y {} -``` - -Use instead: -``` -if x == y {} -``` \ No newline at end of file diff --git a/src/docs/cognitive_complexity.txt b/src/docs/cognitive_complexity.txt deleted file mode 100644 index fdd75f6479cd..000000000000 --- a/src/docs/cognitive_complexity.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for methods with high cognitive complexity. - -### Why is this bad? -Methods of high cognitive complexity tend to be hard to -both read and maintain. Also LLVM will tend to optimize small methods better. - -### Known problems -Sometimes it's hard to find a way to reduce the -complexity. - -### Example -You'll see it when you get the warning. \ No newline at end of file diff --git a/src/docs/collapsible_else_if.txt b/src/docs/collapsible_else_if.txt deleted file mode 100644 index 4ddfca17731f..000000000000 --- a/src/docs/collapsible_else_if.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for collapsible `else { if ... }` expressions -that can be collapsed to `else if ...`. - -### Why is this bad? -Each `if`-statement adds one level of nesting, which -makes code look more complex than it really is. - -### Example -``` - -if x { - … -} else { - if y { - … - } -} -``` - -Should be written: - -``` -if x { - … -} else if y { - … -} -``` \ No newline at end of file diff --git a/src/docs/collapsible_if.txt b/src/docs/collapsible_if.txt deleted file mode 100644 index e1264ee062e9..000000000000 --- a/src/docs/collapsible_if.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for nested `if` statements which can be collapsed -by `&&`-combining their conditions. - -### Why is this bad? -Each `if`-statement adds one level of nesting, which -makes code look more complex than it really is. - -### Example -``` -if x { - if y { - // … - } -} -``` - -Use instead: -``` -if x && y { - // … -} -``` \ No newline at end of file diff --git a/src/docs/collapsible_match.txt b/src/docs/collapsible_match.txt deleted file mode 100644 index 0d59594a03a2..000000000000 --- a/src/docs/collapsible_match.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together -without adding any branches. - -Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only -cases where merging would most likely make the code more readable. - -### Why is this bad? -It is unnecessarily verbose and complex. - -### Example -``` -fn func(opt: Option>) { - let n = match opt { - Some(n) => match n { - Ok(n) => n, - _ => return, - } - None => return, - }; -} -``` -Use instead: -``` -fn func(opt: Option>) { - let n = match opt { - Some(Ok(n)) => n, - _ => return, - }; -} -``` \ No newline at end of file diff --git a/src/docs/collapsible_str_replace.txt b/src/docs/collapsible_str_replace.txt deleted file mode 100644 index c24c25a3028a..000000000000 --- a/src/docs/collapsible_str_replace.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for consecutive calls to `str::replace` (2 or more) -that can be collapsed into a single call. - -### Why is this bad? -Consecutive `str::replace` calls scan the string multiple times -with repetitive code. - -### Example -``` -let hello = "hesuo worpd" - .replace('s', "l") - .replace("u", "l") - .replace('p', "l"); -``` -Use instead: -``` -let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); -``` \ No newline at end of file diff --git a/src/docs/comparison_chain.txt b/src/docs/comparison_chain.txt deleted file mode 100644 index 43b09f31ff4a..000000000000 --- a/src/docs/comparison_chain.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks comparison chains written with `if` that can be -rewritten with `match` and `cmp`. - -### Why is this bad? -`if` is not guaranteed to be exhaustive and conditionals can get -repetitive - -### Known problems -The match statement may be slower due to the compiler -not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354) - -### Example -``` -fn f(x: u8, y: u8) { - if x > y { - a() - } else if x < y { - b() - } else { - c() - } -} -``` - -Use instead: -``` -use std::cmp::Ordering; -fn f(x: u8, y: u8) { - match x.cmp(&y) { - Ordering::Greater => a(), - Ordering::Less => b(), - Ordering::Equal => c() - } -} -``` \ No newline at end of file diff --git a/src/docs/comparison_to_empty.txt b/src/docs/comparison_to_empty.txt deleted file mode 100644 index db6f74fe2706..000000000000 --- a/src/docs/comparison_to_empty.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for comparing to an empty slice such as `""` or `[]`, -and suggests using `.is_empty()` where applicable. - -### Why is this bad? -Some structures can answer `.is_empty()` much faster -than checking for equality. So it is good to get into the habit of using -`.is_empty()`, and having it is cheap. -Besides, it makes the intent clearer than a manual comparison in some contexts. - -### Example - -``` -if s == "" { - .. -} - -if arr == [] { - .. -} -``` -Use instead: -``` -if s.is_empty() { - .. -} - -if arr.is_empty() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/copy_iterator.txt b/src/docs/copy_iterator.txt deleted file mode 100644 index 5f9a2a015b86..000000000000 --- a/src/docs/copy_iterator.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for types that implement `Copy` as well as -`Iterator`. - -### Why is this bad? -Implicit copies can be confusing when working with -iterator combinators. - -### Example -``` -#[derive(Copy, Clone)] -struct Countdown(u8); - -impl Iterator for Countdown { - // ... -} - -let a: Vec<_> = my_iterator.take(1).collect(); -let b: Vec<_> = my_iterator.collect(); -``` \ No newline at end of file diff --git a/src/docs/crate_in_macro_def.txt b/src/docs/crate_in_macro_def.txt deleted file mode 100644 index 047e986dee71..000000000000 --- a/src/docs/crate_in_macro_def.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for use of `crate` as opposed to `$crate` in a macro definition. - -### Why is this bad? -`crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's -crate. Rarely is the former intended. See: -https://doc.rust-lang.org/reference/macros-by-example.html#hygiene - -### Example -``` -#[macro_export] -macro_rules! print_message { - () => { - println!("{}", crate::MESSAGE); - }; -} -pub const MESSAGE: &str = "Hello!"; -``` -Use instead: -``` -#[macro_export] -macro_rules! print_message { - () => { - println!("{}", $crate::MESSAGE); - }; -} -pub const MESSAGE: &str = "Hello!"; -``` - -Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the -macro definition, e.g.: -``` -#[allow(clippy::crate_in_macro_def)] -macro_rules! ok { ... crate::foo ... } -``` \ No newline at end of file diff --git a/src/docs/create_dir.txt b/src/docs/create_dir.txt deleted file mode 100644 index e4e7937684e6..000000000000 --- a/src/docs/create_dir.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. - -### Why is this bad? -Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. - -### Example -``` -std::fs::create_dir("foo"); -``` - -Use instead: -``` -std::fs::create_dir_all("foo"); -``` \ No newline at end of file diff --git a/src/docs/crosspointer_transmute.txt b/src/docs/crosspointer_transmute.txt deleted file mode 100644 index 49dea154970e..000000000000 --- a/src/docs/crosspointer_transmute.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for transmutes between a type `T` and `*T`. - -### Why is this bad? -It's easy to mistakenly transmute between a type and a -pointer to that type. - -### Example -``` -core::intrinsics::transmute(t) // where the result type is the same as - // `*t` or `&t`'s -``` \ No newline at end of file diff --git a/src/docs/dbg_macro.txt b/src/docs/dbg_macro.txt deleted file mode 100644 index 3e1a9a043f9f..000000000000 --- a/src/docs/dbg_macro.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of dbg!() macro. - -### Why is this bad? -`dbg!` macro is intended as a debugging tool. It -should not be in version control. - -### Example -``` -dbg!(true) -``` - -Use instead: -``` -true -``` \ No newline at end of file diff --git a/src/docs/debug_assert_with_mut_call.txt b/src/docs/debug_assert_with_mut_call.txt deleted file mode 100644 index 2c44abe1f05c..000000000000 --- a/src/docs/debug_assert_with_mut_call.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for function/method calls with a mutable -parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros. - -### Why is this bad? -In release builds `debug_assert!` macros are optimized out by the -compiler. -Therefore mutating something in a `debug_assert!` macro results in different behavior -between a release and debug build. - -### Example -``` -debug_assert_eq!(vec![3].pop(), Some(3)); - -// or - -debug_assert!(takes_a_mut_parameter(&mut x)); -``` \ No newline at end of file diff --git a/src/docs/decimal_literal_representation.txt b/src/docs/decimal_literal_representation.txt deleted file mode 100644 index daca9bbb3a84..000000000000 --- a/src/docs/decimal_literal_representation.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Warns if there is a better representation for a numeric literal. - -### Why is this bad? -Especially for big powers of 2 a hexadecimal representation is more -readable than a decimal representation. - -### Example -``` -`255` => `0xFF` -`65_535` => `0xFFFF` -`4_042_322_160` => `0xF0F0_F0F0` -``` \ No newline at end of file diff --git a/src/docs/declare_interior_mutable_const.txt b/src/docs/declare_interior_mutable_const.txt deleted file mode 100644 index 2801b5ccff80..000000000000 --- a/src/docs/declare_interior_mutable_const.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -Checks for declaration of `const` items which is interior -mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). - -### Why is this bad? -Consts are copied everywhere they are referenced, i.e., -every time you refer to the const a fresh instance of the `Cell` or `Mutex` -or `AtomicXxxx` will be created, which defeats the whole purpose of using -these types in the first place. - -The `const` should better be replaced by a `static` item if a global -variable is wanted, or replaced by a `const fn` if a constructor is wanted. - -### Known problems -A "non-constant" const item is a legacy way to supply an -initialized value to downstream `static` items (e.g., the -`std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, -and this lint should be suppressed. - -Even though the lint avoids triggering on a constant whose type has enums that have variants -with interior mutability, and its value uses non interior mutable variants (see -[#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and -[#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); -it complains about associated constants without default values only based on its types; -which might not be preferable. -There're other enums plus associated constants cases that the lint cannot handle. - -Types that have underlying or potential interior mutability trigger the lint whether -the interior mutable field is used or not. See issues -[#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - -### Example -``` -use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; - -const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); -CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged -assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct -``` - -Use instead: -``` -static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15); -STATIC_ATOM.store(9, SeqCst); -assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance -``` \ No newline at end of file diff --git a/src/docs/default_instead_of_iter_empty.txt b/src/docs/default_instead_of_iter_empty.txt deleted file mode 100644 index b63ef3d18fc4..000000000000 --- a/src/docs/default_instead_of_iter_empty.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -It checks for `std::iter::Empty::default()` and suggests replacing it with -`std::iter::empty()`. -### Why is this bad? -`std::iter::empty()` is the more idiomatic way. -### Example -``` -let _ = std::iter::Empty::::default(); -let iter: std::iter::Empty = std::iter::Empty::default(); -``` -Use instead: -``` -let _ = std::iter::empty::(); -let iter: std::iter::Empty = std::iter::empty(); -``` \ No newline at end of file diff --git a/src/docs/default_numeric_fallback.txt b/src/docs/default_numeric_fallback.txt deleted file mode 100644 index 15076a0a68bf..000000000000 --- a/src/docs/default_numeric_fallback.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type -inference. - -Default numeric fallback means that if numeric types have not yet been bound to concrete -types at the end of type inference, then integer type is bound to `i32`, and similarly -floating type is bound to `f64`. - -See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback. - -### Why is this bad? -For those who are very careful about types, default numeric fallback -can be a pitfall that cause unexpected runtime behavior. - -### Known problems -This lint can only be allowed at the function level or above. - -### Example -``` -let i = 10; -let f = 1.23; -``` - -Use instead: -``` -let i = 10i32; -let f = 1.23f64; -``` \ No newline at end of file diff --git a/src/docs/default_trait_access.txt b/src/docs/default_trait_access.txt deleted file mode 100644 index e69298969c8e..000000000000 --- a/src/docs/default_trait_access.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for literal calls to `Default::default()`. - -### Why is this bad? -It's easier for the reader if the name of the type is used, rather than the -generic `Default`. - -### Example -``` -let s: String = Default::default(); -``` - -Use instead: -``` -let s = String::default(); -``` \ No newline at end of file diff --git a/src/docs/default_union_representation.txt b/src/docs/default_union_representation.txt deleted file mode 100644 index f79ff9760e57..000000000000 --- a/src/docs/default_union_representation.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute). - -### Why is this bad? -Unions in Rust have unspecified layout by default, despite many people thinking that they -lay out each field at the start of the union (like C does). That is, there are no guarantees -about the offset of the fields for unions with multiple non-ZST fields without an explicitly -specified layout. These cases may lead to undefined behavior in unsafe blocks. - -### Example -``` -union Foo { - a: i32, - b: u32, -} - -fn main() { - let _x: u32 = unsafe { - Foo { a: 0_i32 }.b // Undefined behavior: `b` is allowed to be padding - }; -} -``` -Use instead: -``` -#[repr(C)] -union Foo { - a: i32, - b: u32, -} - -fn main() { - let _x: u32 = unsafe { - Foo { a: 0_i32 }.b // Now defined behavior, this is just an i32 -> u32 transmute - }; -} -``` \ No newline at end of file diff --git a/src/docs/deprecated_cfg_attr.txt b/src/docs/deprecated_cfg_attr.txt deleted file mode 100644 index 9f264887a057..000000000000 --- a/src/docs/deprecated_cfg_attr.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it -with `#[rustfmt::skip]`. - -### Why is this bad? -Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) -are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes. - -### Known problems -This lint doesn't detect crate level inner attributes, because they get -processed before the PreExpansionPass lints get executed. See -[#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) - -### Example -``` -#[cfg_attr(rustfmt, rustfmt_skip)] -fn main() { } -``` - -Use instead: -``` -#[rustfmt::skip] -fn main() { } -``` \ No newline at end of file diff --git a/src/docs/deprecated_semver.txt b/src/docs/deprecated_semver.txt deleted file mode 100644 index c9574a99b2be..000000000000 --- a/src/docs/deprecated_semver.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for `#[deprecated]` annotations with a `since` -field that is not a valid semantic version. - -### Why is this bad? -For checking the version of the deprecation, it must be -a valid semver. Failing that, the contained information is useless. - -### Example -``` -#[deprecated(since = "forever")] -fn something_else() { /* ... */ } -``` \ No newline at end of file diff --git a/src/docs/deref_addrof.txt b/src/docs/deref_addrof.txt deleted file mode 100644 index fa711b924d48..000000000000 --- a/src/docs/deref_addrof.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `*&` and `*&mut` in expressions. - -### Why is this bad? -Immediately dereferencing a reference is no-op and -makes the code less clear. - -### Known problems -Multiple dereference/addrof pairs are not handled so -the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect. - -### Example -``` -let a = f(*&mut b); -let c = *&d; -``` - -Use instead: -``` -let a = f(b); -let c = d; -``` \ No newline at end of file diff --git a/src/docs/deref_by_slicing.txt b/src/docs/deref_by_slicing.txt deleted file mode 100644 index 4dad24ac00ca..000000000000 --- a/src/docs/deref_by_slicing.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for slicing expressions which are equivalent to dereferencing the -value. - -### Why is this bad? -Some people may prefer to dereference rather than slice. - -### Example -``` -let vec = vec![1, 2, 3]; -let slice = &vec[..]; -``` -Use instead: -``` -let vec = vec![1, 2, 3]; -let slice = &*vec; -``` \ No newline at end of file diff --git a/src/docs/derivable_impls.txt b/src/docs/derivable_impls.txt deleted file mode 100644 index 5cee43956cc3..000000000000 --- a/src/docs/derivable_impls.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Detects manual `std::default::Default` implementations that are identical to a derived implementation. - -### Why is this bad? -It is less concise. - -### Example -``` -struct Foo { - bar: bool -} - -impl Default for Foo { - fn default() -> Self { - Self { - bar: false - } - } -} -``` - -Use instead: -``` -#[derive(Default)] -struct Foo { - bar: bool -} -``` - -### Known problems -Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925) -in generic types and the user defined `impl` may be more generalized or -specialized than what derive will produce. This lint can't detect the manual `impl` -has exactly equal bounds, and therefore this lint is disabled for types with -generic parameters. \ No newline at end of file diff --git a/src/docs/derive_hash_xor_eq.txt b/src/docs/derive_hash_xor_eq.txt deleted file mode 100644 index fbf623d5adbc..000000000000 --- a/src/docs/derive_hash_xor_eq.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for deriving `Hash` but implementing `PartialEq` -explicitly or vice versa. - -### Why is this bad? -The implementation of these traits must agree (for -example for use with `HashMap`) so it’s probably a bad idea to use a -default-generated `Hash` implementation with an explicitly defined -`PartialEq`. In particular, the following must hold for any type: - -``` -k1 == k2 ⇒ hash(k1) == hash(k2) -``` - -### Example -``` -#[derive(Hash)] -struct Foo; - -impl PartialEq for Foo { - ... -} -``` \ No newline at end of file diff --git a/src/docs/derive_ord_xor_partial_ord.txt b/src/docs/derive_ord_xor_partial_ord.txt deleted file mode 100644 index f2107a5f69ee..000000000000 --- a/src/docs/derive_ord_xor_partial_ord.txt +++ /dev/null @@ -1,44 +0,0 @@ -### What it does -Checks for deriving `Ord` but implementing `PartialOrd` -explicitly or vice versa. - -### Why is this bad? -The implementation of these traits must agree (for -example for use with `sort`) so it’s probably a bad idea to use a -default-generated `Ord` implementation with an explicitly defined -`PartialOrd`. In particular, the following must hold for any type -implementing `Ord`: - -``` -k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() -``` - -### Example -``` -#[derive(Ord, PartialEq, Eq)] -struct Foo; - -impl PartialOrd for Foo { - ... -} -``` -Use instead: -``` -#[derive(PartialEq, Eq)] -struct Foo; - -impl PartialOrd for Foo { - fn partial_cmp(&self, other: &Foo) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Foo { - ... -} -``` -or, if you don't need a custom ordering: -``` -#[derive(Ord, PartialOrd, PartialEq, Eq)] -struct Foo; -``` \ No newline at end of file diff --git a/src/docs/derive_partial_eq_without_eq.txt b/src/docs/derive_partial_eq_without_eq.txt deleted file mode 100644 index 932fabad666c..000000000000 --- a/src/docs/derive_partial_eq_without_eq.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for types that derive `PartialEq` and could implement `Eq`. - -### Why is this bad? -If a type `T` derives `PartialEq` and all of its members implement `Eq`, -then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used -in APIs that require `Eq` types. It also allows structs containing `T` to derive -`Eq` themselves. - -### Example -``` -#[derive(PartialEq)] -struct Foo { - i_am_eq: i32, - i_am_eq_too: Vec, -} -``` -Use instead: -``` -#[derive(PartialEq, Eq)] -struct Foo { - i_am_eq: i32, - i_am_eq_too: Vec, -} -``` \ No newline at end of file diff --git a/src/docs/disallowed_macros.txt b/src/docs/disallowed_macros.txt deleted file mode 100644 index 96fa15afabfd..000000000000 --- a/src/docs/disallowed_macros.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Denies the configured macros in clippy.toml - -Note: Even though this lint is warn-by-default, it will only trigger if -macros are defined in the clippy.toml file. - -### Why is this bad? -Some macros are undesirable in certain contexts, and it's beneficial to -lint for them as needed. - -### Example -An example clippy.toml configuration: -``` -disallowed-macros = [ - # Can use a string as the path of the disallowed macro. - "std::print", - # Can also use an inline table with a `path` key. - { path = "std::println" }, - # When using an inline table, can add a `reason` for why the macro - # is disallowed. - { path = "serde::Serialize", reason = "no serializing" }, -] -``` -``` -use serde::Serialize; - -// Example code where clippy issues a warning -println!("warns"); - -// The diagnostic will contain the message "no serializing" -#[derive(Serialize)] -struct Data { - name: String, - value: usize, -} -``` \ No newline at end of file diff --git a/src/docs/disallowed_methods.txt b/src/docs/disallowed_methods.txt deleted file mode 100644 index d8ad5b6a6674..000000000000 --- a/src/docs/disallowed_methods.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Denies the configured methods and functions in clippy.toml - -Note: Even though this lint is warn-by-default, it will only trigger if -methods are defined in the clippy.toml file. - -### Why is this bad? -Some methods are undesirable in certain contexts, and it's beneficial to -lint for them as needed. - -### Example -An example clippy.toml configuration: -``` -disallowed-methods = [ - # Can use a string as the path of the disallowed method. - "std::boxed::Box::new", - # Can also use an inline table with a `path` key. - { path = "std::time::Instant::now" }, - # When using an inline table, can add a `reason` for why the method - # is disallowed. - { path = "std::vec::Vec::leak", reason = "no leaking memory" }, -] -``` - -``` -// Example code where clippy issues a warning -let xs = vec![1, 2, 3, 4]; -xs.leak(); // Vec::leak is disallowed in the config. -// The diagnostic contains the message "no leaking memory". - -let _now = Instant::now(); // Instant::now is disallowed in the config. - -let _box = Box::new(3); // Box::new is disallowed in the config. -``` - -Use instead: -``` -// Example code which does not raise clippy warning -let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. -xs.push(123); // Vec::push is _not_ disallowed in the config. -``` \ No newline at end of file diff --git a/src/docs/disallowed_names.txt b/src/docs/disallowed_names.txt deleted file mode 100644 index f4aaee9c77b7..000000000000 --- a/src/docs/disallowed_names.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for usage of disallowed names for variables, such -as `foo`. - -### Why is this bad? -These names are usually placeholder names and should be -avoided. - -### Example -``` -let foo = 3.14; -``` \ No newline at end of file diff --git a/src/docs/disallowed_script_idents.txt b/src/docs/disallowed_script_idents.txt deleted file mode 100644 index 2151b7a20ded..000000000000 --- a/src/docs/disallowed_script_idents.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for usage of unicode scripts other than those explicitly allowed -by the lint config. - -This lint doesn't take into account non-text scripts such as `Unknown` and `Linear_A`. -It also ignores the `Common` script type. -While configuring, be sure to use official script name [aliases] from -[the list of supported scripts][supported_scripts]. - -See also: [`non_ascii_idents`]. - -[aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases -[supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html - -### Why is this bad? -It may be not desired to have many different scripts for -identifiers in the codebase. - -Note that if you only want to allow plain English, you might want to use -built-in [`non_ascii_idents`] lint instead. - -[`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents - -### Example -``` -// Assuming that `clippy.toml` contains the following line: -// allowed-locales = ["Latin", "Cyrillic"] -let counter = 10; // OK, latin is allowed. -let счётчик = 10; // OK, cyrillic is allowed. -let zähler = 10; // OK, it's still latin. -let カウンタ = 10; // Will spawn the lint. -``` \ No newline at end of file diff --git a/src/docs/disallowed_types.txt b/src/docs/disallowed_types.txt deleted file mode 100644 index 2bcbcddee566..000000000000 --- a/src/docs/disallowed_types.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Denies the configured types in clippy.toml. - -Note: Even though this lint is warn-by-default, it will only trigger if -types are defined in the clippy.toml file. - -### Why is this bad? -Some types are undesirable in certain contexts. - -### Example: -An example clippy.toml configuration: -``` -disallowed-types = [ - # Can use a string as the path of the disallowed type. - "std::collections::BTreeMap", - # Can also use an inline table with a `path` key. - { path = "std::net::TcpListener" }, - # When using an inline table, can add a `reason` for why the type - # is disallowed. - { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" }, -] -``` - -``` -use std::collections::BTreeMap; -// or its use -let x = std::collections::BTreeMap::new(); -``` -Use instead: -``` -// A similar type that is allowed by the config -use std::collections::HashMap; -``` \ No newline at end of file diff --git a/src/docs/diverging_sub_expression.txt b/src/docs/diverging_sub_expression.txt deleted file mode 100644 index 194362218025..000000000000 --- a/src/docs/diverging_sub_expression.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for diverging calls that are not match arms or -statements. - -### Why is this bad? -It is often confusing to read. In addition, the -sub-expression evaluation order for Rust is not well documented. - -### Known problems -Someone might want to use `some_bool || panic!()` as a -shorthand. - -### Example -``` -let a = b() || panic!() || c(); -// `c()` is dead, `panic!()` is only called if `b()` returns `false` -let x = (a, b, c, panic!()); -// can simply be replaced by `panic!()` -``` \ No newline at end of file diff --git a/src/docs/doc_link_with_quotes.txt b/src/docs/doc_link_with_quotes.txt deleted file mode 100644 index 107c8ac116d9..000000000000 --- a/src/docs/doc_link_with_quotes.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) -outside of code blocks -### Why is this bad? -It is likely a typo when defining an intra-doc link - -### Example -``` -/// See also: ['foo'] -fn bar() {} -``` -Use instead: -``` -/// See also: [`foo`] -fn bar() {} -``` \ No newline at end of file diff --git a/src/docs/doc_markdown.txt b/src/docs/doc_markdown.txt deleted file mode 100644 index 94f54c587e30..000000000000 --- a/src/docs/doc_markdown.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for the presence of `_`, `::` or camel-case words -outside ticks in documentation. - -### Why is this bad? -*Rustdoc* supports markdown formatting, `_`, `::` and -camel-case probably indicates some code which should be included between -ticks. `_` can also be used for emphasis in markdown, this lint tries to -consider that. - -### Known problems -Lots of bad docs won’t be fixed, what the lint checks -for is limited, and there are still false positives. HTML elements and their -content are not linted. - -In addition, when writing documentation comments, including `[]` brackets -inside a link text would trip the parser. Therefore, documenting link with -`[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec -would fail. - -### Examples -``` -/// Do something with the foo_bar parameter. See also -/// that::other::module::foo. -// ^ `foo_bar` and `that::other::module::foo` should be ticked. -fn doit(foo_bar: usize) {} -``` - -``` -// Link text with `[]` brackets should be written as following: -/// Consume the array and return the inner -/// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec]. -/// [SmallVec]: SmallVec -fn main() {} -``` \ No newline at end of file diff --git a/src/docs/double_comparisons.txt b/src/docs/double_comparisons.txt deleted file mode 100644 index 7dc6818779f4..000000000000 --- a/src/docs/double_comparisons.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for double comparisons that could be simplified to a single expression. - - -### Why is this bad? -Readability. - -### Example -``` -if x == y || x < y {} -``` - -Use instead: - -``` -if x <= y {} -``` \ No newline at end of file diff --git a/src/docs/double_must_use.txt b/src/docs/double_must_use.txt deleted file mode 100644 index 0017d10d40d3..000000000000 --- a/src/docs/double_must_use.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for a `#[must_use]` attribute without -further information on functions and methods that return a type already -marked as `#[must_use]`. - -### Why is this bad? -The attribute isn't needed. Not using the result -will already be reported. Alternatively, one can add some text to the -attribute to improve the lint message. - -### Examples -``` -#[must_use] -fn double_must_use() -> Result<(), ()> { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/double_neg.txt b/src/docs/double_neg.txt deleted file mode 100644 index a07f67496d7c..000000000000 --- a/src/docs/double_neg.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Detects expressions of the form `--x`. - -### Why is this bad? -It can mislead C/C++ programmers to think `x` was -decremented. - -### Example -``` -let mut x = 3; ---x; -``` \ No newline at end of file diff --git a/src/docs/double_parens.txt b/src/docs/double_parens.txt deleted file mode 100644 index 260d7dd575e5..000000000000 --- a/src/docs/double_parens.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for unnecessary double parentheses. - -### Why is this bad? -This makes code harder to read and might indicate a -mistake. - -### Example -``` -fn simple_double_parens() -> i32 { - ((0)) -} - -foo((0)); -``` - -Use instead: -``` -fn simple_no_parens() -> i32 { - 0 -} - -foo(0); -``` \ No newline at end of file diff --git a/src/docs/drop_copy.txt b/src/docs/drop_copy.txt deleted file mode 100644 index f917ca8ed21a..000000000000 --- a/src/docs/drop_copy.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for calls to `std::mem::drop` with a value -that derives the Copy trait - -### Why is this bad? -Calling `std::mem::drop` [does nothing for types that -implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the -value will be copied and moved into the function on invocation. - -### Example -``` -let x: i32 = 42; // i32 implements Copy -std::mem::drop(x) // A copy of x is passed to the function, leaving the - // original unaffected -``` \ No newline at end of file diff --git a/src/docs/drop_non_drop.txt b/src/docs/drop_non_drop.txt deleted file mode 100644 index ee1e3a6c216e..000000000000 --- a/src/docs/drop_non_drop.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calls to `std::mem::drop` with a value that does not implement `Drop`. - -### Why is this bad? -Calling `std::mem::drop` is no different than dropping such a type. A different value may -have been intended. - -### Example -``` -struct Foo; -let x = Foo; -std::mem::drop(x); -``` \ No newline at end of file diff --git a/src/docs/drop_ref.txt b/src/docs/drop_ref.txt deleted file mode 100644 index c4f7adf0cfa3..000000000000 --- a/src/docs/drop_ref.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for calls to `std::mem::drop` with a reference -instead of an owned value. - -### Why is this bad? -Calling `drop` on a reference will only drop the -reference itself, which is a no-op. It will not call the `drop` method (from -the `Drop` trait implementation) on the underlying referenced value, which -is likely what was intended. - -### Example -``` -let mut lock_guard = mutex.lock(); -std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex -// still locked -operation_that_requires_mutex_to_be_unlocked(); -``` \ No newline at end of file diff --git a/src/docs/duplicate_mod.txt b/src/docs/duplicate_mod.txt deleted file mode 100644 index 709a9aba03ad..000000000000 --- a/src/docs/duplicate_mod.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for files that are included as modules multiple times. - -### Why is this bad? -Loading a file as a module more than once causes it to be compiled -multiple times, taking longer and putting duplicate content into the -module tree. - -### Example -``` -// lib.rs -mod a; -mod b; -``` -``` -// a.rs -#[path = "./b.rs"] -mod b; -``` - -Use instead: - -``` -// lib.rs -mod a; -mod b; -``` -``` -// a.rs -use crate::b; -``` \ No newline at end of file diff --git a/src/docs/duplicate_underscore_argument.txt b/src/docs/duplicate_underscore_argument.txt deleted file mode 100644 index a8fcd6a9fbe6..000000000000 --- a/src/docs/duplicate_underscore_argument.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for function arguments having the similar names -differing by an underscore. - -### Why is this bad? -It affects code readability. - -### Example -``` -fn foo(a: i32, _a: i32) {} -``` - -Use instead: -``` -fn bar(a: i32, _b: i32) {} -``` \ No newline at end of file diff --git a/src/docs/duration_subsec.txt b/src/docs/duration_subsec.txt deleted file mode 100644 index e7e0ca88745e..000000000000 --- a/src/docs/duration_subsec.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for calculation of subsecond microseconds or milliseconds -from other `Duration` methods. - -### Why is this bad? -It's more concise to call `Duration::subsec_micros()` or -`Duration::subsec_millis()` than to calculate them. - -### Example -``` -let micros = duration.subsec_nanos() / 1_000; -let millis = duration.subsec_nanos() / 1_000_000; -``` - -Use instead: -``` -let micros = duration.subsec_micros(); -let millis = duration.subsec_millis(); -``` \ No newline at end of file diff --git a/src/docs/else_if_without_else.txt b/src/docs/else_if_without_else.txt deleted file mode 100644 index 33f5d0f91859..000000000000 --- a/src/docs/else_if_without_else.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for usage of if expressions with an `else if` branch, -but without a final `else` branch. - -### Why is this bad? -Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10). - -### Example -``` -if x.is_positive() { - a(); -} else if x.is_negative() { - b(); -} -``` - -Use instead: - -``` -if x.is_positive() { - a(); -} else if x.is_negative() { - b(); -} else { - // We don't care about zero. -} -``` \ No newline at end of file diff --git a/src/docs/empty_drop.txt b/src/docs/empty_drop.txt deleted file mode 100644 index d0c0c24a9c88..000000000000 --- a/src/docs/empty_drop.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for empty `Drop` implementations. - -### Why is this bad? -Empty `Drop` implementations have no effect when dropping an instance of the type. They are -most likely useless. However, an empty `Drop` implementation prevents a type from being -destructured, which might be the intention behind adding the implementation as a marker. - -### Example -``` -struct S; - -impl Drop for S { - fn drop(&mut self) {} -} -``` -Use instead: -``` -struct S; -``` \ No newline at end of file diff --git a/src/docs/empty_enum.txt b/src/docs/empty_enum.txt deleted file mode 100644 index f7b41c41ee5a..000000000000 --- a/src/docs/empty_enum.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for `enum`s with no variants. - -As of this writing, the `never_type` is still a -nightly-only experimental API. Therefore, this lint is only triggered -if the `never_type` is enabled. - -### Why is this bad? -If you want to introduce a type which -can't be instantiated, you should use `!` (the primitive type "never"), -or a wrapper around it, because `!` has more extensive -compiler support (type inference, etc...) and wrappers -around it are the conventional way to define an uninhabited type. -For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html) - - -### Example -``` -enum Test {} -``` - -Use instead: -``` -#![feature(never_type)] - -struct Test(!); -``` \ No newline at end of file diff --git a/src/docs/empty_line_after_outer_attr.txt b/src/docs/empty_line_after_outer_attr.txt deleted file mode 100644 index c85242bbee0e..000000000000 --- a/src/docs/empty_line_after_outer_attr.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for empty lines after outer attributes - -### Why is this bad? -Most likely the attribute was meant to be an inner attribute using a '!'. -If it was meant to be an outer attribute, then the following item -should not be separated by empty lines. - -### Known problems -Can cause false positives. - -From the clippy side it's difficult to detect empty lines between an attributes and the -following item because empty lines and comments are not part of the AST. The parsing -currently works for basic cases but is not perfect. - -### Example -``` -#[allow(dead_code)] - -fn not_quite_good_code() { } -``` - -Use instead: -``` -// Good (as inner attribute) -#![allow(dead_code)] - -fn this_is_fine() { } - -// or - -// Good (as outer attribute) -#[allow(dead_code)] -fn this_is_fine_too() { } -``` \ No newline at end of file diff --git a/src/docs/empty_loop.txt b/src/docs/empty_loop.txt deleted file mode 100644 index fea49a74d04e..000000000000 --- a/src/docs/empty_loop.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for empty `loop` expressions. - -### Why is this bad? -These busy loops burn CPU cycles without doing -anything. It is _almost always_ a better idea to `panic!` than to have -a busy loop. - -If panicking isn't possible, think of the environment and either: - - block on something - - sleep the thread for some microseconds - - yield or pause the thread - -For `std` targets, this can be done with -[`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html) -or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html). - -For `no_std` targets, doing this is more complicated, especially because -`#[panic_handler]`s can't panic. To stop/pause the thread, you will -probably need to invoke some target-specific intrinsic. Examples include: - - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html) - - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html) - -### Example -``` -loop {} -``` \ No newline at end of file diff --git a/src/docs/empty_structs_with_brackets.txt b/src/docs/empty_structs_with_brackets.txt deleted file mode 100644 index ab5e35ae2ada..000000000000 --- a/src/docs/empty_structs_with_brackets.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Finds structs without fields (a so-called "empty struct") that are declared with brackets. - -### Why is this bad? -Empty brackets after a struct declaration can be omitted. - -### Example -``` -struct Cookie {} -``` -Use instead: -``` -struct Cookie; -``` \ No newline at end of file diff --git a/src/docs/enum_clike_unportable_variant.txt b/src/docs/enum_clike_unportable_variant.txt deleted file mode 100644 index d30a973a5a13..000000000000 --- a/src/docs/enum_clike_unportable_variant.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for C-like enumerations that are -`repr(isize/usize)` and have values that don't fit into an `i32`. - -### Why is this bad? -This will truncate the variant value on 32 bit -architectures, but works fine on 64 bit. - -### Example -``` -#[repr(usize)] -enum NonPortable { - X = 0x1_0000_0000, - Y = 0, -} -``` \ No newline at end of file diff --git a/src/docs/enum_glob_use.txt b/src/docs/enum_glob_use.txt deleted file mode 100644 index 3776822c35b0..000000000000 --- a/src/docs/enum_glob_use.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `use Enum::*`. - -### Why is this bad? -It is usually better style to use the prefixed name of -an enumeration variant, rather than importing variants. - -### Known problems -Old-style enumerations that prefix the variants are -still around. - -### Example -``` -use std::cmp::Ordering::*; - -foo(Less); -``` - -Use instead: -``` -use std::cmp::Ordering; - -foo(Ordering::Less) -``` \ No newline at end of file diff --git a/src/docs/enum_variant_names.txt b/src/docs/enum_variant_names.txt deleted file mode 100644 index e726925edda8..000000000000 --- a/src/docs/enum_variant_names.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Detects enumeration variants that are prefixed or suffixed -by the same characters. - -### Why is this bad? -Enumeration variant names should specify their variant, -not repeat the enumeration name. - -### Limitations -Characters with no casing will be considered when comparing prefixes/suffixes -This applies to numbers and non-ascii characters without casing -e.g. `Foo1` and `Foo2` is considered to have different prefixes -(the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹` - -### Example -``` -enum Cake { - BlackForestCake, - HummingbirdCake, - BattenbergCake, -} -``` -Use instead: -``` -enum Cake { - BlackForest, - Hummingbird, - Battenberg, -} -``` \ No newline at end of file diff --git a/src/docs/eq_op.txt b/src/docs/eq_op.txt deleted file mode 100644 index 2d75a0ec546e..000000000000 --- a/src/docs/eq_op.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for equal operands to comparison, logical and -bitwise, difference and division binary operators (`==`, `>`, etc., `&&`, -`||`, `&`, `|`, `^`, `-` and `/`). - -### Why is this bad? -This is usually just a typo or a copy and paste error. - -### Known problems -False negatives: We had some false positives regarding -calls (notably [racer](https://github.com/phildawes/racer) had one instance -of `x.pop() && x.pop()`), so we removed matching any function or method -calls. We may introduce a list of known pure functions in the future. - -### Example -``` -if x + 1 == x + 1 {} - -// or - -assert_eq!(a, a); -``` \ No newline at end of file diff --git a/src/docs/equatable_if_let.txt b/src/docs/equatable_if_let.txt deleted file mode 100644 index 9997046954c2..000000000000 --- a/src/docs/equatable_if_let.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for pattern matchings that can be expressed using equality. - -### Why is this bad? - -* It reads better and has less cognitive load because equality won't cause binding. -* It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely -criticized for increasing the cognitive load of reading the code. -* Equality is a simple bool expression and can be merged with `&&` and `||` and -reuse if blocks - -### Example -``` -if let Some(2) = x { - do_thing(); -} -``` -Use instead: -``` -if x == Some(2) { - do_thing(); -} -``` \ No newline at end of file diff --git a/src/docs/erasing_op.txt b/src/docs/erasing_op.txt deleted file mode 100644 index 3d285a6d86e4..000000000000 --- a/src/docs/erasing_op.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for erasing operations, e.g., `x * 0`. - -### Why is this bad? -The whole expression can be replaced by zero. -This is most likely not the intended outcome and should probably be -corrected - -### Example -``` -let x = 1; -0 / x; -0 * x; -x & 0; -``` \ No newline at end of file diff --git a/src/docs/err_expect.txt b/src/docs/err_expect.txt deleted file mode 100644 index 1dc83c5ce0ee..000000000000 --- a/src/docs/err_expect.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for `.err().expect()` calls on the `Result` type. - -### Why is this bad? -`.expect_err()` can be called directly to avoid the extra type conversion from `err()`. - -### Example -``` -let x: Result = Ok(10); -x.err().expect("Testing err().expect()"); -``` -Use instead: -``` -let x: Result = Ok(10); -x.expect_err("Testing expect_err"); -``` \ No newline at end of file diff --git a/src/docs/excessive_precision.txt b/src/docs/excessive_precision.txt deleted file mode 100644 index 517879c47152..000000000000 --- a/src/docs/excessive_precision.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for float literals with a precision greater -than that supported by the underlying type. - -### Why is this bad? -Rust will truncate the literal silently. - -### Example -``` -let v: f32 = 0.123_456_789_9; -println!("{}", v); // 0.123_456_789 -``` - -Use instead: -``` -let v: f64 = 0.123_456_789_9; -println!("{}", v); // 0.123_456_789_9 -``` \ No newline at end of file diff --git a/src/docs/exhaustive_enums.txt b/src/docs/exhaustive_enums.txt deleted file mode 100644 index d1032a7a29aa..000000000000 --- a/src/docs/exhaustive_enums.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` - -### Why is this bad? -Exhaustive enums are typically fine, but a project which does -not wish to make a stability commitment around exported enums may wish to -disable them by default. - -### Example -``` -enum Foo { - Bar, - Baz -} -``` -Use instead: -``` -#[non_exhaustive] -enum Foo { - Bar, - Baz -} -``` \ No newline at end of file diff --git a/src/docs/exhaustive_structs.txt b/src/docs/exhaustive_structs.txt deleted file mode 100644 index fd6e4f5caf1f..000000000000 --- a/src/docs/exhaustive_structs.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Warns on any exported `structs`s that are not tagged `#[non_exhaustive]` - -### Why is this bad? -Exhaustive structs are typically fine, but a project which does -not wish to make a stability commitment around exported structs may wish to -disable them by default. - -### Example -``` -struct Foo { - bar: u8, - baz: String, -} -``` -Use instead: -``` -#[non_exhaustive] -struct Foo { - bar: u8, - baz: String, -} -``` \ No newline at end of file diff --git a/src/docs/exit.txt b/src/docs/exit.txt deleted file mode 100644 index 1e6154d43e05..000000000000 --- a/src/docs/exit.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -`exit()` terminates the program and doesn't provide a -stack trace. - -### Why is this bad? -Ideally a program is terminated by finishing -the main function. - -### Example -``` -std::process::exit(0) -``` \ No newline at end of file diff --git a/src/docs/expect_fun_call.txt b/src/docs/expect_fun_call.txt deleted file mode 100644 index d82d9aa9baff..000000000000 --- a/src/docs/expect_fun_call.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, -etc., and suggests to use `unwrap_or_else` instead - -### Why is this bad? -The function will always be called. - -### Known problems -If the function has side-effects, not calling it will -change the semantics of the program, but you shouldn't rely on that anyway. - -### Example -``` -foo.expect(&format!("Err {}: {}", err_code, err_msg)); - -// or - -foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); -``` - -Use instead: -``` -foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg)); -``` \ No newline at end of file diff --git a/src/docs/expect_used.txt b/src/docs/expect_used.txt deleted file mode 100644 index 4a6981e334fd..000000000000 --- a/src/docs/expect_used.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s. - -### Why is this bad? -Usually it is better to handle the `None` or `Err` case. -Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why -this lint is `Allow` by default. - -`result.expect()` will let the thread panic on `Err` -values. Normally, you want to implement more sophisticated error handling, -and propagate errors upwards with `?` operator. - -### Examples -``` -option.expect("one"); -result.expect("one"); -``` - -Use instead: -``` -option?; - -// or - -result?; -``` \ No newline at end of file diff --git a/src/docs/expl_impl_clone_on_copy.txt b/src/docs/expl_impl_clone_on_copy.txt deleted file mode 100644 index 391d93b6713c..000000000000 --- a/src/docs/expl_impl_clone_on_copy.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for explicit `Clone` implementations for `Copy` -types. - -### Why is this bad? -To avoid surprising behavior, these traits should -agree and the behavior of `Copy` cannot be overridden. In almost all -situations a `Copy` type should have a `Clone` implementation that does -nothing more than copy the object, which is what `#[derive(Copy, Clone)]` -gets you. - -### Example -``` -#[derive(Copy)] -struct Foo; - -impl Clone for Foo { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/explicit_auto_deref.txt b/src/docs/explicit_auto_deref.txt deleted file mode 100644 index 65b256317725..000000000000 --- a/src/docs/explicit_auto_deref.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for dereferencing expressions which would be covered by auto-deref. - -### Why is this bad? -This unnecessarily complicates the code. - -### Example -``` -let x = String::new(); -let y: &str = &*x; -``` -Use instead: -``` -let x = String::new(); -let y: &str = &x; -``` \ No newline at end of file diff --git a/src/docs/explicit_counter_loop.txt b/src/docs/explicit_counter_loop.txt deleted file mode 100644 index 2661a43e1034..000000000000 --- a/src/docs/explicit_counter_loop.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks `for` loops over slices with an explicit counter -and suggests the use of `.enumerate()`. - -### Why is this bad? -Using `.enumerate()` makes the intent more clear, -declutters the code and may be faster in some instances. - -### Example -``` -let mut i = 0; -for item in &v { - bar(i, *item); - i += 1; -} -``` - -Use instead: -``` -for (i, item) in v.iter().enumerate() { bar(i, *item); } -``` \ No newline at end of file diff --git a/src/docs/explicit_deref_methods.txt b/src/docs/explicit_deref_methods.txt deleted file mode 100644 index e14e981c7073..000000000000 --- a/src/docs/explicit_deref_methods.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for explicit `deref()` or `deref_mut()` method calls. - -### Why is this bad? -Dereferencing by `&*x` or `&mut *x` is clearer and more concise, -when not part of a method chain. - -### Example -``` -use std::ops::Deref; -let a: &mut String = &mut String::from("foo"); -let b: &str = a.deref(); -``` - -Use instead: -``` -let a: &mut String = &mut String::from("foo"); -let b = &*a; -``` - -This lint excludes: -``` -let _ = d.unwrap().deref(); -``` \ No newline at end of file diff --git a/src/docs/explicit_into_iter_loop.txt b/src/docs/explicit_into_iter_loop.txt deleted file mode 100644 index 3931dfd69a31..000000000000 --- a/src/docs/explicit_into_iter_loop.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for loops on `y.into_iter()` where `y` will do, and -suggests the latter. - -### Why is this bad? -Readability. - -### Example -``` -// with `y` a `Vec` or slice: -for x in y.into_iter() { - // .. -} -``` -can be rewritten to -``` -for x in y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/explicit_iter_loop.txt b/src/docs/explicit_iter_loop.txt deleted file mode 100644 index cabe72e91d04..000000000000 --- a/src/docs/explicit_iter_loop.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for loops on `x.iter()` where `&x` will do, and -suggests the latter. - -### Why is this bad? -Readability. - -### Known problems -False negatives. We currently only warn on some known -types. - -### Example -``` -// with `y` a `Vec` or slice: -for x in y.iter() { - // .. -} -``` - -Use instead: -``` -for x in &y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/explicit_write.txt b/src/docs/explicit_write.txt deleted file mode 100644 index eafed5d39e5c..000000000000 --- a/src/docs/explicit_write.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usage of `write!()` / `writeln()!` which can be -replaced with `(e)print!()` / `(e)println!()` - -### Why is this bad? -Using `(e)println! is clearer and more concise - -### Example -``` -writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap(); -writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap(); -``` - -Use instead: -``` -eprintln!("foo: {:?}", bar); -println!("foo: {:?}", bar); -``` \ No newline at end of file diff --git a/src/docs/extend_with_drain.txt b/src/docs/extend_with_drain.txt deleted file mode 100644 index 2f31dcf5f740..000000000000 --- a/src/docs/extend_with_drain.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for occurrences where one vector gets extended instead of append - -### Why is this bad? -Using `append` instead of `extend` is more concise and faster - -### Example -``` -let mut a = vec![1, 2, 3]; -let mut b = vec![4, 5, 6]; - -a.extend(b.drain(..)); -``` - -Use instead: -``` -let mut a = vec![1, 2, 3]; -let mut b = vec![4, 5, 6]; - -a.append(&mut b); -``` \ No newline at end of file diff --git a/src/docs/extra_unused_lifetimes.txt b/src/docs/extra_unused_lifetimes.txt deleted file mode 100644 index bc1814aa4752..000000000000 --- a/src/docs/extra_unused_lifetimes.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for lifetimes in generics that are never used -anywhere else. - -### Why is this bad? -The additional lifetimes make the code look more -complicated, while there is nothing out of the ordinary going on. Removing -them leads to more readable code. - -### Example -``` -// unnecessary lifetimes -fn unused_lifetime<'a>(x: u8) { - // .. -} -``` - -Use instead: -``` -fn no_lifetime(x: u8) { - // ... -} -``` \ No newline at end of file diff --git a/src/docs/fallible_impl_from.txt b/src/docs/fallible_impl_from.txt deleted file mode 100644 index 588a5bb103d4..000000000000 --- a/src/docs/fallible_impl_from.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for impls of `From<..>` that contain `panic!()` or `unwrap()` - -### Why is this bad? -`TryFrom` should be used if there's a possibility of failure. - -### Example -``` -struct Foo(i32); - -impl From for Foo { - fn from(s: String) -> Self { - Foo(s.parse().unwrap()) - } -} -``` - -Use instead: -``` -struct Foo(i32); - -impl TryFrom for Foo { - type Error = (); - fn try_from(s: String) -> Result { - if let Ok(parsed) = s.parse() { - Ok(Foo(parsed)) - } else { - Err(()) - } - } -} -``` \ No newline at end of file diff --git a/src/docs/field_reassign_with_default.txt b/src/docs/field_reassign_with_default.txt deleted file mode 100644 index e58b7239fde9..000000000000 --- a/src/docs/field_reassign_with_default.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for immediate reassignment of fields initialized -with Default::default(). - -### Why is this bad? -It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax). - -### Known problems -Assignments to patterns that are of tuple type are not linted. - -### Example -``` -let mut a: A = Default::default(); -a.i = 42; -``` - -Use instead: -``` -let a = A { - i: 42, - .. Default::default() -}; -``` \ No newline at end of file diff --git a/src/docs/filetype_is_file.txt b/src/docs/filetype_is_file.txt deleted file mode 100644 index ad14bd62c4de..000000000000 --- a/src/docs/filetype_is_file.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `FileType::is_file()`. - -### Why is this bad? -When people testing a file type with `FileType::is_file` -they are testing whether a path is something they can get bytes from. But -`is_file` doesn't cover special file types in unix-like systems, and doesn't cover -symlink in windows. Using `!FileType::is_dir()` is a better way to that intention. - -### Example -``` -let metadata = std::fs::metadata("foo.txt")?; -let filetype = metadata.file_type(); - -if filetype.is_file() { - // read file -} -``` - -should be written as: - -``` -let metadata = std::fs::metadata("foo.txt")?; -let filetype = metadata.file_type(); - -if !filetype.is_dir() { - // read file -} -``` \ No newline at end of file diff --git a/src/docs/filter_map_identity.txt b/src/docs/filter_map_identity.txt deleted file mode 100644 index 83b666f2e278..000000000000 --- a/src/docs/filter_map_identity.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for usage of `filter_map(|x| x)`. - -### Why is this bad? -Readability, this can be written more concisely by using `flatten`. - -### Example -``` -iter.filter_map(|x| x); -``` -Use instead: -``` -iter.flatten(); -``` \ No newline at end of file diff --git a/src/docs/filter_map_next.txt b/src/docs/filter_map_next.txt deleted file mode 100644 index b38620b56a50..000000000000 --- a/src/docs/filter_map_next.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.filter_map(_).next()`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.find_map(_)`. - -### Example -``` - (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next(); -``` -Can be written as - -``` - (0..3).find_map(|x| if x == 2 { Some(x) } else { None }); -``` \ No newline at end of file diff --git a/src/docs/filter_next.txt b/src/docs/filter_next.txt deleted file mode 100644 index 898a74166dc1..000000000000 --- a/src/docs/filter_next.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.filter(_).next()`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.find(_)`. - -### Example -``` -vec.iter().filter(|x| **x == 0).next(); -``` - -Use instead: -``` -vec.iter().find(|x| **x == 0); -``` \ No newline at end of file diff --git a/src/docs/flat_map_identity.txt b/src/docs/flat_map_identity.txt deleted file mode 100644 index a5ee79b4982f..000000000000 --- a/src/docs/flat_map_identity.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for usage of `flat_map(|x| x)`. - -### Why is this bad? -Readability, this can be written more concisely by using `flatten`. - -### Example -``` -iter.flat_map(|x| x); -``` -Can be written as -``` -iter.flatten(); -``` \ No newline at end of file diff --git a/src/docs/flat_map_option.txt b/src/docs/flat_map_option.txt deleted file mode 100644 index d50b9156d365..000000000000 --- a/src/docs/flat_map_option.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usages of `Iterator::flat_map()` where `filter_map()` could be -used instead. - -### Why is this bad? -When applicable, `filter_map()` is more clear since it shows that -`Option` is used to produce 0 or 1 items. - -### Example -``` -let nums: Vec = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect(); -``` -Use instead: -``` -let nums: Vec = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect(); -``` \ No newline at end of file diff --git a/src/docs/float_arithmetic.txt b/src/docs/float_arithmetic.txt deleted file mode 100644 index 1f9bce5abd59..000000000000 --- a/src/docs/float_arithmetic.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for float arithmetic. - -### Why is this bad? -For some embedded systems or kernel development, it -can be useful to rule out floating-point numbers. - -### Example -``` -a + 1.0; -``` \ No newline at end of file diff --git a/src/docs/float_cmp.txt b/src/docs/float_cmp.txt deleted file mode 100644 index c19907c903e9..000000000000 --- a/src/docs/float_cmp.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for (in-)equality comparisons on floating-point -values (apart from zero), except in functions called `*eq*` (which probably -implement equality for a type involving floats). - -### Why is this bad? -Floating point calculations are usually imprecise, so -asking if two values are *exactly* equal is asking for trouble. For a good -guide on what to do, see [the floating point -guide](http://www.floating-point-gui.de/errors/comparison). - -### Example -``` -let x = 1.2331f64; -let y = 1.2332f64; - -if y == 1.23f64 { } -if y != x {} // where both are floats -``` - -Use instead: -``` -let error_margin = f64::EPSILON; // Use an epsilon for comparison -// Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. -// let error_margin = std::f64::EPSILON; -if (y - 1.23f64).abs() < error_margin { } -if (y - x).abs() > error_margin { } -``` \ No newline at end of file diff --git a/src/docs/float_cmp_const.txt b/src/docs/float_cmp_const.txt deleted file mode 100644 index 9208feaacd81..000000000000 --- a/src/docs/float_cmp_const.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for (in-)equality comparisons on floating-point -value and constant, except in functions called `*eq*` (which probably -implement equality for a type involving floats). - -### Why is this bad? -Floating point calculations are usually imprecise, so -asking if two values are *exactly* equal is asking for trouble. For a good -guide on what to do, see [the floating point -guide](http://www.floating-point-gui.de/errors/comparison). - -### Example -``` -let x: f64 = 1.0; -const ONE: f64 = 1.00; - -if x == ONE { } // where both are floats -``` - -Use instead: -``` -let error_margin = f64::EPSILON; // Use an epsilon for comparison -// Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. -// let error_margin = std::f64::EPSILON; -if (x - ONE).abs() < error_margin { } -``` \ No newline at end of file diff --git a/src/docs/float_equality_without_abs.txt b/src/docs/float_equality_without_abs.txt deleted file mode 100644 index 556b574e15d3..000000000000 --- a/src/docs/float_equality_without_abs.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for statements of the form `(a - b) < f32::EPSILON` or -`(a - b) < f64::EPSILON`. Notes the missing `.abs()`. - -### Why is this bad? -The code without `.abs()` is more likely to have a bug. - -### Known problems -If the user can ensure that b is larger than a, the `.abs()` is -technically unnecessary. However, it will make the code more robust and doesn't have any -large performance implications. If the abs call was deliberately left out for performance -reasons, it is probably better to state this explicitly in the code, which then can be done -with an allow. - -### Example -``` -pub fn is_roughly_equal(a: f32, b: f32) -> bool { - (a - b) < f32::EPSILON -} -``` -Use instead: -``` -pub fn is_roughly_equal(a: f32, b: f32) -> bool { - (a - b).abs() < f32::EPSILON -} -``` \ No newline at end of file diff --git a/src/docs/fn_address_comparisons.txt b/src/docs/fn_address_comparisons.txt deleted file mode 100644 index 7d2b7b681deb..000000000000 --- a/src/docs/fn_address_comparisons.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for comparisons with an address of a function item. - -### Why is this bad? -Function item address is not guaranteed to be unique and could vary -between different code generation units. Furthermore different function items could have -the same address after being merged together. - -### Example -``` -type F = fn(); -fn a() {} -let f: F = a; -if f == a { - // ... -} -``` \ No newline at end of file diff --git a/src/docs/fn_params_excessive_bools.txt b/src/docs/fn_params_excessive_bools.txt deleted file mode 100644 index 2eae0563368c..000000000000 --- a/src/docs/fn_params_excessive_bools.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for excessive use of -bools in function definitions. - -### Why is this bad? -Calls to such functions -are confusing and error prone, because it's -hard to remember argument order and you have -no type system support to back you up. Using -two-variant enums instead of bools often makes -API easier to use. - -### Example -``` -fn f(is_round: bool, is_hot: bool) { ... } -``` - -Use instead: -``` -enum Shape { - Round, - Spiky, -} - -enum Temperature { - Hot, - IceCold, -} - -fn f(shape: Shape, temperature: Temperature) { ... } -``` \ No newline at end of file diff --git a/src/docs/fn_to_numeric_cast.txt b/src/docs/fn_to_numeric_cast.txt deleted file mode 100644 index 1f587f6d7176..000000000000 --- a/src/docs/fn_to_numeric_cast.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for casts of function pointers to something other than usize - -### Why is this bad? -Casting a function pointer to anything other than usize/isize is not portable across -architectures, because you end up losing bits if the target type is too small or end up with a -bunch of extra bits that waste space and add more instructions to the final binary than -strictly necessary for the problem - -Casting to isize also doesn't make sense since there are no signed addresses. - -### Example -``` -fn fun() -> i32 { 1 } -let _ = fun as i64; -``` - -Use instead: -``` -let _ = fun as usize; -``` \ No newline at end of file diff --git a/src/docs/fn_to_numeric_cast_any.txt b/src/docs/fn_to_numeric_cast_any.txt deleted file mode 100644 index ee3c33d23725..000000000000 --- a/src/docs/fn_to_numeric_cast_any.txt +++ /dev/null @@ -1,35 +0,0 @@ -### What it does -Checks for casts of a function pointer to any integer type. - -### Why is this bad? -Casting a function pointer to an integer can have surprising results and can occur -accidentally if parentheses are omitted from a function call. If you aren't doing anything -low-level with function pointers then you can opt-out of casting functions to integers in -order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function -pointer casts in your code. - -### Example -``` -// fn1 is cast as `usize` -fn fn1() -> u16 { - 1 -}; -let _ = fn1 as usize; -``` - -Use instead: -``` -// maybe you intended to call the function? -fn fn2() -> u16 { - 1 -}; -let _ = fn2() as usize; - -// or - -// maybe you intended to cast it to a function type? -fn fn3() -> u16 { - 1 -} -let _ = fn3 as fn() -> u16; -``` \ No newline at end of file diff --git a/src/docs/fn_to_numeric_cast_with_truncation.txt b/src/docs/fn_to_numeric_cast_with_truncation.txt deleted file mode 100644 index 69f12fa319f1..000000000000 --- a/src/docs/fn_to_numeric_cast_with_truncation.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for casts of a function pointer to a numeric type not wide enough to -store address. - -### Why is this bad? -Such a cast discards some bits of the function's address. If this is intended, it would be more -clearly expressed by casting to usize first, then casting the usize to the intended type (with -a comment) to perform the truncation. - -### Example -``` -fn fn1() -> i16 { - 1 -}; -let _ = fn1 as i32; -``` - -Use instead: -``` -// Cast to usize first, then comment with the reason for the truncation -fn fn1() -> i16 { - 1 -}; -let fn_ptr = fn1 as usize; -let fn_ptr_truncated = fn_ptr as i32; -``` \ No newline at end of file diff --git a/src/docs/for_kv_map.txt b/src/docs/for_kv_map.txt deleted file mode 100644 index a9a2ffee9c74..000000000000 --- a/src/docs/for_kv_map.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for iterating a map (`HashMap` or `BTreeMap`) and -ignoring either the keys or values. - -### Why is this bad? -Readability. There are `keys` and `values` methods that -can be used to express that don't need the values or keys. - -### Example -``` -for (k, _) in &map { - .. -} -``` - -could be replaced by - -``` -for k in map.keys() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/for_loops_over_fallibles.txt b/src/docs/for_loops_over_fallibles.txt deleted file mode 100644 index c5a7508e45d4..000000000000 --- a/src/docs/for_loops_over_fallibles.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for `for` loops over `Option` or `Result` values. - -### Why is this bad? -Readability. This is more clearly expressed as an `if -let`. - -### Example -``` -for x in opt { - // .. -} - -for x in &res { - // .. -} - -for x in res.iter() { - // .. -} -``` - -Use instead: -``` -if let Some(x) = opt { - // .. -} - -if let Ok(x) = res { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/forget_copy.txt b/src/docs/forget_copy.txt deleted file mode 100644 index 1d100912e9a4..000000000000 --- a/src/docs/forget_copy.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for calls to `std::mem::forget` with a value that -derives the Copy trait - -### Why is this bad? -Calling `std::mem::forget` [does nothing for types that -implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the -value will be copied and moved into the function on invocation. - -An alternative, but also valid, explanation is that Copy types do not -implement -the Drop trait, which means they have no destructors. Without a destructor, -there -is nothing for `std::mem::forget` to ignore. - -### Example -``` -let x: i32 = 42; // i32 implements Copy -std::mem::forget(x) // A copy of x is passed to the function, leaving the - // original unaffected -``` \ No newline at end of file diff --git a/src/docs/forget_non_drop.txt b/src/docs/forget_non_drop.txt deleted file mode 100644 index 3307d654c17f..000000000000 --- a/src/docs/forget_non_drop.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calls to `std::mem::forget` with a value that does not implement `Drop`. - -### Why is this bad? -Calling `std::mem::forget` is no different than dropping such a type. A different value may -have been intended. - -### Example -``` -struct Foo; -let x = Foo; -std::mem::forget(x); -``` \ No newline at end of file diff --git a/src/docs/forget_ref.txt b/src/docs/forget_ref.txt deleted file mode 100644 index 874fb8786068..000000000000 --- a/src/docs/forget_ref.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for calls to `std::mem::forget` with a reference -instead of an owned value. - -### Why is this bad? -Calling `forget` on a reference will only forget the -reference itself, which is a no-op. It will not forget the underlying -referenced -value, which is likely what was intended. - -### Example -``` -let x = Box::new(1); -std::mem::forget(&x) // Should have been forget(x), x will still be dropped -``` \ No newline at end of file diff --git a/src/docs/format_in_format_args.txt b/src/docs/format_in_format_args.txt deleted file mode 100644 index ac498472f017..000000000000 --- a/src/docs/format_in_format_args.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Detects `format!` within the arguments of another macro that does -formatting such as `format!` itself, `write!` or `println!`. Suggests -inlining the `format!` call. - -### Why is this bad? -The recommended code is both shorter and avoids a temporary allocation. - -### Example -``` -println!("error: {}", format!("something failed at {}", Location::caller())); -``` -Use instead: -``` -println!("error: something failed at {}", Location::caller()); -``` \ No newline at end of file diff --git a/src/docs/format_push_string.txt b/src/docs/format_push_string.txt deleted file mode 100644 index ca409ebc7ec2..000000000000 --- a/src/docs/format_push_string.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Detects cases where the result of a `format!` call is -appended to an existing `String`. - -### Why is this bad? -Introduces an extra, avoidable heap allocation. - -### Known problems -`format!` returns a `String` but `write!` returns a `Result`. -Thus you are forced to ignore the `Err` variant to achieve the same API. - -While using `write!` in the suggested way should never fail, this isn't necessarily clear to the programmer. - -### Example -``` -let mut s = String::new(); -s += &format!("0x{:X}", 1024); -s.push_str(&format!("0x{:X}", 1024)); -``` -Use instead: -``` -use std::fmt::Write as _; // import without risk of name clashing - -let mut s = String::new(); -let _ = write!(s, "0x{:X}", 1024); -``` \ No newline at end of file diff --git a/src/docs/from_iter_instead_of_collect.txt b/src/docs/from_iter_instead_of_collect.txt deleted file mode 100644 index f3fd27597264..000000000000 --- a/src/docs/from_iter_instead_of_collect.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `from_iter()` function calls on types that implement the `FromIterator` -trait. - -### Why is this bad? -It is recommended style to use collect. See -[FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) - -### Example -``` -let five_fives = std::iter::repeat(5).take(5); - -let v = Vec::from_iter(five_fives); - -assert_eq!(v, vec![5, 5, 5, 5, 5]); -``` -Use instead: -``` -let five_fives = std::iter::repeat(5).take(5); - -let v: Vec = five_fives.collect(); - -assert_eq!(v, vec![5, 5, 5, 5, 5]); -``` \ No newline at end of file diff --git a/src/docs/from_over_into.txt b/src/docs/from_over_into.txt deleted file mode 100644 index 0770bcc42c27..000000000000 --- a/src/docs/from_over_into.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. - -### Why is this bad? -According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true. - -### Example -``` -struct StringWrapper(String); - -impl Into for String { - fn into(self) -> StringWrapper { - StringWrapper(self) - } -} -``` -Use instead: -``` -struct StringWrapper(String); - -impl From for StringWrapper { - fn from(s: String) -> StringWrapper { - StringWrapper(s) - } -} -``` \ No newline at end of file diff --git a/src/docs/from_str_radix_10.txt b/src/docs/from_str_radix_10.txt deleted file mode 100644 index f6f319d3eaa1..000000000000 --- a/src/docs/from_str_radix_10.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does - -Checks for function invocations of the form `primitive::from_str_radix(s, 10)` - -### Why is this bad? - -This specific common use case can be rewritten as `s.parse::()` -(and in most cases, the turbofish can be removed), which reduces code length -and complexity. - -### Known problems - -This lint may suggest using (&).parse() instead of .parse() directly -in some cases, which is correct but adds unnecessary complexity to the code. - -### Example -``` -let input: &str = get_input(); -let num = u16::from_str_radix(input, 10)?; -``` -Use instead: -``` -let input: &str = get_input(); -let num: u16 = input.parse()?; -``` \ No newline at end of file diff --git a/src/docs/future_not_send.txt b/src/docs/future_not_send.txt deleted file mode 100644 index 0aa048d27355..000000000000 --- a/src/docs/future_not_send.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -This lint requires Future implementations returned from -functions and methods to implement the `Send` marker trait. It is mostly -used by library authors (public and internal) that target an audience where -multithreaded executors are likely to be used for running these Futures. - -### Why is this bad? -A Future implementation captures some state that it -needs to eventually produce its final value. When targeting a multithreaded -executor (which is the norm on non-embedded devices) this means that this -state may need to be transported to other threads, in other words the -whole Future needs to implement the `Send` marker trait. If it does not, -then the resulting Future cannot be submitted to a thread pool in the -end user’s code. - -Especially for generic functions it can be confusing to leave the -discovery of this problem to the end user: the reported error location -will be far from its cause and can in many cases not even be fixed without -modifying the library where the offending Future implementation is -produced. - -### Example -``` -async fn not_send(bytes: std::rc::Rc<[u8]>) {} -``` -Use instead: -``` -async fn is_send(bytes: std::sync::Arc<[u8]>) {} -``` \ No newline at end of file diff --git a/src/docs/get_first.txt b/src/docs/get_first.txt deleted file mode 100644 index c905a737ddf3..000000000000 --- a/src/docs/get_first.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for using `x.get(0)` instead of -`x.first()`. - -### Why is this bad? -Using `x.first()` is easier to read and has the same -result. - -### Example -``` -let x = vec![2, 3, 5]; -let first_element = x.get(0); -``` - -Use instead: -``` -let x = vec![2, 3, 5]; -let first_element = x.first(); -``` \ No newline at end of file diff --git a/src/docs/get_last_with_len.txt b/src/docs/get_last_with_len.txt deleted file mode 100644 index 31c7f269586a..000000000000 --- a/src/docs/get_last_with_len.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for using `x.get(x.len() - 1)` instead of -`x.last()`. - -### Why is this bad? -Using `x.last()` is easier to read and has the same -result. - -Note that using `x[x.len() - 1]` is semantically different from -`x.last()`. Indexing into the array will panic on out-of-bounds -accesses, while `x.get()` and `x.last()` will return `None`. - -There is another lint (get_unwrap) that covers the case of using -`x.get(index).unwrap()` instead of `x[index]`. - -### Example -``` -let x = vec![2, 3, 5]; -let last_element = x.get(x.len() - 1); -``` - -Use instead: -``` -let x = vec![2, 3, 5]; -let last_element = x.last(); -``` \ No newline at end of file diff --git a/src/docs/get_unwrap.txt b/src/docs/get_unwrap.txt deleted file mode 100644 index 8defc2224416..000000000000 --- a/src/docs/get_unwrap.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for use of `.get().unwrap()` (or -`.get_mut().unwrap`) on a standard library type which implements `Index` - -### Why is this bad? -Using the Index trait (`[]`) is more clear and more -concise. - -### Known problems -Not a replacement for error handling: Using either -`.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic` -if the value being accessed is `None`. If the use of `.get().unwrap()` is a -temporary placeholder for dealing with the `Option` type, then this does -not mitigate the need for error handling. If there is a chance that `.get()` -will be `None` in your program, then it is advisable that the `None` case -is handled in a future refactor instead of using `.unwrap()` or the Index -trait. - -### Example -``` -let mut some_vec = vec![0, 1, 2, 3]; -let last = some_vec.get(3).unwrap(); -*some_vec.get_mut(0).unwrap() = 1; -``` -The correct use would be: -``` -let mut some_vec = vec![0, 1, 2, 3]; -let last = some_vec[3]; -some_vec[0] = 1; -``` \ No newline at end of file diff --git a/src/docs/identity_op.txt b/src/docs/identity_op.txt deleted file mode 100644 index a8e40bb43e9d..000000000000 --- a/src/docs/identity_op.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for identity operations, e.g., `x + 0`. - -### Why is this bad? -This code can be removed without changing the -meaning. So it just obscures what's going on. Delete it mercilessly. - -### Example -``` -x / 1 + 0 * 1 - 0 | 0; -``` \ No newline at end of file diff --git a/src/docs/if_let_mutex.txt b/src/docs/if_let_mutex.txt deleted file mode 100644 index 4d873ade9ace..000000000000 --- a/src/docs/if_let_mutex.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for `Mutex::lock` calls in `if let` expression -with lock calls in any of the else blocks. - -### Why is this bad? -The Mutex lock remains held for the whole -`if let ... else` block and deadlocks. - -### Example -``` -if let Ok(thing) = mutex.lock() { - do_thing(); -} else { - mutex.lock(); -} -``` -Should be written -``` -let locked = mutex.lock(); -if let Ok(thing) = locked { - do_thing(thing); -} else { - use_locked(locked); -} -``` \ No newline at end of file diff --git a/src/docs/if_not_else.txt b/src/docs/if_not_else.txt deleted file mode 100644 index 0e5ac4ce6bb8..000000000000 --- a/src/docs/if_not_else.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for usage of `!` or `!=` in an if condition with an -else branch. - -### Why is this bad? -Negations reduce the readability of statements. - -### Example -``` -if !v.is_empty() { - a() -} else { - b() -} -``` - -Could be written: - -``` -if v.is_empty() { - b() -} else { - a() -} -``` \ No newline at end of file diff --git a/src/docs/if_same_then_else.txt b/src/docs/if_same_then_else.txt deleted file mode 100644 index 75127016bb8c..000000000000 --- a/src/docs/if_same_then_else.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `if/else` with the same body as the *then* part -and the *else* part. - -### Why is this bad? -This is probably a copy & paste error. - -### Example -``` -let foo = if … { - 42 -} else { - 42 -}; -``` \ No newline at end of file diff --git a/src/docs/if_then_some_else_none.txt b/src/docs/if_then_some_else_none.txt deleted file mode 100644 index 13744f920e36..000000000000 --- a/src/docs/if_then_some_else_none.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for if-else that could be written using either `bool::then` or `bool::then_some`. - -### Why is this bad? -Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity. -For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated -in comparison to `bool::then`. - -### Example -``` -let a = if v.is_empty() { - println!("true!"); - Some(42) -} else { - None -}; -``` - -Could be written: - -``` -let a = v.is_empty().then(|| { - println!("true!"); - 42 -}); -``` \ No newline at end of file diff --git a/src/docs/ifs_same_cond.txt b/src/docs/ifs_same_cond.txt deleted file mode 100644 index 024ba5df93a6..000000000000 --- a/src/docs/ifs_same_cond.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for consecutive `if`s with the same condition. - -### Why is this bad? -This is probably a copy & paste error. - -### Example -``` -if a == b { - … -} else if a == b { - … -} -``` - -Note that this lint ignores all conditions with a function call as it could -have side effects: - -``` -if foo() { - … -} else if foo() { // not linted - … -} -``` \ No newline at end of file diff --git a/src/docs/implicit_clone.txt b/src/docs/implicit_clone.txt deleted file mode 100644 index f5aa112c52c3..000000000000 --- a/src/docs/implicit_clone.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer. - -### Why is this bad? -These methods do the same thing as `_.clone()` but may be confusing as -to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned. - -### Example -``` -let a = vec![1, 2, 3]; -let b = a.to_vec(); -let c = a.to_owned(); -``` -Use instead: -``` -let a = vec![1, 2, 3]; -let b = a.clone(); -let c = a.clone(); -``` \ No newline at end of file diff --git a/src/docs/implicit_hasher.txt b/src/docs/implicit_hasher.txt deleted file mode 100644 index 0c1f76620f51..000000000000 --- a/src/docs/implicit_hasher.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for public `impl` or `fn` missing generalization -over different hashers and implicitly defaulting to the default hashing -algorithm (`SipHash`). - -### Why is this bad? -`HashMap` or `HashSet` with custom hashers cannot be -used with them. - -### Known problems -Suggestions for replacing constructors can contain -false-positives. Also applying suggestions can require modification of other -pieces of code, possibly including external crates. - -### Example -``` -impl Serialize for HashMap { } - -pub fn foo(map: &mut HashMap) { } -``` -could be rewritten as -``` -impl Serialize for HashMap { } - -pub fn foo(map: &mut HashMap) { } -``` \ No newline at end of file diff --git a/src/docs/implicit_return.txt b/src/docs/implicit_return.txt deleted file mode 100644 index ee65a636b38c..000000000000 --- a/src/docs/implicit_return.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for missing return statements at the end of a block. - -### Why is this bad? -Actually omitting the return keyword is idiomatic Rust code. Programmers -coming from other languages might prefer the expressiveness of `return`. It's possible to miss -the last returning statement because the only difference is a missing `;`. Especially in bigger -code with multiple return paths having a `return` keyword makes it easier to find the -corresponding statements. - -### Example -``` -fn foo(x: usize) -> usize { - x -} -``` -add return -``` -fn foo(x: usize) -> usize { - return x; -} -``` \ No newline at end of file diff --git a/src/docs/implicit_saturating_add.txt b/src/docs/implicit_saturating_add.txt deleted file mode 100644 index 5883a5363e2b..000000000000 --- a/src/docs/implicit_saturating_add.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for implicit saturating addition. - -### Why is this bad? -The built-in function is more readable and may be faster. - -### Example -``` -let mut u:u32 = 7000; - -if u != u32::MAX { - u += 1; -} -``` -Use instead: -``` -let mut u:u32 = 7000; - -u = u.saturating_add(1); -``` \ No newline at end of file diff --git a/src/docs/implicit_saturating_sub.txt b/src/docs/implicit_saturating_sub.txt deleted file mode 100644 index 03b47905a211..000000000000 --- a/src/docs/implicit_saturating_sub.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for implicit saturating subtraction. - -### Why is this bad? -Simplicity and readability. Instead we can easily use an builtin function. - -### Example -``` -let mut i: u32 = end - start; - -if i != 0 { - i -= 1; -} -``` - -Use instead: -``` -let mut i: u32 = end - start; - -i = i.saturating_sub(1); -``` \ No newline at end of file diff --git a/src/docs/imprecise_flops.txt b/src/docs/imprecise_flops.txt deleted file mode 100644 index e84d81cea98e..000000000000 --- a/src/docs/imprecise_flops.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Looks for floating-point expressions that -can be expressed using built-in methods to improve accuracy -at the cost of performance. - -### Why is this bad? -Negatively impacts accuracy. - -### Example -``` -let a = 3f32; -let _ = a.powf(1.0 / 3.0); -let _ = (1.0 + a).ln(); -let _ = a.exp() - 1.0; -``` - -Use instead: -``` -let a = 3f32; -let _ = a.cbrt(); -let _ = a.ln_1p(); -let _ = a.exp_m1(); -``` \ No newline at end of file diff --git a/src/docs/inconsistent_digit_grouping.txt b/src/docs/inconsistent_digit_grouping.txt deleted file mode 100644 index aa0b072de1c4..000000000000 --- a/src/docs/inconsistent_digit_grouping.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Warns if an integral or floating-point constant is -grouped inconsistently with underscores. - -### Why is this bad? -Readers may incorrectly interpret inconsistently -grouped digits. - -### Example -``` -618_64_9189_73_511 -``` - -Use instead: -``` -61_864_918_973_511 -``` \ No newline at end of file diff --git a/src/docs/inconsistent_struct_constructor.txt b/src/docs/inconsistent_struct_constructor.txt deleted file mode 100644 index eb682109a54e..000000000000 --- a/src/docs/inconsistent_struct_constructor.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Checks for struct constructors where all fields are shorthand and -the order of the field init shorthand in the constructor is inconsistent -with the order in the struct definition. - -### Why is this bad? -Since the order of fields in a constructor doesn't affect the -resulted instance as the below example indicates, - -``` -#[derive(Debug, PartialEq, Eq)] -struct Foo { - x: i32, - y: i32, -} -let x = 1; -let y = 2; - -// This assertion never fails: -assert_eq!(Foo { x, y }, Foo { y, x }); -``` - -inconsistent order can be confusing and decreases readability and consistency. - -### Example -``` -struct Foo { - x: i32, - y: i32, -} -let x = 1; -let y = 2; - -Foo { y, x }; -``` - -Use instead: -``` -Foo { x, y }; -``` \ No newline at end of file diff --git a/src/docs/index_refutable_slice.txt b/src/docs/index_refutable_slice.txt deleted file mode 100644 index 8a7d52761af8..000000000000 --- a/src/docs/index_refutable_slice.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -The lint checks for slice bindings in patterns that are only used to -access individual slice values. - -### Why is this bad? -Accessing slice values using indices can lead to panics. Using refutable -patterns can avoid these. Binding to individual values also improves the -readability as they can be named. - -### Limitations -This lint currently only checks for immutable access inside `if let` -patterns. - -### Example -``` -let slice: Option<&[u32]> = Some(&[1, 2, 3]); - -if let Some(slice) = slice { - println!("{}", slice[0]); -} -``` -Use instead: -``` -let slice: Option<&[u32]> = Some(&[1, 2, 3]); - -if let Some(&[first, ..]) = slice { - println!("{}", first); -} -``` \ No newline at end of file diff --git a/src/docs/indexing_slicing.txt b/src/docs/indexing_slicing.txt deleted file mode 100644 index 76ca6ed318b3..000000000000 --- a/src/docs/indexing_slicing.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for usage of indexing or slicing. Arrays are special cases, this lint -does report on arrays if we can tell that slicing operations are in bounds and does not -lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint. - -### Why is this bad? -Indexing and slicing can panic at runtime and there are -safe alternatives. - -### Example -``` -// Vector -let x = vec![0; 5]; - -x[2]; -&x[2..100]; - -// Array -let y = [0, 1, 2, 3]; - -&y[10..100]; -&y[10..]; -``` - -Use instead: -``` - -x.get(2); -x.get(2..100); - -y.get(10); -y.get(10..100); -``` \ No newline at end of file diff --git a/src/docs/ineffective_bit_mask.txt b/src/docs/ineffective_bit_mask.txt deleted file mode 100644 index f6e7ef556215..000000000000 --- a/src/docs/ineffective_bit_mask.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for bit masks in comparisons which can be removed -without changing the outcome. The basic structure can be seen in the -following table: - -|Comparison| Bit Op |Example |equals | -|----------|----------|------------|-------| -|`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`| -|`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`| - -### Why is this bad? -Not equally evil as [`bad_bit_mask`](#bad_bit_mask), -but still a bit misleading, because the bit mask is ineffective. - -### Known problems -False negatives: This lint will only match instances -where we have figured out the math (which is for a power-of-two compared -value). This means things like `x | 1 >= 7` (which would be better written -as `x >= 6`) will not be reported (but bit masks like this are fairly -uncommon). - -### Example -``` -if (x | 1 > 3) { } -``` \ No newline at end of file diff --git a/src/docs/inefficient_to_string.txt b/src/docs/inefficient_to_string.txt deleted file mode 100644 index f7061d1ce7b0..000000000000 --- a/src/docs/inefficient_to_string.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usage of `.to_string()` on an `&&T` where -`T` implements `ToString` directly (like `&&str` or `&&String`). - -### Why is this bad? -This bypasses the specialized implementation of -`ToString` and instead goes through the more expensive string formatting -facilities. - -### Example -``` -// Generic implementation for `T: Display` is used (slow) -["foo", "bar"].iter().map(|s| s.to_string()); - -// OK, the specialized impl is used -["foo", "bar"].iter().map(|&s| s.to_string()); -``` \ No newline at end of file diff --git a/src/docs/infallible_destructuring_match.txt b/src/docs/infallible_destructuring_match.txt deleted file mode 100644 index 4b5d3c4ba6c4..000000000000 --- a/src/docs/infallible_destructuring_match.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for matches being used to destructure a single-variant enum -or tuple struct where a `let` will suffice. - -### Why is this bad? -Just readability – `let` doesn't nest, whereas a `match` does. - -### Example -``` -enum Wrapper { - Data(i32), -} - -let wrapper = Wrapper::Data(42); - -let data = match wrapper { - Wrapper::Data(i) => i, -}; -``` - -The correct use would be: -``` -enum Wrapper { - Data(i32), -} - -let wrapper = Wrapper::Data(42); -let Wrapper::Data(data) = wrapper; -``` \ No newline at end of file diff --git a/src/docs/infinite_iter.txt b/src/docs/infinite_iter.txt deleted file mode 100644 index 8a22fabc5492..000000000000 --- a/src/docs/infinite_iter.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for iteration that is guaranteed to be infinite. - -### Why is this bad? -While there may be places where this is acceptable -(e.g., in event streams), in most cases this is simply an error. - -### Example -``` -use std::iter; - -iter::repeat(1_u8).collect::>(); -``` \ No newline at end of file diff --git a/src/docs/inherent_to_string.txt b/src/docs/inherent_to_string.txt deleted file mode 100644 index b18e600e9e67..000000000000 --- a/src/docs/inherent_to_string.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`. - -### Why is this bad? -This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred. - -### Example -``` -pub struct A; - -impl A { - pub fn to_string(&self) -> String { - "I am A".to_string() - } -} -``` - -Use instead: -``` -use std::fmt; - -pub struct A; - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "I am A") - } -} -``` \ No newline at end of file diff --git a/src/docs/inherent_to_string_shadow_display.txt b/src/docs/inherent_to_string_shadow_display.txt deleted file mode 100644 index a4bd0b622c4f..000000000000 --- a/src/docs/inherent_to_string_shadow_display.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait. - -### Why is this bad? -This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`. - -### Example -``` -use std::fmt; - -pub struct A; - -impl A { - pub fn to_string(&self) -> String { - "I am A".to_string() - } -} - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "I am A, too") - } -} -``` - -Use instead: -``` -use std::fmt; - -pub struct A; - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "I am A") - } -} -``` \ No newline at end of file diff --git a/src/docs/init_numbered_fields.txt b/src/docs/init_numbered_fields.txt deleted file mode 100644 index ba40af6a5fa5..000000000000 --- a/src/docs/init_numbered_fields.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for tuple structs initialized with field syntax. -It will however not lint if a base initializer is present. -The lint will also ignore code in macros. - -### Why is this bad? -This may be confusing to the uninitiated and adds no -benefit as opposed to tuple initializers - -### Example -``` -struct TupleStruct(u8, u16); - -let _ = TupleStruct { - 0: 1, - 1: 23, -}; - -// should be written as -let base = TupleStruct(1, 23); - -// This is OK however -let _ = TupleStruct { 0: 42, ..base }; -``` \ No newline at end of file diff --git a/src/docs/inline_always.txt b/src/docs/inline_always.txt deleted file mode 100644 index 7721da4c4cc7..000000000000 --- a/src/docs/inline_always.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for items annotated with `#[inline(always)]`, -unless the annotated function is empty or simply panics. - -### Why is this bad? -While there are valid uses of this annotation (and once -you know when to use it, by all means `allow` this lint), it's a common -newbie-mistake to pepper one's code with it. - -As a rule of thumb, before slapping `#[inline(always)]` on a function, -measure if that additional function call really affects your runtime profile -sufficiently to make up for the increase in compile time. - -### Known problems -False positives, big time. This lint is meant to be -deactivated by everyone doing serious performance work. This means having -done the measurement. - -### Example -``` -#[inline(always)] -fn not_quite_hot_code(..) { ... } -``` \ No newline at end of file diff --git a/src/docs/inline_asm_x86_att_syntax.txt b/src/docs/inline_asm_x86_att_syntax.txt deleted file mode 100644 index 8eb49d122d89..000000000000 --- a/src/docs/inline_asm_x86_att_syntax.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of AT&T x86 assembly syntax. - -### Why is this bad? -The lint has been enabled to indicate a preference -for Intel x86 assembly syntax. - -### Example - -``` -asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); -``` -Use instead: -``` -asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); -``` \ No newline at end of file diff --git a/src/docs/inline_asm_x86_intel_syntax.txt b/src/docs/inline_asm_x86_intel_syntax.txt deleted file mode 100644 index 5aa22c8ed235..000000000000 --- a/src/docs/inline_asm_x86_intel_syntax.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of Intel x86 assembly syntax. - -### Why is this bad? -The lint has been enabled to indicate a preference -for AT&T x86 assembly syntax. - -### Example - -``` -asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); -``` -Use instead: -``` -asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); -``` \ No newline at end of file diff --git a/src/docs/inline_fn_without_body.txt b/src/docs/inline_fn_without_body.txt deleted file mode 100644 index 127c161aaa25..000000000000 --- a/src/docs/inline_fn_without_body.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for `#[inline]` on trait methods without bodies - -### Why is this bad? -Only implementations of trait methods may be inlined. -The inline attribute is ignored for trait methods without bodies. - -### Example -``` -trait Animal { - #[inline] - fn name(&self) -> &'static str; -} -``` \ No newline at end of file diff --git a/src/docs/inspect_for_each.txt b/src/docs/inspect_for_each.txt deleted file mode 100644 index 01a46d6c451f..000000000000 --- a/src/docs/inspect_for_each.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for usage of `inspect().for_each()`. - -### Why is this bad? -It is the same as performing the computation -inside `inspect` at the beginning of the closure in `for_each`. - -### Example -``` -[1,2,3,4,5].iter() -.inspect(|&x| println!("inspect the number: {}", x)) -.for_each(|&x| { - assert!(x >= 0); -}); -``` -Can be written as -``` -[1,2,3,4,5].iter() -.for_each(|&x| { - println!("inspect the number: {}", x); - assert!(x >= 0); -}); -``` \ No newline at end of file diff --git a/src/docs/int_plus_one.txt b/src/docs/int_plus_one.txt deleted file mode 100644 index 1b68f3eeb64b..000000000000 --- a/src/docs/int_plus_one.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block - -### Why is this bad? -Readability -- better to use `> y` instead of `>= y + 1`. - -### Example -``` -if x >= y + 1 {} -``` - -Use instead: -``` -if x > y {} -``` \ No newline at end of file diff --git a/src/docs/integer_arithmetic.txt b/src/docs/integer_arithmetic.txt deleted file mode 100644 index ea57a2ef97bf..000000000000 --- a/src/docs/integer_arithmetic.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for integer arithmetic operations which could overflow or panic. - -Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable -of overflowing according to the [Rust -Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), -or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is -attempted. - -### Why is this bad? -Integer overflow will trigger a panic in debug builds or will wrap in -release mode. Division by zero will cause a panic in either mode. In some applications one -wants explicitly checked, wrapping or saturating arithmetic. - -### Example -``` -a + 1; -``` \ No newline at end of file diff --git a/src/docs/integer_division.txt b/src/docs/integer_division.txt deleted file mode 100644 index f6d3349810ed..000000000000 --- a/src/docs/integer_division.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for division of integers - -### Why is this bad? -When outside of some very specific algorithms, -integer division is very often a mistake because it discards the -remainder. - -### Example -``` -let x = 3 / 2; -println!("{}", x); -``` - -Use instead: -``` -let x = 3f32 / 2f32; -println!("{}", x); -``` \ No newline at end of file diff --git a/src/docs/into_iter_on_ref.txt b/src/docs/into_iter_on_ref.txt deleted file mode 100644 index acb6bd474ebf..000000000000 --- a/src/docs/into_iter_on_ref.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for `into_iter` calls on references which should be replaced by `iter` -or `iter_mut`. - -### Why is this bad? -Readability. Calling `into_iter` on a reference will not move out its -content into the resulting iterator, which is confusing. It is better just call `iter` or -`iter_mut` directly. - -### Example -``` -(&vec).into_iter(); -``` - -Use instead: -``` -(&vec).iter(); -``` \ No newline at end of file diff --git a/src/docs/invalid_null_ptr_usage.txt b/src/docs/invalid_null_ptr_usage.txt deleted file mode 100644 index 6fb3fa3f83d6..000000000000 --- a/src/docs/invalid_null_ptr_usage.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint checks for invalid usages of `ptr::null`. - -### Why is this bad? -This causes undefined behavior. - -### Example -``` -// Undefined behavior -unsafe { std::slice::from_raw_parts(ptr::null(), 0); } -``` - -Use instead: -``` -unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } -``` \ No newline at end of file diff --git a/src/docs/invalid_regex.txt b/src/docs/invalid_regex.txt deleted file mode 100644 index 6c9969b6e1a3..000000000000 --- a/src/docs/invalid_regex.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks [regex](https://crates.io/crates/regex) creation -(with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct -regex syntax. - -### Why is this bad? -This will lead to a runtime panic. - -### Example -``` -Regex::new("(") -``` \ No newline at end of file diff --git a/src/docs/invalid_upcast_comparisons.txt b/src/docs/invalid_upcast_comparisons.txt deleted file mode 100644 index 77cb03308037..000000000000 --- a/src/docs/invalid_upcast_comparisons.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for comparisons where the relation is always either -true or false, but where one side has been upcast so that the comparison is -necessary. Only integer types are checked. - -### Why is this bad? -An expression like `let x : u8 = ...; (x as u32) > 300` -will mistakenly imply that it is possible for `x` to be outside the range of -`u8`. - -### Known problems -https://github.com/rust-lang/rust-clippy/issues/886 - -### Example -``` -let x: u8 = 1; -(x as u32) > 300; -``` \ No newline at end of file diff --git a/src/docs/invalid_utf8_in_unchecked.txt b/src/docs/invalid_utf8_in_unchecked.txt deleted file mode 100644 index afb5acbe9c51..000000000000 --- a/src/docs/invalid_utf8_in_unchecked.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal - -### Why is this bad? -Creating such a `str` would result in undefined behavior - -### Example -``` -unsafe { - std::str::from_utf8_unchecked(b"cl\x82ippy"); -} -``` \ No newline at end of file diff --git a/src/docs/invisible_characters.txt b/src/docs/invisible_characters.txt deleted file mode 100644 index 3dda380911f9..000000000000 --- a/src/docs/invisible_characters.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for invisible Unicode characters in the code. - -### Why is this bad? -Having an invisible character in the code makes for all -sorts of April fools, but otherwise is very much frowned upon. - -### Example -You don't see it, but there may be a zero-width space or soft hyphen -some­where in this text. \ No newline at end of file diff --git a/src/docs/is_digit_ascii_radix.txt b/src/docs/is_digit_ascii_radix.txt deleted file mode 100644 index 9f11cf43054f..000000000000 --- a/src/docs/is_digit_ascii_radix.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that -can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or -[`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit). - -### Why is this bad? -`is_digit(..)` is slower and requires specifying the radix. - -### Example -``` -let c: char = '6'; -c.is_digit(10); -c.is_digit(16); -``` -Use instead: -``` -let c: char = '6'; -c.is_ascii_digit(); -c.is_ascii_hexdigit(); -``` \ No newline at end of file diff --git a/src/docs/items_after_statements.txt b/src/docs/items_after_statements.txt deleted file mode 100644 index 6fdfff50d20e..000000000000 --- a/src/docs/items_after_statements.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for items declared after some statement in a block. - -### Why is this bad? -Items live for the entire scope they are declared -in. But statements are processed in order. This might cause confusion as -it's hard to figure out which item is meant in a statement. - -### Example -``` -fn foo() { - println!("cake"); -} - -fn main() { - foo(); // prints "foo" - fn foo() { - println!("foo"); - } - foo(); // prints "foo" -} -``` - -Use instead: -``` -fn foo() { - println!("cake"); -} - -fn main() { - fn foo() { - println!("foo"); - } - foo(); // prints "foo" - foo(); // prints "foo" -} -``` \ No newline at end of file diff --git a/src/docs/iter_cloned_collect.txt b/src/docs/iter_cloned_collect.txt deleted file mode 100644 index 90dc9ebb40f0..000000000000 --- a/src/docs/iter_cloned_collect.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for the use of `.cloned().collect()` on slice to -create a `Vec`. - -### Why is this bad? -`.to_vec()` is clearer - -### Example -``` -let s = [1, 2, 3, 4, 5]; -let s2: Vec = s[..].iter().cloned().collect(); -``` -The better use would be: -``` -let s = [1, 2, 3, 4, 5]; -let s2: Vec = s.to_vec(); -``` \ No newline at end of file diff --git a/src/docs/iter_count.txt b/src/docs/iter_count.txt deleted file mode 100644 index f3db4a26c299..000000000000 --- a/src/docs/iter_count.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for the use of `.iter().count()`. - -### Why is this bad? -`.len()` is more efficient and more -readable. - -### Example -``` -let some_vec = vec![0, 1, 2, 3]; - -some_vec.iter().count(); -&some_vec[..].iter().count(); -``` - -Use instead: -``` -let some_vec = vec![0, 1, 2, 3]; - -some_vec.len(); -&some_vec[..].len(); -``` \ No newline at end of file diff --git a/src/docs/iter_kv_map.txt b/src/docs/iter_kv_map.txt deleted file mode 100644 index a063c8195ef5..000000000000 --- a/src/docs/iter_kv_map.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does - -Checks for iterating a map (`HashMap` or `BTreeMap`) and -ignoring either the keys or values. - -### Why is this bad? - -Readability. There are `keys` and `values` methods that -can be used to express that we only need the keys or the values. - -### Example - -``` -let map: HashMap = HashMap::new(); -let values = map.iter().map(|(_, value)| value).collect::>(); -``` - -Use instead: -``` -let map: HashMap = HashMap::new(); -let values = map.values().collect::>(); -``` \ No newline at end of file diff --git a/src/docs/iter_next_loop.txt b/src/docs/iter_next_loop.txt deleted file mode 100644 index b33eb39d6e1d..000000000000 --- a/src/docs/iter_next_loop.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for loops on `x.next()`. - -### Why is this bad? -`next()` returns either `Some(value)` if there was a -value, or `None` otherwise. The insidious thing is that `Option<_>` -implements `IntoIterator`, so that possibly one value will be iterated, -leading to some hard to find bugs. No one will want to write such code -[except to win an Underhanded Rust -Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr). - -### Example -``` -for x in y.next() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/iter_next_slice.txt b/src/docs/iter_next_slice.txt deleted file mode 100644 index 1cea25eaf301..000000000000 --- a/src/docs/iter_next_slice.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `iter().next()` on a Slice or an Array - -### Why is this bad? -These can be shortened into `.get()` - -### Example -``` -a[2..].iter().next(); -b.iter().next(); -``` -should be written as: -``` -a.get(2); -b.get(0); -``` \ No newline at end of file diff --git a/src/docs/iter_not_returning_iterator.txt b/src/docs/iter_not_returning_iterator.txt deleted file mode 100644 index 0ca862910a6f..000000000000 --- a/src/docs/iter_not_returning_iterator.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`. - -### Why is this bad? -Methods named `iter` or `iter_mut` conventionally return an `Iterator`. - -### Example -``` -// `String` does not implement `Iterator` -struct Data {} -impl Data { - fn iter(&self) -> String { - todo!() - } -} -``` -Use instead: -``` -use std::str::Chars; -struct Data {} -impl Data { - fn iter(&self) -> Chars<'static> { - todo!() - } -} -``` \ No newline at end of file diff --git a/src/docs/iter_nth.txt b/src/docs/iter_nth.txt deleted file mode 100644 index 3d67d583ffde..000000000000 --- a/src/docs/iter_nth.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for use of `.iter().nth()` (and the related -`.iter_mut().nth()`) on standard library types with *O*(1) element access. - -### Why is this bad? -`.get()` and `.get_mut()` are more efficient and more -readable. - -### Example -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.iter().nth(3); -let bad_slice = &some_vec[..].iter().nth(3); -``` -The correct use would be: -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.get(3); -let bad_slice = &some_vec[..].get(3); -``` \ No newline at end of file diff --git a/src/docs/iter_nth_zero.txt b/src/docs/iter_nth_zero.txt deleted file mode 100644 index 8efe47a16a10..000000000000 --- a/src/docs/iter_nth_zero.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for the use of `iter.nth(0)`. - -### Why is this bad? -`iter.next()` is equivalent to -`iter.nth(0)`, as they both consume the next element, - but is more readable. - -### Example -``` -let x = s.iter().nth(0); -``` - -Use instead: -``` -let x = s.iter().next(); -``` \ No newline at end of file diff --git a/src/docs/iter_on_empty_collections.txt b/src/docs/iter_on_empty_collections.txt deleted file mode 100644 index 87c4ec12afae..000000000000 --- a/src/docs/iter_on_empty_collections.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does - -Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections - -### Why is this bad? - -It is simpler to use the empty function from the standard library: - -### Example - -``` -use std::{slice, option}; -let a: slice::Iter = [].iter(); -let f: option::IntoIter = None.into_iter(); -``` -Use instead: -``` -use std::iter; -let a: iter::Empty = iter::empty(); -let b: iter::Empty = iter::empty(); -``` - -### Known problems - -The type of the resulting iterator might become incompatible with its usage \ No newline at end of file diff --git a/src/docs/iter_on_single_items.txt b/src/docs/iter_on_single_items.txt deleted file mode 100644 index d0388f25d045..000000000000 --- a/src/docs/iter_on_single_items.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does - -Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item - -### Why is this bad? - -It is simpler to use the once function from the standard library: - -### Example - -``` -let a = [123].iter(); -let b = Some(123).into_iter(); -``` -Use instead: -``` -use std::iter; -let a = iter::once(&123); -let b = iter::once(123); -``` - -### Known problems - -The type of the resulting iterator might become incompatible with its usage \ No newline at end of file diff --git a/src/docs/iter_overeager_cloned.txt b/src/docs/iter_overeager_cloned.txt deleted file mode 100644 index 2f902a0c2db4..000000000000 --- a/src/docs/iter_overeager_cloned.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `_.cloned().()` where call to `.cloned()` can be postponed. - -### Why is this bad? -It's often inefficient to clone all elements of an iterator, when eventually, only some -of them will be consumed. - -### Known Problems -This `lint` removes the side of effect of cloning items in the iterator. -A code that relies on that side-effect could fail. - -### Examples -``` -vec.iter().cloned().take(10); -vec.iter().cloned().last(); -``` - -Use instead: -``` -vec.iter().take(10).cloned(); -vec.iter().last().cloned(); -``` \ No newline at end of file diff --git a/src/docs/iter_skip_next.txt b/src/docs/iter_skip_next.txt deleted file mode 100644 index da226b041cf2..000000000000 --- a/src/docs/iter_skip_next.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for use of `.skip(x).next()` on iterators. - -### Why is this bad? -`.nth(x)` is cleaner - -### Example -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.iter().skip(3).next(); -let bad_slice = &some_vec[..].iter().skip(3).next(); -``` -The correct use would be: -``` -let some_vec = vec![0, 1, 2, 3]; -let bad_vec = some_vec.iter().nth(3); -let bad_slice = &some_vec[..].iter().nth(3); -``` \ No newline at end of file diff --git a/src/docs/iter_with_drain.txt b/src/docs/iter_with_drain.txt deleted file mode 100644 index 2c52b99f7a5c..000000000000 --- a/src/docs/iter_with_drain.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration. - -### Why is this bad? -`.into_iter()` is simpler with better performance. - -### Example -``` -let mut foo = vec![0, 1, 2, 3]; -let bar: HashSet = foo.drain(..).collect(); -``` -Use instead: -``` -let foo = vec![0, 1, 2, 3]; -let bar: HashSet = foo.into_iter().collect(); -``` \ No newline at end of file diff --git a/src/docs/iterator_step_by_zero.txt b/src/docs/iterator_step_by_zero.txt deleted file mode 100644 index 73ecc99acfcb..000000000000 --- a/src/docs/iterator_step_by_zero.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calling `.step_by(0)` on iterators which panics. - -### Why is this bad? -This very much looks like an oversight. Use `panic!()` instead if you -actually intend to panic. - -### Example -``` -for x in (0..100).step_by(0) { - //.. -} -``` \ No newline at end of file diff --git a/src/docs/just_underscores_and_digits.txt b/src/docs/just_underscores_and_digits.txt deleted file mode 100644 index a8790bcf25be..000000000000 --- a/src/docs/just_underscores_and_digits.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks if you have variables whose name consists of just -underscores and digits. - -### Why is this bad? -It's hard to memorize what a variable means without a -descriptive name. - -### Example -``` -let _1 = 1; -let ___1 = 1; -let __1___2 = 11; -``` \ No newline at end of file diff --git a/src/docs/large_const_arrays.txt b/src/docs/large_const_arrays.txt deleted file mode 100644 index 71f67854f2a1..000000000000 --- a/src/docs/large_const_arrays.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for large `const` arrays that should -be defined as `static` instead. - -### Why is this bad? -Performance: const variables are inlined upon use. -Static items result in only one instance and has a fixed location in memory. - -### Example -``` -pub const a = [0u32; 1_000_000]; -``` - -Use instead: -``` -pub static a = [0u32; 1_000_000]; -``` \ No newline at end of file diff --git a/src/docs/large_digit_groups.txt b/src/docs/large_digit_groups.txt deleted file mode 100644 index f60b19345af4..000000000000 --- a/src/docs/large_digit_groups.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Warns if the digits of an integral or floating-point -constant are grouped into groups that -are too large. - -### Why is this bad? -Negatively impacts readability. - -### Example -``` -let x: u64 = 6186491_8973511; -``` \ No newline at end of file diff --git a/src/docs/large_enum_variant.txt b/src/docs/large_enum_variant.txt deleted file mode 100644 index 1f95430790d2..000000000000 --- a/src/docs/large_enum_variant.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for large size differences between variants on -`enum`s. - -### Why is this bad? -Enum size is bounded by the largest variant. Having one -large variant can penalize the memory layout of that enum. - -### Known problems -This lint obviously cannot take the distribution of -variants in your running program into account. It is possible that the -smaller variants make up less than 1% of all instances, in which case -the overhead is negligible and the boxing is counter-productive. Always -measure the change this lint suggests. - -For types that implement `Copy`, the suggestion to `Box` a variant's -data would require removing the trait impl. The types can of course -still be `Clone`, but that is worse ergonomically. Depending on the -use case it may be possible to store the large data in an auxiliary -structure (e.g. Arena or ECS). - -The lint will ignore the impact of generic types to the type layout by -assuming every type parameter is zero-sized. Depending on your use case, -this may lead to a false positive. - -### Example -``` -enum Test { - A(i32), - B([i32; 8000]), -} -``` - -Use instead: -``` -// Possibly better -enum Test2 { - A(i32), - B(Box<[i32; 8000]>), -} -``` \ No newline at end of file diff --git a/src/docs/large_include_file.txt b/src/docs/large_include_file.txt deleted file mode 100644 index b2a54bd2eb5c..000000000000 --- a/src/docs/large_include_file.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for the inclusion of large files via `include_bytes!()` -and `include_str!()` - -### Why is this bad? -Including large files can increase the size of the binary - -### Example -``` -let included_str = include_str!("very_large_file.txt"); -let included_bytes = include_bytes!("very_large_file.txt"); -``` - -Use instead: -``` -use std::fs; - -// You can load the file at runtime -let string = fs::read_to_string("very_large_file.txt")?; -let bytes = fs::read("very_large_file.txt")?; -``` \ No newline at end of file diff --git a/src/docs/large_stack_arrays.txt b/src/docs/large_stack_arrays.txt deleted file mode 100644 index 4a6f34785b0e..000000000000 --- a/src/docs/large_stack_arrays.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for local arrays that may be too large. - -### Why is this bad? -Large local arrays may cause stack overflow. - -### Example -``` -let a = [0u32; 1_000_000]; -``` \ No newline at end of file diff --git a/src/docs/large_types_passed_by_value.txt b/src/docs/large_types_passed_by_value.txt deleted file mode 100644 index bca07f3ac61b..000000000000 --- a/src/docs/large_types_passed_by_value.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for functions taking arguments by value, where -the argument type is `Copy` and large enough to be worth considering -passing by reference. Does not trigger if the function is being exported, -because that might induce API breakage, if the parameter is declared as mutable, -or if the argument is a `self`. - -### Why is this bad? -Arguments passed by value might result in an unnecessary -shallow copy, taking up more space in the stack and requiring a call to -`memcpy`, which can be expensive. - -### Example -``` -#[derive(Clone, Copy)] -struct TooLarge([u8; 2048]); - -fn foo(v: TooLarge) {} -``` - -Use instead: -``` -fn foo(v: &TooLarge) {} -``` \ No newline at end of file diff --git a/src/docs/len_without_is_empty.txt b/src/docs/len_without_is_empty.txt deleted file mode 100644 index 47a2e8575228..000000000000 --- a/src/docs/len_without_is_empty.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for items that implement `.len()` but not -`.is_empty()`. - -### Why is this bad? -It is good custom to have both methods, because for -some data structures, asking about the length will be a costly operation, -whereas `.is_empty()` can usually answer in constant time. Also it used to -lead to false positives on the [`len_zero`](#len_zero) lint – currently that -lint will ignore such entities. - -### Example -``` -impl X { - pub fn len(&self) -> usize { - .. - } -} -``` \ No newline at end of file diff --git a/src/docs/len_zero.txt b/src/docs/len_zero.txt deleted file mode 100644 index 664124bd391d..000000000000 --- a/src/docs/len_zero.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for getting the length of something via `.len()` -just to compare to zero, and suggests using `.is_empty()` where applicable. - -### Why is this bad? -Some structures can answer `.is_empty()` much faster -than calculating their length. So it is good to get into the habit of using -`.is_empty()`, and having it is cheap. -Besides, it makes the intent clearer than a manual comparison in some contexts. - -### Example -``` -if x.len() == 0 { - .. -} -if y.len() != 0 { - .. -} -``` -instead use -``` -if x.is_empty() { - .. -} -if !y.is_empty() { - .. -} -``` \ No newline at end of file diff --git a/src/docs/let_and_return.txt b/src/docs/let_and_return.txt deleted file mode 100644 index eba5a90ddd66..000000000000 --- a/src/docs/let_and_return.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `let`-bindings, which are subsequently -returned. - -### Why is this bad? -It is just extraneous code. Remove it to make your code -more rusty. - -### Example -``` -fn foo() -> String { - let x = String::new(); - x -} -``` -instead, use -``` -fn foo() -> String { - String::new() -} -``` \ No newline at end of file diff --git a/src/docs/let_underscore_drop.txt b/src/docs/let_underscore_drop.txt deleted file mode 100644 index 29ce9bf50ce6..000000000000 --- a/src/docs/let_underscore_drop.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `let _ = ` -where expr has a type that implements `Drop` - -### Why is this bad? -This statement immediately drops the initializer -expression instead of extending its lifetime to the end of the scope, which -is often not intended. To extend the expression's lifetime to the end of the -scope, use an underscore-prefixed name instead (i.e. _var). If you want to -explicitly drop the expression, `std::mem::drop` conveys your intention -better and is less error-prone. - -### Example -``` -{ - let _ = DroppableItem; - // ^ dropped here - /* more code */ -} -``` - -Use instead: -``` -{ - let _droppable = DroppableItem; - /* more code */ - // dropped at end of scope -} -``` \ No newline at end of file diff --git a/src/docs/let_underscore_lock.txt b/src/docs/let_underscore_lock.txt deleted file mode 100644 index bd8217fb58b3..000000000000 --- a/src/docs/let_underscore_lock.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for `let _ = sync_lock`. -This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`. - -### Why is this bad? -This statement immediately drops the lock instead of -extending its lifetime to the end of the scope, which is often not intended. -To extend lock lifetime to the end of the scope, use an underscore-prefixed -name instead (i.e. _lock). If you want to explicitly drop the lock, -`std::mem::drop` conveys your intention better and is less error-prone. - -### Example -``` -let _ = mutex.lock(); -``` - -Use instead: -``` -let _lock = mutex.lock(); -``` \ No newline at end of file diff --git a/src/docs/let_underscore_must_use.txt b/src/docs/let_underscore_must_use.txt deleted file mode 100644 index 270b81d9a4c4..000000000000 --- a/src/docs/let_underscore_must_use.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for `let _ = ` where expr is `#[must_use]` - -### Why is this bad? -It's better to explicitly handle the value of a `#[must_use]` -expr - -### Example -``` -fn f() -> Result { - Ok(0) -} - -let _ = f(); -// is_ok() is marked #[must_use] -let _ = f().is_ok(); -``` \ No newline at end of file diff --git a/src/docs/let_unit_value.txt b/src/docs/let_unit_value.txt deleted file mode 100644 index bc16d5b3d81b..000000000000 --- a/src/docs/let_unit_value.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for binding a unit value. - -### Why is this bad? -A unit value cannot usefully be used anywhere. So -binding one is kind of pointless. - -### Example -``` -let x = { - 1; -}; -``` \ No newline at end of file diff --git a/src/docs/linkedlist.txt b/src/docs/linkedlist.txt deleted file mode 100644 index 986ff1369e3c..000000000000 --- a/src/docs/linkedlist.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for usage of any `LinkedList`, suggesting to use a -`Vec` or a `VecDeque` (formerly called `RingBuf`). - -### Why is this bad? -Gankro says: - -> The TL;DR of `LinkedList` is that it's built on a massive amount of -pointers and indirection. -> It wastes memory, it has terrible cache locality, and is all-around slow. -`RingBuf`, while -> "only" amortized for push/pop, should be faster in the general case for -almost every possible -> workload, and isn't even amortized at all if you can predict the capacity -you need. -> -> `LinkedList`s are only really good if you're doing a lot of merging or -splitting of lists. -> This is because they can just mangle some pointers instead of actually -copying the data. Even -> if you're doing a lot of insertion in the middle of the list, `RingBuf` -can still be better -> because of how expensive it is to seek to the middle of a `LinkedList`. - -### Known problems -False positives – the instances where using a -`LinkedList` makes sense are few and far between, but they can still happen. - -### Example -``` -let x: LinkedList = LinkedList::new(); -``` \ No newline at end of file diff --git a/src/docs/lossy_float_literal.txt b/src/docs/lossy_float_literal.txt deleted file mode 100644 index bbcb9115ea62..000000000000 --- a/src/docs/lossy_float_literal.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for whole number float literals that -cannot be represented as the underlying type without loss. - -### Why is this bad? -Rust will silently lose precision during -conversion to a float. - -### Example -``` -let _: f32 = 16_777_217.0; // 16_777_216.0 -``` - -Use instead: -``` -let _: f32 = 16_777_216.0; -let _: f64 = 16_777_217.0; -``` \ No newline at end of file diff --git a/src/docs/macro_use_imports.txt b/src/docs/macro_use_imports.txt deleted file mode 100644 index 6a8180a60bc6..000000000000 --- a/src/docs/macro_use_imports.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for `#[macro_use] use...`. - -### Why is this bad? -Since the Rust 2018 edition you can import -macro's directly, this is considered idiomatic. - -### Example -``` -#[macro_use] -use some_macro; -``` \ No newline at end of file diff --git a/src/docs/main_recursion.txt b/src/docs/main_recursion.txt deleted file mode 100644 index e49becd15bbd..000000000000 --- a/src/docs/main_recursion.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for recursion using the entrypoint. - -### Why is this bad? -Apart from special setups (which we could detect following attributes like #![no_std]), -recursing into main() seems like an unintuitive anti-pattern we should be able to detect. - -### Example -``` -fn main() { - main(); -} -``` \ No newline at end of file diff --git a/src/docs/manual_assert.txt b/src/docs/manual_assert.txt deleted file mode 100644 index 93653081a2ce..000000000000 --- a/src/docs/manual_assert.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Detects `if`-then-`panic!` that can be replaced with `assert!`. - -### Why is this bad? -`assert!` is simpler than `if`-then-`panic!`. - -### Example -``` -let sad_people: Vec<&str> = vec![]; -if !sad_people.is_empty() { - panic!("there are sad people: {:?}", sad_people); -} -``` -Use instead: -``` -let sad_people: Vec<&str> = vec![]; -assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people); -``` \ No newline at end of file diff --git a/src/docs/manual_async_fn.txt b/src/docs/manual_async_fn.txt deleted file mode 100644 index d01ac402e0d2..000000000000 --- a/src/docs/manual_async_fn.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -It checks for manual implementations of `async` functions. - -### Why is this bad? -It's more idiomatic to use the dedicated syntax. - -### Example -``` -use std::future::Future; - -fn foo() -> impl Future { async { 42 } } -``` -Use instead: -``` -async fn foo() -> i32 { 42 } -``` \ No newline at end of file diff --git a/src/docs/manual_bits.txt b/src/docs/manual_bits.txt deleted file mode 100644 index b96c2eb151d4..000000000000 --- a/src/docs/manual_bits.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for uses of `std::mem::size_of::() * 8` when -`T::BITS` is available. - -### Why is this bad? -Can be written as the shorter `T::BITS`. - -### Example -``` -std::mem::size_of::() * 8; -``` -Use instead: -``` -usize::BITS as usize; -``` \ No newline at end of file diff --git a/src/docs/manual_clamp.txt b/src/docs/manual_clamp.txt deleted file mode 100644 index 8993f6683adf..000000000000 --- a/src/docs/manual_clamp.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -Identifies good opportunities for a clamp function from std or core, and suggests using it. - -### Why is this bad? -clamp is much shorter, easier to read, and doesn't use any control flow. - -### Known issue(s) -If the clamped variable is NaN this suggestion will cause the code to propagate NaN -rather than returning either `max` or `min`. - -`clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`. -Some may consider panicking in these situations to be desirable, but it also may -introduce panicking where there wasn't any before. - -### Examples -``` -if input > max { - max -} else if input < min { - min -} else { - input -} -``` - -``` -input.max(min).min(max) -``` - -``` -match input { - x if x > max => max, - x if x < min => min, - x => x, -} -``` - -``` -let mut x = input; -if x < min { x = min; } -if x > max { x = max; } -``` -Use instead: -``` -input.clamp(min, max) -``` \ No newline at end of file diff --git a/src/docs/manual_filter.txt b/src/docs/manual_filter.txt deleted file mode 100644 index 19a4d9319d94..000000000000 --- a/src/docs/manual_filter.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usages of `match` which could be implemented using `filter` - -### Why is this bad? -Using the `filter` method is clearer and more concise. - -### Example -``` -match Some(0) { - Some(x) => if x % 2 == 0 { - Some(x) - } else { - None - }, - None => None, -}; -``` -Use instead: -``` -Some(0).filter(|&x| x % 2 == 0); -``` \ No newline at end of file diff --git a/src/docs/manual_filter_map.txt b/src/docs/manual_filter_map.txt deleted file mode 100644 index 3b6860798ff5..000000000000 --- a/src/docs/manual_filter_map.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `_.filter(_).map(_)` that can be written more simply -as `filter_map(_)`. - -### Why is this bad? -Redundant code in the `filter` and `map` operations is poor style and -less performant. - -### Example -``` -(0_i32..10) - .filter(|n| n.checked_add(1).is_some()) - .map(|n| n.checked_add(1).unwrap()); -``` - -Use instead: -``` -(0_i32..10).filter_map(|n| n.checked_add(1)); -``` \ No newline at end of file diff --git a/src/docs/manual_find.txt b/src/docs/manual_find.txt deleted file mode 100644 index e3e07a2771f9..000000000000 --- a/src/docs/manual_find.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Check for manual implementations of Iterator::find - -### Why is this bad? -It doesn't affect performance, but using `find` is shorter and easier to read. - -### Example - -``` -fn example(arr: Vec) -> Option { - for el in arr { - if el == 1 { - return Some(el); - } - } - None -} -``` -Use instead: -``` -fn example(arr: Vec) -> Option { - arr.into_iter().find(|&el| el == 1) -} -``` \ No newline at end of file diff --git a/src/docs/manual_find_map.txt b/src/docs/manual_find_map.txt deleted file mode 100644 index 83b22060c0e1..000000000000 --- a/src/docs/manual_find_map.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `_.find(_).map(_)` that can be written more simply -as `find_map(_)`. - -### Why is this bad? -Redundant code in the `find` and `map` operations is poor style and -less performant. - -### Example -``` -(0_i32..10) - .find(|n| n.checked_add(1).is_some()) - .map(|n| n.checked_add(1).unwrap()); -``` - -Use instead: -``` -(0_i32..10).find_map(|n| n.checked_add(1)); -``` \ No newline at end of file diff --git a/src/docs/manual_flatten.txt b/src/docs/manual_flatten.txt deleted file mode 100644 index 62d5f3ec9357..000000000000 --- a/src/docs/manual_flatten.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Check for unnecessary `if let` usage in a for loop -where only the `Some` or `Ok` variant of the iterator element is used. - -### Why is this bad? -It is verbose and can be simplified -by first calling the `flatten` method on the `Iterator`. - -### Example - -``` -let x = vec![Some(1), Some(2), Some(3)]; -for n in x { - if let Some(n) = n { - println!("{}", n); - } -} -``` -Use instead: -``` -let x = vec![Some(1), Some(2), Some(3)]; -for n in x.into_iter().flatten() { - println!("{}", n); -} -``` \ No newline at end of file diff --git a/src/docs/manual_instant_elapsed.txt b/src/docs/manual_instant_elapsed.txt deleted file mode 100644 index dde3d493c70d..000000000000 --- a/src/docs/manual_instant_elapsed.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Lints subtraction between `Instant::now()` and another `Instant`. - -### Why is this bad? -It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns -as `Instant` subtraction saturates. - -`prev_instant.elapsed()` also more clearly signals intention. - -### Example -``` -use std::time::Instant; -let prev_instant = Instant::now(); -let duration = Instant::now() - prev_instant; -``` -Use instead: -``` -use std::time::Instant; -let prev_instant = Instant::now(); -let duration = prev_instant.elapsed(); -``` \ No newline at end of file diff --git a/src/docs/manual_map.txt b/src/docs/manual_map.txt deleted file mode 100644 index 7f68ccd1037e..000000000000 --- a/src/docs/manual_map.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usages of `match` which could be implemented using `map` - -### Why is this bad? -Using the `map` method is clearer and more concise. - -### Example -``` -match Some(0) { - Some(x) => Some(x + 1), - None => None, -}; -``` -Use instead: -``` -Some(0).map(|x| x + 1); -``` \ No newline at end of file diff --git a/src/docs/manual_memcpy.txt b/src/docs/manual_memcpy.txt deleted file mode 100644 index d7690bf25866..000000000000 --- a/src/docs/manual_memcpy.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for for-loops that manually copy items between -slices that could be optimized by having a memcpy. - -### Why is this bad? -It is not as fast as a memcpy. - -### Example -``` -for i in 0..src.len() { - dst[i + 64] = src[i]; -} -``` - -Use instead: -``` -dst[64..(src.len() + 64)].clone_from_slice(&src[..]); -``` \ No newline at end of file diff --git a/src/docs/manual_non_exhaustive.txt b/src/docs/manual_non_exhaustive.txt deleted file mode 100644 index fb021393bd7c..000000000000 --- a/src/docs/manual_non_exhaustive.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for manual implementations of the non-exhaustive pattern. - -### Why is this bad? -Using the #[non_exhaustive] attribute expresses better the intent -and allows possible optimizations when applied to enums. - -### Example -``` -struct S { - pub a: i32, - pub b: i32, - _c: (), -} - -enum E { - A, - B, - #[doc(hidden)] - _C, -} - -struct T(pub i32, pub i32, ()); -``` -Use instead: -``` -#[non_exhaustive] -struct S { - pub a: i32, - pub b: i32, -} - -#[non_exhaustive] -enum E { - A, - B, -} - -#[non_exhaustive] -struct T(pub i32, pub i32); -``` \ No newline at end of file diff --git a/src/docs/manual_ok_or.txt b/src/docs/manual_ok_or.txt deleted file mode 100644 index 5accdf25965a..000000000000 --- a/src/docs/manual_ok_or.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does - -Finds patterns that reimplement `Option::ok_or`. - -### Why is this bad? - -Concise code helps focusing on behavior instead of boilerplate. - -### Examples -``` -let foo: Option = None; -foo.map_or(Err("error"), |v| Ok(v)); -``` - -Use instead: -``` -let foo: Option = None; -foo.ok_or("error"); -``` \ No newline at end of file diff --git a/src/docs/manual_range_contains.txt b/src/docs/manual_range_contains.txt deleted file mode 100644 index 0ade26951d38..000000000000 --- a/src/docs/manual_range_contains.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for expressions like `x >= 3 && x < 8` that could -be more readably expressed as `(3..8).contains(x)`. - -### Why is this bad? -`contains` expresses the intent better and has less -failure modes (such as fencepost errors or using `||` instead of `&&`). - -### Example -``` -// given -let x = 6; - -assert!(x >= 3 && x < 8); -``` -Use instead: -``` -assert!((3..8).contains(&x)); -``` \ No newline at end of file diff --git a/src/docs/manual_rem_euclid.txt b/src/docs/manual_rem_euclid.txt deleted file mode 100644 index d3bb8c61304e..000000000000 --- a/src/docs/manual_rem_euclid.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation -of `x.rem_euclid(4)`. - -### Why is this bad? -It's simpler and more readable. - -### Example -``` -let x: i32 = 24; -let rem = ((x % 4) + 4) % 4; -``` -Use instead: -``` -let x: i32 = 24; -let rem = x.rem_euclid(4); -``` \ No newline at end of file diff --git a/src/docs/manual_retain.txt b/src/docs/manual_retain.txt deleted file mode 100644 index cd4f65a93fc3..000000000000 --- a/src/docs/manual_retain.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for code to be replaced by `.retain()`. -### Why is this bad? -`.retain()` is simpler and avoids needless allocation. -### Example -``` -let mut vec = vec![0, 1, 2]; -vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); -vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); -``` -Use instead: -``` -let mut vec = vec![0, 1, 2]; -vec.retain(|x| x % 2 == 0); -``` \ No newline at end of file diff --git a/src/docs/manual_saturating_arithmetic.txt b/src/docs/manual_saturating_arithmetic.txt deleted file mode 100644 index d9f5d3d11871..000000000000 --- a/src/docs/manual_saturating_arithmetic.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`. - -### Why is this bad? -These can be written simply with `saturating_add/sub` methods. - -### Example -``` -let add = x.checked_add(y).unwrap_or(u32::MAX); -let sub = x.checked_sub(y).unwrap_or(u32::MIN); -``` - -can be written using dedicated methods for saturating addition/subtraction as: - -``` -let add = x.saturating_add(y); -let sub = x.saturating_sub(y); -``` \ No newline at end of file diff --git a/src/docs/manual_split_once.txt b/src/docs/manual_split_once.txt deleted file mode 100644 index 291ae447de08..000000000000 --- a/src/docs/manual_split_once.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for usages of `str::splitn(2, _)` - -### Why is this bad? -`split_once` is both clearer in intent and slightly more efficient. - -### Example -``` -let s = "key=value=add"; -let (key, value) = s.splitn(2, '=').next_tuple()?; -let value = s.splitn(2, '=').nth(1)?; - -let mut parts = s.splitn(2, '='); -let key = parts.next()?; -let value = parts.next()?; -``` - -Use instead: -``` -let s = "key=value=add"; -let (key, value) = s.split_once('=')?; -let value = s.split_once('=')?.1; - -let (key, value) = s.split_once('=')?; -``` - -### Limitations -The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()` -in two separate `let` statements that immediately follow the `splitn()` \ No newline at end of file diff --git a/src/docs/manual_str_repeat.txt b/src/docs/manual_str_repeat.txt deleted file mode 100644 index 1d4a7a48e203..000000000000 --- a/src/docs/manual_str_repeat.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for manual implementations of `str::repeat` - -### Why is this bad? -These are both harder to read, as well as less performant. - -### Example -``` -let x: String = std::iter::repeat('x').take(10).collect(); -``` - -Use instead: -``` -let x: String = "x".repeat(10); -``` \ No newline at end of file diff --git a/src/docs/manual_string_new.txt b/src/docs/manual_string_new.txt deleted file mode 100644 index 4cbc43f8f843..000000000000 --- a/src/docs/manual_string_new.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does - -Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`, -`String::from("")` and others. - -### Why is this bad? - -Different ways of creating an empty string makes your code less standardized, which can -be confusing. - -### Example -``` -let a = "".to_string(); -let b: String = "".into(); -``` -Use instead: -``` -let a = String::new(); -let b = String::new(); -``` \ No newline at end of file diff --git a/src/docs/manual_strip.txt b/src/docs/manual_strip.txt deleted file mode 100644 index f32d8e7a09bb..000000000000 --- a/src/docs/manual_strip.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using -the pattern's length. - -### Why is this bad? -Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no -slicing which may panic and the compiler does not need to insert this panic code. It is -also sometimes more readable as it removes the need for duplicating or storing the pattern -used by `str::{starts,ends}_with` and in the slicing. - -### Example -``` -let s = "hello, world!"; -if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); -} -``` -Use instead: -``` -let s = "hello, world!"; -if let Some(end) = s.strip_prefix("hello, ") { - assert_eq!(end.to_uppercase(), "WORLD!"); -} -``` \ No newline at end of file diff --git a/src/docs/manual_swap.txt b/src/docs/manual_swap.txt deleted file mode 100644 index bd9526288e35..000000000000 --- a/src/docs/manual_swap.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for manual swapping. - -### Why is this bad? -The `std::mem::swap` function exposes the intent better -without deinitializing or copying either variable. - -### Example -``` -let mut a = 42; -let mut b = 1337; - -let t = b; -b = a; -a = t; -``` -Use std::mem::swap(): -``` -let mut a = 1; -let mut b = 2; -std::mem::swap(&mut a, &mut b); -``` \ No newline at end of file diff --git a/src/docs/manual_unwrap_or.txt b/src/docs/manual_unwrap_or.txt deleted file mode 100644 index 1fd7d831bfca..000000000000 --- a/src/docs/manual_unwrap_or.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. - -### Why is this bad? -Concise code helps focusing on behavior instead of boilerplate. - -### Example -``` -let foo: Option = None; -match foo { - Some(v) => v, - None => 1, -}; -``` - -Use instead: -``` -let foo: Option = None; -foo.unwrap_or(1); -``` \ No newline at end of file diff --git a/src/docs/many_single_char_names.txt b/src/docs/many_single_char_names.txt deleted file mode 100644 index 55ee5da55574..000000000000 --- a/src/docs/many_single_char_names.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for too many variables whose name consists of a -single character. - -### Why is this bad? -It's hard to memorize what a variable means without a -descriptive name. - -### Example -``` -let (a, b, c, d, e, f, g) = (...); -``` \ No newline at end of file diff --git a/src/docs/map_clone.txt b/src/docs/map_clone.txt deleted file mode 100644 index 3ee27f072ef3..000000000000 --- a/src/docs/map_clone.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `map(|x| x.clone())` or -dereferencing closures for `Copy` types, on `Iterator` or `Option`, -and suggests `cloned()` or `copied()` instead - -### Why is this bad? -Readability, this can be written more concisely - -### Example -``` -let x = vec![42, 43]; -let y = x.iter(); -let z = y.map(|i| *i); -``` - -The correct use would be: - -``` -let x = vec![42, 43]; -let y = x.iter(); -let z = y.cloned(); -``` \ No newline at end of file diff --git a/src/docs/map_collect_result_unit.txt b/src/docs/map_collect_result_unit.txt deleted file mode 100644 index 9b720612495c..000000000000 --- a/src/docs/map_collect_result_unit.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for usage of `_.map(_).collect::()`. - -### Why is this bad? -Using `try_for_each` instead is more readable and idiomatic. - -### Example -``` -(0..3).map(|t| Err(t)).collect::>(); -``` -Use instead: -``` -(0..3).try_for_each(|t| Err(t)); -``` \ No newline at end of file diff --git a/src/docs/map_entry.txt b/src/docs/map_entry.txt deleted file mode 100644 index 20dba1798d0d..000000000000 --- a/src/docs/map_entry.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for uses of `contains_key` + `insert` on `HashMap` -or `BTreeMap`. - -### Why is this bad? -Using `entry` is more efficient. - -### Known problems -The suggestion may have type inference errors in some cases. e.g. -``` -let mut map = std::collections::HashMap::new(); -let _ = if !map.contains_key(&0) { - map.insert(0, 0) -} else { - None -}; -``` - -### Example -``` -if !map.contains_key(&k) { - map.insert(k, v); -} -``` -Use instead: -``` -map.entry(k).or_insert(v); -``` \ No newline at end of file diff --git a/src/docs/map_err_ignore.txt b/src/docs/map_err_ignore.txt deleted file mode 100644 index 2606c13a7afd..000000000000 --- a/src/docs/map_err_ignore.txt +++ /dev/null @@ -1,93 +0,0 @@ -### What it does -Checks for instances of `map_err(|_| Some::Enum)` - -### Why is this bad? -This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error - -### Example -Before: -``` -use std::fmt; - -#[derive(Debug)] -enum Error { - Indivisible, - Remainder(u8), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::Indivisible => write!(f, "could not divide input by three"), - Error::Remainder(remainder) => write!( - f, - "input is not divisible by three, remainder = {}", - remainder - ), - } - } -} - -impl std::error::Error for Error {} - -fn divisible_by_3(input: &str) -> Result<(), Error> { - input - .parse::() - .map_err(|_| Error::Indivisible) - .map(|v| v % 3) - .and_then(|remainder| { - if remainder == 0 { - Ok(()) - } else { - Err(Error::Remainder(remainder as u8)) - } - }) -} - ``` - - After: - ```rust -use std::{fmt, num::ParseIntError}; - -#[derive(Debug)] -enum Error { - Indivisible(ParseIntError), - Remainder(u8), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::Indivisible(_) => write!(f, "could not divide input by three"), - Error::Remainder(remainder) => write!( - f, - "input is not divisible by three, remainder = {}", - remainder - ), - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Indivisible(source) => Some(source), - _ => None, - } - } -} - -fn divisible_by_3(input: &str) -> Result<(), Error> { - input - .parse::() - .map_err(Error::Indivisible) - .map(|v| v % 3) - .and_then(|remainder| { - if remainder == 0 { - Ok(()) - } else { - Err(Error::Remainder(remainder as u8)) - } - }) -} -``` \ No newline at end of file diff --git a/src/docs/map_flatten.txt b/src/docs/map_flatten.txt deleted file mode 100644 index 73c0e51407f2..000000000000 --- a/src/docs/map_flatten.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option` - -### Why is this bad? -Readability, this can be written more concisely as -`_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option` - -### Example -``` -let vec = vec![vec![1]]; -let opt = Some(5); - -vec.iter().map(|x| x.iter()).flatten(); -opt.map(|x| Some(x * 2)).flatten(); -``` - -Use instead: -``` -vec.iter().flat_map(|x| x.iter()); -opt.and_then(|x| Some(x * 2)); -``` \ No newline at end of file diff --git a/src/docs/map_identity.txt b/src/docs/map_identity.txt deleted file mode 100644 index e2e7af0bed9b..000000000000 --- a/src/docs/map_identity.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for instances of `map(f)` where `f` is the identity function. - -### Why is this bad? -It can be written more concisely without the call to `map`. - -### Example -``` -let x = [1, 2, 3]; -let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect(); -``` -Use instead: -``` -let x = [1, 2, 3]; -let y: Vec<_> = x.iter().map(|x| 2*x).collect(); -``` \ No newline at end of file diff --git a/src/docs/map_unwrap_or.txt b/src/docs/map_unwrap_or.txt deleted file mode 100644 index 485b29f01b10..000000000000 --- a/src/docs/map_unwrap_or.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or -`result.map(_).unwrap_or_else(_)`. - -### Why is this bad? -Readability, these can be written more concisely (resp.) as -`option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`. - -### Known problems -The order of the arguments is not in execution order - -### Examples -``` -option.map(|a| a + 1).unwrap_or(0); -result.map(|a| a + 1).unwrap_or_else(some_function); -``` - -Use instead: -``` -option.map_or(0, |a| a + 1); -result.map_or_else(some_function, |a| a + 1); -``` \ No newline at end of file diff --git a/src/docs/match_as_ref.txt b/src/docs/match_as_ref.txt deleted file mode 100644 index 5e5f3d645a4e..000000000000 --- a/src/docs/match_as_ref.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for match which is used to add a reference to an -`Option` value. - -### Why is this bad? -Using `as_ref()` or `as_mut()` instead is shorter. - -### Example -``` -let x: Option<()> = None; - -let r: Option<&()> = match x { - None => None, - Some(ref v) => Some(v), -}; -``` - -Use instead: -``` -let x: Option<()> = None; - -let r: Option<&()> = x.as_ref(); -``` \ No newline at end of file diff --git a/src/docs/match_bool.txt b/src/docs/match_bool.txt deleted file mode 100644 index 96f9e1f8b7d1..000000000000 --- a/src/docs/match_bool.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for matches where match expression is a `bool`. It -suggests to replace the expression with an `if...else` block. - -### Why is this bad? -It makes the code less readable. - -### Example -``` -let condition: bool = true; -match condition { - true => foo(), - false => bar(), -} -``` -Use if/else instead: -``` -let condition: bool = true; -if condition { - foo(); -} else { - bar(); -} -``` \ No newline at end of file diff --git a/src/docs/match_like_matches_macro.txt b/src/docs/match_like_matches_macro.txt deleted file mode 100644 index 643e2ddc97ba..000000000000 --- a/src/docs/match_like_matches_macro.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for `match` or `if let` expressions producing a -`bool` that could be written using `matches!` - -### Why is this bad? -Readability and needless complexity. - -### Known problems -This lint falsely triggers, if there are arms with -`cfg` attributes that remove an arm evaluating to `false`. - -### Example -``` -let x = Some(5); - -let a = match x { - Some(0) => true, - _ => false, -}; - -let a = if let Some(0) = x { - true -} else { - false -}; -``` - -Use instead: -``` -let x = Some(5); -let a = matches!(x, Some(0)); -``` \ No newline at end of file diff --git a/src/docs/match_on_vec_items.txt b/src/docs/match_on_vec_items.txt deleted file mode 100644 index 981d18d0f9ed..000000000000 --- a/src/docs/match_on_vec_items.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `match vec[idx]` or `match vec[n..m]`. - -### Why is this bad? -This can panic at runtime. - -### Example -``` -let arr = vec![0, 1, 2, 3]; -let idx = 1; - -match arr[idx] { - 0 => println!("{}", 0), - 1 => println!("{}", 3), - _ => {}, -} -``` - -Use instead: -``` -let arr = vec![0, 1, 2, 3]; -let idx = 1; - -match arr.get(idx) { - Some(0) => println!("{}", 0), - Some(1) => println!("{}", 3), - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/match_overlapping_arm.txt b/src/docs/match_overlapping_arm.txt deleted file mode 100644 index 841c091bd5ca..000000000000 --- a/src/docs/match_overlapping_arm.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for overlapping match arms. - -### Why is this bad? -It is likely to be an error and if not, makes the code -less obvious. - -### Example -``` -let x = 5; -match x { - 1..=10 => println!("1 ... 10"), - 5..=15 => println!("5 ... 15"), - _ => (), -} -``` \ No newline at end of file diff --git a/src/docs/match_ref_pats.txt b/src/docs/match_ref_pats.txt deleted file mode 100644 index b1d902995097..000000000000 --- a/src/docs/match_ref_pats.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for matches where all arms match a reference, -suggesting to remove the reference and deref the matched expression -instead. It also checks for `if let &foo = bar` blocks. - -### Why is this bad? -It just makes the code less readable. That reference -destructuring adds nothing to the code. - -### Example -``` -match x { - &A(ref y) => foo(y), - &B => bar(), - _ => frob(&x), -} -``` - -Use instead: -``` -match *x { - A(ref y) => foo(y), - B => bar(), - _ => frob(x), -} -``` \ No newline at end of file diff --git a/src/docs/match_result_ok.txt b/src/docs/match_result_ok.txt deleted file mode 100644 index eea7c8e00f1b..000000000000 --- a/src/docs/match_result_ok.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for unnecessary `ok()` in `while let`. - -### Why is this bad? -Calling `ok()` in `while let` is unnecessary, instead match -on `Ok(pat)` - -### Example -``` -while let Some(value) = iter.next().ok() { - vec.push(value) -} - -if let Some(value) = iter.next().ok() { - vec.push(value) -} -``` -Use instead: -``` -while let Ok(value) = iter.next() { - vec.push(value) -} - -if let Ok(value) = iter.next() { - vec.push(value) -} -``` \ No newline at end of file diff --git a/src/docs/match_same_arms.txt b/src/docs/match_same_arms.txt deleted file mode 100644 index 14edf12032e0..000000000000 --- a/src/docs/match_same_arms.txt +++ /dev/null @@ -1,38 +0,0 @@ -### What it does -Checks for `match` with identical arm bodies. - -### Why is this bad? -This is probably a copy & paste error. If arm bodies -are the same on purpose, you can factor them -[using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). - -### Known problems -False positive possible with order dependent `match` -(see issue -[#860](https://github.com/rust-lang/rust-clippy/issues/860)). - -### Example -``` -match foo { - Bar => bar(), - Quz => quz(), - Baz => bar(), // <= oops -} -``` - -This should probably be -``` -match foo { - Bar => bar(), - Quz => quz(), - Baz => baz(), // <= fixed -} -``` - -or if the original code was not a typo: -``` -match foo { - Bar | Baz => bar(), // <= shows the intent better - Quz => quz(), -} -``` \ No newline at end of file diff --git a/src/docs/match_single_binding.txt b/src/docs/match_single_binding.txt deleted file mode 100644 index 67ded0bbd553..000000000000 --- a/src/docs/match_single_binding.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for useless match that binds to only one value. - -### Why is this bad? -Readability and needless complexity. - -### Known problems - Suggested replacements may be incorrect when `match` -is actually binding temporary value, bringing a 'dropped while borrowed' error. - -### Example -``` -match (a, b) { - (c, d) => { - // useless match - } -} -``` - -Use instead: -``` -let (c, d) = (a, b); -``` \ No newline at end of file diff --git a/src/docs/match_str_case_mismatch.txt b/src/docs/match_str_case_mismatch.txt deleted file mode 100644 index 19e74c2084ea..000000000000 --- a/src/docs/match_str_case_mismatch.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `match` expressions modifying the case of a string with non-compliant arms - -### Why is this bad? -The arm is unreachable, which is likely a mistake - -### Example -``` -match &*text.to_ascii_lowercase() { - "foo" => {}, - "Bar" => {}, - _ => {}, -} -``` -Use instead: -``` -match &*text.to_ascii_lowercase() { - "foo" => {}, - "bar" => {}, - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/match_wild_err_arm.txt b/src/docs/match_wild_err_arm.txt deleted file mode 100644 index f89b3a23a1ca..000000000000 --- a/src/docs/match_wild_err_arm.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for arm which matches all errors with `Err(_)` -and take drastic actions like `panic!`. - -### Why is this bad? -It is generally a bad practice, similar to -catching all exceptions in java with `catch(Exception)` - -### Example -``` -let x: Result = Ok(3); -match x { - Ok(_) => println!("ok"), - Err(_) => panic!("err"), -} -``` \ No newline at end of file diff --git a/src/docs/match_wildcard_for_single_variants.txt b/src/docs/match_wildcard_for_single_variants.txt deleted file mode 100644 index 25559b9ecdcf..000000000000 --- a/src/docs/match_wildcard_for_single_variants.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for wildcard enum matches for a single variant. - -### Why is this bad? -New enum variants added by library updates can be missed. - -### Known problems -Suggested replacements may not use correct path to enum -if it's not present in the current scope. - -### Example -``` -match x { - Foo::A => {}, - Foo::B => {}, - _ => {}, -} -``` - -Use instead: -``` -match x { - Foo::A => {}, - Foo::B => {}, - Foo::C => {}, -} -``` \ No newline at end of file diff --git a/src/docs/maybe_infinite_iter.txt b/src/docs/maybe_infinite_iter.txt deleted file mode 100644 index 1204a49b4668..000000000000 --- a/src/docs/maybe_infinite_iter.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for iteration that may be infinite. - -### Why is this bad? -While there may be places where this is acceptable -(e.g., in event streams), in most cases this is simply an error. - -### Known problems -The code may have a condition to stop iteration, but -this lint is not clever enough to analyze it. - -### Example -``` -let infinite_iter = 0..; -[0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); -``` \ No newline at end of file diff --git a/src/docs/mem_forget.txt b/src/docs/mem_forget.txt deleted file mode 100644 index a6888c48fc33..000000000000 --- a/src/docs/mem_forget.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for usage of `std::mem::forget(t)` where `t` is -`Drop`. - -### Why is this bad? -`std::mem::forget(t)` prevents `t` from running its -destructor, possibly causing leaks. - -### Example -``` -mem::forget(Rc::new(55)) -``` \ No newline at end of file diff --git a/src/docs/mem_replace_option_with_none.txt b/src/docs/mem_replace_option_with_none.txt deleted file mode 100644 index 7f243d1c1656..000000000000 --- a/src/docs/mem_replace_option_with_none.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `mem::replace()` on an `Option` with -`None`. - -### Why is this bad? -`Option` already has the method `take()` for -taking its current value (Some(..) or None) and replacing it with -`None`. - -### Example -``` -use std::mem; - -let mut an_option = Some(0); -let replaced = mem::replace(&mut an_option, None); -``` -Is better expressed with: -``` -let mut an_option = Some(0); -let taken = an_option.take(); -``` \ No newline at end of file diff --git a/src/docs/mem_replace_with_default.txt b/src/docs/mem_replace_with_default.txt deleted file mode 100644 index 24e0913a30c9..000000000000 --- a/src/docs/mem_replace_with_default.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for `std::mem::replace` on a value of type -`T` with `T::default()`. - -### Why is this bad? -`std::mem` module already has the method `take` to -take the current value and replace it with the default value of that type. - -### Example -``` -let mut text = String::from("foo"); -let replaced = std::mem::replace(&mut text, String::default()); -``` -Is better expressed with: -``` -let mut text = String::from("foo"); -let taken = std::mem::take(&mut text); -``` \ No newline at end of file diff --git a/src/docs/mem_replace_with_uninit.txt b/src/docs/mem_replace_with_uninit.txt deleted file mode 100644 index 0bb483668abc..000000000000 --- a/src/docs/mem_replace_with_uninit.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for `mem::replace(&mut _, mem::uninitialized())` -and `mem::replace(&mut _, mem::zeroed())`. - -### Why is this bad? -This will lead to undefined behavior even if the -value is overwritten later, because the uninitialized value may be -observed in the case of a panic. - -### Example -``` -use std::mem; - -#[allow(deprecated, invalid_value)] -fn myfunc (v: &mut Vec) { - let taken_v = unsafe { mem::replace(v, mem::uninitialized()) }; - let new_v = may_panic(taken_v); // undefined behavior on panic - mem::forget(mem::replace(v, new_v)); -} -``` - -The [take_mut](https://docs.rs/take_mut) crate offers a sound solution, -at the cost of either lazily creating a replacement value or aborting -on panic, to ensure that the uninitialized value cannot be observed. \ No newline at end of file diff --git a/src/docs/min_max.txt b/src/docs/min_max.txt deleted file mode 100644 index 6acf0f932e98..000000000000 --- a/src/docs/min_max.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for expressions where `std::cmp::min` and `max` are -used to clamp values, but switched so that the result is constant. - -### Why is this bad? -This is in all probability not the intended outcome. At -the least it hurts readability of the code. - -### Example -``` -min(0, max(100, x)) - -// or - -x.max(100).min(0) -``` -It will always be equal to `0`. Probably the author meant to clamp the value -between 0 and 100, but has erroneously swapped `min` and `max`. \ No newline at end of file diff --git a/src/docs/mismatched_target_os.txt b/src/docs/mismatched_target_os.txt deleted file mode 100644 index 51e5ec6e7c5c..000000000000 --- a/src/docs/mismatched_target_os.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for cfg attributes having operating systems used in target family position. - -### Why is this bad? -The configuration option will not be recognised and the related item will not be included -by the conditional compilation engine. - -### Example -``` -#[cfg(linux)] -fn conditional() { } -``` - -Use instead: -``` -#[cfg(target_os = "linux")] -fn conditional() { } - -// or - -#[cfg(unix)] -fn conditional() { } -``` -Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details. \ No newline at end of file diff --git a/src/docs/mismatching_type_param_order.txt b/src/docs/mismatching_type_param_order.txt deleted file mode 100644 index ffc7f32d0aad..000000000000 --- a/src/docs/mismatching_type_param_order.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for type parameters which are positioned inconsistently between -a type definition and impl block. Specifically, a parameter in an impl -block which has the same name as a parameter in the type def, but is in -a different place. - -### Why is this bad? -Type parameters are determined by their position rather than name. -Naming type parameters inconsistently may cause you to refer to the -wrong type parameter. - -### Limitations -This lint only applies to impl blocks with simple generic params, e.g. -`A`. If there is anything more complicated, such as a tuple, it will be -ignored. - -### Example -``` -struct Foo { - x: A, - y: B, -} -// inside the impl, B refers to Foo::A -impl Foo {} -``` -Use instead: -``` -struct Foo { - x: A, - y: B, -} -impl Foo {} -``` \ No newline at end of file diff --git a/src/docs/misrefactored_assign_op.txt b/src/docs/misrefactored_assign_op.txt deleted file mode 100644 index 3d691fe4178b..000000000000 --- a/src/docs/misrefactored_assign_op.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for `a op= a op b` or `a op= b op a` patterns. - -### Why is this bad? -Most likely these are bugs where one meant to write `a -op= b`. - -### Known problems -Clippy cannot know for sure if `a op= a op b` should have -been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both. -If `a op= a op b` is really the correct behavior it should be -written as `a = a op a op b` as it's less confusing. - -### Example -``` -let mut a = 5; -let b = 2; -// ... -a += a + b; -``` \ No newline at end of file diff --git a/src/docs/missing_const_for_fn.txt b/src/docs/missing_const_for_fn.txt deleted file mode 100644 index 067614d4c46b..000000000000 --- a/src/docs/missing_const_for_fn.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Suggests the use of `const` in functions and methods where possible. - -### Why is this bad? -Not having the function const prevents callers of the function from being const as well. - -### Known problems -Const functions are currently still being worked on, with some features only being available -on nightly. This lint does not consider all edge cases currently and the suggestions may be -incorrect if you are using this lint on stable. - -Also, the lint only runs one pass over the code. Consider these two non-const functions: - -``` -fn a() -> i32 { - 0 -} -fn b() -> i32 { - a() -} -``` - -When running Clippy, the lint will only suggest to make `a` const, because `b` at this time -can't be const as it calls a non-const function. Making `a` const and running Clippy again, -will suggest to make `b` const, too. - -### Example -``` -fn new() -> Self { - Self { random_number: 42 } -} -``` - -Could be a const fn: - -``` -const fn new() -> Self { - Self { random_number: 42 } -} -``` \ No newline at end of file diff --git a/src/docs/missing_docs_in_private_items.txt b/src/docs/missing_docs_in_private_items.txt deleted file mode 100644 index 5d37505bb171..000000000000 --- a/src/docs/missing_docs_in_private_items.txt +++ /dev/null @@ -1,9 +0,0 @@ -### What it does -Warns if there is missing doc for any documentable item -(public or private). - -### Why is this bad? -Doc is good. *rustc* has a `MISSING_DOCS` -allowed-by-default lint for -public members, but has no way to enforce documentation of private items. -This lint fixes that. \ No newline at end of file diff --git a/src/docs/missing_enforced_import_renames.txt b/src/docs/missing_enforced_import_renames.txt deleted file mode 100644 index 8f4649bd5923..000000000000 --- a/src/docs/missing_enforced_import_renames.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for imports that do not rename the item as specified -in the `enforce-import-renames` config option. - -### Why is this bad? -Consistency is important, if a project has defined import -renames they should be followed. More practically, some item names are too -vague outside of their defining scope this can enforce a more meaningful naming. - -### Example -An example clippy.toml configuration: -``` -enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }] -``` - -``` -use serde_json::Value; -``` -Use instead: -``` -use serde_json::Value as JsonValue; -``` \ No newline at end of file diff --git a/src/docs/missing_errors_doc.txt b/src/docs/missing_errors_doc.txt deleted file mode 100644 index 028778d85aeb..000000000000 --- a/src/docs/missing_errors_doc.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks the doc comments of publicly visible functions that -return a `Result` type and warns if there is no `# Errors` section. - -### Why is this bad? -Documenting the type of errors that can be returned from a -function can help callers write code to handle the errors appropriately. - -### Examples -Since the following function returns a `Result` it has an `# Errors` section in -its doc comment: - -``` -/// # Errors -/// -/// Will return `Err` if `filename` does not exist or the user does not have -/// permission to read it. -pub fn read(filename: String) -> io::Result { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/missing_inline_in_public_items.txt b/src/docs/missing_inline_in_public_items.txt deleted file mode 100644 index d90c50fe7f9e..000000000000 --- a/src/docs/missing_inline_in_public_items.txt +++ /dev/null @@ -1,45 +0,0 @@ -### What it does -It lints if an exported function, method, trait method with default impl, -or trait method impl is not `#[inline]`. - -### Why is this bad? -In general, it is not. Functions can be inlined across -crates when that's profitable as long as any form of LTO is used. When LTO is disabled, -functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates -might intend for most of the methods in their public API to be able to be inlined across -crates even when LTO is disabled. For these types of crates, enabling this lint might make -sense. It allows the crate to require all exported methods to be `#[inline]` by default, and -then opt out for specific methods where this might not make sense. - -### Example -``` -pub fn foo() {} // missing #[inline] -fn ok() {} // ok -#[inline] pub fn bar() {} // ok -#[inline(always)] pub fn baz() {} // ok - -pub trait Bar { - fn bar(); // ok - fn def_bar() {} // missing #[inline] -} - -struct Baz; -impl Baz { - fn private() {} // ok -} - -impl Bar for Baz { - fn bar() {} // ok - Baz is not exported -} - -pub struct PubBaz; -impl PubBaz { - fn private() {} // ok - pub fn not_private() {} // missing #[inline] -} - -impl Bar for PubBaz { - fn bar() {} // missing #[inline] - fn def_bar() {} // missing #[inline] -} -``` \ No newline at end of file diff --git a/src/docs/missing_panics_doc.txt b/src/docs/missing_panics_doc.txt deleted file mode 100644 index e5e39a824519..000000000000 --- a/src/docs/missing_panics_doc.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks the doc comments of publicly visible functions that -may panic and warns if there is no `# Panics` section. - -### Why is this bad? -Documenting the scenarios in which panicking occurs -can help callers who do not want to panic to avoid those situations. - -### Examples -Since the following function may panic it has a `# Panics` section in -its doc comment: - -``` -/// # Panics -/// -/// Will panic if y is 0 -pub fn divide_by(x: i32, y: i32) -> i32 { - if y == 0 { - panic!("Cannot divide by 0") - } else { - x / y - } -} -``` \ No newline at end of file diff --git a/src/docs/missing_safety_doc.txt b/src/docs/missing_safety_doc.txt deleted file mode 100644 index 6492eb84f63b..000000000000 --- a/src/docs/missing_safety_doc.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the doc comments of publicly visible -unsafe functions and warns if there is no `# Safety` section. - -### Why is this bad? -Unsafe functions should document their safety -preconditions, so that users can be sure they are using them safely. - -### Examples -``` -/// This function should really be documented -pub unsafe fn start_apocalypse(u: &mut Universe) { - unimplemented!(); -} -``` - -At least write a line about safety: - -``` -/// # Safety -/// -/// This function should not be called before the horsemen are ready. -pub unsafe fn start_apocalypse(u: &mut Universe) { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/missing_spin_loop.txt b/src/docs/missing_spin_loop.txt deleted file mode 100644 index 3a06a91d7183..000000000000 --- a/src/docs/missing_spin_loop.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Check for empty spin loops - -### Why is this bad? -The loop body should have something like `thread::park()` or at least -`std::hint::spin_loop()` to avoid needlessly burning cycles and conserve -energy. Perhaps even better use an actual lock, if possible. - -### Known problems -This lint doesn't currently trigger on `while let` or -`loop { match .. { .. } }` loops, which would be considered idiomatic in -combination with e.g. `AtomicBool::compare_exchange_weak`. - -### Example - -``` -use core::sync::atomic::{AtomicBool, Ordering}; -let b = AtomicBool::new(true); -// give a ref to `b` to another thread,wait for it to become false -while b.load(Ordering::Acquire) {}; -``` -Use instead: -``` -while b.load(Ordering::Acquire) { - std::hint::spin_loop() -} -``` \ No newline at end of file diff --git a/src/docs/mistyped_literal_suffixes.txt b/src/docs/mistyped_literal_suffixes.txt deleted file mode 100644 index 1760fcbfeacc..000000000000 --- a/src/docs/mistyped_literal_suffixes.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Warns for mistyped suffix in literals - -### Why is this bad? -This is most probably a typo - -### Known problems -- Does not match on integers too large to fit in the corresponding unsigned type -- Does not match on `_127` since that is a valid grouping for decimal and octal numbers - -### Example -``` -`2_32` => `2_i32` -`250_8 => `250_u8` -``` \ No newline at end of file diff --git a/src/docs/mixed_case_hex_literals.txt b/src/docs/mixed_case_hex_literals.txt deleted file mode 100644 index d2d01e0c98eb..000000000000 --- a/src/docs/mixed_case_hex_literals.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Warns on hexadecimal literals with mixed-case letter -digits. - -### Why is this bad? -It looks confusing. - -### Example -``` -0x1a9BAcD -``` - -Use instead: -``` -0x1A9BACD -``` \ No newline at end of file diff --git a/src/docs/mixed_read_write_in_expression.txt b/src/docs/mixed_read_write_in_expression.txt deleted file mode 100644 index 02d1c5d05252..000000000000 --- a/src/docs/mixed_read_write_in_expression.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for a read and a write to the same variable where -whether the read occurs before or after the write depends on the evaluation -order of sub-expressions. - -### Why is this bad? -It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands), -the operands of these expressions are evaluated before applying the effects of the expression. - -### Known problems -Code which intentionally depends on the evaluation -order, or which is correct for any evaluation order. - -### Example -``` -let mut x = 0; - -let a = { - x = 1; - 1 -} + x; -// Unclear whether a is 1 or 2. -``` - -Use instead: -``` -let tmp = { - x = 1; - 1 -}; -let a = tmp + x; -``` \ No newline at end of file diff --git a/src/docs/mod_module_files.txt b/src/docs/mod_module_files.txt deleted file mode 100644 index 95bca583afd3..000000000000 --- a/src/docs/mod_module_files.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks that module layout uses only self named module files, bans `mod.rs` files. - -### Why is this bad? -Having multiple module layout styles in a project can be confusing. - -### Example -``` -src/ - stuff/ - stuff_files.rs - mod.rs - lib.rs -``` -Use instead: -``` -src/ - stuff/ - stuff_files.rs - stuff.rs - lib.rs -``` \ No newline at end of file diff --git a/src/docs/module_inception.txt b/src/docs/module_inception.txt deleted file mode 100644 index d80a1b8d8fe5..000000000000 --- a/src/docs/module_inception.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for modules that have the same name as their -parent module - -### Why is this bad? -A typical beginner mistake is to have `mod foo;` and -again `mod foo { .. -}` in `foo.rs`. -The expectation is that items inside the inner `mod foo { .. }` are then -available -through `foo::x`, but they are only available through -`foo::foo::x`. -If this is done on purpose, it would be better to choose a more -representative module name. - -### Example -``` -// lib.rs -mod foo; -// foo.rs -mod foo { - ... -} -``` \ No newline at end of file diff --git a/src/docs/module_name_repetitions.txt b/src/docs/module_name_repetitions.txt deleted file mode 100644 index 3bc05d02780c..000000000000 --- a/src/docs/module_name_repetitions.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Detects type names that are prefixed or suffixed by the -containing module's name. - -### Why is this bad? -It requires the user to type the module name twice. - -### Example -``` -mod cake { - struct BlackForestCake; -} -``` - -Use instead: -``` -mod cake { - struct BlackForest; -} -``` \ No newline at end of file diff --git a/src/docs/modulo_arithmetic.txt b/src/docs/modulo_arithmetic.txt deleted file mode 100644 index ff7296f3c5b8..000000000000 --- a/src/docs/modulo_arithmetic.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for modulo arithmetic. - -### Why is this bad? -The results of modulo (%) operation might differ -depending on the language, when negative numbers are involved. -If you interop with different languages it might be beneficial -to double check all places that use modulo arithmetic. - -For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`. - -### Example -``` -let x = -17 % 3; -``` \ No newline at end of file diff --git a/src/docs/modulo_one.txt b/src/docs/modulo_one.txt deleted file mode 100644 index bc8f95b0be69..000000000000 --- a/src/docs/modulo_one.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for getting the remainder of a division by one or minus -one. - -### Why is this bad? -The result for a divisor of one can only ever be zero; for -minus one it can cause panic/overflow (if the left operand is the minimal value of -the respective integer type) or results in zero. No one will write such code -deliberately, unless trying to win an Underhanded Rust Contest. Even for that -contest, it's probably a bad idea. Use something more underhanded. - -### Example -``` -let a = x % 1; -let a = x % -1; -``` \ No newline at end of file diff --git a/src/docs/multi_assignments.txt b/src/docs/multi_assignments.txt deleted file mode 100644 index ed1f1b420cbd..000000000000 --- a/src/docs/multi_assignments.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for nested assignments. - -### Why is this bad? -While this is in most cases already a type mismatch, -the result of an assignment being `()` can throw off people coming from languages like python or C, -where such assignments return a copy of the assigned value. - -### Example -``` -a = b = 42; -``` -Use instead: -``` -b = 42; -a = b; -``` \ No newline at end of file diff --git a/src/docs/multiple_crate_versions.txt b/src/docs/multiple_crate_versions.txt deleted file mode 100644 index cf2d2c6abee3..000000000000 --- a/src/docs/multiple_crate_versions.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks to see if multiple versions of a crate are being -used. - -### Why is this bad? -This bloats the size of targets, and can lead to -confusing error messages when structs or traits are used interchangeably -between different versions of a crate. - -### Known problems -Because this can be caused purely by the dependencies -themselves, it's not always possible to fix this issue. - -### Example -``` -[dependencies] -ctrlc = "=3.1.0" -ansi_term = "=0.11.0" -``` \ No newline at end of file diff --git a/src/docs/multiple_inherent_impl.txt b/src/docs/multiple_inherent_impl.txt deleted file mode 100644 index 9d42286560ce..000000000000 --- a/src/docs/multiple_inherent_impl.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for multiple inherent implementations of a struct - -### Why is this bad? -Splitting the implementation of a type makes the code harder to navigate. - -### Example -``` -struct X; -impl X { - fn one() {} -} -impl X { - fn other() {} -} -``` - -Could be written: - -``` -struct X; -impl X { - fn one() {} - fn other() {} -} -``` \ No newline at end of file diff --git a/src/docs/must_use_candidate.txt b/src/docs/must_use_candidate.txt deleted file mode 100644 index 70890346fe6b..000000000000 --- a/src/docs/must_use_candidate.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for public functions that have no -`#[must_use]` attribute, but return something not already marked -must-use, have no mutable arg and mutate no statics. - -### Why is this bad? -Not bad at all, this lint just shows places where -you could add the attribute. - -### Known problems -The lint only checks the arguments for mutable -types without looking if they are actually changed. On the other hand, -it also ignores a broad range of potentially interesting side effects, -because we cannot decide whether the programmer intends the function to -be called for the side effect or the result. Expect many false -positives. At least we don't lint if the result type is unit or already -`#[must_use]`. - -### Examples -``` -// this could be annotated with `#[must_use]`. -fn id(t: T) -> T { t } -``` \ No newline at end of file diff --git a/src/docs/must_use_unit.txt b/src/docs/must_use_unit.txt deleted file mode 100644 index cabbb23f8651..000000000000 --- a/src/docs/must_use_unit.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for a `#[must_use]` attribute on -unit-returning functions and methods. - -### Why is this bad? -Unit values are useless. The attribute is likely -a remnant of a refactoring that removed the return type. - -### Examples -``` -#[must_use] -fn useless() { } -``` \ No newline at end of file diff --git a/src/docs/mut_from_ref.txt b/src/docs/mut_from_ref.txt deleted file mode 100644 index cc1da12549a5..000000000000 --- a/src/docs/mut_from_ref.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -This lint checks for functions that take immutable references and return -mutable ones. This will not trigger if no unsafe code exists as there -are multiple safe functions which will do this transformation - -To be on the conservative side, if there's at least one mutable -reference with the output lifetime, this lint will not trigger. - -### Why is this bad? -Creating a mutable reference which can be repeatably derived from an -immutable reference is unsound as it allows creating multiple live -mutable references to the same object. - -This [error](https://github.com/rust-lang/rust/issues/39465) actually -lead to an interim Rust release 1.15.1. - -### Known problems -This pattern is used by memory allocators to allow allocating multiple -objects while returning mutable references to each one. So long as -different mutable references are returned each time such a function may -be safe. - -### Example -``` -fn foo(&Foo) -> &mut Bar { .. } -``` \ No newline at end of file diff --git a/src/docs/mut_mut.txt b/src/docs/mut_mut.txt deleted file mode 100644 index 0bd34dd24b26..000000000000 --- a/src/docs/mut_mut.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for instances of `mut mut` references. - -### Why is this bad? -Multiple `mut`s don't add anything meaningful to the -source. This is either a copy'n'paste error, or it shows a fundamental -misunderstanding of references. - -### Example -``` -let x = &mut &mut y; -``` \ No newline at end of file diff --git a/src/docs/mut_mutex_lock.txt b/src/docs/mut_mutex_lock.txt deleted file mode 100644 index 5e9ad8a3f176..000000000000 --- a/src/docs/mut_mutex_lock.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for `&mut Mutex::lock` calls - -### Why is this bad? -`Mutex::lock` is less efficient than -calling `Mutex::get_mut`. In addition you also have a statically -guarantee that the mutex isn't locked, instead of just a runtime -guarantee. - -### Example -``` -use std::sync::{Arc, Mutex}; - -let mut value_rc = Arc::new(Mutex::new(42_u8)); -let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - -let mut value = value_mutex.lock().unwrap(); -*value += 1; -``` -Use instead: -``` -use std::sync::{Arc, Mutex}; - -let mut value_rc = Arc::new(Mutex::new(42_u8)); -let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - -let value = value_mutex.get_mut().unwrap(); -*value += 1; -``` \ No newline at end of file diff --git a/src/docs/mut_range_bound.txt b/src/docs/mut_range_bound.txt deleted file mode 100644 index e9c38a543b14..000000000000 --- a/src/docs/mut_range_bound.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for loops which have a range bound that is a mutable variable - -### Why is this bad? -One might think that modifying the mutable variable changes the loop bounds - -### Known problems -False positive when mutation is followed by a `break`, but the `break` is not immediately -after the mutation: - -``` -let mut x = 5; -for _ in 0..x { - x += 1; // x is a range bound that is mutated - ..; // some other expression - break; // leaves the loop, so mutation is not an issue -} -``` - -False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072)) - -### Example -``` -let mut foo = 42; -for i in 0..foo { - foo -= 1; - println!("{}", i); // prints numbers from 0 to 42, not 0 to 21 -} -``` \ No newline at end of file diff --git a/src/docs/mutable_key_type.txt b/src/docs/mutable_key_type.txt deleted file mode 100644 index 15fe34f2bb5e..000000000000 --- a/src/docs/mutable_key_type.txt +++ /dev/null @@ -1,61 +0,0 @@ -### What it does -Checks for sets/maps with mutable key types. - -### Why is this bad? -All of `HashMap`, `HashSet`, `BTreeMap` and -`BtreeSet` rely on either the hash or the order of keys be unchanging, -so having types with interior mutability is a bad idea. - -### Known problems - -#### False Positives -It's correct to use a struct that contains interior mutability as a key, when its -implementation of `Hash` or `Ord` doesn't access any of the interior mutable types. -However, this lint is unable to recognize this, so it will often cause false positives in -theses cases. The `bytes` crate is a great example of this. - -#### False Negatives -For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind -indirection. For example, `struct BadKey<'a>(&'a Cell)` will be seen as immutable -and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`. - -This lint does check a few cases for indirection. Firstly, using some standard library -types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and -`BTreeSet`) directly as keys (e.g. in `HashMap>, ()>`) **will** trigger the -lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their -contained type. - -Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`) -apply only to the **address** of the contained value. Therefore, interior mutability -behind raw pointers (e.g. in `HashSet<*mut Cell>`) can't impact the value of `Hash` -or `Ord`, and therefore will not trigger this link. For more info, see issue -[#6745](https://github.com/rust-lang/rust-clippy/issues/6745). - -### Example -``` -use std::cmp::{PartialEq, Eq}; -use std::collections::HashSet; -use std::hash::{Hash, Hasher}; -use std::sync::atomic::AtomicUsize; - -struct Bad(AtomicUsize); -impl PartialEq for Bad { - fn eq(&self, rhs: &Self) -> bool { - .. -; unimplemented!(); - } -} - -impl Eq for Bad {} - -impl Hash for Bad { - fn hash(&self, h: &mut H) { - .. -; unimplemented!(); - } -} - -fn main() { - let _: HashSet = HashSet::new(); -} -``` \ No newline at end of file diff --git a/src/docs/mutex_atomic.txt b/src/docs/mutex_atomic.txt deleted file mode 100644 index 062ac8b323b7..000000000000 --- a/src/docs/mutex_atomic.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usages of `Mutex` where an atomic will do. - -### Why is this bad? -Using a mutex just to make access to a plain bool or -reference sequential is shooting flies with cannons. -`std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and -faster. - -### Known problems -This lint cannot detect if the mutex is actually used -for waiting before a critical section. - -### Example -``` -let x = Mutex::new(&y); -``` - -Use instead: -``` -let x = AtomicBool::new(y); -``` \ No newline at end of file diff --git a/src/docs/mutex_integer.txt b/src/docs/mutex_integer.txt deleted file mode 100644 index f9dbdfb904c9..000000000000 --- a/src/docs/mutex_integer.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usages of `Mutex` where `X` is an integral -type. - -### Why is this bad? -Using a mutex just to make access to a plain integer -sequential is -shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster. - -### Known problems -This lint cannot detect if the mutex is actually used -for waiting before a critical section. - -### Example -``` -let x = Mutex::new(0usize); -``` - -Use instead: -``` -let x = AtomicUsize::new(0usize); -``` \ No newline at end of file diff --git a/src/docs/naive_bytecount.txt b/src/docs/naive_bytecount.txt deleted file mode 100644 index 24659dc79ae7..000000000000 --- a/src/docs/naive_bytecount.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for naive byte counts - -### Why is this bad? -The [`bytecount`](https://crates.io/crates/bytecount) -crate has methods to count your bytes faster, especially for large slices. - -### Known problems -If you have predominantly small slices, the -`bytecount::count(..)` method may actually be slower. However, if you can -ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be -faster in those cases. - -### Example -``` -let count = vec.iter().filter(|x| **x == 0u8).count(); -``` - -Use instead: -``` -let count = bytecount::count(&vec, 0u8); -``` \ No newline at end of file diff --git a/src/docs/needless_arbitrary_self_type.txt b/src/docs/needless_arbitrary_self_type.txt deleted file mode 100644 index 8216a3a3fb6e..000000000000 --- a/src/docs/needless_arbitrary_self_type.txt +++ /dev/null @@ -1,44 +0,0 @@ -### What it does -The lint checks for `self` in fn parameters that -specify the `Self`-type explicitly -### Why is this bad? -Increases the amount and decreases the readability of code - -### Example -``` -enum ValType { - I32, - I64, - F32, - F64, -} - -impl ValType { - pub fn bytes(self: Self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } -} -``` - -Could be rewritten as - -``` -enum ValType { - I32, - I64, - F32, - F64, -} - -impl ValType { - pub fn bytes(self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } -} -``` \ No newline at end of file diff --git a/src/docs/needless_bitwise_bool.txt b/src/docs/needless_bitwise_bool.txt deleted file mode 100644 index fcd7b730aaae..000000000000 --- a/src/docs/needless_bitwise_bool.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using -a lazy and. - -### Why is this bad? -The bitwise operators do not support short-circuiting, so it may hinder code performance. -Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold` - -### Known problems -This lint evaluates only when the right side is determined to have no side effects. At this time, that -determination is quite conservative. - -### Example -``` -let (x,y) = (true, false); -if x & !y {} // where both x and y are booleans -``` -Use instead: -``` -let (x,y) = (true, false); -if x && !y {} -``` \ No newline at end of file diff --git a/src/docs/needless_bool.txt b/src/docs/needless_bool.txt deleted file mode 100644 index b5c78871f14f..000000000000 --- a/src/docs/needless_bool.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for expressions of the form `if c { true } else { -false }` (or vice versa) and suggests using the condition directly. - -### Why is this bad? -Redundant code. - -### Known problems -Maybe false positives: Sometimes, the two branches are -painstakingly documented (which we, of course, do not detect), so they *may* -have some value. Even then, the documentation can be rewritten to match the -shorter code. - -### Example -``` -if x { - false -} else { - true -} -``` - -Use instead: -``` -!x -``` \ No newline at end of file diff --git a/src/docs/needless_borrow.txt b/src/docs/needless_borrow.txt deleted file mode 100644 index 4debcf473723..000000000000 --- a/src/docs/needless_borrow.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for address of operations (`&`) that are going to -be dereferenced immediately by the compiler. - -### Why is this bad? -Suggests that the receiver of the expression borrows -the expression. - -### Example -``` -fn fun(_a: &i32) {} - -let x: &i32 = &&&&&&5; -fun(&x); -``` - -Use instead: -``` -let x: &i32 = &5; -fun(x); -``` \ No newline at end of file diff --git a/src/docs/needless_borrowed_reference.txt b/src/docs/needless_borrowed_reference.txt deleted file mode 100644 index 152459ba1c9d..000000000000 --- a/src/docs/needless_borrowed_reference.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for bindings that needlessly destructure a reference and borrow the inner -value with `&ref`. - -### Why is this bad? -This pattern has no effect in almost all cases. - -### Example -``` -let mut v = Vec::::new(); -v.iter_mut().filter(|&ref a| a.is_empty()); - -if let &[ref first, ref second] = v.as_slice() {} -``` - -Use instead: -``` -let mut v = Vec::::new(); -v.iter_mut().filter(|a| a.is_empty()); - -if let [first, second] = v.as_slice() {} -``` \ No newline at end of file diff --git a/src/docs/needless_collect.txt b/src/docs/needless_collect.txt deleted file mode 100644 index 275c39afc9df..000000000000 --- a/src/docs/needless_collect.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for functions collecting an iterator when collect -is not needed. - -### Why is this bad? -`collect` causes the allocation of a new data structure, -when this allocation may not be needed. - -### Example -``` -let len = iterator.clone().collect::>().len(); -// should be -let len = iterator.count(); -``` \ No newline at end of file diff --git a/src/docs/needless_continue.txt b/src/docs/needless_continue.txt deleted file mode 100644 index 2cee621c1af0..000000000000 --- a/src/docs/needless_continue.txt +++ /dev/null @@ -1,61 +0,0 @@ -### What it does -The lint checks for `if`-statements appearing in loops -that contain a `continue` statement in either their main blocks or their -`else`-blocks, when omitting the `else`-block possibly with some -rearrangement of code can make the code easier to understand. - -### Why is this bad? -Having explicit `else` blocks for `if` statements -containing `continue` in their THEN branch adds unnecessary branching and -nesting to the code. Having an else block containing just `continue` can -also be better written by grouping the statements following the whole `if` -statement within the THEN block and omitting the else block completely. - -### Example -``` -while condition() { - update_condition(); - if x { - // ... - } else { - continue; - } - println!("Hello, world"); -} -``` - -Could be rewritten as - -``` -while condition() { - update_condition(); - if x { - // ... - println!("Hello, world"); - } -} -``` - -As another example, the following code - -``` -loop { - if waiting() { - continue; - } else { - // Do something useful - } - # break; -} -``` -Could be rewritten as - -``` -loop { - if waiting() { - continue; - } - // Do something useful - # break; -} -``` \ No newline at end of file diff --git a/src/docs/needless_doctest_main.txt b/src/docs/needless_doctest_main.txt deleted file mode 100644 index 8f91a7baa715..000000000000 --- a/src/docs/needless_doctest_main.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `fn main() { .. }` in doctests - -### Why is this bad? -The test can be shorter (and likely more readable) -if the `fn main()` is left implicit. - -### Examples -``` -/// An example of a doctest with a `main()` function -/// -/// # Examples -/// -/// ``` -/// fn main() { -/// // this needs not be in an `fn` -/// } -/// ``` -fn needless_main() { - unimplemented!(); -} -``` \ No newline at end of file diff --git a/src/docs/needless_for_each.txt b/src/docs/needless_for_each.txt deleted file mode 100644 index 9ae6dd360c8f..000000000000 --- a/src/docs/needless_for_each.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for usage of `for_each` that would be more simply written as a -`for` loop. - -### Why is this bad? -`for_each` may be used after applying iterator transformers like -`filter` for better readability and performance. It may also be used to fit a simple -operation on one line. -But when none of these apply, a simple `for` loop is more idiomatic. - -### Example -``` -let v = vec![0, 1, 2]; -v.iter().for_each(|elem| { - println!("{}", elem); -}) -``` -Use instead: -``` -let v = vec![0, 1, 2]; -for elem in v.iter() { - println!("{}", elem); -} -``` \ No newline at end of file diff --git a/src/docs/needless_late_init.txt b/src/docs/needless_late_init.txt deleted file mode 100644 index 9e7bbcea9982..000000000000 --- a/src/docs/needless_late_init.txt +++ /dev/null @@ -1,42 +0,0 @@ -### What it does -Checks for late initializations that can be replaced by a `let` statement -with an initializer. - -### Why is this bad? -Assigning in the `let` statement is less repetitive. - -### Example -``` -let a; -a = 1; - -let b; -match 3 { - 0 => b = "zero", - 1 => b = "one", - _ => b = "many", -} - -let c; -if true { - c = 1; -} else { - c = -1; -} -``` -Use instead: -``` -let a = 1; - -let b = match 3 { - 0 => "zero", - 1 => "one", - _ => "many", -}; - -let c = if true { - 1 -} else { - -1 -}; -``` \ No newline at end of file diff --git a/src/docs/needless_lifetimes.txt b/src/docs/needless_lifetimes.txt deleted file mode 100644 index b280caa66b58..000000000000 --- a/src/docs/needless_lifetimes.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for lifetime annotations which can be removed by -relying on lifetime elision. - -### Why is this bad? -The additional lifetimes make the code look more -complicated, while there is nothing out of the ordinary going on. Removing -them leads to more readable code. - -### Known problems -- We bail out if the function has a `where` clause where lifetimes -are mentioned due to potential false positives. -- Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the -placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. - -### Example -``` -// Unnecessary lifetime annotations -fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { - x -} -``` - -Use instead: -``` -fn elided(x: &u8, y: u8) -> &u8 { - x -} -``` \ No newline at end of file diff --git a/src/docs/needless_match.txt b/src/docs/needless_match.txt deleted file mode 100644 index 92b40a5df648..000000000000 --- a/src/docs/needless_match.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result` -when function signatures are the same. - -### Why is this bad? -This `match` block does nothing and might not be what the coder intended. - -### Example -``` -fn foo() -> Result<(), i32> { - match result { - Ok(val) => Ok(val), - Err(err) => Err(err), - } -} - -fn bar() -> Option { - if let Some(val) = option { - Some(val) - } else { - None - } -} -``` - -Could be replaced as - -``` -fn foo() -> Result<(), i32> { - result -} - -fn bar() -> Option { - option -} -``` \ No newline at end of file diff --git a/src/docs/needless_option_as_deref.txt b/src/docs/needless_option_as_deref.txt deleted file mode 100644 index 226396c97ac4..000000000000 --- a/src/docs/needless_option_as_deref.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for no-op uses of `Option::{as_deref, as_deref_mut}`, -for example, `Option<&T>::as_deref()` returns the same type. - -### Why is this bad? -Redundant code and improving readability. - -### Example -``` -let a = Some(&1); -let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> -``` - -Use instead: -``` -let a = Some(&1); -let b = a; -``` \ No newline at end of file diff --git a/src/docs/needless_option_take.txt b/src/docs/needless_option_take.txt deleted file mode 100644 index 6bac65a13b5b..000000000000 --- a/src/docs/needless_option_take.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for calling `take` function after `as_ref`. - -### Why is this bad? -Redundant code. `take` writes `None` to its argument. -In this case the modification is useless as it's a temporary that cannot be read from afterwards. - -### Example -``` -let x = Some(3); -x.as_ref().take(); -``` -Use instead: -``` -let x = Some(3); -x.as_ref(); -``` \ No newline at end of file diff --git a/src/docs/needless_parens_on_range_literals.txt b/src/docs/needless_parens_on_range_literals.txt deleted file mode 100644 index 85fab10cb5f6..000000000000 --- a/src/docs/needless_parens_on_range_literals.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -The lint checks for parenthesis on literals in range statements that are -superfluous. - -### Why is this bad? -Having superfluous parenthesis makes the code less readable -overhead when reading. - -### Example - -``` -for i in (0)..10 { - println!("{i}"); -} -``` - -Use instead: - -``` -for i in 0..10 { - println!("{i}"); -} -``` \ No newline at end of file diff --git a/src/docs/needless_pass_by_value.txt b/src/docs/needless_pass_by_value.txt deleted file mode 100644 index 58c420b19f62..000000000000 --- a/src/docs/needless_pass_by_value.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for functions taking arguments by value, but not -consuming them in its -body. - -### Why is this bad? -Taking arguments by reference is more flexible and can -sometimes avoid -unnecessary allocations. - -### Known problems -* This lint suggests taking an argument by reference, -however sometimes it is better to let users decide the argument type -(by using `Borrow` trait, for example), depending on how the function is used. - -### Example -``` -fn foo(v: Vec) { - assert_eq!(v.len(), 42); -} -``` -should be -``` -fn foo(v: &[i32]) { - assert_eq!(v.len(), 42); -} -``` \ No newline at end of file diff --git a/src/docs/needless_question_mark.txt b/src/docs/needless_question_mark.txt deleted file mode 100644 index 540739fd45fa..000000000000 --- a/src/docs/needless_question_mark.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Suggests alternatives for useless applications of `?` in terminating expressions - -### Why is this bad? -There's no reason to use `?` to short-circuit when execution of the body will end there anyway. - -### Example -``` -struct TO { - magic: Option, -} - -fn f(to: TO) -> Option { - Some(to.magic?) -} - -struct TR { - magic: Result, -} - -fn g(tr: Result) -> Result { - tr.and_then(|t| Ok(t.magic?)) -} - -``` -Use instead: -``` -struct TO { - magic: Option, -} - -fn f(to: TO) -> Option { - to.magic -} - -struct TR { - magic: Result, -} - -fn g(tr: Result) -> Result { - tr.and_then(|t| t.magic) -} -``` \ No newline at end of file diff --git a/src/docs/needless_range_loop.txt b/src/docs/needless_range_loop.txt deleted file mode 100644 index 583c09b28497..000000000000 --- a/src/docs/needless_range_loop.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for looping over the range of `0..len` of some -collection just to get the values by index. - -### Why is this bad? -Just iterating the collection itself makes the intent -more clear and is probably faster. - -### Example -``` -let vec = vec!['a', 'b', 'c']; -for i in 0..vec.len() { - println!("{}", vec[i]); -} -``` - -Use instead: -``` -let vec = vec!['a', 'b', 'c']; -for i in vec { - println!("{}", i); -} -``` \ No newline at end of file diff --git a/src/docs/needless_return.txt b/src/docs/needless_return.txt deleted file mode 100644 index 48782cb0ca84..000000000000 --- a/src/docs/needless_return.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for return statements at the end of a block. - -### Why is this bad? -Removing the `return` and semicolon will make the code -more rusty. - -### Example -``` -fn foo(x: usize) -> usize { - return x; -} -``` -simplify to -``` -fn foo(x: usize) -> usize { - x -} -``` \ No newline at end of file diff --git a/src/docs/needless_splitn.txt b/src/docs/needless_splitn.txt deleted file mode 100644 index b10a84fbc42d..000000000000 --- a/src/docs/needless_splitn.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same. -### Why is this bad? -The function `split` is simpler and there is no performance difference in these cases, considering -that both functions return a lazy iterator. -### Example -``` -let str = "key=value=add"; -let _ = str.splitn(3, '=').next().unwrap(); -``` - -Use instead: -``` -let str = "key=value=add"; -let _ = str.split('=').next().unwrap(); -``` \ No newline at end of file diff --git a/src/docs/needless_update.txt b/src/docs/needless_update.txt deleted file mode 100644 index 82adabf6482c..000000000000 --- a/src/docs/needless_update.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for needlessly including a base struct on update -when all fields are changed anyway. - -This lint is not applied to structs marked with -[non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html). - -### Why is this bad? -This will cost resources (because the base has to be -somewhere), and make the code less readable. - -### Example -``` -Point { - x: 1, - y: 1, - z: 1, - ..zero_point -}; -``` - -Use instead: -``` -// Missing field `z` -Point { - x: 1, - y: 1, - ..zero_point -}; -``` \ No newline at end of file diff --git a/src/docs/neg_cmp_op_on_partial_ord.txt b/src/docs/neg_cmp_op_on_partial_ord.txt deleted file mode 100644 index fa55c6cfd74b..000000000000 --- a/src/docs/neg_cmp_op_on_partial_ord.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the usage of negated comparison operators on types which only implement -`PartialOrd` (e.g., `f64`). - -### Why is this bad? -These operators make it easy to forget that the underlying types actually allow not only three -potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is -especially easy to miss if the operator based comparison result is negated. - -### Example -``` -let a = 1.0; -let b = f64::NAN; - -let not_less_or_equal = !(a <= b); -``` - -Use instead: -``` -use std::cmp::Ordering; - -let _not_less_or_equal = match a.partial_cmp(&b) { - None | Some(Ordering::Greater) => true, - _ => false, -}; -``` \ No newline at end of file diff --git a/src/docs/neg_multiply.txt b/src/docs/neg_multiply.txt deleted file mode 100644 index 4e8b096eb9cc..000000000000 --- a/src/docs/neg_multiply.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for multiplication by -1 as a form of negation. - -### Why is this bad? -It's more readable to just negate. - -### Known problems -This only catches integers (for now). - -### Example -``` -let a = x * -1; -``` - -Use instead: -``` -let a = -x; -``` \ No newline at end of file diff --git a/src/docs/negative_feature_names.txt b/src/docs/negative_feature_names.txt deleted file mode 100644 index 01ee9efb318b..000000000000 --- a/src/docs/negative_feature_names.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for negative feature names with prefix `no-` or `not-` - -### Why is this bad? -Features are supposed to be additive, and negatively-named features violate it. - -### Example -``` -[features] -default = [] -no-abc = [] -not-def = [] - -``` -Use instead: -``` -[features] -default = ["abc", "def"] -abc = [] -def = [] - -``` \ No newline at end of file diff --git a/src/docs/never_loop.txt b/src/docs/never_loop.txt deleted file mode 100644 index 737ccf415cb9..000000000000 --- a/src/docs/never_loop.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for loops that will always `break`, `return` or -`continue` an outer loop. - -### Why is this bad? -This loop never loops, all it does is obfuscating the -code. - -### Example -``` -loop { - ..; - break; -} -``` \ No newline at end of file diff --git a/src/docs/new_ret_no_self.txt b/src/docs/new_ret_no_self.txt deleted file mode 100644 index 291bad24a643..000000000000 --- a/src/docs/new_ret_no_self.txt +++ /dev/null @@ -1,47 +0,0 @@ -### What it does -Checks for `new` not returning a type that contains `Self`. - -### Why is this bad? -As a convention, `new` methods are used to make a new -instance of a type. - -### Example -In an impl block: -``` -impl Foo { - fn new() -> NotAFoo { - } -} -``` - -``` -struct Bar(Foo); -impl Foo { - // Bad. The type name must contain `Self` - fn new() -> Bar { - } -} -``` - -``` -impl Foo { - // Good. Return type contains `Self` - fn new() -> Result { - } -} -``` - -Or in a trait definition: -``` -pub trait Trait { - // Bad. The type name must contain `Self` - fn new(); -} -``` - -``` -pub trait Trait { - // Good. Return type contains `Self` - fn new() -> Self; -} -``` \ No newline at end of file diff --git a/src/docs/new_without_default.txt b/src/docs/new_without_default.txt deleted file mode 100644 index 662d39c8efdd..000000000000 --- a/src/docs/new_without_default.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for public types with a `pub fn new() -> Self` method and no -implementation of -[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html). - -### Why is this bad? -The user might expect to be able to use -[`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the -type can be constructed without arguments. - -### Example -``` -pub struct Foo(Bar); - -impl Foo { - pub fn new() -> Self { - Foo(Bar::new()) - } -} -``` - -To fix the lint, add a `Default` implementation that delegates to `new`: - -``` -pub struct Foo(Bar); - -impl Default for Foo { - fn default() -> Self { - Foo::new() - } -} -``` \ No newline at end of file diff --git a/src/docs/no_effect.txt b/src/docs/no_effect.txt deleted file mode 100644 index d4cc08fa8a7d..000000000000 --- a/src/docs/no_effect.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for statements which have no effect. - -### Why is this bad? -Unlike dead code, these statements are actually -executed. However, as they have no effect, all they do is make the code less -readable. - -### Example -``` -0; -``` \ No newline at end of file diff --git a/src/docs/no_effect_replace.txt b/src/docs/no_effect_replace.txt deleted file mode 100644 index 646d45287ef5..000000000000 --- a/src/docs/no_effect_replace.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for `replace` statements which have no effect. - -### Why is this bad? -It's either a mistake or confusing. - -### Example -``` -"1234".replace("12", "12"); -"1234".replacen("12", "12", 1); -``` \ No newline at end of file diff --git a/src/docs/no_effect_underscore_binding.txt b/src/docs/no_effect_underscore_binding.txt deleted file mode 100644 index 972f60dd01e9..000000000000 --- a/src/docs/no_effect_underscore_binding.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for binding to underscore prefixed variable without side-effects. - -### Why is this bad? -Unlike dead code, these bindings are actually -executed. However, as they have no effect and shouldn't be used further on, all they -do is make the code less readable. - -### Known problems -Further usage of this variable is not checked, which can lead to false positives if it is -used later in the code. - -### Example -``` -let _i_serve_no_purpose = 1; -``` \ No newline at end of file diff --git a/src/docs/non_ascii_literal.txt b/src/docs/non_ascii_literal.txt deleted file mode 100644 index 164902b4726e..000000000000 --- a/src/docs/non_ascii_literal.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for non-ASCII characters in string and char literals. - -### Why is this bad? -Yeah, we know, the 90's called and wanted their charset -back. Even so, there still are editors and other programs out there that -don't work well with Unicode. So if the code is meant to be used -internationally, on multiple operating systems, or has other portability -requirements, activating this lint could be useful. - -### Example -``` -let x = String::from("€"); -``` - -Use instead: -``` -let x = String::from("\u{20ac}"); -``` \ No newline at end of file diff --git a/src/docs/non_octal_unix_permissions.txt b/src/docs/non_octal_unix_permissions.txt deleted file mode 100644 index 4a468e94db16..000000000000 --- a/src/docs/non_octal_unix_permissions.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for non-octal values used to set Unix file permissions. - -### Why is this bad? -They will be converted into octal, creating potentially -unintended file permissions. - -### Example -``` -use std::fs::OpenOptions; -use std::os::unix::fs::OpenOptionsExt; - -let mut options = OpenOptions::new(); -options.mode(644); -``` -Use instead: -``` -use std::fs::OpenOptions; -use std::os::unix::fs::OpenOptionsExt; - -let mut options = OpenOptions::new(); -options.mode(0o644); -``` \ No newline at end of file diff --git a/src/docs/non_send_fields_in_send_ty.txt b/src/docs/non_send_fields_in_send_ty.txt deleted file mode 100644 index 11e6f6e162cf..000000000000 --- a/src/docs/non_send_fields_in_send_ty.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -This lint warns about a `Send` implementation for a type that -contains fields that are not safe to be sent across threads. -It tries to detect fields that can cause a soundness issue -when sent to another thread (e.g., `Rc`) while allowing `!Send` fields -that are expected to exist in a `Send` type, such as raw pointers. - -### Why is this bad? -Sending the struct to another thread effectively sends all of its fields, -and the fields that do not implement `Send` can lead to soundness bugs -such as data races when accessed in a thread -that is different from the thread that created it. - -See: -* [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html) -* [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) - -### Known Problems -This lint relies on heuristics to distinguish types that are actually -unsafe to be sent across threads and `!Send` types that are expected to -exist in `Send` type. Its rule can filter out basic cases such as -`Vec<*const T>`, but it's not perfect. Feel free to create an issue if -you have a suggestion on how this heuristic can be improved. - -### Example -``` -struct ExampleStruct { - rc_is_not_send: Rc, - unbounded_generic_field: T, -} - -// This impl is unsound because it allows sending `!Send` types through `ExampleStruct` -unsafe impl Send for ExampleStruct {} -``` -Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) -or specify correct bounds on generic type parameters (`T: Send`). \ No newline at end of file diff --git a/src/docs/nonminimal_bool.txt b/src/docs/nonminimal_bool.txt deleted file mode 100644 index 488980ddf023..000000000000 --- a/src/docs/nonminimal_bool.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for boolean expressions that can be written more -concisely. - -### Why is this bad? -Readability of boolean expressions suffers from -unnecessary duplication. - -### Known problems -Ignores short circuiting behavior of `||` and -`&&`. Ignores `|`, `&` and `^`. - -### Example -``` -if a && true {} -if !(a == b) {} -``` - -Use instead: -``` -if a {} -if a != b {} -``` \ No newline at end of file diff --git a/src/docs/nonsensical_open_options.txt b/src/docs/nonsensical_open_options.txt deleted file mode 100644 index 7a95443b51a5..000000000000 --- a/src/docs/nonsensical_open_options.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for duplicate open options as well as combinations -that make no sense. - -### Why is this bad? -In the best case, the code will be harder to read than -necessary. I don't know the worst case. - -### Example -``` -use std::fs::OpenOptions; - -OpenOptions::new().read(true).truncate(true); -``` \ No newline at end of file diff --git a/src/docs/nonstandard_macro_braces.txt b/src/docs/nonstandard_macro_braces.txt deleted file mode 100644 index 7e8d0d2d33bf..000000000000 --- a/src/docs/nonstandard_macro_braces.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks that common macros are used with consistent bracing. - -### Why is this bad? -This is mostly a consistency lint although using () or [] -doesn't give you a semicolon in item position, which can be unexpected. - -### Example -``` -vec!{1, 2, 3}; -``` -Use instead: -``` -vec![1, 2, 3]; -``` \ No newline at end of file diff --git a/src/docs/not_unsafe_ptr_arg_deref.txt b/src/docs/not_unsafe_ptr_arg_deref.txt deleted file mode 100644 index 31355fbb7b66..000000000000 --- a/src/docs/not_unsafe_ptr_arg_deref.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for public functions that dereference raw pointer -arguments but are not marked `unsafe`. - -### Why is this bad? -The function should probably be marked `unsafe`, since -for an arbitrary raw pointer, there is no way of telling for sure if it is -valid. - -### Known problems -* It does not check functions recursively so if the pointer is passed to a -private non-`unsafe` function which does the dereferencing, the lint won't -trigger. -* It only checks for arguments whose type are raw pointers, not raw pointers -got from an argument in some other way (`fn foo(bar: &[*const u8])` or -`some_argument.get_raw_ptr()`). - -### Example -``` -pub fn foo(x: *const u8) { - println!("{}", unsafe { *x }); -} -``` - -Use instead: -``` -pub unsafe fn foo(x: *const u8) { - println!("{}", unsafe { *x }); -} -``` \ No newline at end of file diff --git a/src/docs/obfuscated_if_else.txt b/src/docs/obfuscated_if_else.txt deleted file mode 100644 index 638f63b0db5e..000000000000 --- a/src/docs/obfuscated_if_else.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for usages of `.then_some(..).unwrap_or(..)` - -### Why is this bad? -This can be written more clearly with `if .. else ..` - -### Limitations -This lint currently only looks for usages of -`.then_some(..).unwrap_or(..)`, but will be expanded -to account for similar patterns. - -### Example -``` -let x = true; -x.then_some("a").unwrap_or("b"); -``` -Use instead: -``` -let x = true; -if x { "a" } else { "b" }; -``` \ No newline at end of file diff --git a/src/docs/octal_escapes.txt b/src/docs/octal_escapes.txt deleted file mode 100644 index eee820587158..000000000000 --- a/src/docs/octal_escapes.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for `\0` escapes in string and byte literals that look like octal -character escapes in C. - -### Why is this bad? - -C and other languages support octal character escapes in strings, where -a backslash is followed by up to three octal digits. For example, `\033` -stands for the ASCII character 27 (ESC). Rust does not support this -notation, but has the escape code `\0` which stands for a null -byte/character, and any following digits do not form part of the escape -sequence. Therefore, `\033` is not a compiler error but the result may -be surprising. - -### Known problems -The actual meaning can be the intended one. `\x00` can be used in these -cases to be unambiguous. - -The lint does not trigger for format strings in `print!()`, `write!()` -and friends since the string is already preprocessed when Clippy lints -can see it. - -### Example -``` -let one = "\033[1m Bold? \033[0m"; // \033 intended as escape -let two = "\033\0"; // \033 intended as null-3-3 -``` - -Use instead: -``` -let one = "\x1b[1mWill this be bold?\x1b[0m"; -let two = "\x0033\x00"; -``` \ No newline at end of file diff --git a/src/docs/ok_expect.txt b/src/docs/ok_expect.txt deleted file mode 100644 index fd5205d49dc0..000000000000 --- a/src/docs/ok_expect.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `ok().expect(..)`. - -### Why is this bad? -Because you usually call `expect()` on the `Result` -directly to get a better error message. - -### Known problems -The error type needs to implement `Debug` - -### Example -``` -x.ok().expect("why did I do this again?"); -``` - -Use instead: -``` -x.expect("why did I do this again?"); -``` \ No newline at end of file diff --git a/src/docs/only_used_in_recursion.txt b/src/docs/only_used_in_recursion.txt deleted file mode 100644 index f19f47ff9eb5..000000000000 --- a/src/docs/only_used_in_recursion.txt +++ /dev/null @@ -1,58 +0,0 @@ -### What it does -Checks for arguments that are only used in recursion with no side-effects. - -### Why is this bad? -It could contain a useless calculation and can make function simpler. - -The arguments can be involved in calculations and assignments but as long as -the calculations have no side-effects (function calls or mutating dereference) -and the assigned variables are also only in recursion, it is useless. - -### Known problems -Too many code paths in the linting code are currently untested and prone to produce false -positives or are prone to have performance implications. - -In some cases, this would not catch all useless arguments. - -``` -fn foo(a: usize, b: usize) -> usize { - let f = |x| x + 1; - - if a == 0 { - 1 - } else { - foo(a - 1, f(b)) - } -} -``` - -For example, the argument `b` is only used in recursion, but the lint would not catch it. - -List of some examples that can not be caught: -- binary operation of non-primitive types -- closure usage -- some `break` relative operations -- struct pattern binding - -Also, when you recurse the function name with path segments, it is not possible to detect. - -### Example -``` -fn f(a: usize, b: usize) -> usize { - if a == 0 { - 1 - } else { - f(a - 1, b + 1) - } -} -``` -Use instead: -``` -fn f(a: usize) -> usize { - if a == 0 { - 1 - } else { - f(a - 1) - } -} -``` \ No newline at end of file diff --git a/src/docs/op_ref.txt b/src/docs/op_ref.txt deleted file mode 100644 index 7a7ed1bc9bad..000000000000 --- a/src/docs/op_ref.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for arguments to `==` which have their address -taken to satisfy a bound -and suggests to dereference the other argument instead - -### Why is this bad? -It is more idiomatic to dereference the other argument. - -### Example -``` -&x == y -``` - -Use instead: -``` -x == *y -``` \ No newline at end of file diff --git a/src/docs/option_as_ref_deref.txt b/src/docs/option_as_ref_deref.txt deleted file mode 100644 index ad7411d3d4bd..000000000000 --- a/src/docs/option_as_ref_deref.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). - -### Why is this bad? -Readability, this can be written more concisely as -`_.as_deref()`. - -### Example -``` -opt.as_ref().map(String::as_str) -``` -Can be written as -``` -opt.as_deref() -``` \ No newline at end of file diff --git a/src/docs/option_env_unwrap.txt b/src/docs/option_env_unwrap.txt deleted file mode 100644 index c952cba8e261..000000000000 --- a/src/docs/option_env_unwrap.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `option_env!(...).unwrap()` and -suggests usage of the `env!` macro. - -### Why is this bad? -Unwrapping the result of `option_env!` will panic -at run-time if the environment variable doesn't exist, whereas `env!` -catches it at compile-time. - -### Example -``` -let _ = option_env!("HOME").unwrap(); -``` - -Is better expressed as: - -``` -let _ = env!("HOME"); -``` \ No newline at end of file diff --git a/src/docs/option_filter_map.txt b/src/docs/option_filter_map.txt deleted file mode 100644 index 25f7bde7b4d0..000000000000 --- a/src/docs/option_filter_map.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for indirect collection of populated `Option` - -### Why is this bad? -`Option` is like a collection of 0-1 things, so `flatten` -automatically does this without suspicious-looking `unwrap` calls. - -### Example -``` -let _ = std::iter::empty::>().filter(Option::is_some).map(Option::unwrap); -``` -Use instead: -``` -let _ = std::iter::empty::>().flatten(); -``` \ No newline at end of file diff --git a/src/docs/option_if_let_else.txt b/src/docs/option_if_let_else.txt deleted file mode 100644 index 43652db513b4..000000000000 --- a/src/docs/option_if_let_else.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -Lints usage of `if let Some(v) = ... { y } else { x }` and -`match .. { Some(v) => y, None/_ => x }` which are more -idiomatically done with `Option::map_or` (if the else bit is a pure -expression) or `Option::map_or_else` (if the else bit is an impure -expression). - -### Why is this bad? -Using the dedicated functions of the `Option` type is clearer and -more concise than an `if let` expression. - -### Known problems -This lint uses a deliberately conservative metric for checking -if the inside of either body contains breaks or continues which will -cause it to not suggest a fix if either block contains a loop with -continues or breaks contained within the loop. - -### Example -``` -let _ = if let Some(foo) = optional { - foo -} else { - 5 -}; -let _ = match optional { - Some(val) => val + 1, - None => 5 -}; -let _ = if let Some(foo) = optional { - foo -} else { - let y = do_complicated_function(); - y*y -}; -``` - -should be - -``` -let _ = optional.map_or(5, |foo| foo); -let _ = optional.map_or(5, |val| val + 1); -let _ = optional.map_or_else(||{ - let y = do_complicated_function(); - y*y -}, |foo| foo); -``` \ No newline at end of file diff --git a/src/docs/option_map_or_none.txt b/src/docs/option_map_or_none.txt deleted file mode 100644 index c86c65215f01..000000000000 --- a/src/docs/option_map_or_none.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `_.map_or(None, _)`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.and_then(_)`. - -### Known problems -The order of the arguments is not in execution order. - -### Example -``` -opt.map_or(None, |a| Some(a + 1)); -``` - -Use instead: -``` -opt.and_then(|a| Some(a + 1)); -``` \ No newline at end of file diff --git a/src/docs/option_map_unit_fn.txt b/src/docs/option_map_unit_fn.txt deleted file mode 100644 index fc4b528f0923..000000000000 --- a/src/docs/option_map_unit_fn.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for usage of `option.map(f)` where f is a function -or closure that returns the unit type `()`. - -### Why is this bad? -Readability, this can be written more clearly with -an if let statement - -### Example -``` -let x: Option = do_stuff(); -x.map(log_err_msg); -x.map(|msg| log_err_msg(format_msg(msg))); -``` - -The correct use would be: - -``` -let x: Option = do_stuff(); -if let Some(msg) = x { - log_err_msg(msg); -} - -if let Some(msg) = x { - log_err_msg(format_msg(msg)); -} -``` \ No newline at end of file diff --git a/src/docs/option_option.txt b/src/docs/option_option.txt deleted file mode 100644 index b4324bd83990..000000000000 --- a/src/docs/option_option.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for use of `Option>` in function signatures and type -definitions - -### Why is this bad? -`Option<_>` represents an optional value. `Option>` -represents an optional optional value which is logically the same thing as an optional -value but has an unneeded extra level of wrapping. - -If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases, -consider a custom `enum` instead, with clear names for each case. - -### Example -``` -fn get_data() -> Option> { - None -} -``` - -Better: - -``` -pub enum Contents { - Data(Vec), // Was Some(Some(Vec)) - NotYetFetched, // Was Some(None) - None, // Was None -} - -fn get_data() -> Contents { - Contents::None -} -``` \ No newline at end of file diff --git a/src/docs/or_fun_call.txt b/src/docs/or_fun_call.txt deleted file mode 100644 index 6ce77cc268c5..000000000000 --- a/src/docs/or_fun_call.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, -`.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`, -`.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()` -etc. instead. - -### Why is this bad? -The function will always be called and potentially -allocate an object acting as the default. - -### Known problems -If the function has side-effects, not calling it will -change the semantic of the program, but you shouldn't rely on that anyway. - -### Example -``` -foo.unwrap_or(String::new()); -``` - -Use instead: -``` -foo.unwrap_or_else(String::new); - -// or - -foo.unwrap_or_default(); -``` \ No newline at end of file diff --git a/src/docs/or_then_unwrap.txt b/src/docs/or_then_unwrap.txt deleted file mode 100644 index 64ac53749e8a..000000000000 --- a/src/docs/or_then_unwrap.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `.or(…).unwrap()` calls to Options and Results. - -### Why is this bad? -You should use `.unwrap_or(…)` instead for clarity. - -### Example -``` -// Result -let value = result.or::(Ok(fallback)).unwrap(); - -// Option -let value = option.or(Some(fallback)).unwrap(); -``` -Use instead: -``` -// Result -let value = result.unwrap_or(fallback); - -// Option -let value = option.unwrap_or(fallback); -``` \ No newline at end of file diff --git a/src/docs/out_of_bounds_indexing.txt b/src/docs/out_of_bounds_indexing.txt deleted file mode 100644 index 5802eea29966..000000000000 --- a/src/docs/out_of_bounds_indexing.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for out of bounds array indexing with a constant -index. - -### Why is this bad? -This will always panic at runtime. - -### Example -``` -let x = [1, 2, 3, 4]; - -x[9]; -&x[2..9]; -``` - -Use instead: -``` -// Index within bounds - -x[0]; -x[3]; -``` \ No newline at end of file diff --git a/src/docs/overflow_check_conditional.txt b/src/docs/overflow_check_conditional.txt deleted file mode 100644 index a09cc18a0bcc..000000000000 --- a/src/docs/overflow_check_conditional.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Detects classic underflow/overflow checks. - -### Why is this bad? -Most classic C underflow/overflow checks will fail in -Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead. - -### Example -``` -a + b < a; -``` \ No newline at end of file diff --git a/src/docs/overly_complex_bool_expr.txt b/src/docs/overly_complex_bool_expr.txt deleted file mode 100644 index 65ca18392e75..000000000000 --- a/src/docs/overly_complex_bool_expr.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for boolean expressions that contain terminals that -can be eliminated. - -### Why is this bad? -This is most likely a logic bug. - -### Known problems -Ignores short circuiting behavior. - -### Example -``` -// The `b` is unnecessary, the expression is equivalent to `if a`. -if a && b || a { ... } -``` - -Use instead: -``` -if a {} -``` \ No newline at end of file diff --git a/src/docs/panic.txt b/src/docs/panic.txt deleted file mode 100644 index f9bdc6e87ccf..000000000000 --- a/src/docs/panic.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `panic!`. - -### Why is this bad? -`panic!` will stop the execution of the executable - -### Example -``` -panic!("even with a good reason"); -``` \ No newline at end of file diff --git a/src/docs/panic_in_result_fn.txt b/src/docs/panic_in_result_fn.txt deleted file mode 100644 index 51c2f8ae5a30..000000000000 --- a/src/docs/panic_in_result_fn.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result. - -### Why is this bad? -For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. - -### Known problems -Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked. - -### Example -``` -fn result_with_panic() -> Result -{ - panic!("error"); -} -``` -Use instead: -``` -fn result_without_panic() -> Result { - Err(String::from("error")) -} -``` \ No newline at end of file diff --git a/src/docs/panicking_unwrap.txt b/src/docs/panicking_unwrap.txt deleted file mode 100644 index 1fbc245c8ec3..000000000000 --- a/src/docs/panicking_unwrap.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for calls of `unwrap[_err]()` that will always fail. - -### Why is this bad? -If panicking is desired, an explicit `panic!()` should be used. - -### Known problems -This lint only checks `if` conditions not assignments. -So something like `let x: Option<()> = None; x.unwrap();` will not be recognized. - -### Example -``` -if option.is_none() { - do_something_with(option.unwrap()) -} -``` - -This code will always panic. The if condition should probably be inverted. \ No newline at end of file diff --git a/src/docs/partialeq_ne_impl.txt b/src/docs/partialeq_ne_impl.txt deleted file mode 100644 index 78f55188bab8..000000000000 --- a/src/docs/partialeq_ne_impl.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for manual re-implementations of `PartialEq::ne`. - -### Why is this bad? -`PartialEq::ne` is required to always return the -negated result of `PartialEq::eq`, which is exactly what the default -implementation does. Therefore, there should never be any need to -re-implement it. - -### Example -``` -struct Foo; - -impl PartialEq for Foo { - fn eq(&self, other: &Foo) -> bool { true } - fn ne(&self, other: &Foo) -> bool { !(self == other) } -} -``` \ No newline at end of file diff --git a/src/docs/partialeq_to_none.txt b/src/docs/partialeq_to_none.txt deleted file mode 100644 index 5cc07bf88435..000000000000 --- a/src/docs/partialeq_to_none.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does - -Checks for binary comparisons to a literal `Option::None`. - -### Why is this bad? - -A programmer checking if some `foo` is `None` via a comparison `foo == None` -is usually inspired from other programming languages (e.g. `foo is None` -in Python). -Checking if a value of type `Option` is (not) equal to `None` in that -way relies on `T: PartialEq` to do the comparison, which is unneeded. - -### Example -``` -fn foo(f: Option) -> &'static str { - if f != None { "yay" } else { "nay" } -} -``` -Use instead: -``` -fn foo(f: Option) -> &'static str { - if f.is_some() { "yay" } else { "nay" } -} -``` \ No newline at end of file diff --git a/src/docs/path_buf_push_overwrite.txt b/src/docs/path_buf_push_overwrite.txt deleted file mode 100644 index 34f8901da239..000000000000 --- a/src/docs/path_buf_push_overwrite.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) -calls on `PathBuf` that can cause overwrites. - -### Why is this bad? -Calling `push` with a root path at the start can overwrite the -previous defined path. - -### Example -``` -use std::path::PathBuf; - -let mut x = PathBuf::from("/foo"); -x.push("/bar"); -assert_eq!(x, PathBuf::from("/bar")); -``` -Could be written: - -``` -use std::path::PathBuf; - -let mut x = PathBuf::from("/foo"); -x.push("bar"); -assert_eq!(x, PathBuf::from("/foo/bar")); -``` \ No newline at end of file diff --git a/src/docs/pattern_type_mismatch.txt b/src/docs/pattern_type_mismatch.txt deleted file mode 100644 index 64da881d592a..000000000000 --- a/src/docs/pattern_type_mismatch.txt +++ /dev/null @@ -1,64 +0,0 @@ -### What it does -Checks for patterns that aren't exact representations of the types -they are applied to. - -To satisfy this lint, you will have to adjust either the expression that is matched -against or the pattern itself, as well as the bindings that are introduced by the -adjusted patterns. For matching you will have to either dereference the expression -with the `*` operator, or amend the patterns to explicitly match against `&` -or `&mut ` depending on the reference mutability. For the bindings you need -to use the inverse. You can leave them as plain bindings if you wish for the value -to be copied, but you must use `ref mut ` or `ref ` to construct -a reference into the matched structure. - -If you are looking for a way to learn about ownership semantics in more detail, it -is recommended to look at IDE options available to you to highlight types, lifetimes -and reference semantics in your code. The available tooling would expose these things -in a general way even outside of the various pattern matching mechanics. Of course -this lint can still be used to highlight areas of interest and ensure a good understanding -of ownership semantics. - -### Why is this bad? -It isn't bad in general. But in some contexts it can be desirable -because it increases ownership hints in the code, and will guard against some changes -in ownership. - -### Example -This example shows the basic adjustments necessary to satisfy the lint. Note how -the matched expression is explicitly dereferenced with `*` and the `inner` variable -is bound to a shared borrow via `ref inner`. - -``` -// Bad -let value = &Some(Box::new(23)); -match value { - Some(inner) => println!("{}", inner), - None => println!("none"), -} - -// Good -let value = &Some(Box::new(23)); -match *value { - Some(ref inner) => println!("{}", inner), - None => println!("none"), -} -``` - -The following example demonstrates one of the advantages of the more verbose style. -Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable -borrow, while `b` is simply taken by value. This ensures that the loop body cannot -accidentally modify the wrong part of the structure. - -``` -// Bad -let mut values = vec![(2, 3), (3, 4)]; -for (a, b) in &mut values { - *a += *b; -} - -// Good -let mut values = vec![(2, 3), (3, 4)]; -for &mut (ref mut a, b) in &mut values { - *a += b; -} -``` \ No newline at end of file diff --git a/src/docs/possible_missing_comma.txt b/src/docs/possible_missing_comma.txt deleted file mode 100644 index 5d92f4cae91e..000000000000 --- a/src/docs/possible_missing_comma.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for possible missing comma in an array. It lints if -an array element is a binary operator expression and it lies on two lines. - -### Why is this bad? -This could lead to unexpected results. - -### Example -``` -let a = &[ - -1, -2, -3 // <= no comma here - -4, -5, -6 -]; -``` \ No newline at end of file diff --git a/src/docs/precedence.txt b/src/docs/precedence.txt deleted file mode 100644 index fda0b831f335..000000000000 --- a/src/docs/precedence.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for operations where precedence may be unclear -and suggests to add parentheses. Currently it catches the following: -* mixed usage of arithmetic and bit shifting/combining operators without -parentheses -* a "negative" numeric literal (which is really a unary `-` followed by a -numeric literal) - followed by a method call - -### Why is this bad? -Not everyone knows the precedence of those operators by -heart, so expressions like these may trip others trying to reason about the -code. - -### Example -* `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 -* `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1 \ No newline at end of file diff --git a/src/docs/print_in_format_impl.txt b/src/docs/print_in_format_impl.txt deleted file mode 100644 index 140d23d6faab..000000000000 --- a/src/docs/print_in_format_impl.txt +++ /dev/null @@ -1,34 +0,0 @@ -### What it does -Checks for use of `println`, `print`, `eprintln` or `eprint` in an -implementation of a formatting trait. - -### Why is this bad? -Using a print macro is likely unintentional since formatting traits -should write to the `Formatter`, not stdout/stderr. - -### Example -``` -use std::fmt::{Display, Error, Formatter}; - -struct S; -impl Display for S { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - println!("S"); - - Ok(()) - } -} -``` -Use instead: -``` -use std::fmt::{Display, Error, Formatter}; - -struct S; -impl Display for S { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - writeln!(f, "S"); - - Ok(()) - } -} -``` \ No newline at end of file diff --git a/src/docs/print_literal.txt b/src/docs/print_literal.txt deleted file mode 100644 index a6252a68780b..000000000000 --- a/src/docs/print_literal.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns about the use of literals as `print!`/`println!` args. - -### Why is this bad? -Using literals as `println!` args is inefficient -(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary -(i.e., just put the literal in the format string) - -### Example -``` -println!("{}", "foo"); -``` -use the literal without formatting: -``` -println!("foo"); -``` \ No newline at end of file diff --git a/src/docs/print_stderr.txt b/src/docs/print_stderr.txt deleted file mode 100644 index 9c6edeeef125..000000000000 --- a/src/docs/print_stderr.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for printing on *stderr*. The purpose of this lint -is to catch debugging remnants. - -### Why is this bad? -People often print on *stderr* while debugging an -application and might forget to remove those prints afterward. - -### Known problems -Only catches `eprint!` and `eprintln!` calls. - -### Example -``` -eprintln!("Hello world!"); -``` \ No newline at end of file diff --git a/src/docs/print_stdout.txt b/src/docs/print_stdout.txt deleted file mode 100644 index d2cbd811d1b2..000000000000 --- a/src/docs/print_stdout.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for printing on *stdout*. The purpose of this lint -is to catch debugging remnants. - -### Why is this bad? -People often print on *stdout* while debugging an -application and might forget to remove those prints afterward. - -### Known problems -Only catches `print!` and `println!` calls. - -### Example -``` -println!("Hello world!"); -``` \ No newline at end of file diff --git a/src/docs/print_with_newline.txt b/src/docs/print_with_newline.txt deleted file mode 100644 index 640323e822db..000000000000 --- a/src/docs/print_with_newline.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns when you use `print!()` with a format -string that ends in a newline. - -### Why is this bad? -You should use `println!()` instead, which appends the -newline. - -### Example -``` -print!("Hello {}!\n", name); -``` -use println!() instead -``` -println!("Hello {}!", name); -``` \ No newline at end of file diff --git a/src/docs/println_empty_string.txt b/src/docs/println_empty_string.txt deleted file mode 100644 index b980413022ca..000000000000 --- a/src/docs/println_empty_string.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns when you use `println!("")` to -print a newline. - -### Why is this bad? -You should use `println!()`, which is simpler. - -### Example -``` -println!(""); -``` - -Use instead: -``` -println!(); -``` \ No newline at end of file diff --git a/src/docs/ptr_arg.txt b/src/docs/ptr_arg.txt deleted file mode 100644 index 796b0a65b717..000000000000 --- a/src/docs/ptr_arg.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -This lint checks for function arguments of type `&String`, `&Vec`, -`&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls -with the appropriate `.to_owned()`/`to_string()` calls. - -### Why is this bad? -Requiring the argument to be of the specific size -makes the function less useful for no benefit; slices in the form of `&[T]` -or `&str` usually suffice and can be obtained from other types, too. - -### Known problems -There may be `fn(&Vec)`-typed references pointing to your function. -If you have them, you will get a compiler error after applying this lint's -suggestions. You then have the choice to undo your changes or change the -type of the reference. - -Note that if the function is part of your public interface, there may be -other crates referencing it, of which you may not be aware. Carefully -deprecate the function before applying the lint suggestions in this case. - -### Example -``` -fn foo(&Vec) { .. } -``` - -Use instead: -``` -fn foo(&[u32]) { .. } -``` \ No newline at end of file diff --git a/src/docs/ptr_as_ptr.txt b/src/docs/ptr_as_ptr.txt deleted file mode 100644 index 8fb35c4aae8f..000000000000 --- a/src/docs/ptr_as_ptr.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for `as` casts between raw pointers without changing its mutability, -namely `*const T` to `*const U` and `*mut T` to `*mut U`. - -### Why is this bad? -Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because -it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. - -### Example -``` -let ptr: *const u32 = &42_u32; -let mut_ptr: *mut u32 = &mut 42_u32; -let _ = ptr as *const i32; -let _ = mut_ptr as *mut i32; -``` -Use instead: -``` -let ptr: *const u32 = &42_u32; -let mut_ptr: *mut u32 = &mut 42_u32; -let _ = ptr.cast::(); -let _ = mut_ptr.cast::(); -``` \ No newline at end of file diff --git a/src/docs/ptr_eq.txt b/src/docs/ptr_eq.txt deleted file mode 100644 index 06b36ca55e69..000000000000 --- a/src/docs/ptr_eq.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Use `std::ptr::eq` when applicable - -### Why is this bad? -`ptr::eq` can be used to compare `&T` references -(which coerce to `*const T` implicitly) by their address rather than -comparing the values they point to. - -### Example -``` -let a = &[1, 2, 3]; -let b = &[1, 2, 3]; - -assert!(a as *const _ as usize == b as *const _ as usize); -``` -Use instead: -``` -let a = &[1, 2, 3]; -let b = &[1, 2, 3]; - -assert!(std::ptr::eq(a, b)); -``` \ No newline at end of file diff --git a/src/docs/ptr_offset_with_cast.txt b/src/docs/ptr_offset_with_cast.txt deleted file mode 100644 index f204e769bf4b..000000000000 --- a/src/docs/ptr_offset_with_cast.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for usage of the `offset` pointer method with a `usize` casted to an -`isize`. - -### Why is this bad? -If we’re always increasing the pointer address, we can avoid the numeric -cast by using the `add` method instead. - -### Example -``` -let vec = vec![b'a', b'b', b'c']; -let ptr = vec.as_ptr(); -let offset = 1_usize; - -unsafe { - ptr.offset(offset as isize); -} -``` - -Could be written: - -``` -let vec = vec![b'a', b'b', b'c']; -let ptr = vec.as_ptr(); -let offset = 1_usize; - -unsafe { - ptr.add(offset); -} -``` \ No newline at end of file diff --git a/src/docs/pub_use.txt b/src/docs/pub_use.txt deleted file mode 100644 index 407cafa01903..000000000000 --- a/src/docs/pub_use.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does - -Restricts the usage of `pub use ...` - -### Why is this bad? - -`pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent -unintentional exports or to encourage placing exported items directly in public modules - -### Example -``` -pub mod outer { - mod inner { - pub struct Test {} - } - pub use inner::Test; -} - -use outer::Test; -``` -Use instead: -``` -pub mod outer { - pub struct Test {} -} - -use outer::Test; -``` \ No newline at end of file diff --git a/src/docs/question_mark.txt b/src/docs/question_mark.txt deleted file mode 100644 index 4dc987be8813..000000000000 --- a/src/docs/question_mark.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for expressions that could be replaced by the question mark operator. - -### Why is this bad? -Question mark usage is more idiomatic. - -### Example -``` -if option.is_none() { - return None; -} -``` - -Could be written: - -``` -option?; -``` \ No newline at end of file diff --git a/src/docs/range_minus_one.txt b/src/docs/range_minus_one.txt deleted file mode 100644 index fcb96dcc34ed..000000000000 --- a/src/docs/range_minus_one.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for inclusive ranges where 1 is subtracted from -the upper bound, e.g., `x..=(y-1)`. - -### Why is this bad? -The code is more readable with an exclusive range -like `x..y`. - -### Known problems -This will cause a warning that cannot be fixed if -the consumer of the range only accepts a specific range type, instead of -the generic `RangeBounds` trait -([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). - -### Example -``` -for i in x..=(y-1) { - // .. -} -``` - -Use instead: -``` -for i in x..y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/range_plus_one.txt b/src/docs/range_plus_one.txt deleted file mode 100644 index 193c85f9cbc7..000000000000 --- a/src/docs/range_plus_one.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for exclusive ranges where 1 is added to the -upper bound, e.g., `x..(y+1)`. - -### Why is this bad? -The code is more readable with an inclusive range -like `x..=y`. - -### Known problems -Will add unnecessary pair of parentheses when the -expression is not wrapped in a pair but starts with an opening parenthesis -and ends with a closing one. -I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`. - -Also in many cases, inclusive ranges are still slower to run than -exclusive ranges, because they essentially add an extra branch that -LLVM may fail to hoist out of the loop. - -This will cause a warning that cannot be fixed if the consumer of the -range only accepts a specific range type, instead of the generic -`RangeBounds` trait -([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). - -### Example -``` -for i in x..(y+1) { - // .. -} -``` - -Use instead: -``` -for i in x..=y { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/range_zip_with_len.txt b/src/docs/range_zip_with_len.txt deleted file mode 100644 index 24c1efec789a..000000000000 --- a/src/docs/range_zip_with_len.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for zipping a collection with the range of -`0.._.len()`. - -### Why is this bad? -The code is better expressed with `.enumerate()`. - -### Example -``` -let _ = x.iter().zip(0..x.len()); -``` - -Use instead: -``` -let _ = x.iter().enumerate(); -``` \ No newline at end of file diff --git a/src/docs/rc_buffer.txt b/src/docs/rc_buffer.txt deleted file mode 100644 index 82ac58eeb308..000000000000 --- a/src/docs/rc_buffer.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`. - -### Why is this bad? -Expressions such as `Rc` usually have no advantage over `Rc`, since -it is larger and involves an extra level of indirection, and doesn't implement `Borrow`. - -While mutating a buffer type would still be possible with `Rc::get_mut()`, it only -works if there are no additional references yet, which usually defeats the purpose of -enclosing it in a shared ownership type. Instead, additionally wrapping the inner -type with an interior mutable container (such as `RefCell` or `Mutex`) would normally -be used. - -### Known problems -This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for -cases where mutation only happens before there are any additional references. - -### Example -``` -fn foo(interned: Rc) { ... } -``` - -Better: - -``` -fn foo(interned: Rc) { ... } -``` \ No newline at end of file diff --git a/src/docs/rc_clone_in_vec_init.txt b/src/docs/rc_clone_in_vec_init.txt deleted file mode 100644 index 6fc08aaf9ab5..000000000000 --- a/src/docs/rc_clone_in_vec_init.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`) -in `vec![elem; len]` - -### Why is this bad? -This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak` -is a bit misleading, as it will create references to the same pointer, rather -than different instances. - -### Example -``` -let v = vec![std::sync::Arc::new("some data".to_string()); 100]; -// or -let v = vec![std::rc::Rc::new("some data".to_string()); 100]; -``` -Use instead: -``` -// Initialize each value separately: -let mut data = Vec::with_capacity(100); -for _ in 0..100 { - data.push(std::rc::Rc::new("some data".to_string())); -} - -// Or if you want clones of the same reference, -// Create the reference beforehand to clarify that -// it should be cloned for each value -let data = std::rc::Rc::new("some data".to_string()); -let v = vec![data; 100]; -``` \ No newline at end of file diff --git a/src/docs/rc_mutex.txt b/src/docs/rc_mutex.txt deleted file mode 100644 index ed7a1e344d08..000000000000 --- a/src/docs/rc_mutex.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for `Rc>`. - -### Why is this bad? -`Rc` is used in single thread and `Mutex` is used in multi thread. -Consider using `Rc>` in single thread or `Arc>` in multi thread. - -### Known problems -Sometimes combining generic types can lead to the requirement that a -type use Rc in conjunction with Mutex. We must consider those cases false positives, but -alas they are quite hard to rule out. Luckily they are also rare. - -### Example -``` -use std::rc::Rc; -use std::sync::Mutex; -fn foo(interned: Rc>) { ... } -``` - -Better: - -``` -use std::rc::Rc; -use std::cell::RefCell -fn foo(interned: Rc>) { ... } -``` \ No newline at end of file diff --git a/src/docs/read_zero_byte_vec.txt b/src/docs/read_zero_byte_vec.txt deleted file mode 100644 index cef5604e01c0..000000000000 --- a/src/docs/read_zero_byte_vec.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -This lint catches reads into a zero-length `Vec`. -Especially in the case of a call to `with_capacity`, this lint warns that read -gets the number of bytes from the `Vec`'s length, not its capacity. - -### Why is this bad? -Reading zero bytes is almost certainly not the intended behavior. - -### Known problems -In theory, a very unusual read implementation could assign some semantic meaning -to zero-byte reads. But it seems exceptionally unlikely that code intending to do -a zero-byte read would allocate a `Vec` for it. - -### Example -``` -use std::io; -fn foo(mut f: F) { - let mut data = Vec::with_capacity(100); - f.read(&mut data).unwrap(); -} -``` -Use instead: -``` -use std::io; -fn foo(mut f: F) { - let mut data = Vec::with_capacity(100); - data.resize(100, 0); - f.read(&mut data).unwrap(); -} -``` \ No newline at end of file diff --git a/src/docs/recursive_format_impl.txt b/src/docs/recursive_format_impl.txt deleted file mode 100644 index 32fffd84cf43..000000000000 --- a/src/docs/recursive_format_impl.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for format trait implementations (e.g. `Display`) with a recursive call to itself -which uses `self` as a parameter. -This is typically done indirectly with the `write!` macro or with `to_string()`. - -### Why is this bad? -This will lead to infinite recursion and a stack overflow. - -### Example - -``` -use std::fmt; - -struct Structure(i32); -impl fmt::Display for Structure { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_string()) - } -} - -``` -Use instead: -``` -use std::fmt; - -struct Structure(i32); -impl fmt::Display for Structure { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} -``` \ No newline at end of file diff --git a/src/docs/redundant_allocation.txt b/src/docs/redundant_allocation.txt deleted file mode 100644 index 86bf51e8dfec..000000000000 --- a/src/docs/redundant_allocation.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for use of redundant allocations anywhere in the code. - -### Why is this bad? -Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Rc>`, `Arc<&T>`, `Arc>`, -`Arc>`, `Arc>`, `Box<&T>`, `Box>`, `Box>`, `Box>`, add an unnecessary level of indirection. - -### Example -``` -fn foo(bar: Rc<&usize>) {} -``` - -Better: - -``` -fn foo(bar: &usize) {} -``` \ No newline at end of file diff --git a/src/docs/redundant_clone.txt b/src/docs/redundant_clone.txt deleted file mode 100644 index b29aed0b5e77..000000000000 --- a/src/docs/redundant_clone.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for a redundant `clone()` (and its relatives) which clones an owned -value that is going to be dropped without further use. - -### Why is this bad? -It is not always possible for the compiler to eliminate useless -allocations and deallocations generated by redundant `clone()`s. - -### Known problems -False-negatives: analysis performed by this lint is conservative and limited. - -### Example -``` -{ - let x = Foo::new(); - call(x.clone()); - call(x.clone()); // this can just pass `x` -} - -["lorem", "ipsum"].join(" ").to_string(); - -Path::new("/a/b").join("c").to_path_buf(); -``` \ No newline at end of file diff --git a/src/docs/redundant_closure.txt b/src/docs/redundant_closure.txt deleted file mode 100644 index 0faa9513f97f..000000000000 --- a/src/docs/redundant_closure.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for closures which just call another function where -the function can be called directly. `unsafe` functions or calls where types -get adjusted are ignored. - -### Why is this bad? -Needlessly creating a closure adds code for no benefit -and gives the optimizer more work. - -### Known problems -If creating the closure inside the closure has a side- -effect then moving the closure creation out will change when that side- -effect runs. -See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details. - -### Example -``` -xs.map(|x| foo(x)) -``` - -Use instead: -``` -// where `foo(_)` is a plain function that takes the exact argument type of `x`. -xs.map(foo) -``` \ No newline at end of file diff --git a/src/docs/redundant_closure_call.txt b/src/docs/redundant_closure_call.txt deleted file mode 100644 index 913d1a705e29..000000000000 --- a/src/docs/redundant_closure_call.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Detects closures called in the same expression where they -are defined. - -### Why is this bad? -It is unnecessarily adding to the expression's -complexity. - -### Example -``` -let a = (|| 42)(); -``` - -Use instead: -``` -let a = 42; -``` \ No newline at end of file diff --git a/src/docs/redundant_closure_for_method_calls.txt b/src/docs/redundant_closure_for_method_calls.txt deleted file mode 100644 index 865510e14755..000000000000 --- a/src/docs/redundant_closure_for_method_calls.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for closures which only invoke a method on the closure -argument and can be replaced by referencing the method directly. - -### Why is this bad? -It's unnecessary to create the closure. - -### Example -``` -Some('a').map(|s| s.to_uppercase()); -``` -may be rewritten as -``` -Some('a').map(char::to_uppercase); -``` \ No newline at end of file diff --git a/src/docs/redundant_else.txt b/src/docs/redundant_else.txt deleted file mode 100644 index 3f4e86917603..000000000000 --- a/src/docs/redundant_else.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for `else` blocks that can be removed without changing semantics. - -### Why is this bad? -The `else` block adds unnecessary indentation and verbosity. - -### Known problems -Some may prefer to keep the `else` block for clarity. - -### Example -``` -fn my_func(count: u32) { - if count == 0 { - print!("Nothing to do"); - return; - } else { - print!("Moving on..."); - } -} -``` -Use instead: -``` -fn my_func(count: u32) { - if count == 0 { - print!("Nothing to do"); - return; - } - print!("Moving on..."); -} -``` \ No newline at end of file diff --git a/src/docs/redundant_feature_names.txt b/src/docs/redundant_feature_names.txt deleted file mode 100644 index 5bd6925ed47d..000000000000 --- a/src/docs/redundant_feature_names.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for feature names with prefix `use-`, `with-` or suffix `-support` - -### Why is this bad? -These prefixes and suffixes have no significant meaning. - -### Example -``` -[features] -default = ["use-abc", "with-def", "ghi-support"] -use-abc = [] // redundant -with-def = [] // redundant -ghi-support = [] // redundant -``` - -Use instead: -``` -[features] -default = ["abc", "def", "ghi"] -abc = [] -def = [] -ghi = [] -``` diff --git a/src/docs/redundant_field_names.txt b/src/docs/redundant_field_names.txt deleted file mode 100644 index 35f20a466b37..000000000000 --- a/src/docs/redundant_field_names.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for fields in struct literals where shorthands -could be used. - -### Why is this bad? -If the field and variable names are the same, -the field name is redundant. - -### Example -``` -let bar: u8 = 123; - -struct Foo { - bar: u8, -} - -let foo = Foo { bar: bar }; -``` -the last line can be simplified to -``` -let foo = Foo { bar }; -``` \ No newline at end of file diff --git a/src/docs/redundant_pattern.txt b/src/docs/redundant_pattern.txt deleted file mode 100644 index 45f6cfc86702..000000000000 --- a/src/docs/redundant_pattern.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for patterns in the form `name @ _`. - -### Why is this bad? -It's almost always more readable to just use direct -bindings. - -### Example -``` -match v { - Some(x) => (), - y @ _ => (), -} -``` - -Use instead: -``` -match v { - Some(x) => (), - y => (), -} -``` \ No newline at end of file diff --git a/src/docs/redundant_pattern_matching.txt b/src/docs/redundant_pattern_matching.txt deleted file mode 100644 index 77b1021e0db3..000000000000 --- a/src/docs/redundant_pattern_matching.txt +++ /dev/null @@ -1,45 +0,0 @@ -### What it does -Lint for redundant pattern matching over `Result`, `Option`, -`std::task::Poll` or `std::net::IpAddr` - -### Why is this bad? -It's more concise and clear to just use the proper -utility function - -### Known problems -This will change the drop order for the matched type. Both `if let` and -`while let` will drop the value at the end of the block, both `if` and `while` will drop the -value before entering the block. For most types this change will not matter, but for a few -types this will not be an acceptable change (e.g. locks). See the -[reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about -drop order. - -### Example -``` -if let Ok(_) = Ok::(42) {} -if let Err(_) = Err::(42) {} -if let None = None::<()> {} -if let Some(_) = Some(42) {} -if let Poll::Pending = Poll::Pending::<()> {} -if let Poll::Ready(_) = Poll::Ready(42) {} -if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {} -if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {} -match Ok::(42) { - Ok(_) => true, - Err(_) => false, -}; -``` - -The more idiomatic use would be: - -``` -if Ok::(42).is_ok() {} -if Err::(42).is_err() {} -if None::<()>.is_none() {} -if Some(42).is_some() {} -if Poll::Pending::<()>.is_pending() {} -if Poll::Ready(42).is_ready() {} -if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {} -if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {} -Ok::(42).is_ok(); -``` \ No newline at end of file diff --git a/src/docs/redundant_pub_crate.txt b/src/docs/redundant_pub_crate.txt deleted file mode 100644 index a527bb5acda1..000000000000 --- a/src/docs/redundant_pub_crate.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for items declared `pub(crate)` that are not crate visible because they -are inside a private module. - -### Why is this bad? -Writing `pub(crate)` is misleading when it's redundant due to the parent -module's visibility. - -### Example -``` -mod internal { - pub(crate) fn internal_fn() { } -} -``` -This function is not visible outside the module and it can be declared with `pub` or -private visibility -``` -mod internal { - pub fn internal_fn() { } -} -``` \ No newline at end of file diff --git a/src/docs/redundant_slicing.txt b/src/docs/redundant_slicing.txt deleted file mode 100644 index 6798911ed76c..000000000000 --- a/src/docs/redundant_slicing.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for redundant slicing expressions which use the full range, and -do not change the type. - -### Why is this bad? -It unnecessarily adds complexity to the expression. - -### Known problems -If the type being sliced has an implementation of `Index` -that actually changes anything then it can't be removed. However, this would be surprising -to people reading the code and should have a note with it. - -### Example -``` -fn get_slice(x: &[u32]) -> &[u32] { - &x[..] -} -``` -Use instead: -``` -fn get_slice(x: &[u32]) -> &[u32] { - x -} -``` \ No newline at end of file diff --git a/src/docs/redundant_static_lifetimes.txt b/src/docs/redundant_static_lifetimes.txt deleted file mode 100644 index edb8e7b5c62b..000000000000 --- a/src/docs/redundant_static_lifetimes.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for constants and statics with an explicit `'static` lifetime. - -### Why is this bad? -Adding `'static` to every reference can create very -complicated types. - -### Example -``` -const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = -&[...] -static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] = -&[...] -``` -This code can be rewritten as -``` - const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...] - static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...] -``` \ No newline at end of file diff --git a/src/docs/ref_binding_to_reference.txt b/src/docs/ref_binding_to_reference.txt deleted file mode 100644 index dc391cd988e5..000000000000 --- a/src/docs/ref_binding_to_reference.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for `ref` bindings which create a reference to a reference. - -### Why is this bad? -The address-of operator at the use site is clearer about the need for a reference. - -### Example -``` -let x = Some(""); -if let Some(ref x) = x { - // use `x` here -} -``` - -Use instead: -``` -let x = Some(""); -if let Some(x) = x { - // use `&x` here -} -``` \ No newline at end of file diff --git a/src/docs/ref_option_ref.txt b/src/docs/ref_option_ref.txt deleted file mode 100644 index 951c7bd7f7ec..000000000000 --- a/src/docs/ref_option_ref.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for usage of `&Option<&T>`. - -### Why is this bad? -Since `&` is Copy, it's useless to have a -reference on `Option<&T>`. - -### Known problems -It may be irrelevant to use this lint on -public API code as it will make a breaking change to apply it. - -### Example -``` -let x: &Option<&u32> = &Some(&0u32); -``` -Use instead: -``` -let x: Option<&u32> = Some(&0u32); -``` \ No newline at end of file diff --git a/src/docs/repeat_once.txt b/src/docs/repeat_once.txt deleted file mode 100644 index 3ba189c1945d..000000000000 --- a/src/docs/repeat_once.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for usage of `.repeat(1)` and suggest the following method for each types. -- `.to_string()` for `str` -- `.clone()` for `String` -- `.to_vec()` for `slice` - -The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if -they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306)) - -### Why is this bad? -For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning -the string is the intention behind this, `clone()` should be used. - -### Example -``` -fn main() { - let x = String::from("hello world").repeat(1); -} -``` -Use instead: -``` -fn main() { - let x = String::from("hello world").clone(); -} -``` \ No newline at end of file diff --git a/src/docs/rest_pat_in_fully_bound_structs.txt b/src/docs/rest_pat_in_fully_bound_structs.txt deleted file mode 100644 index 40ebbe754a37..000000000000 --- a/src/docs/rest_pat_in_fully_bound_structs.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched. - -### Why is this bad? -Correctness and readability. It's like having a wildcard pattern after -matching all enum variants explicitly. - -### Example -``` -let a = A { a: 5 }; - -match a { - A { a: 5, .. } => {}, - _ => {}, -} -``` - -Use instead: -``` -match a { - A { a: 5 } => {}, - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/result_large_err.txt b/src/docs/result_large_err.txt deleted file mode 100644 index e5fab3c5cfcf..000000000000 --- a/src/docs/result_large_err.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for functions that return `Result` with an unusually large -`Err`-variant. - -### Why is this bad? -A `Result` is at least as large as the `Err`-variant. While we -expect that variant to be seldomly used, the compiler needs to reserve -and move that much memory every single time. - -### Known problems -The size determined by Clippy is platform-dependent. - -### Examples -``` -pub enum ParseError { - UnparsedBytes([u8; 512]), - UnexpectedEof, -} - -// The `Result` has at least 512 bytes, even in the `Ok`-case -pub fn parse() -> Result<(), ParseError> { - Ok(()) -} -``` -should be -``` -pub enum ParseError { - UnparsedBytes(Box<[u8; 512]>), - UnexpectedEof, -} - -// The `Result` is slightly larger than a pointer -pub fn parse() -> Result<(), ParseError> { - Ok(()) -} -``` \ No newline at end of file diff --git a/src/docs/result_map_or_into_option.txt b/src/docs/result_map_or_into_option.txt deleted file mode 100644 index 899d98c307c8..000000000000 --- a/src/docs/result_map_or_into_option.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.map_or(None, Some)`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.ok()`. - -### Example -``` -assert_eq!(Some(1), r.map_or(None, Some)); -``` - -Use instead: -``` -assert_eq!(Some(1), r.ok()); -``` \ No newline at end of file diff --git a/src/docs/result_map_unit_fn.txt b/src/docs/result_map_unit_fn.txt deleted file mode 100644 index 3455c5c1f9b8..000000000000 --- a/src/docs/result_map_unit_fn.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for usage of `result.map(f)` where f is a function -or closure that returns the unit type `()`. - -### Why is this bad? -Readability, this can be written more clearly with -an if let statement - -### Example -``` -let x: Result = do_stuff(); -x.map(log_err_msg); -x.map(|msg| log_err_msg(format_msg(msg))); -``` - -The correct use would be: - -``` -let x: Result = do_stuff(); -if let Ok(msg) = x { - log_err_msg(msg); -}; -if let Ok(msg) = x { - log_err_msg(format_msg(msg)); -}; -``` \ No newline at end of file diff --git a/src/docs/result_unit_err.txt b/src/docs/result_unit_err.txt deleted file mode 100644 index 7c8ec2ffcf94..000000000000 --- a/src/docs/result_unit_err.txt +++ /dev/null @@ -1,40 +0,0 @@ -### What it does -Checks for public functions that return a `Result` -with an `Err` type of `()`. It suggests using a custom type that -implements `std::error::Error`. - -### Why is this bad? -Unit does not implement `Error` and carries no -further information about what went wrong. - -### Known problems -Of course, this lint assumes that `Result` is used -for a fallible operation (which is after all the intended use). However -code may opt to (mis)use it as a basic two-variant-enum. In that case, -the suggestion is misguided, and the code should use a custom enum -instead. - -### Examples -``` -pub fn read_u8() -> Result { Err(()) } -``` -should become -``` -use std::fmt; - -#[derive(Debug)] -pub struct EndOfStream; - -impl fmt::Display for EndOfStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "End of Stream") - } -} - -impl std::error::Error for EndOfStream { } - -pub fn read_u8() -> Result { Err(EndOfStream) } -``` - -Note that there are crates that simplify creating the error type, e.g. -[`thiserror`](https://docs.rs/thiserror). \ No newline at end of file diff --git a/src/docs/return_self_not_must_use.txt b/src/docs/return_self_not_must_use.txt deleted file mode 100644 index 4a4fd2c6e517..000000000000 --- a/src/docs/return_self_not_must_use.txt +++ /dev/null @@ -1,46 +0,0 @@ -### What it does -This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute. - -### Why is this bad? -Methods returning `Self` often create new values, having the `#[must_use]` attribute -prevents users from "forgetting" to use the newly created value. - -The `#[must_use]` attribute can be added to the type itself to ensure that instances -are never forgotten. Functions returning a type marked with `#[must_use]` will not be -linted, as the usage is already enforced by the type attribute. - -### Limitations -This lint is only applied on methods taking a `self` argument. It would be mostly noise -if it was added on constructors for example. - -### Example -``` -pub struct Bar; -impl Bar { - // Missing attribute - pub fn bar(&self) -> Self { - Self - } -} -``` - -Use instead: -``` -// It's better to have the `#[must_use]` attribute on the method like this: -pub struct Bar; -impl Bar { - #[must_use] - pub fn bar(&self) -> Self { - Self - } -} - -// Or on the type definition like this: -#[must_use] -pub struct Bar; -impl Bar { - pub fn bar(&self) -> Self { - Self - } -} -``` \ No newline at end of file diff --git a/src/docs/reversed_empty_ranges.txt b/src/docs/reversed_empty_ranges.txt deleted file mode 100644 index 39f481193866..000000000000 --- a/src/docs/reversed_empty_ranges.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for range expressions `x..y` where both `x` and `y` -are constant and `x` is greater or equal to `y`. - -### Why is this bad? -Empty ranges yield no values so iterating them is a no-op. -Moreover, trying to use a reversed range to index a slice will panic at run-time. - -### Example -``` -fn main() { - (10..=0).for_each(|x| println!("{}", x)); - - let arr = [1, 2, 3, 4, 5]; - let sub = &arr[3..1]; -} -``` -Use instead: -``` -fn main() { - (0..=10).rev().for_each(|x| println!("{}", x)); - - let arr = [1, 2, 3, 4, 5]; - let sub = &arr[1..3]; -} -``` \ No newline at end of file diff --git a/src/docs/same_functions_in_if_condition.txt b/src/docs/same_functions_in_if_condition.txt deleted file mode 100644 index a0a90eec681e..000000000000 --- a/src/docs/same_functions_in_if_condition.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for consecutive `if`s with the same function call. - -### Why is this bad? -This is probably a copy & paste error. -Despite the fact that function can have side effects and `if` works as -intended, such an approach is implicit and can be considered a "code smell". - -### Example -``` -if foo() == bar { - … -} else if foo() == bar { - … -} -``` - -This probably should be: -``` -if foo() == bar { - … -} else if foo() == baz { - … -} -``` - -or if the original code was not a typo and called function mutates a state, -consider move the mutation out of the `if` condition to avoid similarity to -a copy & paste error: - -``` -let first = foo(); -if first == bar { - … -} else { - let second = foo(); - if second == bar { - … - } -} -``` \ No newline at end of file diff --git a/src/docs/same_item_push.txt b/src/docs/same_item_push.txt deleted file mode 100644 index 7e724073f8aa..000000000000 --- a/src/docs/same_item_push.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks whether a for loop is being used to push a constant -value into a Vec. - -### Why is this bad? -This kind of operation can be expressed more succinctly with -`vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also -have better performance. - -### Example -``` -let item1 = 2; -let item2 = 3; -let mut vec: Vec = Vec::new(); -for _ in 0..20 { - vec.push(item1); -} -for _ in 0..30 { - vec.push(item2); -} -``` - -Use instead: -``` -let item1 = 2; -let item2 = 3; -let mut vec: Vec = vec![item1; 20]; -vec.resize(20 + 30, item2); -``` \ No newline at end of file diff --git a/src/docs/same_name_method.txt b/src/docs/same_name_method.txt deleted file mode 100644 index 792dd717fc64..000000000000 --- a/src/docs/same_name_method.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -It lints if a struct has two methods with the same name: -one from a trait, another not from trait. - -### Why is this bad? -Confusing. - -### Example -``` -trait T { - fn foo(&self) {} -} - -struct S; - -impl T for S { - fn foo(&self) {} -} - -impl S { - fn foo(&self) {} -} -``` \ No newline at end of file diff --git a/src/docs/search_is_some.txt b/src/docs/search_is_some.txt deleted file mode 100644 index 67292d54585f..000000000000 --- a/src/docs/search_is_some.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for an iterator or string search (such as `find()`, -`position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`. - -### Why is this bad? -Readability, this can be written more concisely as: -* `_.any(_)`, or `_.contains(_)` for `is_some()`, -* `!_.any(_)`, or `!_.contains(_)` for `is_none()`. - -### Example -``` -let vec = vec![1]; -vec.iter().find(|x| **x == 0).is_some(); - -"hello world".find("world").is_none(); -``` - -Use instead: -``` -let vec = vec![1]; -vec.iter().any(|x| *x == 0); - -!"hello world".contains("world"); -``` \ No newline at end of file diff --git a/src/docs/self_assignment.txt b/src/docs/self_assignment.txt deleted file mode 100644 index ea60ea077ab5..000000000000 --- a/src/docs/self_assignment.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Checks for explicit self-assignments. - -### Why is this bad? -Self-assignments are redundant and unlikely to be -intentional. - -### Known problems -If expression contains any deref coercions or -indexing operations they are assumed not to have any side effects. - -### Example -``` -struct Event { - x: i32, -} - -fn copy_position(a: &mut Event, b: &Event) { - a.x = a.x; -} -``` - -Should be: -``` -struct Event { - x: i32, -} - -fn copy_position(a: &mut Event, b: &Event) { - a.x = b.x; -} -``` \ No newline at end of file diff --git a/src/docs/self_named_constructors.txt b/src/docs/self_named_constructors.txt deleted file mode 100644 index a01669a84542..000000000000 --- a/src/docs/self_named_constructors.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Warns when constructors have the same name as their types. - -### Why is this bad? -Repeating the name of the type is redundant. - -### Example -``` -struct Foo {} - -impl Foo { - pub fn foo() -> Foo { - Foo {} - } -} -``` -Use instead: -``` -struct Foo {} - -impl Foo { - pub fn new() -> Foo { - Foo {} - } -} -``` \ No newline at end of file diff --git a/src/docs/self_named_module_files.txt b/src/docs/self_named_module_files.txt deleted file mode 100644 index 73e805136288..000000000000 --- a/src/docs/self_named_module_files.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks that module layout uses only `mod.rs` files. - -### Why is this bad? -Having multiple module layout styles in a project can be confusing. - -### Example -``` -src/ - stuff/ - stuff_files.rs - stuff.rs - lib.rs -``` -Use instead: -``` -src/ - stuff/ - stuff_files.rs - mod.rs - lib.rs -``` \ No newline at end of file diff --git a/src/docs/semicolon_if_nothing_returned.txt b/src/docs/semicolon_if_nothing_returned.txt deleted file mode 100644 index 30c963ca211b..000000000000 --- a/src/docs/semicolon_if_nothing_returned.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Looks for blocks of expressions and fires if the last expression returns -`()` but is not followed by a semicolon. - -### Why is this bad? -The semicolon might be optional but when extending the block with new -code, it doesn't require a change in previous last line. - -### Example -``` -fn main() { - println!("Hello world") -} -``` -Use instead: -``` -fn main() { - println!("Hello world"); -} -``` \ No newline at end of file diff --git a/src/docs/separated_literal_suffix.txt b/src/docs/separated_literal_suffix.txt deleted file mode 100644 index 226a6b8a9876..000000000000 --- a/src/docs/separated_literal_suffix.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Warns if literal suffixes are separated by an underscore. -To enforce separated literal suffix style, -see the `unseparated_literal_suffix` lint. - -### Why is this bad? -Suffix style should be consistent. - -### Example -``` -123832_i32 -``` - -Use instead: -``` -123832i32 -``` \ No newline at end of file diff --git a/src/docs/serde_api_misuse.txt b/src/docs/serde_api_misuse.txt deleted file mode 100644 index 8a3c89ac11ad..000000000000 --- a/src/docs/serde_api_misuse.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for mis-uses of the serde API. - -### Why is this bad? -Serde is very finnicky about how its API should be -used, but the type system can't be used to enforce it (yet?). - -### Example -Implementing `Visitor::visit_string` but not -`Visitor::visit_str`. \ No newline at end of file diff --git a/src/docs/shadow_reuse.txt b/src/docs/shadow_reuse.txt deleted file mode 100644 index 9eb8e7ad164b..000000000000 --- a/src/docs/shadow_reuse.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for bindings that shadow other bindings already in -scope, while reusing the original value. - -### Why is this bad? -Not too much, in fact it's a common pattern in Rust -code. Still, some argue that name shadowing like this hurts readability, -because a value may be bound to different things depending on position in -the code. - -### Example -``` -let x = 2; -let x = x + 1; -``` -use different variable name: -``` -let x = 2; -let y = x + 1; -``` \ No newline at end of file diff --git a/src/docs/shadow_same.txt b/src/docs/shadow_same.txt deleted file mode 100644 index 3cd96f560a5e..000000000000 --- a/src/docs/shadow_same.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for bindings that shadow other bindings already in -scope, while just changing reference level or mutability. - -### Why is this bad? -Not much, in fact it's a very common pattern in Rust -code. Still, some may opt to avoid it in their code base, they can set this -lint to `Warn`. - -### Example -``` -let x = &x; -``` - -Use instead: -``` -let y = &x; // use different variable name -``` \ No newline at end of file diff --git a/src/docs/shadow_unrelated.txt b/src/docs/shadow_unrelated.txt deleted file mode 100644 index 436251c520a9..000000000000 --- a/src/docs/shadow_unrelated.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for bindings that shadow other bindings already in -scope, either without an initialization or with one that does not even use -the original value. - -### Why is this bad? -Name shadowing can hurt readability, especially in -large code bases, because it is easy to lose track of the active binding at -any place in the code. This can be alleviated by either giving more specific -names to bindings or introducing more scopes to contain the bindings. - -### Example -``` -let x = y; -let x = z; // shadows the earlier binding -``` - -Use instead: -``` -let x = y; -let w = z; // use different variable name -``` \ No newline at end of file diff --git a/src/docs/short_circuit_statement.txt b/src/docs/short_circuit_statement.txt deleted file mode 100644 index 31492bed03d4..000000000000 --- a/src/docs/short_circuit_statement.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for the use of short circuit boolean conditions as -a -statement. - -### Why is this bad? -Using a short circuit boolean condition as a statement -may hide the fact that the second part is executed or not depending on the -outcome of the first part. - -### Example -``` -f() && g(); // We should write `if f() { g(); }`. -``` \ No newline at end of file diff --git a/src/docs/should_implement_trait.txt b/src/docs/should_implement_trait.txt deleted file mode 100644 index 02e74751ae03..000000000000 --- a/src/docs/should_implement_trait.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for methods that should live in a trait -implementation of a `std` trait (see [llogiq's blog -post](http://llogiq.github.io/2015/07/30/traits.html) for further -information) instead of an inherent implementation. - -### Why is this bad? -Implementing the traits improve ergonomics for users of -the code, often with very little cost. Also people seeing a `mul(...)` -method -may expect `*` to work equally, so you should have good reason to disappoint -them. - -### Example -``` -struct X; -impl X { - fn add(&self, other: &X) -> X { - // .. - } -} -``` \ No newline at end of file diff --git a/src/docs/significant_drop_in_scrutinee.txt b/src/docs/significant_drop_in_scrutinee.txt deleted file mode 100644 index f869def0ddb7..000000000000 --- a/src/docs/significant_drop_in_scrutinee.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Check for temporaries returned from function calls in a match scrutinee that have the -`clippy::has_significant_drop` attribute. - -### Why is this bad? -The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have -an important side-effect, such as unlocking a mutex, making it important for users to be -able to accurately understand their lifetimes. When a temporary is returned in a function -call in a match scrutinee, its lifetime lasts until the end of the match block, which may -be surprising. - -For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a -function call that returns a `MutexGuard` and then tries to lock again in one of the match -arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of -the match block and thus will not unlock. - -### Example -``` -let mutex = Mutex::new(State {}); - -match mutex.lock().unwrap().foo() { - true => { - mutex.lock().unwrap().bar(); // Deadlock! - } - false => {} -}; - -println!("All done!"); -``` -Use instead: -``` -let mutex = Mutex::new(State {}); - -let is_foo = mutex.lock().unwrap().foo(); -match is_foo { - true => { - mutex.lock().unwrap().bar(); - } - false => {} -}; - -println!("All done!"); -``` \ No newline at end of file diff --git a/src/docs/similar_names.txt b/src/docs/similar_names.txt deleted file mode 100644 index f9eff21b6793..000000000000 --- a/src/docs/similar_names.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for names that are very similar and thus confusing. - -Note: this lint looks for similar names throughout each -scope. To allow it, you need to allow it on the scope -level, not on the name that is reported. - -### Why is this bad? -It's hard to distinguish between names that differ only -by a single character. - -### Example -``` -let checked_exp = something; -let checked_expr = something_else; -``` \ No newline at end of file diff --git a/src/docs/single_char_add_str.txt b/src/docs/single_char_add_str.txt deleted file mode 100644 index cf23dc0c89bb..000000000000 --- a/src/docs/single_char_add_str.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Warns when using `push_str`/`insert_str` with a single-character string literal -where `push`/`insert` with a `char` would work fine. - -### Why is this bad? -It's less clear that we are pushing a single character. - -### Example -``` -string.insert_str(0, "R"); -string.push_str("R"); -``` - -Use instead: -``` -string.insert(0, 'R'); -string.push('R'); -``` \ No newline at end of file diff --git a/src/docs/single_char_lifetime_names.txt b/src/docs/single_char_lifetime_names.txt deleted file mode 100644 index 92dd24bf247e..000000000000 --- a/src/docs/single_char_lifetime_names.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for lifetimes with names which are one character -long. - -### Why is this bad? -A single character is likely not enough to express the -purpose of a lifetime. Using a longer name can make code -easier to understand, especially for those who are new to -Rust. - -### Known problems -Rust programmers and learning resources tend to use single -character lifetimes, so this lint is at odds with the -ecosystem at large. In addition, the lifetime's purpose may -be obvious or, rarely, expressible in one character. - -### Example -``` -struct DiagnosticCtx<'a> { - source: &'a str, -} -``` -Use instead: -``` -struct DiagnosticCtx<'src> { - source: &'src str, -} -``` \ No newline at end of file diff --git a/src/docs/single_char_pattern.txt b/src/docs/single_char_pattern.txt deleted file mode 100644 index 9e5ad1e7d639..000000000000 --- a/src/docs/single_char_pattern.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for string methods that receive a single-character -`str` as an argument, e.g., `_.split("x")`. - -### Why is this bad? -Performing these methods using a `char` is faster than -using a `str`. - -### Known problems -Does not catch multi-byte unicode characters. - -### Example -``` -_.split("x"); -``` - -Use instead: -``` -_.split('x'); -``` \ No newline at end of file diff --git a/src/docs/single_component_path_imports.txt b/src/docs/single_component_path_imports.txt deleted file mode 100644 index 3a026388183e..000000000000 --- a/src/docs/single_component_path_imports.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checking for imports with single component use path. - -### Why is this bad? -Import with single component use path such as `use cratename;` -is not necessary, and thus should be removed. - -### Example -``` -use regex; - -fn main() { - regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); -} -``` -Better as -``` -fn main() { - regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); -} -``` \ No newline at end of file diff --git a/src/docs/single_element_loop.txt b/src/docs/single_element_loop.txt deleted file mode 100644 index 6f0c15a85f77..000000000000 --- a/src/docs/single_element_loop.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks whether a for loop has a single element. - -### Why is this bad? -There is no reason to have a loop of a -single element. - -### Example -``` -let item1 = 2; -for item in &[item1] { - println!("{}", item); -} -``` - -Use instead: -``` -let item1 = 2; -let item = &item1; -println!("{}", item); -``` \ No newline at end of file diff --git a/src/docs/single_match.txt b/src/docs/single_match.txt deleted file mode 100644 index 31dde4da8489..000000000000 --- a/src/docs/single_match.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for matches with a single arm where an `if let` -will usually suffice. - -### Why is this bad? -Just readability – `if let` nests less than a `match`. - -### Example -``` -match x { - Some(ref foo) => bar(foo), - _ => (), -} -``` - -Use instead: -``` -if let Some(ref foo) = x { - bar(foo); -} -``` \ No newline at end of file diff --git a/src/docs/single_match_else.txt b/src/docs/single_match_else.txt deleted file mode 100644 index 29a447af09b8..000000000000 --- a/src/docs/single_match_else.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for matches with two arms where an `if let else` will -usually suffice. - -### Why is this bad? -Just readability – `if let` nests less than a `match`. - -### Known problems -Personal style preferences may differ. - -### Example -Using `match`: - -``` -match x { - Some(ref foo) => bar(foo), - _ => bar(&other_ref), -} -``` - -Using `if let` with `else`: - -``` -if let Some(ref foo) = x { - bar(foo); -} else { - bar(&other_ref); -} -``` \ No newline at end of file diff --git a/src/docs/size_of_in_element_count.txt b/src/docs/size_of_in_element_count.txt deleted file mode 100644 index d893ec6a2a01..000000000000 --- a/src/docs/size_of_in_element_count.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Detects expressions where -`size_of::` or `size_of_val::` is used as a -count of elements of type `T` - -### Why is this bad? -These functions expect a count -of `T` and not a number of bytes - -### Example -``` -const SIZE: usize = 128; -let x = [2u8; SIZE]; -let mut y = [2u8; SIZE]; -unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; -``` \ No newline at end of file diff --git a/src/docs/skip_while_next.txt b/src/docs/skip_while_next.txt deleted file mode 100644 index 1ec8a3a28d5b..000000000000 --- a/src/docs/skip_while_next.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for usage of `_.skip_while(condition).next()`. - -### Why is this bad? -Readability, this can be written more concisely as -`_.find(!condition)`. - -### Example -``` -vec.iter().skip_while(|x| **x == 0).next(); -``` - -Use instead: -``` -vec.iter().find(|x| **x != 0); -``` \ No newline at end of file diff --git a/src/docs/slow_vector_initialization.txt b/src/docs/slow_vector_initialization.txt deleted file mode 100644 index 53442e179654..000000000000 --- a/src/docs/slow_vector_initialization.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks slow zero-filled vector initialization - -### Why is this bad? -These structures are non-idiomatic and less efficient than simply using -`vec![0; len]`. - -### Example -``` -let mut vec1 = Vec::with_capacity(len); -vec1.resize(len, 0); - -let mut vec1 = Vec::with_capacity(len); -vec1.resize(vec1.capacity(), 0); - -let mut vec2 = Vec::with_capacity(len); -vec2.extend(repeat(0).take(len)); -``` - -Use instead: -``` -let mut vec1 = vec![0; len]; -let mut vec2 = vec![0; len]; -``` \ No newline at end of file diff --git a/src/docs/stable_sort_primitive.txt b/src/docs/stable_sort_primitive.txt deleted file mode 100644 index 6465dbee46b1..000000000000 --- a/src/docs/stable_sort_primitive.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -When sorting primitive values (integers, bools, chars, as well -as arrays, slices, and tuples of such items), it is typically better to -use an unstable sort than a stable sort. - -### Why is this bad? -Typically, using a stable sort consumes more memory and cpu cycles. -Because values which compare equal are identical, preserving their -relative order (the guarantee that a stable sort provides) means -nothing, while the extra costs still apply. - -### Known problems - -As pointed out in -[issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), -a stable sort can instead be significantly faster for certain scenarios -(eg. when a sorted vector is extended with new data and resorted). - -For more information and benchmarking results, please refer to the -issue linked above. - -### Example -``` -let mut vec = vec![2, 1, 3]; -vec.sort(); -``` -Use instead: -``` -let mut vec = vec![2, 1, 3]; -vec.sort_unstable(); -``` \ No newline at end of file diff --git a/src/docs/std_instead_of_alloc.txt b/src/docs/std_instead_of_alloc.txt deleted file mode 100644 index c2d32704e505..000000000000 --- a/src/docs/std_instead_of_alloc.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does - -Finds items imported through `std` when available through `alloc`. - -### Why is this bad? - -Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from -alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful -for crates migrating to become `no_std` compatible. - -### Example -``` -use std::vec::Vec; -``` -Use instead: -``` -use alloc::vec::Vec; -``` \ No newline at end of file diff --git a/src/docs/std_instead_of_core.txt b/src/docs/std_instead_of_core.txt deleted file mode 100644 index f1e1518c6a62..000000000000 --- a/src/docs/std_instead_of_core.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does - -Finds items imported through `std` when available through `core`. - -### Why is this bad? - -Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure -disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates -migrating to become `no_std` compatible. - -### Example -``` -use std::hash::Hasher; -``` -Use instead: -``` -use core::hash::Hasher; -``` \ No newline at end of file diff --git a/src/docs/str_to_string.txt b/src/docs/str_to_string.txt deleted file mode 100644 index a24755223747..000000000000 --- a/src/docs/str_to_string.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -This lint checks for `.to_string()` method calls on values of type `&str`. - -### Why is this bad? -The `to_string` method is also used on other types to convert them to a string. -When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better -expressed with `.to_owned()`. - -### Example -``` -// example code where clippy issues a warning -let _ = "str".to_string(); -``` -Use instead: -``` -// example code which does not raise clippy warning -let _ = "str".to_owned(); -``` \ No newline at end of file diff --git a/src/docs/string_add.txt b/src/docs/string_add.txt deleted file mode 100644 index 23dafd0d0333..000000000000 --- a/src/docs/string_add.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for all instances of `x + _` where `x` is of type -`String`, but only if [`string_add_assign`](#string_add_assign) does *not* -match. - -### Why is this bad? -It's not bad in and of itself. However, this particular -`Add` implementation is asymmetric (the other operand need not be `String`, -but `x` does), while addition as mathematically defined is symmetric, also -the `String::push_str(_)` function is a perfectly good replacement. -Therefore, some dislike it and wish not to have it in their code. - -That said, other people think that string addition, having a long tradition -in other languages is actually fine, which is why we decided to make this -particular lint `allow` by default. - -### Example -``` -let x = "Hello".to_owned(); -x + ", World"; -``` - -Use instead: -``` -let mut x = "Hello".to_owned(); -x.push_str(", World"); -``` \ No newline at end of file diff --git a/src/docs/string_add_assign.txt b/src/docs/string_add_assign.txt deleted file mode 100644 index 7438be855db8..000000000000 --- a/src/docs/string_add_assign.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for string appends of the form `x = x + y` (without -`let`!). - -### Why is this bad? -It's not really bad, but some people think that the -`.push_str(_)` method is more readable. - -### Example -``` -let mut x = "Hello".to_owned(); -x = x + ", World"; - -// More readable -x += ", World"; -x.push_str(", World"); -``` \ No newline at end of file diff --git a/src/docs/string_extend_chars.txt b/src/docs/string_extend_chars.txt deleted file mode 100644 index 619ea3e11867..000000000000 --- a/src/docs/string_extend_chars.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for the use of `.extend(s.chars())` where s is a -`&str` or `String`. - -### Why is this bad? -`.push_str(s)` is clearer - -### Example -``` -let abc = "abc"; -let def = String::from("def"); -let mut s = String::new(); -s.extend(abc.chars()); -s.extend(def.chars()); -``` -The correct use would be: -``` -let abc = "abc"; -let def = String::from("def"); -let mut s = String::new(); -s.push_str(abc); -s.push_str(&def); -``` \ No newline at end of file diff --git a/src/docs/string_from_utf8_as_bytes.txt b/src/docs/string_from_utf8_as_bytes.txt deleted file mode 100644 index 9102d73471cb..000000000000 --- a/src/docs/string_from_utf8_as_bytes.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Check if the string is transformed to byte array and casted back to string. - -### Why is this bad? -It's unnecessary, the string can be used directly. - -### Example -``` -std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); -``` - -Use instead: -``` -&"Hello World!"[6..11]; -``` \ No newline at end of file diff --git a/src/docs/string_lit_as_bytes.txt b/src/docs/string_lit_as_bytes.txt deleted file mode 100644 index a125b97ed656..000000000000 --- a/src/docs/string_lit_as_bytes.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for the `as_bytes` method called on string literals -that contain only ASCII characters. - -### Why is this bad? -Byte string literals (e.g., `b"foo"`) can be used -instead. They are shorter but less discoverable than `as_bytes()`. - -### Known problems -`"str".as_bytes()` and the suggested replacement of `b"str"` are not -equivalent because they have different types. The former is `&[u8]` -while the latter is `&[u8; 3]`. That means in general they will have a -different set of methods and different trait implementations. - -``` -fn f(v: Vec) {} - -f("...".as_bytes().to_owned()); // works -f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec - -fn g(r: impl std::io::Read) {} - -g("...".as_bytes()); // works -g(b"..."); // does not work -``` - -The actual equivalent of `"str".as_bytes()` with the same type is not -`b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not -more readable than a function call. - -### Example -``` -let bstr = "a byte string".as_bytes(); -``` - -Use instead: -``` -let bstr = b"a byte string"; -``` \ No newline at end of file diff --git a/src/docs/string_slice.txt b/src/docs/string_slice.txt deleted file mode 100644 index 3d9e49dd39eb..000000000000 --- a/src/docs/string_slice.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for slice operations on strings - -### Why is this bad? -UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character -counts and string indices. This may lead to panics, and should warrant some test cases -containing wide UTF-8 characters. This lint is most useful in code that should avoid -panics at all costs. - -### Known problems -Probably lots of false positives. If an index comes from a known valid position (e.g. -obtained via `char_indices` over the same string), it is totally OK. - -# Example -``` -&"Ölkanne"[1..]; -``` \ No newline at end of file diff --git a/src/docs/string_to_string.txt b/src/docs/string_to_string.txt deleted file mode 100644 index deb7eebe7842..000000000000 --- a/src/docs/string_to_string.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -This lint checks for `.to_string()` method calls on values of type `String`. - -### Why is this bad? -The `to_string` method is also used on other types to convert them to a string. -When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`. - -### Example -``` -// example code where clippy issues a warning -let msg = String::from("Hello World"); -let _ = msg.to_string(); -``` -Use instead: -``` -// example code which does not raise clippy warning -let msg = String::from("Hello World"); -let _ = msg.clone(); -``` \ No newline at end of file diff --git a/src/docs/strlen_on_c_strings.txt b/src/docs/strlen_on_c_strings.txt deleted file mode 100644 index 0454abf55a20..000000000000 --- a/src/docs/strlen_on_c_strings.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for usage of `libc::strlen` on a `CString` or `CStr` value, -and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead. - -### Why is this bad? -This avoids calling an unsafe `libc` function. -Currently, it also avoids calculating the length. - -### Example -``` -use std::ffi::CString; -let cstring = CString::new("foo").expect("CString::new failed"); -let len = unsafe { libc::strlen(cstring.as_ptr()) }; -``` -Use instead: -``` -use std::ffi::CString; -let cstring = CString::new("foo").expect("CString::new failed"); -let len = cstring.as_bytes().len(); -``` \ No newline at end of file diff --git a/src/docs/struct_excessive_bools.txt b/src/docs/struct_excessive_bools.txt deleted file mode 100644 index 9e197c786201..000000000000 --- a/src/docs/struct_excessive_bools.txt +++ /dev/null @@ -1,29 +0,0 @@ -### What it does -Checks for excessive -use of bools in structs. - -### Why is this bad? -Excessive bools in a struct -is often a sign that it's used as a state machine, -which is much better implemented as an enum. -If it's not the case, excessive bools usually benefit -from refactoring into two-variant enums for better -readability and API. - -### Example -``` -struct S { - is_pending: bool, - is_processing: bool, - is_finished: bool, -} -``` - -Use instead: -``` -enum S { - Pending, - Processing, - Finished, -} -``` \ No newline at end of file diff --git a/src/docs/suboptimal_flops.txt b/src/docs/suboptimal_flops.txt deleted file mode 100644 index f1c9c665b085..000000000000 --- a/src/docs/suboptimal_flops.txt +++ /dev/null @@ -1,50 +0,0 @@ -### What it does -Looks for floating-point expressions that -can be expressed using built-in methods to improve both -accuracy and performance. - -### Why is this bad? -Negatively impacts accuracy and performance. - -### Example -``` -use std::f32::consts::E; - -let a = 3f32; -let _ = (2f32).powf(a); -let _ = E.powf(a); -let _ = a.powf(1.0 / 2.0); -let _ = a.log(2.0); -let _ = a.log(10.0); -let _ = a.log(E); -let _ = a.powf(2.0); -let _ = a * 2.0 + 4.0; -let _ = if a < 0.0 { - -a -} else { - a -}; -let _ = if a < 0.0 { - a -} else { - -a -}; -``` - -is better expressed as - -``` -use std::f32::consts::E; - -let a = 3f32; -let _ = a.exp2(); -let _ = a.exp(); -let _ = a.sqrt(); -let _ = a.log2(); -let _ = a.log10(); -let _ = a.ln(); -let _ = a.powi(2); -let _ = a.mul_add(2.0, 4.0); -let _ = a.abs(); -let _ = -a.abs(); -``` \ No newline at end of file diff --git a/src/docs/suspicious_arithmetic_impl.txt b/src/docs/suspicious_arithmetic_impl.txt deleted file mode 100644 index d67ff279346d..000000000000 --- a/src/docs/suspicious_arithmetic_impl.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Lints for suspicious operations in impls of arithmetic operators, e.g. -subtracting elements in an Add impl. - -### Why is this bad? -This is probably a typo or copy-and-paste error and not intended. - -### Example -``` -impl Add for Foo { - type Output = Foo; - - fn add(self, other: Foo) -> Foo { - Foo(self.0 - other.0) - } -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_assignment_formatting.txt b/src/docs/suspicious_assignment_formatting.txt deleted file mode 100644 index b889827cdf27..000000000000 --- a/src/docs/suspicious_assignment_formatting.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for use of the non-existent `=*`, `=!` and `=-` -operators. - -### Why is this bad? -This is either a typo of `*=`, `!=` or `-=` or -confusing. - -### Example -``` -a =- 42; // confusing, should it be `a -= 42` or `a = -42`? -``` \ No newline at end of file diff --git a/src/docs/suspicious_else_formatting.txt b/src/docs/suspicious_else_formatting.txt deleted file mode 100644 index 3cf2f74868eb..000000000000 --- a/src/docs/suspicious_else_formatting.txt +++ /dev/null @@ -1,30 +0,0 @@ -### What it does -Checks for formatting of `else`. It lints if the `else` -is followed immediately by a newline or the `else` seems to be missing. - -### Why is this bad? -This is probably some refactoring remnant, even if the -code is correct, it might look confusing. - -### Example -``` -if foo { -} { // looks like an `else` is missing here -} - -if foo { -} if bar { // looks like an `else` is missing here -} - -if foo { -} else - -{ // this is the `else` block of the previous `if`, but should it be? -} - -if foo { -} else - -if bar { // this is the `else` block of the previous `if`, but should it be? -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_map.txt b/src/docs/suspicious_map.txt deleted file mode 100644 index d8fa52c43fb0..000000000000 --- a/src/docs/suspicious_map.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for calls to `map` followed by a `count`. - -### Why is this bad? -It looks suspicious. Maybe `map` was confused with `filter`. -If the `map` call is intentional, this should be rewritten -using `inspect`. Or, if you intend to drive the iterator to -completion, you can just use `for_each` instead. - -### Example -``` -let _ = (0..3).map(|x| x + 2).count(); -``` \ No newline at end of file diff --git a/src/docs/suspicious_op_assign_impl.txt b/src/docs/suspicious_op_assign_impl.txt deleted file mode 100644 index 81abfbecae07..000000000000 --- a/src/docs/suspicious_op_assign_impl.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Lints for suspicious operations in impls of OpAssign, e.g. -subtracting elements in an AddAssign impl. - -### Why is this bad? -This is probably a typo or copy-and-paste error and not intended. - -### Example -``` -impl AddAssign for Foo { - fn add_assign(&mut self, other: Foo) { - *self = *self - other; - } -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_operation_groupings.txt b/src/docs/suspicious_operation_groupings.txt deleted file mode 100644 index 81ede5d3da57..000000000000 --- a/src/docs/suspicious_operation_groupings.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for unlikely usages of binary operators that are almost -certainly typos and/or copy/paste errors, given the other usages -of binary operators nearby. - -### Why is this bad? -They are probably bugs and if they aren't then they look like bugs -and you should add a comment explaining why you are doing such an -odd set of operations. - -### Known problems -There may be some false positives if you are trying to do something -unusual that happens to look like a typo. - -### Example -``` -struct Vec3 { - x: f64, - y: f64, - z: f64, -} - -impl Eq for Vec3 {} - -impl PartialEq for Vec3 { - fn eq(&self, other: &Self) -> bool { - // This should trigger the lint because `self.x` is compared to `other.y` - self.x == other.y && self.y == other.y && self.z == other.z - } -} -``` -Use instead: -``` -// same as above except: -impl PartialEq for Vec3 { - fn eq(&self, other: &Self) -> bool { - // Note we now compare other.x to self.x - self.x == other.x && self.y == other.y && self.z == other.z - } -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_splitn.txt b/src/docs/suspicious_splitn.txt deleted file mode 100644 index 79a3dbfa6f4a..000000000000 --- a/src/docs/suspicious_splitn.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for calls to [`splitn`] -(https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and -related functions with either zero or one splits. - -### Why is this bad? -These calls don't actually split the value and are -likely to be intended as a different number. - -### Example -``` -for x in s.splitn(1, ":") { - // .. -} -``` - -Use instead: -``` -for x in s.splitn(2, ":") { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/suspicious_to_owned.txt b/src/docs/suspicious_to_owned.txt deleted file mode 100644 index 8cbf61dc7175..000000000000 --- a/src/docs/suspicious_to_owned.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`. - -### Why is this bad? -Calling `to_owned()` on a `Cow` creates a clone of the `Cow` -itself, without taking ownership of the `Cow` contents (i.e. -it's equivalent to calling `Cow::clone`). -The similarly named `into_owned` method, on the other hand, -clones the `Cow` contents, effectively turning any `Cow::Borrowed` -into a `Cow::Owned`. - -Given the potential ambiguity, consider replacing `to_owned` -with `clone` for better readability or, if getting a `Cow::Owned` -was the original intent, using `into_owned` instead. - -### Example -``` -let s = "Hello world!"; -let cow = Cow::Borrowed(s); - -let data = cow.to_owned(); -assert!(matches!(data, Cow::Borrowed(_))) -``` -Use instead: -``` -let s = "Hello world!"; -let cow = Cow::Borrowed(s); - -let data = cow.clone(); -assert!(matches!(data, Cow::Borrowed(_))) -``` -or -``` -let s = "Hello world!"; -let cow = Cow::Borrowed(s); - -let data = cow.into_owned(); -assert!(matches!(data, String)) -``` \ No newline at end of file diff --git a/src/docs/suspicious_unary_op_formatting.txt b/src/docs/suspicious_unary_op_formatting.txt deleted file mode 100644 index 06fb09db76d0..000000000000 --- a/src/docs/suspicious_unary_op_formatting.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks the formatting of a unary operator on the right hand side -of a binary operator. It lints if there is no space between the binary and unary operators, -but there is a space between the unary and its operand. - -### Why is this bad? -This is either a typo in the binary operator or confusing. - -### Example -``` -// &&! looks like a different operator -if foo &&! bar {} -``` - -Use instead: -``` -if foo && !bar {} -``` \ No newline at end of file diff --git a/src/docs/swap_ptr_to_ref.txt b/src/docs/swap_ptr_to_ref.txt deleted file mode 100644 index 0215d1e8a421..000000000000 --- a/src/docs/swap_ptr_to_ref.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for calls to `core::mem::swap` where either parameter is derived from a pointer - -### Why is this bad? -When at least one parameter to `swap` is derived from a pointer it may overlap with the -other. This would then lead to undefined behavior. - -### Example -``` -unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { - for (&x, &y) in x.iter().zip(y) { - core::mem::swap(&mut *x, &mut *y); - } -} -``` -Use instead: -``` -unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { - for (&x, &y) in x.iter().zip(y) { - core::ptr::swap(x, y); - } -} -``` \ No newline at end of file diff --git a/src/docs/tabs_in_doc_comments.txt b/src/docs/tabs_in_doc_comments.txt deleted file mode 100644 index f83dbe2b73cb..000000000000 --- a/src/docs/tabs_in_doc_comments.txt +++ /dev/null @@ -1,44 +0,0 @@ -### What it does -Checks doc comments for usage of tab characters. - -### Why is this bad? -The rust style-guide promotes spaces instead of tabs for indentation. -To keep a consistent view on the source, also doc comments should not have tabs. -Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the -display settings of the author and reader differ. - -### Example -``` -/// -/// Struct to hold two strings: -/// - first one -/// - second one -pub struct DoubleString { - /// - /// - First String: - /// - needs to be inside here - first_string: String, - /// - /// - Second String: - /// - needs to be inside here - second_string: String, -} -``` - -Will be converted to: -``` -/// -/// Struct to hold two strings: -/// - first one -/// - second one -pub struct DoubleString { - /// - /// - First String: - /// - needs to be inside here - first_string: String, - /// - /// - Second String: - /// - needs to be inside here - second_string: String, -} -``` \ No newline at end of file diff --git a/src/docs/temporary_assignment.txt b/src/docs/temporary_assignment.txt deleted file mode 100644 index 195b42cf0d49..000000000000 --- a/src/docs/temporary_assignment.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for construction of a structure or tuple just to -assign a value in it. - -### Why is this bad? -Readability. If the structure is only created to be -updated, why not write the structure you want in the first place? - -### Example -``` -(0, 0).0 = 1 -``` \ No newline at end of file diff --git a/src/docs/to_digit_is_some.txt b/src/docs/to_digit_is_some.txt deleted file mode 100644 index eee8375adf7a..000000000000 --- a/src/docs/to_digit_is_some.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `.to_digit(..).is_some()` on `char`s. - -### Why is this bad? -This is a convoluted way of checking if a `char` is a digit. It's -more straight forward to use the dedicated `is_digit` method. - -### Example -``` -let is_digit = c.to_digit(radix).is_some(); -``` -can be written as: -``` -let is_digit = c.is_digit(radix); -``` \ No newline at end of file diff --git a/src/docs/to_string_in_format_args.txt b/src/docs/to_string_in_format_args.txt deleted file mode 100644 index 34b20583585a..000000000000 --- a/src/docs/to_string_in_format_args.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string) -applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) -in a macro that does formatting. - -### Why is this bad? -Since the type implements `Display`, the use of `to_string` is -unnecessary. - -### Example -``` -println!("error: something failed at {}", Location::caller().to_string()); -``` -Use instead: -``` -println!("error: something failed at {}", Location::caller()); -``` \ No newline at end of file diff --git a/src/docs/todo.txt b/src/docs/todo.txt deleted file mode 100644 index 661eb1ac5cff..000000000000 --- a/src/docs/todo.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `todo!`. - -### Why is this bad? -This macro should not be present in production code - -### Example -``` -todo!(); -``` \ No newline at end of file diff --git a/src/docs/too_many_arguments.txt b/src/docs/too_many_arguments.txt deleted file mode 100644 index 4669f9f82e66..000000000000 --- a/src/docs/too_many_arguments.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for functions with too many parameters. - -### Why is this bad? -Functions with lots of parameters are considered bad -style and reduce readability (“what does the 5th parameter mean?”). Consider -grouping some parameters into a new type. - -### Example -``` -fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { - // .. -} -``` \ No newline at end of file diff --git a/src/docs/too_many_lines.txt b/src/docs/too_many_lines.txt deleted file mode 100644 index 425db348bbd7..000000000000 --- a/src/docs/too_many_lines.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for functions with a large amount of lines. - -### Why is this bad? -Functions with a lot of lines are harder to understand -due to having to look at a larger amount of code to understand what the -function is doing. Consider splitting the body of the function into -multiple functions. - -### Example -``` -fn im_too_long() { - println!(""); - // ... 100 more LoC - println!(""); -} -``` \ No newline at end of file diff --git a/src/docs/toplevel_ref_arg.txt b/src/docs/toplevel_ref_arg.txt deleted file mode 100644 index 96a9e2db8b7e..000000000000 --- a/src/docs/toplevel_ref_arg.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for function arguments and let bindings denoted as -`ref`. - -### Why is this bad? -The `ref` declaration makes the function take an owned -value, but turns the argument into a reference (which means that the value -is destroyed when exiting the function). This adds not much value: either -take a reference type, or take an owned value and create references in the -body. - -For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The -type of `x` is more obvious with the former. - -### Known problems -If the argument is dereferenced within the function, -removing the `ref` will lead to errors. This can be fixed by removing the -dereferences, e.g., changing `*x` to `x` within the function. - -### Example -``` -fn foo(ref _x: u8) {} -``` - -Use instead: -``` -fn foo(_x: &u8) {} -``` \ No newline at end of file diff --git a/src/docs/trailing_empty_array.txt b/src/docs/trailing_empty_array.txt deleted file mode 100644 index db1908cc96d0..000000000000 --- a/src/docs/trailing_empty_array.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute. - -### Why is this bad? -Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjunction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed. - -### Example -``` -struct RarelyUseful { - some_field: u32, - last: [u32; 0], -} -``` - -Use instead: -``` -#[repr(C)] -struct MoreOftenUseful { - some_field: usize, - last: [u32; 0], -} -``` \ No newline at end of file diff --git a/src/docs/trait_duplication_in_bounds.txt b/src/docs/trait_duplication_in_bounds.txt deleted file mode 100644 index 509736bb3644..000000000000 --- a/src/docs/trait_duplication_in_bounds.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for cases where generics are being used and multiple -syntax specifications for trait bounds are used simultaneously. - -### Why is this bad? -Duplicate bounds makes the code -less readable than specifying them only once. - -### Example -``` -fn func(arg: T) where T: Clone + Default {} -``` - -Use instead: -``` -fn func(arg: T) {} - -// or - -fn func(arg: T) where T: Clone + Default {} -``` - -``` -fn foo(bar: T) {} -``` -Use instead: -``` -fn foo(bar: T) {} -``` - -``` -fn foo(bar: T) where T: Default + Default {} -``` -Use instead: -``` -fn foo(bar: T) where T: Default {} -``` \ No newline at end of file diff --git a/src/docs/transmute_bytes_to_str.txt b/src/docs/transmute_bytes_to_str.txt deleted file mode 100644 index 75889b9c73ae..000000000000 --- a/src/docs/transmute_bytes_to_str.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for transmutes from a `&[u8]` to a `&str`. - -### Why is this bad? -Not every byte slice is a valid UTF-8 string. - -### Known problems -- [`from_utf8`] which this lint suggests using is slower than `transmute` -as it needs to validate the input. -If you are certain that the input is always a valid UTF-8, -use [`from_utf8_unchecked`] which is as fast as `transmute` -but has a semantically meaningful name. -- You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`. - -[`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html -[`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html - -### Example -``` -let b: &[u8] = &[1_u8, 2_u8]; -unsafe { - let _: &str = std::mem::transmute(b); // where b: &[u8] -} - -// should be: -let _ = std::str::from_utf8(b).unwrap(); -``` \ No newline at end of file diff --git a/src/docs/transmute_float_to_int.txt b/src/docs/transmute_float_to_int.txt deleted file mode 100644 index 1877e5a465a4..000000000000 --- a/src/docs/transmute_float_to_int.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from a float to an integer. - -### Why is this bad? -Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive -and safe. - -### Example -``` -unsafe { - let _: u32 = std::mem::transmute(1f32); -} - -// should be: -let _: u32 = 1f32.to_bits(); -``` \ No newline at end of file diff --git a/src/docs/transmute_int_to_bool.txt b/src/docs/transmute_int_to_bool.txt deleted file mode 100644 index 07c10f8d0bca..000000000000 --- a/src/docs/transmute_int_to_bool.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from an integer to a `bool`. - -### Why is this bad? -This might result in an invalid in-memory representation of a `bool`. - -### Example -``` -let x = 1_u8; -unsafe { - let _: bool = std::mem::transmute(x); // where x: u8 -} - -// should be: -let _: bool = x != 0; -``` \ No newline at end of file diff --git a/src/docs/transmute_int_to_char.txt b/src/docs/transmute_int_to_char.txt deleted file mode 100644 index 836d22d3f99c..000000000000 --- a/src/docs/transmute_int_to_char.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for transmutes from an integer to a `char`. - -### Why is this bad? -Not every integer is a Unicode scalar value. - -### Known problems -- [`from_u32`] which this lint suggests using is slower than `transmute` -as it needs to validate the input. -If you are certain that the input is always a valid Unicode scalar value, -use [`from_u32_unchecked`] which is as fast as `transmute` -but has a semantically meaningful name. -- You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`. - -[`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html -[`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html - -### Example -``` -let x = 1_u32; -unsafe { - let _: char = std::mem::transmute(x); // where x: u32 -} - -// should be: -let _ = std::char::from_u32(x).unwrap(); -``` \ No newline at end of file diff --git a/src/docs/transmute_int_to_float.txt b/src/docs/transmute_int_to_float.txt deleted file mode 100644 index 75cdc62e9727..000000000000 --- a/src/docs/transmute_int_to_float.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from an integer to a float. - -### Why is this bad? -Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive -and safe. - -### Example -``` -unsafe { - let _: f32 = std::mem::transmute(1_u32); // where x: u32 -} - -// should be: -let _: f32 = f32::from_bits(1_u32); -``` \ No newline at end of file diff --git a/src/docs/transmute_num_to_bytes.txt b/src/docs/transmute_num_to_bytes.txt deleted file mode 100644 index a2c39a1b947e..000000000000 --- a/src/docs/transmute_num_to_bytes.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes from a number to an array of `u8` - -### Why this is bad? -Transmutes are dangerous and error-prone, whereas `to_ne_bytes` -is intuitive and safe. - -### Example -``` -unsafe { - let x: [u8; 8] = std::mem::transmute(1i64); -} - -// should be -let x: [u8; 8] = 0i64.to_ne_bytes(); -``` \ No newline at end of file diff --git a/src/docs/transmute_ptr_to_ptr.txt b/src/docs/transmute_ptr_to_ptr.txt deleted file mode 100644 index 65777db98618..000000000000 --- a/src/docs/transmute_ptr_to_ptr.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for transmutes from a pointer to a pointer, or -from a reference to a reference. - -### Why is this bad? -Transmutes are dangerous, and these can instead be -written as casts. - -### Example -``` -let ptr = &1u32 as *const u32; -unsafe { - // pointer-to-pointer transmute - let _: *const f32 = std::mem::transmute(ptr); - // ref-ref transmute - let _: &f32 = std::mem::transmute(&1u32); -} -// These can be respectively written: -let _ = ptr as *const f32; -let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; -``` \ No newline at end of file diff --git a/src/docs/transmute_ptr_to_ref.txt b/src/docs/transmute_ptr_to_ref.txt deleted file mode 100644 index aca550f5036e..000000000000 --- a/src/docs/transmute_ptr_to_ref.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Checks for transmutes from a pointer to a reference. - -### Why is this bad? -This can always be rewritten with `&` and `*`. - -### Known problems -- `mem::transmute` in statics and constants is stable from Rust 1.46.0, -while dereferencing raw pointer is not stable yet. -If you need to do this in those places, -you would have to use `transmute` instead. - -### Example -``` -unsafe { - let _: &T = std::mem::transmute(p); // where p: *const T -} - -// can be written: -let _: &T = &*p; -``` \ No newline at end of file diff --git a/src/docs/transmute_undefined_repr.txt b/src/docs/transmute_undefined_repr.txt deleted file mode 100644 index 5ee6aaf4ca92..000000000000 --- a/src/docs/transmute_undefined_repr.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for transmutes between types which do not have a representation defined relative to -each other. - -### Why is this bad? -The results of such a transmute are not defined. - -### Known problems -This lint has had multiple problems in the past and was moved to `nursery`. See issue -[#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details. - -### Example -``` -struct Foo(u32, T); -let _ = unsafe { core::mem::transmute::, Foo>(Foo(0u32, 0u32)) }; -``` -Use instead: -``` -#[repr(C)] -struct Foo(u32, T); -let _ = unsafe { core::mem::transmute::, Foo>(Foo(0u32, 0u32)) }; -``` \ No newline at end of file diff --git a/src/docs/transmutes_expressible_as_ptr_casts.txt b/src/docs/transmutes_expressible_as_ptr_casts.txt deleted file mode 100644 index b68a8cda9c74..000000000000 --- a/src/docs/transmutes_expressible_as_ptr_casts.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Checks for transmutes that could be a pointer cast. - -### Why is this bad? -Readability. The code tricks people into thinking that -something complex is going on. - -### Example - -``` -unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; -``` -Use instead: -``` -p as *const [u16]; -``` \ No newline at end of file diff --git a/src/docs/transmuting_null.txt b/src/docs/transmuting_null.txt deleted file mode 100644 index f8bacfc0b90b..000000000000 --- a/src/docs/transmuting_null.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for transmute calls which would receive a null pointer. - -### Why is this bad? -Transmuting a null pointer is undefined behavior. - -### Known problems -Not all cases can be detected at the moment of this writing. -For example, variables which hold a null pointer and are then fed to a `transmute` -call, aren't detectable yet. - -### Example -``` -let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) }; -``` \ No newline at end of file diff --git a/src/docs/trim_split_whitespace.txt b/src/docs/trim_split_whitespace.txt deleted file mode 100644 index f7e3e7858f94..000000000000 --- a/src/docs/trim_split_whitespace.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Warns about calling `str::trim` (or variants) before `str::split_whitespace`. - -### Why is this bad? -`split_whitespace` already ignores leading and trailing whitespace. - -### Example -``` -" A B C ".trim().split_whitespace(); -``` -Use instead: -``` -" A B C ".split_whitespace(); -``` \ No newline at end of file diff --git a/src/docs/trivial_regex.txt b/src/docs/trivial_regex.txt deleted file mode 100644 index f71d667fd771..000000000000 --- a/src/docs/trivial_regex.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for trivial [regex](https://crates.io/crates/regex) -creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`). - -### Why is this bad? -Matching the regex can likely be replaced by `==` or -`str::starts_with`, `str::ends_with` or `std::contains` or other `str` -methods. - -### Known problems -If the same regex is going to be applied to multiple -inputs, the precomputations done by `Regex` construction can give -significantly better performance than any of the `str`-based methods. - -### Example -``` -Regex::new("^foobar") -``` \ No newline at end of file diff --git a/src/docs/trivially_copy_pass_by_ref.txt b/src/docs/trivially_copy_pass_by_ref.txt deleted file mode 100644 index f54cce5e2bd7..000000000000 --- a/src/docs/trivially_copy_pass_by_ref.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Checks for functions taking arguments by reference, where -the argument type is `Copy` and small enough to be more efficient to always -pass by value. - -### Why is this bad? -In many calling conventions instances of structs will -be passed through registers if they fit into two or less general purpose -registers. - -### Known problems -This lint is target register size dependent, it is -limited to 32-bit to try and reduce portability problems between 32 and -64-bit, but if you are compiling for 8 or 16-bit targets then the limit -will be different. - -The configuration option `trivial_copy_size_limit` can be set to override -this limit for a project. - -This lint attempts to allow passing arguments by reference if a reference -to that argument is returned. This is implemented by comparing the lifetime -of the argument and return value for equality. However, this can cause -false positives in cases involving multiple lifetimes that are bounded by -each other. - -Also, it does not take account of other similar cases where getting memory addresses -matters; namely, returning the pointer to the argument in question, -and passing the argument, as both references and pointers, -to a function that needs the memory address. For further details, refer to -[this issue](https://github.com/rust-lang/rust-clippy/issues/5953) -that explains a real case in which this false positive -led to an **undefined behavior** introduced with unsafe code. - -### Example - -``` -fn foo(v: &u32) {} -``` - -Use instead: -``` -fn foo(v: u32) {} -``` \ No newline at end of file diff --git a/src/docs/try_err.txt b/src/docs/try_err.txt deleted file mode 100644 index e3d4ef3a09df..000000000000 --- a/src/docs/try_err.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for usages of `Err(x)?`. - -### Why is this bad? -The `?` operator is designed to allow calls that -can fail to be easily chained. For example, `foo()?.bar()` or -`foo(bar()?)`. Because `Err(x)?` can't be used that way (it will -always return), it is more clear to write `return Err(x)`. - -### Example -``` -fn foo(fail: bool) -> Result { - if fail { - Err("failed")?; - } - Ok(0) -} -``` -Could be written: - -``` -fn foo(fail: bool) -> Result { - if fail { - return Err("failed".into()); - } - Ok(0) -} -``` \ No newline at end of file diff --git a/src/docs/type_complexity.txt b/src/docs/type_complexity.txt deleted file mode 100644 index 69cd87500504..000000000000 --- a/src/docs/type_complexity.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for types used in structs, parameters and `let` -declarations above a certain complexity threshold. - -### Why is this bad? -Too complex types make the code less readable. Consider -using a `type` definition to simplify them. - -### Example -``` -struct Foo { - inner: Rc>>>, -} -``` \ No newline at end of file diff --git a/src/docs/type_repetition_in_bounds.txt b/src/docs/type_repetition_in_bounds.txt deleted file mode 100644 index 18ed372fd13e..000000000000 --- a/src/docs/type_repetition_in_bounds.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns about unnecessary type repetitions in trait bounds - -### Why is this bad? -Repeating the type for every bound makes the code -less readable than combining the bounds - -### Example -``` -pub fn foo(t: T) where T: Copy, T: Clone {} -``` - -Use instead: -``` -pub fn foo(t: T) where T: Copy + Clone {} -``` \ No newline at end of file diff --git a/src/docs/undocumented_unsafe_blocks.txt b/src/docs/undocumented_unsafe_blocks.txt deleted file mode 100644 index f3af4753c5f7..000000000000 --- a/src/docs/undocumented_unsafe_blocks.txt +++ /dev/null @@ -1,43 +0,0 @@ -### What it does -Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment -explaining why the unsafe operations performed inside -the block are safe. - -Note the comment must appear on the line(s) preceding the unsafe block -with nothing appearing in between. The following is ok: -``` -foo( - // SAFETY: - // This is a valid safety comment - unsafe { *x } -) -``` -But neither of these are: -``` -// SAFETY: -// This is not a valid safety comment -foo( - /* SAFETY: Neither is this */ unsafe { *x }, -); -``` - -### Why is this bad? -Undocumented unsafe blocks and impls can make it difficult to -read and maintain code, as well as uncover unsoundness -and bugs. - -### Example -``` -use std::ptr::NonNull; -let a = &mut 42; - -let ptr = unsafe { NonNull::new_unchecked(a) }; -``` -Use instead: -``` -use std::ptr::NonNull; -let a = &mut 42; - -// SAFETY: references are guaranteed to be non-null. -let ptr = unsafe { NonNull::new_unchecked(a) }; -``` \ No newline at end of file diff --git a/src/docs/undropped_manually_drops.txt b/src/docs/undropped_manually_drops.txt deleted file mode 100644 index 85e3ec56653c..000000000000 --- a/src/docs/undropped_manually_drops.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. - -### Why is this bad? -The safe `drop` function does not drop the inner value of a `ManuallyDrop`. - -### Known problems -Does not catch cases if the user binds `std::mem::drop` -to a different name and calls it that way. - -### Example -``` -struct S; -drop(std::mem::ManuallyDrop::new(S)); -``` -Use instead: -``` -struct S; -unsafe { - std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); -} -``` \ No newline at end of file diff --git a/src/docs/unicode_not_nfc.txt b/src/docs/unicode_not_nfc.txt deleted file mode 100644 index c660c51dadb2..000000000000 --- a/src/docs/unicode_not_nfc.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for string literals that contain Unicode in a form -that is not equal to its -[NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms). - -### Why is this bad? -If such a string is compared to another, the results -may be surprising. - -### Example -You may not see it, but "à"" and "à"" aren't the same string. The -former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`. \ No newline at end of file diff --git a/src/docs/unimplemented.txt b/src/docs/unimplemented.txt deleted file mode 100644 index 7095594fb2e7..000000000000 --- a/src/docs/unimplemented.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `unimplemented!`. - -### Why is this bad? -This macro should not be present in production code - -### Example -``` -unimplemented!(); -``` \ No newline at end of file diff --git a/src/docs/uninit_assumed_init.txt b/src/docs/uninit_assumed_init.txt deleted file mode 100644 index cca24093d40d..000000000000 --- a/src/docs/uninit_assumed_init.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for `MaybeUninit::uninit().assume_init()`. - -### Why is this bad? -For most types, this is undefined behavior. - -### Known problems -For now, we accept empty tuples and tuples / arrays -of `MaybeUninit`. There may be other types that allow uninitialized -data, but those are not yet rigorously defined. - -### Example -``` -// Beware the UB -use std::mem::MaybeUninit; - -let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; -``` - -Note that the following is OK: - -``` -use std::mem::MaybeUninit; - -let _: [MaybeUninit; 5] = unsafe { - MaybeUninit::uninit().assume_init() -}; -``` \ No newline at end of file diff --git a/src/docs/uninit_vec.txt b/src/docs/uninit_vec.txt deleted file mode 100644 index cd50afe78f6f..000000000000 --- a/src/docs/uninit_vec.txt +++ /dev/null @@ -1,41 +0,0 @@ -### What it does -Checks for `set_len()` call that creates `Vec` with uninitialized elements. -This is commonly caused by calling `set_len()` right after allocating or -reserving a buffer with `new()`, `default()`, `with_capacity()`, or `reserve()`. - -### Why is this bad? -It creates a `Vec` with uninitialized data, which leads to -undefined behavior with most safe operations. Notably, uninitialized -`Vec` must not be used with generic `Read`. - -Moreover, calling `set_len()` on a `Vec` created with `new()` or `default()` -creates out-of-bound values that lead to heap memory corruption when used. - -### Known Problems -This lint only checks directly adjacent statements. - -### Example -``` -let mut vec: Vec = Vec::with_capacity(1000); -unsafe { vec.set_len(1000); } -reader.read(&mut vec); // undefined behavior! -``` - -### How to fix? -1. Use an initialized buffer: - ```rust,ignore - let mut vec: Vec = vec![0; 1000]; - reader.read(&mut vec); - ``` -2. Wrap the content in `MaybeUninit`: - ```rust,ignore - let mut vec: Vec> = Vec::with_capacity(1000); - vec.set_len(1000); // `MaybeUninit` can be uninitialized - ``` -3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available: - ```rust,ignore - let mut vec: Vec = Vec::with_capacity(1000); - let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit]` - // perform initialization with `remaining` - vec.set_len(...); // Safe to call `set_len()` on initialized part - ``` \ No newline at end of file diff --git a/src/docs/uninlined_format_args.txt b/src/docs/uninlined_format_args.txt deleted file mode 100644 index 3d2966c84dbe..000000000000 --- a/src/docs/uninlined_format_args.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Detect when a variable is not inlined in a format string, -and suggests to inline it. - -### Why is this bad? -Non-inlined code is slightly more difficult to read and understand, -as it requires arguments to be matched against the format string. -The inlined syntax, where allowed, is simpler. - -### Example -``` -format!("{}", var); -format!("{v:?}", v = var); -format!("{0} {0}", var); -format!("{0:1$}", var, width); -format!("{:.*}", prec, var); -``` -Use instead: -``` -format!("{var}"); -format!("{var:?}"); -format!("{var} {var}"); -format!("{var:width$}"); -format!("{var:.prec$}"); -``` - -### Known Problems - -There may be a false positive if the format string is expanded from certain proc macros: - -``` -println!(indoc!("{}"), var); -``` - -If a format string contains a numbered argument that cannot be inlined -nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. \ No newline at end of file diff --git a/src/docs/unit_arg.txt b/src/docs/unit_arg.txt deleted file mode 100644 index eb83403bb275..000000000000 --- a/src/docs/unit_arg.txt +++ /dev/null @@ -1,14 +0,0 @@ -### What it does -Checks for passing a unit value as an argument to a function without using a -unit literal (`()`). - -### Why is this bad? -This is likely the result of an accidental semicolon. - -### Example -``` -foo({ - let a = bar(); - baz(a); -}) -``` \ No newline at end of file diff --git a/src/docs/unit_cmp.txt b/src/docs/unit_cmp.txt deleted file mode 100644 index 6f3d62010dc5..000000000000 --- a/src/docs/unit_cmp.txt +++ /dev/null @@ -1,33 +0,0 @@ -### What it does -Checks for comparisons to unit. This includes all binary -comparisons (like `==` and `<`) and asserts. - -### Why is this bad? -Unit is always equal to itself, and thus is just a -clumsily written constant. Mostly this happens when someone accidentally -adds semicolons at the end of the operands. - -### Example -``` -if { - foo(); -} == { - bar(); -} { - baz(); -} -``` -is equal to -``` -{ - foo(); - bar(); - baz(); -} -``` - -For asserts: -``` -assert_eq!({ foo(); }, { bar(); }); -``` -will always succeed \ No newline at end of file diff --git a/src/docs/unit_hash.txt b/src/docs/unit_hash.txt deleted file mode 100644 index a22d2994602a..000000000000 --- a/src/docs/unit_hash.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Detects `().hash(_)`. - -### Why is this bad? -Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op. - -### Example -``` -match my_enum { - Empty => ().hash(&mut state), - WithValue(x) => x.hash(&mut state), -} -``` -Use instead: -``` -match my_enum { - Empty => 0_u8.hash(&mut state), - WithValue(x) => x.hash(&mut state), -} -``` \ No newline at end of file diff --git a/src/docs/unit_return_expecting_ord.txt b/src/docs/unit_return_expecting_ord.txt deleted file mode 100644 index 781feac5afcf..000000000000 --- a/src/docs/unit_return_expecting_ord.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for functions that expect closures of type -Fn(...) -> Ord where the implemented closure returns the unit type. -The lint also suggests to remove the semi-colon at the end of the statement if present. - -### Why is this bad? -Likely, returning the unit type is unintentional, and -could simply be caused by an extra semi-colon. Since () implements Ord -it doesn't cause a compilation error. -This is the same reasoning behind the unit_cmp lint. - -### Known problems -If returning unit is intentional, then there is no -way of specifying this without triggering needless_return lint - -### Example -``` -let mut twins = vec!((1, 1), (2, 2)); -twins.sort_by_key(|x| { x.1; }); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_cast.txt b/src/docs/unnecessary_cast.txt deleted file mode 100644 index 603f2606099c..000000000000 --- a/src/docs/unnecessary_cast.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for casts to the same type, casts of int literals to integer types -and casts of float literals to float types. - -### Why is this bad? -It's just unnecessary. - -### Example -``` -let _ = 2i32 as i32; -let _ = 0.5 as f32; -``` - -Better: - -``` -let _ = 2_i32; -let _ = 0.5_f32; -``` \ No newline at end of file diff --git a/src/docs/unnecessary_filter_map.txt b/src/docs/unnecessary_filter_map.txt deleted file mode 100644 index b19341ecf660..000000000000 --- a/src/docs/unnecessary_filter_map.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for `filter_map` calls that could be replaced by `filter` or `map`. -More specifically it checks if the closure provided is only performing one of the -filter or map operations and suggests the appropriate option. - -### Why is this bad? -Complexity. The intent is also clearer if only a single -operation is being performed. - -### Example -``` -let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None }); - -// As there is no transformation of the argument this could be written as: -let _ = (0..3).filter(|&x| x > 2); -``` - -``` -let _ = (0..4).filter_map(|x| Some(x + 1)); - -// As there is no conditional check on the argument this could be written as: -let _ = (0..4).map(|x| x + 1); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_find_map.txt b/src/docs/unnecessary_find_map.txt deleted file mode 100644 index f9444dc48ad6..000000000000 --- a/src/docs/unnecessary_find_map.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for `find_map` calls that could be replaced by `find` or `map`. More -specifically it checks if the closure provided is only performing one of the -find or map operations and suggests the appropriate option. - -### Why is this bad? -Complexity. The intent is also clearer if only a single -operation is being performed. - -### Example -``` -let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None }); - -// As there is no transformation of the argument this could be written as: -let _ = (0..3).find(|&x| x > 2); -``` - -``` -let _ = (0..4).find_map(|x| Some(x + 1)); - -// As there is no conditional check on the argument this could be written as: -let _ = (0..4).map(|x| x + 1).next(); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_fold.txt b/src/docs/unnecessary_fold.txt deleted file mode 100644 index e1b0e65f5197..000000000000 --- a/src/docs/unnecessary_fold.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for using `fold` when a more succinct alternative exists. -Specifically, this checks for `fold`s which could be replaced by `any`, `all`, -`sum` or `product`. - -### Why is this bad? -Readability. - -### Example -``` -(0..3).fold(false, |acc, x| acc || x > 2); -``` - -Use instead: -``` -(0..3).any(|x| x > 2); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_join.txt b/src/docs/unnecessary_join.txt deleted file mode 100644 index ee4e78601f84..000000000000 --- a/src/docs/unnecessary_join.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for use of `.collect::>().join("")` on iterators. - -### Why is this bad? -`.collect::()` is more concise and might be more performant - -### Example -``` -let vector = vec!["hello", "world"]; -let output = vector.iter().map(|item| item.to_uppercase()).collect::>().join(""); -println!("{}", output); -``` -The correct use would be: -``` -let vector = vec!["hello", "world"]; -let output = vector.iter().map(|item| item.to_uppercase()).collect::(); -println!("{}", output); -``` -### Known problems -While `.collect::()` is sometimes more performant, there are cases where -using `.collect::()` over `.collect::>().join("")` -will prevent loop unrolling and will result in a negative performance impact. - -Additionally, differences have been observed between aarch64 and x86_64 assembly output, -with aarch64 tending to producing faster assembly in more cases when using `.collect::()` \ No newline at end of file diff --git a/src/docs/unnecessary_lazy_evaluations.txt b/src/docs/unnecessary_lazy_evaluations.txt deleted file mode 100644 index 208188ce971a..000000000000 --- a/src/docs/unnecessary_lazy_evaluations.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -As the counterpart to `or_fun_call`, this lint looks for unnecessary -lazily evaluated closures on `Option` and `Result`. - -This lint suggests changing the following functions, when eager evaluation results in -simpler code: - - `unwrap_or_else` to `unwrap_or` - - `and_then` to `and` - - `or_else` to `or` - - `get_or_insert_with` to `get_or_insert` - - `ok_or_else` to `ok_or` - -### Why is this bad? -Using eager evaluation is shorter and simpler in some cases. - -### Known problems -It is possible, but not recommended for `Deref` and `Index` to have -side effects. Eagerly evaluating them can change the semantics of the program. - -### Example -``` -// example code where clippy issues a warning -let opt: Option = None; - -opt.unwrap_or_else(|| 42); -``` -Use instead: -``` -let opt: Option = None; - -opt.unwrap_or(42); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_mut_passed.txt b/src/docs/unnecessary_mut_passed.txt deleted file mode 100644 index 2f8bdd113dfd..000000000000 --- a/src/docs/unnecessary_mut_passed.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Detects passing a mutable reference to a function that only -requires an immutable reference. - -### Why is this bad? -The mutable reference rules out all other references to -the value. Also the code misleads about the intent of the call site. - -### Example -``` -vec.push(&mut value); -``` - -Use instead: -``` -vec.push(&value); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_operation.txt b/src/docs/unnecessary_operation.txt deleted file mode 100644 index 7f455e264cb3..000000000000 --- a/src/docs/unnecessary_operation.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for expression statements that can be reduced to a -sub-expression. - -### Why is this bad? -Expressions by themselves often have no side-effects. -Having such expressions reduces readability. - -### Example -``` -compute_array()[0]; -``` \ No newline at end of file diff --git a/src/docs/unnecessary_owned_empty_strings.txt b/src/docs/unnecessary_owned_empty_strings.txt deleted file mode 100644 index 8cd9fba603e0..000000000000 --- a/src/docs/unnecessary_owned_empty_strings.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does - -Detects cases of owned empty strings being passed as an argument to a function expecting `&str` - -### Why is this bad? - -This results in longer and less readable code - -### Example -``` -vec!["1", "2", "3"].join(&String::new()); -``` -Use instead: -``` -vec!["1", "2", "3"].join(""); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_self_imports.txt b/src/docs/unnecessary_self_imports.txt deleted file mode 100644 index b909cd5a76da..000000000000 --- a/src/docs/unnecessary_self_imports.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for imports ending in `::{self}`. - -### Why is this bad? -In most cases, this can be written much more cleanly by omitting `::{self}`. - -### Known problems -Removing `::{self}` will cause any non-module items at the same path to also be imported. -This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt -to detect this scenario and that is why it is a restriction lint. - -### Example -``` -use std::io::{self}; -``` -Use instead: -``` -use std::io; -``` \ No newline at end of file diff --git a/src/docs/unnecessary_sort_by.txt b/src/docs/unnecessary_sort_by.txt deleted file mode 100644 index 6913b62c48eb..000000000000 --- a/src/docs/unnecessary_sort_by.txt +++ /dev/null @@ -1,21 +0,0 @@ -### What it does -Detects uses of `Vec::sort_by` passing in a closure -which compares the two arguments, either directly or indirectly. - -### Why is this bad? -It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if -possible) than to use `Vec::sort_by` and a more complicated -closure. - -### Known problems -If the suggested `Vec::sort_by_key` uses Reverse and it isn't already -imported by a use statement, then it will need to be added manually. - -### Example -``` -vec.sort_by(|a, b| a.foo().cmp(&b.foo())); -``` -Use instead: -``` -vec.sort_by_key(|a| a.foo()); -``` \ No newline at end of file diff --git a/src/docs/unnecessary_to_owned.txt b/src/docs/unnecessary_to_owned.txt deleted file mode 100644 index 5d4213bdaf89..000000000000 --- a/src/docs/unnecessary_to_owned.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) -and other `to_owned`-like functions. - -### Why is this bad? -The unnecessary calls result in useless allocations. - -### Known problems -`unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an -owned copy of a resource and the resource is later used mutably. See -[#8148](https://github.com/rust-lang/rust-clippy/issues/8148). - -### Example -``` -let path = std::path::Path::new("x"); -foo(&path.to_string_lossy().to_string()); -fn foo(s: &str) {} -``` -Use instead: -``` -let path = std::path::Path::new("x"); -foo(&path.to_string_lossy()); -fn foo(s: &str) {} -``` \ No newline at end of file diff --git a/src/docs/unnecessary_unwrap.txt b/src/docs/unnecessary_unwrap.txt deleted file mode 100644 index 50ae845bb44f..000000000000 --- a/src/docs/unnecessary_unwrap.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for calls of `unwrap[_err]()` that cannot fail. - -### Why is this bad? -Using `if let` or `match` is more idiomatic. - -### Example -``` -if option.is_some() { - do_something_with(option.unwrap()) -} -``` - -Could be written: - -``` -if let Some(value) = option { - do_something_with(value) -} -``` \ No newline at end of file diff --git a/src/docs/unnecessary_wraps.txt b/src/docs/unnecessary_wraps.txt deleted file mode 100644 index c0a23d492889..000000000000 --- a/src/docs/unnecessary_wraps.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for private functions that only return `Ok` or `Some`. - -### Why is this bad? -It is not meaningful to wrap values when no `None` or `Err` is returned. - -### Known problems -There can be false positives if the function signature is designed to -fit some external requirement. - -### Example -``` -fn get_cool_number(a: bool, b: bool) -> Option { - if a && b { - return Some(50); - } - if a { - Some(0) - } else { - Some(10) - } -} -``` -Use instead: -``` -fn get_cool_number(a: bool, b: bool) -> i32 { - if a && b { - return 50; - } - if a { - 0 - } else { - 10 - } -} -``` \ No newline at end of file diff --git a/src/docs/unneeded_field_pattern.txt b/src/docs/unneeded_field_pattern.txt deleted file mode 100644 index 3cd00a0f3e38..000000000000 --- a/src/docs/unneeded_field_pattern.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for structure field patterns bound to wildcards. - -### Why is this bad? -Using `..` instead is shorter and leaves the focus on -the fields that are actually bound. - -### Example -``` -let f = Foo { a: 0, b: 0, c: 0 }; - -match f { - Foo { a: _, b: 0, .. } => {}, - Foo { a: _, b: _, c: _ } => {}, -} -``` - -Use instead: -``` -let f = Foo { a: 0, b: 0, c: 0 }; - -match f { - Foo { b: 0, .. } => {}, - Foo { .. } => {}, -} -``` \ No newline at end of file diff --git a/src/docs/unneeded_wildcard_pattern.txt b/src/docs/unneeded_wildcard_pattern.txt deleted file mode 100644 index 817061efd162..000000000000 --- a/src/docs/unneeded_wildcard_pattern.txt +++ /dev/null @@ -1,28 +0,0 @@ -### What it does -Checks for tuple patterns with a wildcard -pattern (`_`) is next to a rest pattern (`..`). - -_NOTE_: While `_, ..` means there is at least one element left, `..` -means there are 0 or more elements left. This can make a difference -when refactoring, but shouldn't result in errors in the refactored code, -since the wildcard pattern isn't used anyway. - -### Why is this bad? -The wildcard pattern is unneeded as the rest pattern -can match that element as well. - -### Example -``` -match t { - TupleStruct(0, .., _) => (), - _ => (), -} -``` - -Use instead: -``` -match t { - TupleStruct(0, ..) => (), - _ => (), -} -``` \ No newline at end of file diff --git a/src/docs/unnested_or_patterns.txt b/src/docs/unnested_or_patterns.txt deleted file mode 100644 index 49c45d4ee5e9..000000000000 --- a/src/docs/unnested_or_patterns.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and -suggests replacing the pattern with a nested one, `Some(0 | 2)`. - -Another way to think of this is that it rewrites patterns in -*disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. - -### Why is this bad? -In the example above, `Some` is repeated, which unnecessarily complicates the pattern. - -### Example -``` -fn main() { - if let Some(0) | Some(2) = Some(0) {} -} -``` -Use instead: -``` -fn main() { - if let Some(0 | 2) = Some(0) {} -} -``` \ No newline at end of file diff --git a/src/docs/unreachable.txt b/src/docs/unreachable.txt deleted file mode 100644 index 10469ca77454..000000000000 --- a/src/docs/unreachable.txt +++ /dev/null @@ -1,10 +0,0 @@ -### What it does -Checks for usage of `unreachable!`. - -### Why is this bad? -This macro can cause code to panic - -### Example -``` -unreachable!(); -``` \ No newline at end of file diff --git a/src/docs/unreadable_literal.txt b/src/docs/unreadable_literal.txt deleted file mode 100644 index e168f90a84c1..000000000000 --- a/src/docs/unreadable_literal.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Warns if a long integral or floating-point constant does -not contain underscores. - -### Why is this bad? -Reading long numbers is difficult without separators. - -### Example -``` -61864918973511 -``` - -Use instead: -``` -61_864_918_973_511 -``` \ No newline at end of file diff --git a/src/docs/unsafe_derive_deserialize.txt b/src/docs/unsafe_derive_deserialize.txt deleted file mode 100644 index f56c48044f4f..000000000000 --- a/src/docs/unsafe_derive_deserialize.txt +++ /dev/null @@ -1,27 +0,0 @@ -### What it does -Checks for deriving `serde::Deserialize` on a type that -has methods using `unsafe`. - -### Why is this bad? -Deriving `serde::Deserialize` will create a constructor -that may violate invariants hold by another constructor. - -### Example -``` -use serde::Deserialize; - -#[derive(Deserialize)] -pub struct Foo { - // .. -} - -impl Foo { - pub fn new() -> Self { - // setup here .. - } - - pub unsafe fn parts() -> (&str, &str) { - // assumes invariants hold - } -} -``` \ No newline at end of file diff --git a/src/docs/unsafe_removed_from_name.txt b/src/docs/unsafe_removed_from_name.txt deleted file mode 100644 index 6f55c1815dd6..000000000000 --- a/src/docs/unsafe_removed_from_name.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for imports that remove "unsafe" from an item's -name. - -### Why is this bad? -Renaming makes it less clear which traits and -structures are unsafe. - -### Example -``` -use std::cell::{UnsafeCell as TotallySafeCell}; - -extern crate crossbeam; -use crossbeam::{spawn_unsafe as spawn}; -``` \ No newline at end of file diff --git a/src/docs/unseparated_literal_suffix.txt b/src/docs/unseparated_literal_suffix.txt deleted file mode 100644 index d80248e34d0c..000000000000 --- a/src/docs/unseparated_literal_suffix.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Warns if literal suffixes are not separated by an -underscore. -To enforce unseparated literal suffix style, -see the `separated_literal_suffix` lint. - -### Why is this bad? -Suffix style should be consistent. - -### Example -``` -123832i32 -``` - -Use instead: -``` -123832_i32 -``` \ No newline at end of file diff --git a/src/docs/unsound_collection_transmute.txt b/src/docs/unsound_collection_transmute.txt deleted file mode 100644 index 29db9258e83f..000000000000 --- a/src/docs/unsound_collection_transmute.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for transmutes between collections whose -types have different ABI, size or alignment. - -### Why is this bad? -This is undefined behavior. - -### Known problems -Currently, we cannot know whether a type is a -collection, so we just lint the ones that come with `std`. - -### Example -``` -// different size, therefore likely out-of-bounds memory access -// You absolutely do not want this in your code! -unsafe { - std::mem::transmute::<_, Vec>(vec![2_u16]) -}; -``` - -You must always iterate, map and collect the values: - -``` -vec![2_u16].into_iter().map(u32::from).collect::>(); -``` \ No newline at end of file diff --git a/src/docs/unused_async.txt b/src/docs/unused_async.txt deleted file mode 100644 index 26def11aa17a..000000000000 --- a/src/docs/unused_async.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for functions that are declared `async` but have no `.await`s inside of them. - -### Why is this bad? -Async functions with no async code create overhead, both mentally and computationally. -Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which -causes runtime overhead and hassle for the caller. - -### Example -``` -async fn get_random_number() -> i64 { - 4 // Chosen by fair dice roll. Guaranteed to be random. -} -let number_future = get_random_number(); -``` - -Use instead: -``` -fn get_random_number_improved() -> i64 { - 4 // Chosen by fair dice roll. Guaranteed to be random. -} -let number_future = async { get_random_number_improved() }; -``` \ No newline at end of file diff --git a/src/docs/unused_io_amount.txt b/src/docs/unused_io_amount.txt deleted file mode 100644 index fbc4c299c7bb..000000000000 --- a/src/docs/unused_io_amount.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for unused written/read amount. - -### Why is this bad? -`io::Write::write(_vectored)` and -`io::Read::read(_vectored)` are not guaranteed to -process the entire buffer. They return how many bytes were processed, which -might be smaller -than a given buffer's length. If you don't need to deal with -partial-write/read, use -`write_all`/`read_exact` instead. - -When working with asynchronous code (either with the `futures` -crate or with `tokio`), a similar issue exists for -`AsyncWriteExt::write()` and `AsyncReadExt::read()` : these -functions are also not guaranteed to process the entire -buffer. Your code should either handle partial-writes/reads, or -call the `write_all`/`read_exact` methods on those traits instead. - -### Known problems -Detects only common patterns. - -### Examples -``` -use std::io; -fn foo(w: &mut W) -> io::Result<()> { - // must be `w.write_all(b"foo")?;` - w.write(b"foo")?; - Ok(()) -} -``` \ No newline at end of file diff --git a/src/docs/unused_peekable.txt b/src/docs/unused_peekable.txt deleted file mode 100644 index 268de1ce3bec..000000000000 --- a/src/docs/unused_peekable.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for the creation of a `peekable` iterator that is never `.peek()`ed - -### Why is this bad? -Creating a peekable iterator without using any of its methods is likely a mistake, -or just a leftover after a refactor. - -### Example -``` -let collection = vec![1, 2, 3]; -let iter = collection.iter().peekable(); - -for item in iter { - // ... -} -``` - -Use instead: -``` -let collection = vec![1, 2, 3]; -let iter = collection.iter(); - -for item in iter { - // ... -} -``` \ No newline at end of file diff --git a/src/docs/unused_rounding.txt b/src/docs/unused_rounding.txt deleted file mode 100644 index 70947aceee77..000000000000 --- a/src/docs/unused_rounding.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does - -Detects cases where a whole-number literal float is being rounded, using -the `floor`, `ceil`, or `round` methods. - -### Why is this bad? - -This is unnecessary and confusing to the reader. Doing this is probably a mistake. - -### Example -``` -let x = 1f32.ceil(); -``` -Use instead: -``` -let x = 1f32; -``` \ No newline at end of file diff --git a/src/docs/unused_self.txt b/src/docs/unused_self.txt deleted file mode 100644 index a8d0fc759895..000000000000 --- a/src/docs/unused_self.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks methods that contain a `self` argument but don't use it - -### Why is this bad? -It may be clearer to define the method as an associated function instead -of an instance method if it doesn't require `self`. - -### Example -``` -struct A; -impl A { - fn method(&self) {} -} -``` - -Could be written: - -``` -struct A; -impl A { - fn method() {} -} -``` \ No newline at end of file diff --git a/src/docs/unused_unit.txt b/src/docs/unused_unit.txt deleted file mode 100644 index 48d16ca65523..000000000000 --- a/src/docs/unused_unit.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for unit (`()`) expressions that can be removed. - -### Why is this bad? -Such expressions add no value, but can make the code -less readable. Depending on formatting they can make a `break` or `return` -statement look like a function call. - -### Example -``` -fn return_unit() -> () { - () -} -``` -is equivalent to -``` -fn return_unit() {} -``` \ No newline at end of file diff --git a/src/docs/unusual_byte_groupings.txt b/src/docs/unusual_byte_groupings.txt deleted file mode 100644 index 9a1f132a6112..000000000000 --- a/src/docs/unusual_byte_groupings.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Warns if hexadecimal or binary literals are not grouped -by nibble or byte. - -### Why is this bad? -Negatively impacts readability. - -### Example -``` -let x: u32 = 0xFFF_FFF; -let y: u8 = 0b01_011_101; -``` \ No newline at end of file diff --git a/src/docs/unwrap_in_result.txt b/src/docs/unwrap_in_result.txt deleted file mode 100644 index 7497dd863d35..000000000000 --- a/src/docs/unwrap_in_result.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for functions of type `Result` that contain `expect()` or `unwrap()` - -### Why is this bad? -These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. - -### Known problems -This can cause false positives in functions that handle both recoverable and non recoverable errors. - -### Example -Before: -``` -fn divisible_by_3(i_str: String) -> Result<(), String> { - let i = i_str - .parse::() - .expect("cannot divide the input by three"); - - if i % 3 != 0 { - Err("Number is not divisible by 3")? - } - - Ok(()) -} -``` - -After: -``` -fn divisible_by_3(i_str: String) -> Result<(), String> { - let i = i_str - .parse::() - .map_err(|e| format!("cannot divide the input by three: {}", e))?; - - if i % 3 != 0 { - Err("Number is not divisible by 3")? - } - - Ok(()) -} -``` \ No newline at end of file diff --git a/src/docs/unwrap_or_else_default.txt b/src/docs/unwrap_or_else_default.txt deleted file mode 100644 index 34b4cf088581..000000000000 --- a/src/docs/unwrap_or_else_default.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and -`Result` values. - -### Why is this bad? -Readability, these can be written as `_.unwrap_or_default`, which is -simpler and more concise. - -### Examples -``` -x.unwrap_or_else(Default::default); -x.unwrap_or_else(u32::default); -``` - -Use instead: -``` -x.unwrap_or_default(); -``` \ No newline at end of file diff --git a/src/docs/unwrap_used.txt b/src/docs/unwrap_used.txt deleted file mode 100644 index 9b4713df515d..000000000000 --- a/src/docs/unwrap_used.txt +++ /dev/null @@ -1,37 +0,0 @@ -### What it does -Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s. - -### Why is this bad? -It is better to handle the `None` or `Err` case, -or at least call `.expect(_)` with a more helpful message. Still, for a lot of -quick-and-dirty code, `unwrap` is a good choice, which is why this lint is -`Allow` by default. - -`result.unwrap()` will let the thread panic on `Err` values. -Normally, you want to implement more sophisticated error handling, -and propagate errors upwards with `?` operator. - -Even if you want to panic on errors, not all `Error`s implement good -messages on display. Therefore, it may be beneficial to look at the places -where they may get displayed. Activate this lint to do just that. - -### Examples -``` -option.unwrap(); -result.unwrap(); -``` - -Use instead: -``` -option.expect("more helpful message"); -result.expect("more helpful message"); -``` - -If [expect_used](#expect_used) is enabled, instead: -``` -option?; - -// or - -result?; -``` \ No newline at end of file diff --git a/src/docs/upper_case_acronyms.txt b/src/docs/upper_case_acronyms.txt deleted file mode 100644 index a1e39c7e10c6..000000000000 --- a/src/docs/upper_case_acronyms.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for fully capitalized names and optionally names containing a capitalized acronym. - -### Why is this bad? -In CamelCase, acronyms count as one word. -See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case) -for more. - -By default, the lint only triggers on fully-capitalized names. -You can use the `upper-case-acronyms-aggressive: true` config option to enable linting -on all camel case names - -### Known problems -When two acronyms are contiguous, the lint can't tell where -the first acronym ends and the second starts, so it suggests to lowercase all of -the letters in the second acronym. - -### Example -``` -struct HTTPResponse; -``` -Use instead: -``` -struct HttpResponse; -``` \ No newline at end of file diff --git a/src/docs/use_debug.txt b/src/docs/use_debug.txt deleted file mode 100644 index 94d4a6fd29ae..000000000000 --- a/src/docs/use_debug.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for use of `Debug` formatting. The purpose of this -lint is to catch debugging remnants. - -### Why is this bad? -The purpose of the `Debug` trait is to facilitate -debugging Rust code. It should not be used in user-facing output. - -### Example -``` -println!("{:?}", foo); -``` \ No newline at end of file diff --git a/src/docs/use_self.txt b/src/docs/use_self.txt deleted file mode 100644 index bd37ed1e0025..000000000000 --- a/src/docs/use_self.txt +++ /dev/null @@ -1,31 +0,0 @@ -### What it does -Checks for unnecessary repetition of structure name when a -replacement with `Self` is applicable. - -### Why is this bad? -Unnecessary repetition. Mixed use of `Self` and struct -name -feels inconsistent. - -### Known problems -- Unaddressed false negative in fn bodies of trait implementations -- False positive with associated types in traits (#4140) - -### Example -``` -struct Foo; -impl Foo { - fn new() -> Foo { - Foo {} - } -} -``` -could be -``` -struct Foo; -impl Foo { - fn new() -> Self { - Self {} - } -} -``` \ No newline at end of file diff --git a/src/docs/used_underscore_binding.txt b/src/docs/used_underscore_binding.txt deleted file mode 100644 index ed67c41eb0db..000000000000 --- a/src/docs/used_underscore_binding.txt +++ /dev/null @@ -1,19 +0,0 @@ -### What it does -Checks for the use of bindings with a single leading -underscore. - -### Why is this bad? -A single leading underscore is usually used to indicate -that a binding will not be used. Using such a binding breaks this -expectation. - -### Known problems -The lint does not work properly with desugaring and -macro, it has been allowed in the mean time. - -### Example -``` -let _x = 0; -let y = _x + 1; // Here we are using `_x`, even though it has a leading - // underscore. We should rename `_x` to `x` -``` \ No newline at end of file diff --git a/src/docs/useless_asref.txt b/src/docs/useless_asref.txt deleted file mode 100644 index f777cd3775ed..000000000000 --- a/src/docs/useless_asref.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for usage of `.as_ref()` or `.as_mut()` where the -types before and after the call are the same. - -### Why is this bad? -The call is unnecessary. - -### Example -``` -let x: &[i32] = &[1, 2, 3, 4, 5]; -do_stuff(x.as_ref()); -``` -The correct use would be: -``` -let x: &[i32] = &[1, 2, 3, 4, 5]; -do_stuff(x); -``` \ No newline at end of file diff --git a/src/docs/useless_attribute.txt b/src/docs/useless_attribute.txt deleted file mode 100644 index e02d4c907898..000000000000 --- a/src/docs/useless_attribute.txt +++ /dev/null @@ -1,36 +0,0 @@ -### What it does -Checks for `extern crate` and `use` items annotated with -lint attributes. - -This lint permits lint attributes for lints emitted on the items themself. -For `use` items these lints are: -* deprecated -* unreachable_pub -* unused_imports -* clippy::enum_glob_use -* clippy::macro_use_imports -* clippy::wildcard_imports - -For `extern crate` items these lints are: -* `unused_imports` on items with `#[macro_use]` - -### Why is this bad? -Lint attributes have no effect on crate imports. Most -likely a `!` was forgotten. - -### Example -``` -#[deny(dead_code)] -extern crate foo; -#[forbid(dead_code)] -use foo::bar; -``` - -Use instead: -``` -#[allow(unused_imports)] -use foo::baz; -#[allow(unused_imports)] -#[macro_use] -extern crate baz; -``` \ No newline at end of file diff --git a/src/docs/useless_conversion.txt b/src/docs/useless_conversion.txt deleted file mode 100644 index 06000a7ad98d..000000000000 --- a/src/docs/useless_conversion.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls -which uselessly convert to the same type. - -### Why is this bad? -Redundant code. - -### Example -``` -// format!() returns a `String` -let s: String = format!("hello").into(); -``` - -Use instead: -``` -let s: String = format!("hello"); -``` \ No newline at end of file diff --git a/src/docs/useless_format.txt b/src/docs/useless_format.txt deleted file mode 100644 index eb4819da1e95..000000000000 --- a/src/docs/useless_format.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for the use of `format!("string literal with no -argument")` and `format!("{}", foo)` where `foo` is a string. - -### Why is this bad? -There is no point of doing that. `format!("foo")` can -be replaced by `"foo".to_owned()` if you really need a `String`. The even -worse `&format!("foo")` is often encountered in the wild. `format!("{}", -foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()` -if `foo: &str`. - -### Examples -``` -let foo = "foo"; -format!("{}", foo); -``` - -Use instead: -``` -let foo = "foo"; -foo.to_owned(); -``` \ No newline at end of file diff --git a/src/docs/useless_let_if_seq.txt b/src/docs/useless_let_if_seq.txt deleted file mode 100644 index c6dcd57eb2e2..000000000000 --- a/src/docs/useless_let_if_seq.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for variable declarations immediately followed by a -conditional affectation. - -### Why is this bad? -This is not idiomatic Rust. - -### Example -``` -let foo; - -if bar() { - foo = 42; -} else { - foo = 0; -} - -let mut baz = None; - -if bar() { - baz = Some(42); -} -``` - -should be written - -``` -let foo = if bar() { - 42 -} else { - 0 -}; - -let baz = if bar() { - Some(42) -} else { - None -}; -``` \ No newline at end of file diff --git a/src/docs/useless_transmute.txt b/src/docs/useless_transmute.txt deleted file mode 100644 index 1d3a17588145..000000000000 --- a/src/docs/useless_transmute.txt +++ /dev/null @@ -1,12 +0,0 @@ -### What it does -Checks for transmutes to the original type of the object -and transmutes that could be a cast. - -### Why is this bad? -Readability. The code tricks people into thinking that -something complex is going on. - -### Example -``` -core::intrinsics::transmute(t); // where the result type is the same as `t`'s -``` \ No newline at end of file diff --git a/src/docs/useless_vec.txt b/src/docs/useless_vec.txt deleted file mode 100644 index ee5afc99e4bf..000000000000 --- a/src/docs/useless_vec.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -Checks for usage of `&vec![..]` when using `&[..]` would -be possible. - -### Why is this bad? -This is less efficient. - -### Example -``` -fn foo(_x: &[u8]) {} - -foo(&vec![1, 2]); -``` - -Use instead: -``` -foo(&[1, 2]); -``` \ No newline at end of file diff --git a/src/docs/vec_box.txt b/src/docs/vec_box.txt deleted file mode 100644 index 701b1c9ce9b9..000000000000 --- a/src/docs/vec_box.txt +++ /dev/null @@ -1,26 +0,0 @@ -### What it does -Checks for use of `Vec>` where T: Sized anywhere in the code. -Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. - -### Why is this bad? -`Vec` already keeps its contents in a separate area on -the heap. So if you `Box` its contents, you just add another level of indirection. - -### Known problems -Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530), -1st comment). - -### Example -``` -struct X { - values: Vec>, -} -``` - -Better: - -``` -struct X { - values: Vec, -} -``` \ No newline at end of file diff --git a/src/docs/vec_init_then_push.txt b/src/docs/vec_init_then_push.txt deleted file mode 100644 index 445f2874796b..000000000000 --- a/src/docs/vec_init_then_push.txt +++ /dev/null @@ -1,23 +0,0 @@ -### What it does -Checks for calls to `push` immediately after creating a new `Vec`. - -If the `Vec` is created using `with_capacity` this will only lint if the capacity is a -constant and the number of pushes is greater than or equal to the initial capacity. - -If the `Vec` is extended after the initial sequence of pushes and it was default initialized -then this will only lint after there were at least four pushes. This number may change in -the future. - -### Why is this bad? -The `vec![]` macro is both more performant and easier to read than -multiple `push` calls. - -### Example -``` -let mut v = Vec::new(); -v.push(0); -``` -Use instead: -``` -let v = vec![0]; -``` \ No newline at end of file diff --git a/src/docs/vec_resize_to_zero.txt b/src/docs/vec_resize_to_zero.txt deleted file mode 100644 index 0b92686772bb..000000000000 --- a/src/docs/vec_resize_to_zero.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Finds occurrences of `Vec::resize(0, an_int)` - -### Why is this bad? -This is probably an argument inversion mistake. - -### Example -``` -vec!(1, 2, 3, 4, 5).resize(0, 5) -``` - -Use instead: -``` -vec!(1, 2, 3, 4, 5).clear() -``` \ No newline at end of file diff --git a/src/docs/verbose_bit_mask.txt b/src/docs/verbose_bit_mask.txt deleted file mode 100644 index 87a84702925e..000000000000 --- a/src/docs/verbose_bit_mask.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for bit masks that can be replaced by a call -to `trailing_zeros` - -### Why is this bad? -`x.trailing_zeros() > 4` is much clearer than `x & 15 -== 0` - -### Known problems -llvm generates better code for `x & 15 == 0` on x86 - -### Example -``` -if x & 0b1111 == 0 { } -``` \ No newline at end of file diff --git a/src/docs/verbose_file_reads.txt b/src/docs/verbose_file_reads.txt deleted file mode 100644 index 9703df423a57..000000000000 --- a/src/docs/verbose_file_reads.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for use of File::read_to_end and File::read_to_string. - -### Why is this bad? -`fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. -See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) - -### Example -``` -let mut f = File::open("foo.txt").unwrap(); -let mut bytes = Vec::new(); -f.read_to_end(&mut bytes).unwrap(); -``` -Can be written more concisely as -``` -let mut bytes = fs::read("foo.txt").unwrap(); -``` \ No newline at end of file diff --git a/src/docs/vtable_address_comparisons.txt b/src/docs/vtable_address_comparisons.txt deleted file mode 100644 index 4a34e4ba78ef..000000000000 --- a/src/docs/vtable_address_comparisons.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -Checks for comparisons with an address of a trait vtable. - -### Why is this bad? -Comparing trait objects pointers compares an vtable addresses which -are not guaranteed to be unique and could vary between different code generation units. -Furthermore vtables for different types could have the same address after being merged -together. - -### Example -``` -let a: Rc = ... -let b: Rc = ... -if Rc::ptr_eq(&a, &b) { - ... -} -``` \ No newline at end of file diff --git a/src/docs/while_immutable_condition.txt b/src/docs/while_immutable_condition.txt deleted file mode 100644 index 71800701f489..000000000000 --- a/src/docs/while_immutable_condition.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks whether variables used within while loop condition -can be (and are) mutated in the body. - -### Why is this bad? -If the condition is unchanged, entering the body of the loop -will lead to an infinite loop. - -### Known problems -If the `while`-loop is in a closure, the check for mutation of the -condition variables in the body can cause false negatives. For example when only `Upvar` `a` is -in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger. - -### Example -``` -let i = 0; -while i > 10 { - println!("let me loop forever!"); -} -``` \ No newline at end of file diff --git a/src/docs/while_let_loop.txt b/src/docs/while_let_loop.txt deleted file mode 100644 index ab7bf60975ec..000000000000 --- a/src/docs/while_let_loop.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Detects `loop + match` combinations that are easier -written as a `while let` loop. - -### Why is this bad? -The `while let` loop is usually shorter and more -readable. - -### Known problems -Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). - -### Example -``` -loop { - let x = match y { - Some(x) => x, - None => break, - }; - // .. do something with x -} -// is easier written as -while let Some(x) = y { - // .. do something with x -}; -``` \ No newline at end of file diff --git a/src/docs/while_let_on_iterator.txt b/src/docs/while_let_on_iterator.txt deleted file mode 100644 index af053c541199..000000000000 --- a/src/docs/while_let_on_iterator.txt +++ /dev/null @@ -1,20 +0,0 @@ -### What it does -Checks for `while let` expressions on iterators. - -### Why is this bad? -Readability. A simple `for` loop is shorter and conveys -the intent better. - -### Example -``` -while let Some(val) = iter.next() { - .. -} -``` - -Use instead: -``` -for val in &mut iter { - .. -} -``` \ No newline at end of file diff --git a/src/docs/wildcard_dependencies.txt b/src/docs/wildcard_dependencies.txt deleted file mode 100644 index 2affaf9740d9..000000000000 --- a/src/docs/wildcard_dependencies.txt +++ /dev/null @@ -1,13 +0,0 @@ -### What it does -Checks for wildcard dependencies in the `Cargo.toml`. - -### Why is this bad? -[As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html), -it is highly unlikely that you work with any possible version of your dependency, -and wildcard dependencies would cause unnecessary breakage in the ecosystem. - -### Example -``` -[dependencies] -regex = "*" -``` \ No newline at end of file diff --git a/src/docs/wildcard_enum_match_arm.txt b/src/docs/wildcard_enum_match_arm.txt deleted file mode 100644 index 09807c01c652..000000000000 --- a/src/docs/wildcard_enum_match_arm.txt +++ /dev/null @@ -1,25 +0,0 @@ -### What it does -Checks for wildcard enum matches using `_`. - -### Why is this bad? -New enum variants added by library updates can be missed. - -### Known problems -Suggested replacements may be incorrect if guards exhaustively cover some -variants, and also may not use correct path to enum if it's not present in the current scope. - -### Example -``` -match x { - Foo::A(_) => {}, - _ => {}, -} -``` - -Use instead: -``` -match x { - Foo::A(_) => {}, - Foo::B(_) => {}, -} -``` \ No newline at end of file diff --git a/src/docs/wildcard_imports.txt b/src/docs/wildcard_imports.txt deleted file mode 100644 index bd56aa5b0828..000000000000 --- a/src/docs/wildcard_imports.txt +++ /dev/null @@ -1,45 +0,0 @@ -### What it does -Checks for wildcard imports `use _::*`. - -### Why is this bad? -wildcard imports can pollute the namespace. This is especially bad if -you try to import something through a wildcard, that already has been imported by name from -a different source: - -``` -use crate1::foo; // Imports a function named foo -use crate2::*; // Has a function named foo - -foo(); // Calls crate1::foo -``` - -This can lead to confusing error messages at best and to unexpected behavior at worst. - -### Exceptions -Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) -provide modules named "prelude" specifically designed for wildcard import. - -`use super::*` is allowed in test modules. This is defined as any module with "test" in the name. - -These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag. - -### Known problems -If macros are imported through the wildcard, this macro is not included -by the suggestion and has to be added by hand. - -Applying the suggestion when explicit imports of the things imported with a glob import -exist, may result in `unused_imports` warnings. - -### Example -``` -use crate1::*; - -foo(); -``` - -Use instead: -``` -use crate1::foo; - -foo(); -``` \ No newline at end of file diff --git a/src/docs/wildcard_in_or_patterns.txt b/src/docs/wildcard_in_or_patterns.txt deleted file mode 100644 index 70468ca41e0b..000000000000 --- a/src/docs/wildcard_in_or_patterns.txt +++ /dev/null @@ -1,22 +0,0 @@ -### What it does -Checks for wildcard pattern used with others patterns in same match arm. - -### Why is this bad? -Wildcard pattern already covers any other pattern as it will match anyway. -It makes the code less readable, especially to spot wildcard pattern use in match arm. - -### Example -``` -match s { - "a" => {}, - "bar" | _ => {}, -} -``` - -Use instead: -``` -match s { - "a" => {}, - _ => {}, -} -``` \ No newline at end of file diff --git a/src/docs/write_literal.txt b/src/docs/write_literal.txt deleted file mode 100644 index a7a884d08711..000000000000 --- a/src/docs/write_literal.txt +++ /dev/null @@ -1,17 +0,0 @@ -### What it does -This lint warns about the use of literals as `write!`/`writeln!` args. - -### Why is this bad? -Using literals as `writeln!` args is inefficient -(c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary -(i.e., just put the literal in the format string) - -### Example -``` -writeln!(buf, "{}", "foo"); -``` - -Use instead: -``` -writeln!(buf, "foo"); -``` \ No newline at end of file diff --git a/src/docs/write_with_newline.txt b/src/docs/write_with_newline.txt deleted file mode 100644 index 22845fd6515b..000000000000 --- a/src/docs/write_with_newline.txt +++ /dev/null @@ -1,18 +0,0 @@ -### What it does -This lint warns when you use `write!()` with a format -string that -ends in a newline. - -### Why is this bad? -You should use `writeln!()` instead, which appends the -newline. - -### Example -``` -write!(buf, "Hello {}!\n", name); -``` - -Use instead: -``` -writeln!(buf, "Hello {}!", name); -``` \ No newline at end of file diff --git a/src/docs/writeln_empty_string.txt b/src/docs/writeln_empty_string.txt deleted file mode 100644 index 3b3aeb79a48b..000000000000 --- a/src/docs/writeln_empty_string.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -This lint warns when you use `writeln!(buf, "")` to -print a newline. - -### Why is this bad? -You should use `writeln!(buf)`, which is simpler. - -### Example -``` -writeln!(buf, ""); -``` - -Use instead: -``` -writeln!(buf); -``` \ No newline at end of file diff --git a/src/docs/wrong_self_convention.txt b/src/docs/wrong_self_convention.txt deleted file mode 100644 index d6b69ab87f86..000000000000 --- a/src/docs/wrong_self_convention.txt +++ /dev/null @@ -1,39 +0,0 @@ -### What it does -Checks for methods with certain name prefixes and which -doesn't match how self is taken. The actual rules are: - -|Prefix |Postfix |`self` taken | `self` type | -|-------|------------|-------------------------------|--------------| -|`as_` | none |`&self` or `&mut self` | any | -|`from_`| none | none | any | -|`into_`| none |`self` | any | -|`is_` | none |`&mut self` or `&self` or none | any | -|`to_` | `_mut` |`&mut self` | any | -|`to_` | not `_mut` |`self` | `Copy` | -|`to_` | not `_mut` |`&self` | not `Copy` | - -Note: Clippy doesn't trigger methods with `to_` prefix in: -- Traits definition. -Clippy can not tell if a type that implements a trait is `Copy` or not. -- Traits implementation, when `&self` is taken. -The method signature is controlled by the trait and often `&self` is required for all types that implement the trait -(see e.g. the `std::string::ToString` trait). - -Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required. - -Please find more info here: -https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv - -### Why is this bad? -Consistency breeds readability. If you follow the -conventions, your users won't be surprised that they, e.g., need to supply a -mutable reference to a `as_..` function. - -### Example -``` -impl X { - fn as_str(self) -> &'static str { - // .. - } -} -``` \ No newline at end of file diff --git a/src/docs/wrong_transmute.txt b/src/docs/wrong_transmute.txt deleted file mode 100644 index 9fc71e0e382e..000000000000 --- a/src/docs/wrong_transmute.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for transmutes that can't ever be correct on any -architecture. - -### Why is this bad? -It's basically guaranteed to be undefined behavior. - -### Known problems -When accessing C, users might want to store pointer -sized objects in `extradata` arguments to save an allocation. - -### Example -``` -let ptr: *const T = core::intrinsics::transmute('x') -``` \ No newline at end of file diff --git a/src/docs/zero_divided_by_zero.txt b/src/docs/zero_divided_by_zero.txt deleted file mode 100644 index 394de20c0c0c..000000000000 --- a/src/docs/zero_divided_by_zero.txt +++ /dev/null @@ -1,15 +0,0 @@ -### What it does -Checks for `0.0 / 0.0`. - -### Why is this bad? -It's less readable than `f32::NAN` or `f64::NAN`. - -### Example -``` -let nan = 0.0f32 / 0.0; -``` - -Use instead: -``` -let nan = f32::NAN; -``` \ No newline at end of file diff --git a/src/docs/zero_prefixed_literal.txt b/src/docs/zero_prefixed_literal.txt deleted file mode 100644 index 5c5588725363..000000000000 --- a/src/docs/zero_prefixed_literal.txt +++ /dev/null @@ -1,32 +0,0 @@ -### What it does -Warns if an integral constant literal starts with `0`. - -### Why is this bad? -In some languages (including the infamous C language -and most of its -family), this marks an octal constant. In Rust however, this is a decimal -constant. This could -be confusing for both the writer and a reader of the constant. - -### Example - -In Rust: -``` -fn main() { - let a = 0123; - println!("{}", a); -} -``` - -prints `123`, while in C: - -``` -#include - -int main() { - int a = 0123; - printf("%d\n", a); -} -``` - -prints `83` (as `83 == 0o123` while `123 == 0o173`). \ No newline at end of file diff --git a/src/docs/zero_ptr.txt b/src/docs/zero_ptr.txt deleted file mode 100644 index e768a0236600..000000000000 --- a/src/docs/zero_ptr.txt +++ /dev/null @@ -1,16 +0,0 @@ -### What it does -Catch casts from `0` to some pointer type - -### Why is this bad? -This generally means `null` and is better expressed as -{`std`, `core`}`::ptr::`{`null`, `null_mut`}. - -### Example -``` -let a = 0 as *const u32; -``` - -Use instead: -``` -let a = std::ptr::null::(); -``` \ No newline at end of file diff --git a/src/docs/zero_sized_map_values.txt b/src/docs/zero_sized_map_values.txt deleted file mode 100644 index 0502bdbf3950..000000000000 --- a/src/docs/zero_sized_map_values.txt +++ /dev/null @@ -1,24 +0,0 @@ -### What it does -Checks for maps with zero-sized value types anywhere in the code. - -### Why is this bad? -Since there is only a single value for a zero-sized type, a map -containing zero sized values is effectively a set. Using a set in that case improves -readability and communicates intent more clearly. - -### Known problems -* A zero-sized type cannot be recovered later if it contains private fields. -* This lints the signature of public items - -### Example -``` -fn unique_words(text: &str) -> HashMap<&str, ()> { - todo!(); -} -``` -Use instead: -``` -fn unique_words(text: &str) -> HashSet<&str> { - todo!(); -} -``` \ No newline at end of file diff --git a/src/docs/zst_offset.txt b/src/docs/zst_offset.txt deleted file mode 100644 index 5810455ee955..000000000000 --- a/src/docs/zst_offset.txt +++ /dev/null @@ -1,11 +0,0 @@ -### What it does -Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to -zero-sized types - -### Why is this bad? -This is a no-op, and likely unintended - -### Example -``` -unsafe { (&() as *const ()).offset(1) }; -``` \ No newline at end of file diff --git a/src/driver.rs b/src/driver.rs index b12208ac62a8..d521e8d88398 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,5 +1,7 @@ #![feature(rustc_private)] +#![feature(let_chains)] #![feature(once_cell)] +#![feature(lint_reasons)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -17,14 +19,13 @@ extern crate rustc_span; use rustc_interface::interface; use rustc_session::parse::ParseSess; use rustc_span::symbol::Symbol; -use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; use std::ops::Deref; use std::panic; -use std::path::{Path, PathBuf}; -use std::process::{exit, Command}; +use std::path::Path; +use std::process::exit; use std::sync::LazyLock; /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If @@ -71,6 +72,37 @@ fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option) )); } +/// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy +/// when any of them are modified +fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { + let file_depinfo = parse_sess.file_depinfo.get_mut(); + + // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` + // with the current directory set to `CARGO_MANIFEST_DIR` so a relative path is fine + if Path::new("Cargo.toml").exists() { + file_depinfo.insert(Symbol::intern("Cargo.toml")); + } + + // `clippy.toml` + if let Some(path) = conf_path_string { + file_depinfo.insert(Symbol::intern(&path)); + } + + // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever + // it is rebuilt + #[expect( + clippy::collapsible_if, + reason = "Due to a bug in let_chains this if statement can't be collapsed" + )] + if cfg!(debug_assertions) { + if let Ok(current_exe) = env::current_exe() + && let Some(current_exe) = current_exe.to_str() + { + file_depinfo.insert(Symbol::intern(current_exe)); + } + } +} + struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} @@ -97,10 +129,18 @@ impl rustc_driver::Callbacks for ClippyCallbacks { // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level` #[allow(rustc::bad_opt_access)] fn config(&mut self, config: &mut interface::Config) { + let conf_path = clippy_lints::lookup_conf_file(); + let conf_path_string = if let Ok(Some(path)) = &conf_path { + path.to_str().map(String::from) + } else { + None + }; + let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take(); config.parse_sess_created = Some(Box::new(move |parse_sess| { track_clippy_args(parse_sess, &clippy_args_var); + track_files(parse_sess, conf_path_string); })); config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that @@ -109,7 +149,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } - let conf = clippy_lints::read_conf(sess); + let conf = clippy_lints::read_conf(sess, &conf_path); clippy_lints::register_plugins(lint_store, sess, &conf); clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf); clippy_lints::register_renamed(lint_store); @@ -179,6 +219,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { false, None, false, + false, )); let handler = rustc_errors::Handler::with_emitter(true, None, emitter); @@ -209,68 +250,22 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { interface::try_print_query_stack(&handler, num_frames); } -fn toolchain_path(home: Option, toolchain: Option) -> Option { - home.and_then(|home| { - toolchain.map(|toolchain| { - let mut path = PathBuf::from(home); - path.push("toolchains"); - path.push(toolchain); - path - }) - }) -} - #[allow(clippy::too_many_lines)] pub fn main() { rustc_driver::init_rustc_env_logger(); LazyLock::force(&ICE_HOOK); exit(rustc_driver::catch_with_exit_code(move || { let mut orig_args: Vec = env::args().collect(); - - // Get the sysroot, looking from most specific to this invocation to the least: - // - command line - // - runtime environment - // - SYSROOT - // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN - // - sysroot from rustc in the path - // - compile-time environment - // - SYSROOT - // - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN - let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true); - let have_sys_root_arg = sys_root_arg.is_some(); - let sys_root = sys_root_arg - .map(PathBuf::from) - .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from)) - .or_else(|| { - let home = std::env::var("RUSTUP_HOME") - .or_else(|_| std::env::var("MULTIRUST_HOME")) - .ok(); - let toolchain = std::env::var("RUSTUP_TOOLCHAIN") - .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN")) - .ok(); - toolchain_path(home, toolchain) - }) - .or_else(|| { - Command::new("rustc") - .arg("--print") - .arg("sysroot") - .output() - .ok() - .and_then(|out| String::from_utf8(out.stdout).ok()) - .map(|s| PathBuf::from(s.trim())) - }) - .or_else(|| option_env!("SYSROOT").map(PathBuf::from)) - .or_else(|| { - let home = option_env!("RUSTUP_HOME") - .or(option_env!("MULTIRUST_HOME")) - .map(ToString::to_string); - let toolchain = option_env!("RUSTUP_TOOLCHAIN") - .or(option_env!("MULTIRUST_TOOLCHAIN")) - .map(ToString::to_string); - toolchain_path(home, toolchain) - }) - .map(|pb| pb.to_string_lossy().to_string()) - .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"); + let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some(); + + let sys_root_env = std::env::var("SYSROOT").ok(); + let pass_sysroot_env_if_given = |args: &mut Vec, sys_root_env| { + if let Some(sys_root) = sys_root_env { + if !has_sysroot_arg { + args.extend(vec!["--sysroot".into(), sys_root]); + } + }; + }; // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver @@ -279,11 +274,8 @@ pub fn main() { orig_args.remove(pos); orig_args[0] = "rustc".to_string(); - // if we call "rustc", we need to pass --sysroot here as well let mut args: Vec = orig_args.clone(); - if !have_sys_root_arg { - args.extend(vec!["--sysroot".into(), sys_root]); - }; + pass_sysroot_env_if_given(&mut args, sys_root_env); return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); } @@ -308,13 +300,8 @@ pub fn main() { exit(0); } - // this conditional check for the --sysroot flag is there so users can call - // `clippy_driver` directly - // without having to pass --sysroot or anything let mut args: Vec = orig_args.clone(); - if !have_sys_root_arg { - args.extend(vec!["--sysroot".into(), sys_root]); - }; + pass_sysroot_env_if_given(&mut args, sys_root_env); let mut no_deps = false; let clippy_args_var = env::var("CLIPPY_ARGS").ok(); diff --git a/src/main.rs b/src/main.rs index fce3cdfc462e..82147eba33f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,13 +2,10 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use rustc_tools_util::VersionInfo; use std::env; use std::path::PathBuf; use std::process::{self, Command}; -mod docs; - const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code. Usage: @@ -31,7 +28,7 @@ with: -D --deny OPT Set lint denied -F --forbid OPT Set lint forbidden -You can use tool lints to allow or deny lints from your code, eg.: +You can use tool lints to allow or deny lints from your code, e.g.: #[allow(clippy::needless_lifetimes)] "#; @@ -60,7 +57,7 @@ pub fn main() { if let Some(pos) = env::args().position(|a| a == "--explain") { if let Some(mut lint) = env::args().nth(pos + 1) { lint.make_ascii_lowercase(); - docs::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_")); + clippy_lints::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_")); } else { show_help(); } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 961525bbd910..6d0022f7a5cc 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -20,7 +20,14 @@ fn dogfood_clippy() { } // "" is the root package - for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { + for package in &[ + "", + "clippy_dev", + "clippy_lints", + "clippy_utils", + "lintcheck", + "rustc_tools_util", + ] { run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } diff --git a/tests/integration.rs b/tests/integration.rs index 818ff70b33f4..a771d8b87c81 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,3 +1,12 @@ +//! This test is meant to only be run in CI. To run it locally use: +//! +//! `env INTEGRATION=rust-lang/log cargo test --test integration --features=integration` +//! +//! You can use a different `INTEGRATION` value to test different repositories. +//! +//! This test will clone the specified repository and run Clippy on it. The test succeeds, if +//! Clippy doesn't produce an ICE. Lint warnings are ignored by this test. + #![cfg(feature = "integration")] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr index 9a7d802dc6d3..163f8bb35e79 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr @@ -12,5 +12,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error; 1 warning emitted +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr index a280e1bacdfd..259d39b12526 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr index a280e1bacdfd..259d39b12526 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr index a280e1bacdfd..259d39b12526 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:7:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr b/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr index 88f6e00922bc..97e6c3d5a559 100644 --- a/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr +++ b/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr @@ -10,5 +10,11 @@ note: the lint level is defined here LL | #![deny(clippy::use_self)] | ^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: unnecessary structure name repetition + --> $DIR/main.rs:12:9 + | +LL | Foo + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 2 previous errors diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs index 5057a018300e..4be04f77f5bd 100644 --- a/tests/ui-internal/custom_ice_message.rs +++ b/tests/ui-internal/custom_ice_message.rs @@ -2,6 +2,7 @@ // normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" // normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" // normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" +// normalize-stderr-test: "'rustc'" -> "''" #![deny(clippy::internal)] #![allow(clippy::missing_clippy_version_attribute)] diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index a1b8e2ee162c..2ba5890660fc 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +thread '' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic diff --git a/tests/ui-internal/invalid_msrv_attr_impl.fixed b/tests/ui-internal/invalid_msrv_attr_impl.fixed index 900a8fffd408..08634063a575 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.fixed +++ b/tests/ui-internal/invalid_msrv_attr_impl.fixed @@ -11,9 +11,9 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; use clippy_utils::extract_msrv_attr; +use clippy_utils::msrvs::Msrv; use rustc_hir::Expr; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_semver::RustcVersion; declare_lint! { pub TEST_LINT, @@ -22,7 +22,7 @@ declare_lint! { } struct Pass { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Pass => [TEST_LINT]); diff --git a/tests/ui-internal/invalid_msrv_attr_impl.rs b/tests/ui-internal/invalid_msrv_attr_impl.rs index 4bc8164db67b..f8af77e6d395 100644 --- a/tests/ui-internal/invalid_msrv_attr_impl.rs +++ b/tests/ui-internal/invalid_msrv_attr_impl.rs @@ -11,9 +11,9 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; use clippy_utils::extract_msrv_attr; +use clippy_utils::msrvs::Msrv; use rustc_hir::Expr; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_semver::RustcVersion; declare_lint! { pub TEST_LINT, @@ -22,7 +22,7 @@ declare_lint! { } struct Pass { - msrv: Option, + msrv: Msrv, } impl_lint_pass!(Pass => [TEST_LINT]); diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index b823ff7fe37f..9a9790a4bae5 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -1,5 +1,5 @@ #![warn(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)] mod paths { // Good path diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed index 4c050332f2cc..e474f370a5d1 100644 --- a/tests/ui-internal/unnecessary_def_path.fixed +++ b/tests/ui-internal/unnecessary_def_path.fixed @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_diagnostic_item(cx, ty, sym::Result); let _ = is_type_diagnostic_item(cx, ty, sym::Result); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = is_type_diagnostic_item(cx, ty, sym::Rc); @@ -48,14 +48,14 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox); let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit); - let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did); + let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did); let _ = cx.tcx.is_diagnostic_item(sym::Option, did); - let _ = cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did); + let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did); let _ = is_trait_method(cx, expr, sym::AsRef); let _ = is_path_diagnostic_item(cx, expr, sym::Option); - let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id)); + let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id)); let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome); } diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index 6506f1f164ac..f17fed6c6530 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -28,9 +28,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] static OPTION: [&str; 3] = ["core", "option", "Option"]; -#[allow(unused)] +#[allow(unused, clippy::unnecessary_def_path)] const RESULT: &[&str] = &["core", "result", "Result"]; fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { @@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) { let _ = match_type(cx, ty, RESULT); let _ = match_type(cx, ty, &["core", "result", "Result"]); - #[allow(unused)] + #[allow(unused, clippy::unnecessary_def_path)] let rc_path = &["alloc", "rc", "Rc"]; let _ = clippy_utils::ty::match_type(cx, ty, rc_path); diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr index a99a8f71fa6a..3ca29f099771 100644 --- a/tests/ui-internal/unnecessary_def_path.stderr +++ b/tests/ui-internal/unnecessary_def_path.stderr @@ -57,7 +57,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:51:13 | LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)` error: use of a def path to a diagnostic item --> $DIR/unnecessary_def_path.rs:52:13 @@ -69,7 +69,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:53:13 | LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OptionSome).ok() == Some(did)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)` | = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead @@ -89,7 +89,7 @@ error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:58:13 | LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))` error: use of a def path to a `LangItem` --> $DIR/unnecessary_def_path.rs:59:13 diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs new file mode 100644 index 000000000000..b5ff3a542056 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs @@ -0,0 +1,16 @@ +#![feature(rustc_private)] +#![allow(unused)] +#![warn(clippy::unnecessary_def_path)] + +extern crate rustc_hir; + +use rustc_hir::LangItem; + +fn main() { + const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + + // Don't lint, not yet a diagnostic or language item + const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; +} diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr new file mode 100644 index 000000000000..2a240cc249b0 --- /dev/null +++ b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -0,0 +1,27 @@ +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + | +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` + +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + | +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::deref_method` + +error: hardcoded path to a language item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 + | +LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `LangItem::DerefMut` + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml new file mode 100644 index 000000000000..b95e806aae24 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml @@ -0,0 +1 @@ +allow-mixed-uninlined-format-args = false diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed new file mode 100644 index 000000000000..aa8b45b5fe7d --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed @@ -0,0 +1,14 @@ +// run-rustfix +#![warn(clippy::uninlined_format_args)] + +fn main() { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + + println!("val='{local_i32}'"); + println!("Hello x is {local_f64:.local_i32$}"); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("{local_i32}, {}", local_opt.unwrap()); +} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs new file mode 100644 index 000000000000..ad2e4863ee8e --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs @@ -0,0 +1,14 @@ +// run-rustfix +#![warn(clippy::uninlined_format_args)] + +fn main() { + let local_i32 = 1; + let local_f64 = 2.0; + let local_opt: Option = Some(3); + + println!("val='{}'", local_i32); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + println!("{}, {}", local_i32, local_opt.unwrap()); +} diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr new file mode 100644 index 000000000000..ee9417621961 --- /dev/null +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr @@ -0,0 +1,76 @@ +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:9:5 + | +LL | println!("val='{}'", local_i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::uninlined-format-args` implied by `-D warnings` +help: change this to + | +LL - println!("val='{}'", local_i32); +LL + println!("val='{local_i32}'"); + | + +error: literal with an empty format string + --> $DIR/uninlined_format_args.rs:10:35 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^ + | + = note: `-D clippy::print-literal` implied by `-D warnings` +help: try this + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello x is {:.*}", local_i32, local_f64); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:10:5 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:11:5 + | +LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:12:5 + | +LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); +LL + println!("Hello {local_i32} is {local_f64:.*}", 5); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:13:5 + | +LL | println!("{}, {}", local_i32, local_opt.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("{}, {}", local_i32, local_opt.unwrap()); +LL + println!("{local_i32}, {}", local_opt.unwrap()); + | + +error: aborting due to 6 previous errors + diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs index 1aed09b7c7bd..fb5b1b193f84 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs @@ -1,24 +1,118 @@ #![warn(clippy::arithmetic_side_effects)] -use core::ops::Add; +use core::ops::{Add, Neg}; -#[derive(Clone, Copy)] -struct Point { - x: i32, - y: i32, +macro_rules! create { + ($name:ident) => { + #[allow(clippy::arithmetic_side_effects)] + #[derive(Clone, Copy)] + struct $name; + + impl Add<$name> for $name { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Add for $name { + type Output = $name; + fn add(self, other: i32) -> Self::Output { + todo!() + } + } + + impl Add<$name> for i32 { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Add for $name { + type Output = $name; + fn add(self, other: i64) -> Self::Output { + todo!() + } + } + + impl Add<$name> for i64 { + type Output = $name; + fn add(self, other: $name) -> Self::Output { + todo!() + } + } + + impl Neg for $name { + type Output = $name; + fn neg(self) -> Self::Output { + todo!() + } + } + }; } -impl Add for Point { - type Output = Self; +create!(Foo); +create!(Bar); +create!(Baz); +create!(OutOfNames); - fn add(self, other: Self) -> Self { - todo!() - } +fn lhs_and_rhs_are_equal() { + // is explicitly on the list + let _ = OutOfNames + OutOfNames; + // is explicitly on the list + let _ = Foo + Foo; + // is implicitly on the list + let _ = Bar + Bar; + // not on the list + let _ = Baz + Baz; } -fn main() { - let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 }; +fn lhs_is_different() { + // is explicitly on the list + let _ = 1i32 + OutOfNames; + // is explicitly on the list + let _ = 1i32 + Foo; + // is implicitly on the list + let _ = 1i32 + Bar; + // not on the list + let _ = 1i32 + Baz; - let point: Point = Point { x: 1, y: 0 }; - let _ = point + point; + // not on the list + let _ = 1i64 + Foo; + // is implicitly on the list + let _ = 1i64 + Bar; + // not on the list + let _ = 1i64 + Baz; } + +fn rhs_is_different() { + // is explicitly on the list + let _ = OutOfNames + 1i32; + // is explicitly on the list + let _ = Foo + 1i32; + // is implicitly on the list + let _ = Bar + 1i32; + // not on the list + let _ = Baz + 1i32; + + // not on the list + let _ = Foo + 1i64; + // is implicitly on the list + let _ = Bar + 1i64; + // not on the list + let _ = Baz + 1i64; +} + +fn unary() { + // is explicitly on the list + let _ = -OutOfNames; + // is explicitly on the list + let _ = -Foo; + // not on the list + let _ = -Bar; + // not on the list + let _ = -Baz; +} + +fn main() {} diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr new file mode 100644 index 000000000000..ad89534aa1b0 --- /dev/null +++ b/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.stderr @@ -0,0 +1,58 @@ +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:68:13 + | +LL | let _ = Baz + Baz; + | ^^^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:79:13 + | +LL | let _ = 1i32 + Baz; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:82:13 + | +LL | let _ = 1i64 + Foo; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:86:13 + | +LL | let _ = 1i64 + Baz; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:97:13 + | +LL | let _ = Baz + 1i32; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:100:13 + | +LL | let _ = Foo + 1i64; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:104:13 + | +LL | let _ = Baz + 1i64; + | ^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:113:13 + | +LL | let _ = -Bar; + | ^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects_allowed.rs:115:13 + | +LL | let _ = -Baz; + | ^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml b/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml index e736256f29a4..89cbea7ecfe4 100644 --- a/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml +++ b/tests/ui-toml/arithmetic_side_effects_allowed/clippy.toml @@ -1 +1,11 @@ -arithmetic-side-effects-allowed = ["Point"] +arithmetic-side-effects-allowed = [ + "OutOfNames" +] +arithmetic-side-effects-allowed-binary = [ + ["Foo", "Foo"], + ["Foo", "i32"], + ["i32", "Foo"], + ["Bar", "*"], + ["*", "Bar"], +] +arithmetic-side-effects-allowed-unary = ["Foo"] diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr index 4c75998437fd..825aa1487e7a 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -4,7 +4,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy LL | let _x = String::from("hello"); | ^^ | - = note: strings are bad + = note: strings are bad (from clippy.toml) = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` @@ -19,7 +19,7 @@ error: `std::string::String` may not be held across an `await` point per `clippy LL | let _x = String::from("hi!"); | ^^ | - = note: strings are bad + = note: strings are bad (from clippy.toml) error: aborting due to 3 previous errors diff --git a/tests/ui-toml/dbg_macro/dbg_macro.stderr b/tests/ui-toml/dbg_macro/dbg_macro.stderr index 46efb86dcfc5..859383a71194 100644 --- a/tests/ui-toml/dbg_macro/dbg_macro.stderr +++ b/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -1,99 +1,99 @@ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:9:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if n <= 1 { | ~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:10:9 | LL | dbg!(1) | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1 | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:12:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | n * factorial(n - 1) | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(42); | ^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 42; | ~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:19:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:20:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs index 22dcd3ae9d69..bff97d97df77 100644 --- a/tests/ui-toml/expect_used/expect_used.rs +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -16,14 +16,19 @@ fn main() { expect_result(); } -#[test] -fn test_expect_option() { - let opt = Some(0); - let _ = opt.expect(""); -} +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); -#[test] -fn test_expect_result() { - let res: Result = Ok(0); - let _ = res.expect(""); + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + } } diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr index 28a08599c67c..1e9bb48c333c 100644 --- a/tests/ui-toml/expect_used/expect_used.stderr +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect_used.rs:6:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect_used.rs:11:13 | LL | let _ = res.expect(""); diff --git a/tests/ui-toml/mut_key/clippy.toml b/tests/ui-toml/mut_key/clippy.toml new file mode 100644 index 000000000000..6d33e192ee89 --- /dev/null +++ b/tests/ui-toml/mut_key/clippy.toml @@ -0,0 +1 @@ +ignore-interior-mutability = ["mut_key::Counted"] \ No newline at end of file diff --git a/tests/ui-toml/mut_key/mut_key.rs b/tests/ui-toml/mut_key/mut_key.rs new file mode 100644 index 000000000000..667c51cb4a3f --- /dev/null +++ b/tests/ui-toml/mut_key/mut_key.rs @@ -0,0 +1,53 @@ +// compile-flags: --crate-name mut_key + +#![warn(clippy::mutable_key_type)] + +use std::cmp::{Eq, PartialEq}; +use std::collections::{HashMap, HashSet}; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; +use std::sync::atomic::{AtomicUsize, Ordering}; + +struct Counted { + count: AtomicUsize, + val: T, +} + +impl Clone for Counted { + fn clone(&self) -> Self { + Self { + count: AtomicUsize::new(0), + val: self.val.clone(), + } + } +} + +impl PartialEq for Counted { + fn eq(&self, other: &Self) -> bool { + self.val == other.val + } +} +impl Eq for Counted {} + +impl Hash for Counted { + fn hash(&self, state: &mut H) { + self.val.hash(state); + } +} + +impl Deref for Counted { + type Target = T; + + fn deref(&self) -> &T { + self.count.fetch_add(1, Ordering::AcqRel); + &self.val + } +} + +// This is not linted because `"mut_key::Counted"` is in +// `arc_like_types` in `clippy.toml` +fn should_not_take_this_arg(_v: HashSet>) {} + +fn main() { + should_not_take_this_arg(HashSet::new()); +} diff --git a/tests/ui-toml/print_macro/clippy.toml b/tests/ui-toml/print_macro/clippy.toml new file mode 100644 index 000000000000..40b1dda5b2ea --- /dev/null +++ b/tests/ui-toml/print_macro/clippy.toml @@ -0,0 +1 @@ +allow-print-in-tests = true diff --git a/tests/ui-toml/print_macro/print_macro.rs b/tests/ui-toml/print_macro/print_macro.rs new file mode 100644 index 000000000000..5aefb6a6b4d4 --- /dev/null +++ b/tests/ui-toml/print_macro/print_macro.rs @@ -0,0 +1,20 @@ +// compile-flags: --test +#![warn(clippy::print_stdout)] +#![warn(clippy::print_stderr)] + +fn foo(n: u32) { + print!("{n}"); + eprint!("{n}"); +} + +#[test] +pub fn foo1() { + print!("{}", 1); + eprint!("{}", 1); +} + +#[cfg(test)] +fn foo3() { + print!("{}", 1); + eprint!("{}", 1); +} diff --git a/tests/ui-toml/print_macro/print_macro.stderr b/tests/ui-toml/print_macro/print_macro.stderr new file mode 100644 index 000000000000..d4b1ae84fd7d --- /dev/null +++ b/tests/ui-toml/print_macro/print_macro.stderr @@ -0,0 +1,18 @@ +error: use of `print!` + --> $DIR/print_macro.rs:6:5 + | +LL | print!("{n}"); + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stdout` implied by `-D warnings` + +error: use of `eprint!` + --> $DIR/print_macro.rs:7:5 + | +LL | eprint!("{n}"); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stderr` implied by `-D warnings` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/suppress_lint_in_const/clippy.toml b/tests/ui-toml/suppress_lint_in_const/clippy.toml new file mode 100644 index 000000000000..1b9384d7e3ee --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/clippy.toml @@ -0,0 +1 @@ +suppress-restriction-lint-in-const = true diff --git a/tests/ui-toml/suppress_lint_in_const/test.rs b/tests/ui-toml/suppress_lint_in_const/test.rs new file mode 100644 index 000000000000..5a2df9f6c5d9 --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/test.rs @@ -0,0 +1,60 @@ +#![feature(inline_const)] +#![warn(clippy::indexing_slicing)] +// We also check the out_of_bounds_indexing lint here, because it lints similar things and +// we want to avoid false positives. +#![warn(clippy::out_of_bounds_indexing)] +#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] + +const ARR: [i32; 2] = [1, 2]; +const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. +const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + +const fn idx() -> usize { + 1 +} +const fn idx4() -> usize { + 4 +} + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + x[index]; + x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + + x[0]; // Ok, should not produce stderr. + x[3]; // Ok, should not produce stderr. + x[const { idx() }]; // Ok, should not produce stderr. + x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + const { &ARR[idx()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + + let y = &x; + y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 + y[4]; // Ok, rustc will handle references too. + + let v = vec![0; 5]; + v[0]; + v[10]; + v[1 << 3]; + + const N: usize = 15; // Out of bounds + const M: usize = 3; // In bounds + x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. + x[M]; // Ok, should not produce stderr. + v[N]; + v[M]; +} + +/// An opaque integer representation +pub struct Integer<'a> { + /// The underlying data + value: &'a [u8], +} +impl<'a> Integer<'a> { + // Check whether `self` holds a negative number or not + pub const fn is_negative(&self) -> bool { + self.value[0] & 0b1000_0000 != 0 + } +} diff --git a/tests/ui-toml/suppress_lint_in_const/test.stderr b/tests/ui-toml/suppress_lint_in_const/test.stderr new file mode 100644 index 000000000000..bc178b7e1319 --- /dev/null +++ b/tests/ui-toml/suppress_lint_in_const/test.stderr @@ -0,0 +1,70 @@ +error[E0080]: evaluation of `main::{constant#3}` failed + --> $DIR/test.rs:31:14 + | +LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +note: erroneous constant used + --> $DIR/test.rs:31:5 + | +LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true. + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: indexing may panic + --> $DIR/test.rs:22:5 + | +LL | x[index]; + | ^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: indexing may panic + --> $DIR/test.rs:38:5 + | +LL | v[0]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:39:5 + | +LL | v[10]; + | ^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:40:5 + | +LL | v[1 << 3]; + | ^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:46:5 + | +LL | v[N]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic + --> $DIR/test.rs:47:5 + | +LL | v[M]; + | ^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + +error[E0080]: evaluation of constant value failed + --> $DIR/test.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui-toml/toml_disallowed_methods/clippy.toml b/tests/ui-toml/toml_disallowed_methods/clippy.toml index 28774db625bb..41dbd5068479 100644 --- a/tests/ui-toml/toml_disallowed_methods/clippy.toml +++ b/tests/ui-toml/toml_disallowed_methods/clippy.toml @@ -8,4 +8,10 @@ disallowed-methods = [ { path = "regex::Regex::is_match", reason = "no matching allowed" }, # can use an inline table but omit reason { path = "regex::Regex::new" }, + # local paths + "conf_disallowed_methods::local_fn", + "conf_disallowed_methods::local_mod::f", + "conf_disallowed_methods::Struct::method", + "conf_disallowed_methods::Trait::provided_method", + "conf_disallowed_methods::Trait::implemented_method", ] diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs index b483f1600289..2f3160c83383 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs @@ -1,3 +1,5 @@ +// compile-flags: --crate-name conf_disallowed_methods + #![warn(clippy::disallowed_methods)] extern crate futures; @@ -6,6 +8,27 @@ extern crate regex; use futures::stream::{empty, select_all}; use regex::Regex; +fn local_fn() {} + +struct Struct; + +impl Struct { + fn method(&self) {} +} + +trait Trait { + fn provided_method(&self) {} + fn implemented_method(&self); +} + +impl Trait for Struct { + fn implemented_method(&self) {} +} + +mod local_mod { + pub fn f() {} +} + fn main() { let re = Regex::new(r"ab.*c").unwrap(); re.is_match("abc"); @@ -26,4 +49,11 @@ fn main() { // resolve ambiguity between `futures::stream::select_all` the module and the function let same_name_as_module = select_all(vec![empty::<()>()]); + + local_fn(); + local_mod::f(); + let s = Struct; + s.method(); + s.provided_method(); + s.implemented_method(); } diff --git a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr index 6d78c32e127e..148d1cae51f1 100644 --- a/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr +++ b/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr @@ -1,5 +1,5 @@ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:10:14 + --> $DIR/conf_disallowed_methods.rs:33:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); | ^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); = note: `-D clippy::disallowed-methods` implied by `-D warnings` error: use of a disallowed method `regex::Regex::is_match` - --> $DIR/conf_disallowed_methods.rs:11:5 + --> $DIR/conf_disallowed_methods.rs:34:5 | LL | re.is_match("abc"); | ^^^^^^^^^^^^^^^^^^ @@ -15,46 +15,76 @@ LL | re.is_match("abc"); = note: no matching allowed (from clippy.toml) error: use of a disallowed method `std::iter::Iterator::sum` - --> $DIR/conf_disallowed_methods.rs:14:5 + --> $DIR/conf_disallowed_methods.rs:37:5 | LL | a.iter().sum::(); | ^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `slice::sort_unstable` - --> $DIR/conf_disallowed_methods.rs:16:5 + --> $DIR/conf_disallowed_methods.rs:39:5 | LL | a.sort_unstable(); | ^^^^^^^^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:18:13 + --> $DIR/conf_disallowed_methods.rs:41:13 | LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:21:61 + --> $DIR/conf_disallowed_methods.rs:44:61 | LL | let indirect: fn(&str) -> Result = Regex::new; | ^^^^^^^^^^ error: use of a disallowed method `f32::clamp` - --> $DIR/conf_disallowed_methods.rs:24:28 + --> $DIR/conf_disallowed_methods.rs:47:28 | LL | let in_call = Box::new(f32::clamp); | ^^^^^^^^^^ error: use of a disallowed method `regex::Regex::new` - --> $DIR/conf_disallowed_methods.rs:25:53 + --> $DIR/conf_disallowed_methods.rs:48:53 | LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new); | ^^^^^^^^^^ error: use of a disallowed method `futures::stream::select_all` - --> $DIR/conf_disallowed_methods.rs:28:31 + --> $DIR/conf_disallowed_methods.rs:51:31 | LL | let same_name_as_module = select_all(vec![empty::<()>()]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: use of a disallowed method `conf_disallowed_methods::local_fn` + --> $DIR/conf_disallowed_methods.rs:53:5 + | +LL | local_fn(); + | ^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::local_mod::f` + --> $DIR/conf_disallowed_methods.rs:54:5 + | +LL | local_mod::f(); + | ^^^^^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Struct::method` + --> $DIR/conf_disallowed_methods.rs:56:5 + | +LL | s.method(); + | ^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method` + --> $DIR/conf_disallowed_methods.rs:57:5 + | +LL | s.provided_method(); + | ^^^^^^^^^^^^^^^^^^^ + +error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method` + --> $DIR/conf_disallowed_methods.rs:58:5 + | +LL | s.implemented_method(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 82ee80541321..a22c6a5a0607 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,9 +1,13 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of allow-dbg-in-tests allow-expect-in-tests + allow-mixed-uninlined-format-args + allow-print-in-tests allow-unwrap-in-tests allowed-scripts arithmetic-side-effects-allowed + arithmetic-side-effects-allowed-binary + arithmetic-side-effects-allowed-unary array-size-threshold avoid-breaking-exported-api await-holding-invalid-types @@ -20,8 +24,10 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold + ignore-interior-mutability large-error-threshold literal-representation-threshold + matches-for-let-else max-fn-params-bools max-include-file-size max-struct-bools @@ -31,6 +37,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie pass-by-value-size-limit single-char-binding-names-threshold standard-macro-braces + suppress-restriction-lint-in-const third-party too-large-for-stack too-many-arguments-threshold diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 0e82fb20e455..bc8e8c1f0703 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -66,8 +66,21 @@ fn main() { } } -#[test] -fn test() { - let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); - let _ = boxed_slice.get(1).unwrap(); +#[cfg(test)] +mod issue9612 { + // should not lint in `#[cfg(test)]` modules + #[test] + fn test_fn() { + let _a: u8 = 2.try_into().unwrap(); + let _a: u8 = 3.try_into().expect(""); + + util(); + } + + fn util() { + let _a: u8 = 4.try_into().unwrap(); + let _a: u8 = 5.try_into().expect(""); + // should still warn + let _ = Box::new([0]).get(1).unwrap(); + } } diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 681b5eaf54db..94b5ef663add 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_used.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); @@ -188,10 +188,10 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:72:13 + --> $DIR/unwrap_used.rs:84:17 | -LL | let _ = boxed_slice.get(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` +LL | let _ = Box::new([0]).get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]` error: aborting due to 27 previous errors diff --git a/tests/ui-toml/vec_box_sized/test.rs b/tests/ui-toml/vec_box_sized/test.rs index bf04bee16373..4c46deb585b6 100644 --- a/tests/ui-toml/vec_box_sized/test.rs +++ b/tests/ui-toml/vec_box_sized/test.rs @@ -7,8 +7,9 @@ struct C { } struct Foo(Vec>); -struct Bar(Vec>); -struct Baz(Vec>); +struct Bar(Vec>); +struct Quux(Vec>); +struct Baz(Vec>); struct BarBaz(Vec>); struct FooBarBaz(Vec>); diff --git a/tests/ui-toml/vec_box_sized/test.stderr b/tests/ui-toml/vec_box_sized/test.stderr index cf194de3c553..55de68f8ecf4 100644 --- a/tests/ui-toml/vec_box_sized/test.stderr +++ b/tests/ui-toml/vec_box_sized/test.stderr @@ -9,11 +9,11 @@ LL | struct Foo(Vec>); error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:10:12 | -LL | struct Bar(Vec>); - | ^^^^^^^^^^^^^ help: try: `Vec` +LL | struct Bar(Vec>); + | ^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/test.rs:13:18 + --> $DIR/test.rs:14:18 | LL | struct FooBarBaz(Vec>); | ^^^^^^^^^^^ help: try: `Vec` diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr deleted file mode 100644 index 3de44c72c1b9..000000000000 --- a/tests/ui/almost_complete_letter_range.stderr +++ /dev/null @@ -1,113 +0,0 @@ -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:30:17 - | -LL | let _ = ('a') ..'z'; - | ^^^^^^--^^^ - | | - | help: use an inclusive range: `..=` - | - = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:31:17 - | -LL | let _ = 'A' .. ('Z'); - | ^^^^--^^^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:37:13 - | -LL | let _ = (b'a')..(b'z'); - | ^^^^^^--^^^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:38:13 - | -LL | let _ = b'A'..b'Z'; - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:43:13 - | -LL | let _ = a!()..'z'; - | ^^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:46:9 - | -LL | b'a'..b'z' if true => 1, - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:47:9 - | -LL | b'A'..b'Z' if true => 2, - | ^^^^--^^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:54:9 - | -LL | 'a'..'z' if true => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:55:9 - | -LL | 'A'..'Z' if true => 2, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:23:17 - | -LL | let _ = 'a'..'z'; - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` -... -LL | b!(); - | ---- in this macro invocation - | - = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:68:9 - | -LL | 'a'..'z' => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `...` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:75:13 - | -LL | let _ = 'a'..'z'; - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:77:9 - | -LL | 'a'..'z' => 1, - | ^^^--^^^ - | | - | help: use an inclusive range: `..=` - -error: aborting due to 13 previous errors - diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_range.fixed similarity index 54% rename from tests/ui/almost_complete_letter_range.fixed rename to tests/ui/almost_complete_range.fixed index 079b7c000dce..6046addf7196 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_range.fixed @@ -2,12 +2,12 @@ // edition:2018 // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] -#![warn(clippy::almost_complete_letter_range)] +#![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#![allow(clippy::double_parens)] #[macro_use] extern crate macro_rules; @@ -17,10 +17,22 @@ macro_rules! a { 'a' }; } +macro_rules! A { + () => { + 'A' + }; +} +macro_rules! zero { + () => { + '0' + }; +} macro_rules! b { () => { let _ = 'a'..='z'; + let _ = 'A'..='Z'; + let _ = '0'..='9'; }; } @@ -29,52 +41,68 @@ fn main() { { let _ = ('a') ..='z'; let _ = 'A' ..= ('Z'); + let _ = ((('0'))) ..= ('9'); } let _ = 'b'..'z'; let _ = 'B'..'Z'; + let _ = '1'..'9'; let _ = (b'a')..=(b'z'); let _ = b'A'..=b'Z'; + let _ = b'0'..=b'9'; let _ = b'b'..b'z'; let _ = b'B'..b'Z'; + let _ = b'1'..b'9'; let _ = a!()..='z'; + let _ = A!()..='Z'; + let _ = zero!()..='9'; let _ = match 0u8 { b'a'..=b'z' if true => 1, b'A'..=b'Z' if true => 2, - b'b'..b'z' => 3, - b'B'..b'Z' => 4, - _ => 5, + b'0'..=b'9' if true => 3, + b'b'..b'z' => 4, + b'B'..b'Z' => 5, + b'1'..b'9' => 6, + _ => 7, }; let _ = match 'x' { 'a'..='z' if true => 1, 'A'..='Z' if true => 2, - 'b'..'z' => 3, - 'B'..'Z' => 4, - _ => 5, + '0'..='9' if true => 3, + 'b'..'z' => 4, + 'B'..'Z' => 5, + '1'..'9' => 6, + _ => 7, }; - almost_complete_letter_range!(); + almost_complete_range!(); b!(); } +#[clippy::msrv = "1.25"] fn _under_msrv() { - #![clippy::msrv = "1.25"] let _ = match 'a' { 'a'...'z' => 1, - _ => 2, + 'A'...'Z' => 2, + '0'...'9' => 3, + _ => 4, }; } +#[clippy::msrv = "1.26"] fn _meets_msrv() { - #![clippy::msrv = "1.26"] let _ = 'a'..='z'; + let _ = 'A'..='Z'; + let _ = '0'..='9'; let _ = match 'a' { 'a'..='z' => 1, - _ => 2, + 'A'..='Z' => 1, + '0'..='9' => 3, + _ => 4, }; } diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_range.rs similarity index 54% rename from tests/ui/almost_complete_letter_range.rs rename to tests/ui/almost_complete_range.rs index a66900a976ef..ae7e07ab872b 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_range.rs @@ -2,12 +2,12 @@ // edition:2018 // aux-build:macro_rules.rs -#![feature(custom_inner_attributes)] #![feature(exclusive_range_pattern)] #![feature(stmt_expr_attributes)] -#![warn(clippy::almost_complete_letter_range)] +#![warn(clippy::almost_complete_range)] #![allow(ellipsis_inclusive_range_patterns)] #![allow(clippy::needless_parens_on_range_literals)] +#![allow(clippy::double_parens)] #[macro_use] extern crate macro_rules; @@ -17,10 +17,22 @@ macro_rules! a { 'a' }; } +macro_rules! A { + () => { + 'A' + }; +} +macro_rules! zero { + () => { + '0' + }; +} macro_rules! b { () => { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; }; } @@ -29,52 +41,68 @@ fn main() { { let _ = ('a') ..'z'; let _ = 'A' .. ('Z'); + let _ = ((('0'))) .. ('9'); } let _ = 'b'..'z'; let _ = 'B'..'Z'; + let _ = '1'..'9'; let _ = (b'a')..(b'z'); let _ = b'A'..b'Z'; + let _ = b'0'..b'9'; let _ = b'b'..b'z'; let _ = b'B'..b'Z'; + let _ = b'1'..b'9'; let _ = a!()..'z'; + let _ = A!()..'Z'; + let _ = zero!()..'9'; let _ = match 0u8 { b'a'..b'z' if true => 1, b'A'..b'Z' if true => 2, - b'b'..b'z' => 3, - b'B'..b'Z' => 4, - _ => 5, + b'0'..b'9' if true => 3, + b'b'..b'z' => 4, + b'B'..b'Z' => 5, + b'1'..b'9' => 6, + _ => 7, }; let _ = match 'x' { 'a'..'z' if true => 1, 'A'..'Z' if true => 2, - 'b'..'z' => 3, - 'B'..'Z' => 4, - _ => 5, + '0'..'9' if true => 3, + 'b'..'z' => 4, + 'B'..'Z' => 5, + '1'..'9' => 6, + _ => 7, }; - almost_complete_letter_range!(); + almost_complete_range!(); b!(); } +#[clippy::msrv = "1.25"] fn _under_msrv() { - #![clippy::msrv = "1.25"] let _ = match 'a' { 'a'..'z' => 1, - _ => 2, + 'A'..'Z' => 2, + '0'..'9' => 3, + _ => 4, }; } +#[clippy::msrv = "1.26"] fn _meets_msrv() { - #![clippy::msrv = "1.26"] let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; let _ = match 'a' { 'a'..'z' => 1, - _ => 2, + 'A'..'Z' => 1, + '0'..'9' => 3, + _ => 4, }; } diff --git a/tests/ui/almost_complete_range.stderr b/tests/ui/almost_complete_range.stderr new file mode 100644 index 000000000000..a7a532878502 --- /dev/null +++ b/tests/ui/almost_complete_range.stderr @@ -0,0 +1,235 @@ +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:42:17 + | +LL | let _ = ('a') ..'z'; + | ^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + | + = note: `-D clippy::almost-complete-range` implied by `-D warnings` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:43:17 + | +LL | let _ = 'A' .. ('Z'); + | ^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:44:17 + | +LL | let _ = ((('0'))) .. ('9'); + | ^^^^^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:51:13 + | +LL | let _ = (b'a')..(b'z'); + | ^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:52:13 + | +LL | let _ = b'A'..b'Z'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:53:13 + | +LL | let _ = b'0'..b'9'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:59:13 + | +LL | let _ = a!()..'z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:60:13 + | +LL | let _ = A!()..'Z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:61:13 + | +LL | let _ = zero!()..'9'; + | ^^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:64:9 + | +LL | b'a'..b'z' if true => 1, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:65:9 + | +LL | b'A'..b'Z' if true => 2, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:66:9 + | +LL | b'0'..b'9' if true => 3, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:74:9 + | +LL | 'a'..'z' if true => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:75:9 + | +LL | 'A'..'Z' if true => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:76:9 + | +LL | '0'..'9' if true => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:33:17 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:34:17 + | +LL | let _ = 'A'..'Z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:35:17 + | +LL | let _ = '0'..'9'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` +... +LL | b!(); + | ---- in this macro invocation + | + = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:90:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:91:9 + | +LL | 'A'..'Z' => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:92:9 + | +LL | '0'..'9' => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:99:13 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:100:13 + | +LL | let _ = 'A'..'Z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:101:13 + | +LL | let _ = '0'..'9'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:103:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:104:9 + | +LL | 'A'..'Z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii range + --> $DIR/almost_complete_range.rs:105:9 + | +LL | '0'..'9' => 3, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: aborting due to 27 previous errors + diff --git a/tests/ui/arithmetic_side_effects.rs b/tests/ui/arithmetic_side_effects.rs index b25e68f13061..918cf81c600a 100644 --- a/tests/ui/arithmetic_side_effects.rs +++ b/tests/ui/arithmetic_side_effects.rs @@ -2,6 +2,7 @@ clippy::assign_op_pattern, clippy::erasing_op, clippy::identity_op, + clippy::no_effect, clippy::op_ref, clippy::unnecessary_owned_empty_strings, arithmetic_overflow, @@ -12,31 +13,95 @@ use core::num::{Saturating, Wrapping}; +#[derive(Clone, Copy)] pub struct Custom; macro_rules! impl_arith { - ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => { + ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => { $( - impl core::ops::$_trait<$ty> for Custom { - type Output = Self; - fn $method(self, _: $ty) -> Self::Output { Self } + impl core::ops::$_trait<$lhs> for $rhs { + type Output = Custom; + fn $method(self, _: $lhs) -> Self::Output { todo!() } + } + )* + } +} + +macro_rules! impl_assign_arith { + ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => { + $( + impl core::ops::$_trait<$lhs> for $rhs { + fn $method(&mut self, _: $lhs) {} } )* } } impl_arith!( - Add, i32, add; - Div, i32, div; - Mul, i32, mul; - Sub, i32, sub; - - Add, f64, add; - Div, f64, div; - Mul, f64, mul; - Sub, f64, sub; + Add, Custom, Custom, add; + Div, Custom, Custom, div; + Mul, Custom, Custom, mul; + Rem, Custom, Custom, rem; + Sub, Custom, Custom, sub; + + Add, Custom, &Custom, add; + Div, Custom, &Custom, div; + Mul, Custom, &Custom, mul; + Rem, Custom, &Custom, rem; + Sub, Custom, &Custom, sub; + + Add, &Custom, Custom, add; + Div, &Custom, Custom, div; + Mul, &Custom, Custom, mul; + Rem, &Custom, Custom, rem; + Sub, &Custom, Custom, sub; + + Add, &Custom, &Custom, add; + Div, &Custom, &Custom, div; + Mul, &Custom, &Custom, mul; + Rem, &Custom, &Custom, rem; + Sub, &Custom, &Custom, sub; +); + +impl_assign_arith!( + AddAssign, Custom, Custom, add_assign; + DivAssign, Custom, Custom, div_assign; + MulAssign, Custom, Custom, mul_assign; + RemAssign, Custom, Custom, rem_assign; + SubAssign, Custom, Custom, sub_assign; + + AddAssign, Custom, &Custom, add_assign; + DivAssign, Custom, &Custom, div_assign; + MulAssign, Custom, &Custom, mul_assign; + RemAssign, Custom, &Custom, rem_assign; + SubAssign, Custom, &Custom, sub_assign; + + AddAssign, &Custom, Custom, add_assign; + DivAssign, &Custom, Custom, div_assign; + MulAssign, &Custom, Custom, mul_assign; + RemAssign, &Custom, Custom, rem_assign; + SubAssign, &Custom, Custom, sub_assign; + + AddAssign, &Custom, &Custom, add_assign; + DivAssign, &Custom, &Custom, div_assign; + MulAssign, &Custom, &Custom, mul_assign; + RemAssign, &Custom, &Custom, rem_assign; + SubAssign, &Custom, &Custom, sub_assign; ); +impl core::ops::Neg for Custom { + type Output = Custom; + fn neg(self) -> Self::Output { + todo!() + } +} +impl core::ops::Neg for &Custom { + type Output = Custom; + fn neg(self) -> Self::Output { + todo!() + } +} + pub fn association_with_structures_should_not_trigger_the_lint() { enum Foo { Bar = -2, @@ -125,6 +190,18 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n *= &0; _n *= 1; _n *= &1; + _n += -0; + _n += &-0; + _n -= -0; + _n -= &-0; + _n /= -99; + _n /= &-99; + _n %= -99; + _n %= &-99; + _n *= -0; + _n *= &-0; + _n *= -1; + _n *= &-1; // Binary _n = _n + 0; @@ -150,12 +227,17 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = 23 + 85; // Unary - _n = -1; - _n = -(-1); + _n = -2147483647; + _n = -i32::MAX; + _n = -i32::MIN; + _n = -&2147483647; + _n = -&i32::MAX; + _n = -&i32::MIN; } -pub fn runtime_ops() { +pub fn unknown_ops_or_runtime_ops_that_can_overflow() { let mut _n = i32::MAX; + let mut _custom = Custom; // Assign _n += 1; @@ -168,6 +250,36 @@ pub fn runtime_ops() { _n %= &0; _n *= 2; _n *= &2; + _n += -1; + _n += &-1; + _n -= -1; + _n -= &-1; + _n /= -0; + _n /= &-0; + _n %= -0; + _n %= &-0; + _n *= -2; + _n *= &-2; + _custom += Custom; + _custom += &Custom; + _custom -= Custom; + _custom -= &Custom; + _custom /= Custom; + _custom /= &Custom; + _custom %= Custom; + _custom %= &Custom; + _custom *= Custom; + _custom *= &Custom; + _custom += -Custom; + _custom += &-Custom; + _custom -= -Custom; + _custom -= &-Custom; + _custom /= -Custom; + _custom /= &-Custom; + _custom %= -Custom; + _custom %= &-Custom; + _custom *= -Custom; + _custom *= &-Custom; // Binary _n = _n + 1; @@ -189,36 +301,73 @@ pub fn runtime_ops() { _n = 23 + &85; _n = &23 + 85; _n = &23 + &85; - - // Custom - let _ = Custom + 0; - let _ = Custom + 1; - let _ = Custom + 2; - let _ = Custom + 0.0; - let _ = Custom + 1.0; - let _ = Custom + 2.0; - let _ = Custom - 0; - let _ = Custom - 1; - let _ = Custom - 2; - let _ = Custom - 0.0; - let _ = Custom - 1.0; - let _ = Custom - 2.0; - let _ = Custom / 0; - let _ = Custom / 1; - let _ = Custom / 2; - let _ = Custom / 0.0; - let _ = Custom / 1.0; - let _ = Custom / 2.0; - let _ = Custom * 0; - let _ = Custom * 1; - let _ = Custom * 2; - let _ = Custom * 0.0; - let _ = Custom * 1.0; - let _ = Custom * 2.0; + _custom = _custom + _custom; + _custom = _custom + &_custom; + _custom = Custom + _custom; + _custom = &Custom + _custom; + _custom = _custom - Custom; + _custom = _custom - &Custom; + _custom = Custom - _custom; + _custom = &Custom - _custom; + _custom = _custom / Custom; + _custom = _custom / &Custom; + _custom = _custom % Custom; + _custom = _custom % &Custom; + _custom = _custom * Custom; + _custom = _custom * &Custom; + _custom = Custom * _custom; + _custom = &Custom * _custom; + _custom = Custom + &Custom; + _custom = &Custom + Custom; + _custom = &Custom + &Custom; // Unary _n = -_n; _n = -&_n; + _custom = -_custom; + _custom = -&_custom; +} + +// Copied and pasted from the `integer_arithmetic` lint for comparison. +pub fn integer_arithmetic() { + let mut i = 1i32; + let mut var1 = 0i32; + let mut var2 = -1i32; + + 1 + i; + i * 2; + 1 % i / 2; + i - 2 + 2 - i; + -i; + i >> 1; + i << 1; + + -1; + -(-1); + + i & 1; + i | 1; + i ^ 1; + + i += 1; + i -= 1; + i *= 2; + i /= 2; + i /= 0; + i /= -1; + i /= var1; + i /= var2; + i %= 2; + i %= 0; + i %= -1; + i %= var1; + i %= var2; + i <<= 3; + i >>= 2; + + i |= 1; + i &= 1; + i ^= i; } fn main() {} diff --git a/tests/ui/arithmetic_side_effects.stderr b/tests/ui/arithmetic_side_effects.stderr index 0f06e22bae96..5e349f6b497c 100644 --- a/tests/ui/arithmetic_side_effects.stderr +++ b/tests/ui/arithmetic_side_effects.stderr @@ -1,352 +1,598 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:78:13 - | -LL | let _ = String::new() + ""; - | ^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:86:27 - | -LL | let inferred_string = string + ""; - | ^^^^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:90:13 - | -LL | let _ = inferred_string + ""; - | ^^^^^^^^^^^^^^^^^^^^ - -error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:161:5 + --> $DIR/arithmetic_side_effects.rs:243:5 | LL | _n += 1; | ^^^^^^^ + | + = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:162:5 + --> $DIR/arithmetic_side_effects.rs:244:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:163:5 + --> $DIR/arithmetic_side_effects.rs:245:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:164:5 + --> $DIR/arithmetic_side_effects.rs:246:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:165:5 + --> $DIR/arithmetic_side_effects.rs:247:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:166:5 + --> $DIR/arithmetic_side_effects.rs:248:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:167:5 + --> $DIR/arithmetic_side_effects.rs:249:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:168:5 + --> $DIR/arithmetic_side_effects.rs:250:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:169:5 + --> $DIR/arithmetic_side_effects.rs:251:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:170:5 + --> $DIR/arithmetic_side_effects.rs:252:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:173:10 + --> $DIR/arithmetic_side_effects.rs:253:5 + | +LL | _n += -1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:254:5 + | +LL | _n += &-1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:255:5 + | +LL | _n -= -1; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:256:5 + | +LL | _n -= &-1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:257:5 + | +LL | _n /= -0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:258:5 + | +LL | _n /= &-0; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:259:5 + | +LL | _n %= -0; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:260:5 + | +LL | _n %= &-0; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:261:5 + | +LL | _n *= -2; + | ^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:262:5 + | +LL | _n *= &-2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:263:5 + | +LL | _custom += Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:264:5 + | +LL | _custom += &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:265:5 + | +LL | _custom -= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:266:5 + | +LL | _custom -= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:267:5 + | +LL | _custom /= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:268:5 + | +LL | _custom /= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:269:5 + | +LL | _custom %= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:270:5 + | +LL | _custom %= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:271:5 + | +LL | _custom *= Custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:272:5 + | +LL | _custom *= &Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:273:5 + | +LL | _custom += -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:274:5 + | +LL | _custom += &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:275:5 + | +LL | _custom -= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:276:5 + | +LL | _custom -= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:277:5 + | +LL | _custom /= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:278:5 + | +LL | _custom /= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:279:5 + | +LL | _custom %= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:280:5 + | +LL | _custom %= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:281:5 + | +LL | _custom *= -Custom; + | ^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:282:5 + | +LL | _custom *= &-Custom; + | ^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:285:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:174:10 + --> $DIR/arithmetic_side_effects.rs:286:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:175:10 + --> $DIR/arithmetic_side_effects.rs:287:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:176:10 + --> $DIR/arithmetic_side_effects.rs:288:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:177:10 + --> $DIR/arithmetic_side_effects.rs:289:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:178:10 + --> $DIR/arithmetic_side_effects.rs:290:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:179:10 + --> $DIR/arithmetic_side_effects.rs:291:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:180:10 + --> $DIR/arithmetic_side_effects.rs:292:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:181:10 + --> $DIR/arithmetic_side_effects.rs:293:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:182:10 + --> $DIR/arithmetic_side_effects.rs:294:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:183:10 + --> $DIR/arithmetic_side_effects.rs:295:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:184:10 + --> $DIR/arithmetic_side_effects.rs:296:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:185:10 + --> $DIR/arithmetic_side_effects.rs:297:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:186:10 + --> $DIR/arithmetic_side_effects.rs:298:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:187:10 + --> $DIR/arithmetic_side_effects.rs:299:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:188:10 + --> $DIR/arithmetic_side_effects.rs:300:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:189:10 + --> $DIR/arithmetic_side_effects.rs:301:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:190:10 + --> $DIR/arithmetic_side_effects.rs:302:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:191:10 + --> $DIR/arithmetic_side_effects.rs:303:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:194:13 + --> $DIR/arithmetic_side_effects.rs:304:15 + | +LL | _custom = _custom + _custom; + | ^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:305:15 | -LL | let _ = Custom + 0; - | ^^^^^^^^^^ +LL | _custom = _custom + &_custom; + | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:195:13 + --> $DIR/arithmetic_side_effects.rs:306:15 | -LL | let _ = Custom + 1; - | ^^^^^^^^^^ +LL | _custom = Custom + _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:196:13 + --> $DIR/arithmetic_side_effects.rs:307:15 | -LL | let _ = Custom + 2; - | ^^^^^^^^^^ +LL | _custom = &Custom + _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:197:13 + --> $DIR/arithmetic_side_effects.rs:308:15 | -LL | let _ = Custom + 0.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom - Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:198:13 + --> $DIR/arithmetic_side_effects.rs:309:15 | -LL | let _ = Custom + 1.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom - &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:199:13 + --> $DIR/arithmetic_side_effects.rs:310:15 | -LL | let _ = Custom + 2.0; - | ^^^^^^^^^^^^ +LL | _custom = Custom - _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:200:13 + --> $DIR/arithmetic_side_effects.rs:311:15 | -LL | let _ = Custom - 0; - | ^^^^^^^^^^ +LL | _custom = &Custom - _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:201:13 + --> $DIR/arithmetic_side_effects.rs:312:15 | -LL | let _ = Custom - 1; - | ^^^^^^^^^^ +LL | _custom = _custom / Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:202:13 + --> $DIR/arithmetic_side_effects.rs:313:15 | -LL | let _ = Custom - 2; - | ^^^^^^^^^^ +LL | _custom = _custom / &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:203:13 + --> $DIR/arithmetic_side_effects.rs:314:15 | -LL | let _ = Custom - 0.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom % Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:204:13 + --> $DIR/arithmetic_side_effects.rs:315:15 | -LL | let _ = Custom - 1.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom % &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:205:13 + --> $DIR/arithmetic_side_effects.rs:316:15 | -LL | let _ = Custom - 2.0; - | ^^^^^^^^^^^^ +LL | _custom = _custom * Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:206:13 + --> $DIR/arithmetic_side_effects.rs:317:15 | -LL | let _ = Custom / 0; - | ^^^^^^^^^^ +LL | _custom = _custom * &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:207:13 + --> $DIR/arithmetic_side_effects.rs:318:15 | -LL | let _ = Custom / 1; - | ^^^^^^^^^^ +LL | _custom = Custom * _custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:208:13 + --> $DIR/arithmetic_side_effects.rs:319:15 | -LL | let _ = Custom / 2; - | ^^^^^^^^^^ +LL | _custom = &Custom * _custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:209:13 + --> $DIR/arithmetic_side_effects.rs:320:15 | -LL | let _ = Custom / 0.0; - | ^^^^^^^^^^^^ +LL | _custom = Custom + &Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:210:13 + --> $DIR/arithmetic_side_effects.rs:321:15 | -LL | let _ = Custom / 1.0; - | ^^^^^^^^^^^^ +LL | _custom = &Custom + Custom; + | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:211:13 + --> $DIR/arithmetic_side_effects.rs:322:15 | -LL | let _ = Custom / 2.0; - | ^^^^^^^^^^^^ +LL | _custom = &Custom + &Custom; + | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:212:13 + --> $DIR/arithmetic_side_effects.rs:325:10 | -LL | let _ = Custom * 0; - | ^^^^^^^^^^ +LL | _n = -_n; + | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:213:13 + --> $DIR/arithmetic_side_effects.rs:326:10 | -LL | let _ = Custom * 1; - | ^^^^^^^^^^ +LL | _n = -&_n; + | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:214:13 + --> $DIR/arithmetic_side_effects.rs:327:15 | -LL | let _ = Custom * 2; - | ^^^^^^^^^^ +LL | _custom = -_custom; + | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:215:13 + --> $DIR/arithmetic_side_effects.rs:328:15 | -LL | let _ = Custom * 0.0; - | ^^^^^^^^^^^^ +LL | _custom = -&_custom; + | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:216:13 + --> $DIR/arithmetic_side_effects.rs:337:5 | -LL | let _ = Custom * 1.0; - | ^^^^^^^^^^^^ +LL | 1 + i; + | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:217:13 + --> $DIR/arithmetic_side_effects.rs:338:5 | -LL | let _ = Custom * 2.0; - | ^^^^^^^^^^^^ +LL | i * 2; + | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:220:10 + --> $DIR/arithmetic_side_effects.rs:340:5 | -LL | _n = -_n; - | ^^^ +LL | i - 2 + 2 - i; + | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:221:10 + --> $DIR/arithmetic_side_effects.rs:341:5 | -LL | _n = -&_n; - | ^^^^ +LL | -i; + | ^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:342:5 + | +LL | i >> 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:343:5 + | +LL | i << 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:352:5 + | +LL | i += 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:353:5 + | +LL | i -= 1; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:354:5 + | +LL | i *= 2; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:356:5 + | +LL | i /= 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:358:5 + | +LL | i /= var1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:359:5 + | +LL | i /= var2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:361:5 + | +LL | i %= 0; + | ^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:363:5 + | +LL | i %= var1; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:364:5 + | +LL | i %= var2; + | ^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:365:5 + | +LL | i <<= 3; + | ^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> $DIR/arithmetic_side_effects.rs:366:5 + | +LL | i >>= 2; + | ^^^^^^^ -error: aborting due to 58 previous errors +error: aborting due to 99 previous errors diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index b0c4215e7ddf..22ce1c6f6471 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -2,14 +2,13 @@ error: an async construct yields a type which is itself awaitable --> $DIR/async_yields_async.rs:39:9 | LL | let _h = async { - | ____________________- -LL | | async { - | |_________^ + | _____________________- +LL | |/ async { LL | || 3 LL | || } | ||_________^ awaitable value not awaited -LL | | }; - | |_____- outer async construct +LL | | }; + | |______- outer async construct | = note: `-D clippy::async-yields-async` implied by `-D warnings` help: consider awaiting this value @@ -36,14 +35,13 @@ error: an async construct yields a type which is itself awaitable --> $DIR/async_yields_async.rs:50:9 | LL | let _j = async || { - | _______________________- -LL | | async { - | |_________^ + | ________________________- +LL | |/ async { LL | || 3 LL | || } | ||_________^ awaitable value not awaited -LL | | }; - | |_____- outer async construct +LL | | }; + | |______- outer async construct | help: consider awaiting this value | diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout index 9de0550d81d0..c6acf24c21ec 100644 --- a/tests/ui/author/blocks.stdout +++ b/tests/ui/author/blocks.stdout @@ -45,7 +45,7 @@ if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kin && expr1 = &cx.tcx.hir().body(body_id).value && let ExprKind::Call(func, args) = expr1.kind && let ExprKind::Path(ref qpath) = func.kind - && matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _)) + && matches!(qpath, QPath::LangItem(LangItem::IdentityFuture, _)) && args.len() == 1 && let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind && let FnRetTy::DefaultReturn(_) = fn_decl1.output diff --git a/tests/ui/auxiliary/doc_unsafe_macros.rs b/tests/ui/auxiliary/doc_unsafe_macros.rs index 869672d1eda5..3d917e3dc75e 100644 --- a/tests/ui/auxiliary/doc_unsafe_macros.rs +++ b/tests/ui/auxiliary/doc_unsafe_macros.rs @@ -6,3 +6,11 @@ macro_rules! undocd_unsafe { } }; } +#[macro_export] +macro_rules! undocd_safe { + () => { + pub fn vey_oy() { + unimplemented!(); + } + }; +} diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index ef3ca9aea380..a13af5652038 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -142,8 +142,29 @@ macro_rules! equatable_if_let { } #[macro_export] -macro_rules! almost_complete_letter_range { +macro_rules! almost_complete_range { () => { let _ = 'a'..'z'; + let _ = 'A'..'Z'; + let _ = '0'..'9'; + }; +} + +#[macro_export] +macro_rules! unsafe_macro { + () => { + unsafe { + *core::ptr::null::<()>(); + *core::ptr::null::<()>(); + } + }; +} + +#[macro_export] +macro_rules! needless_lifetime { + () => { + fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } }; } diff --git a/tests/ui/blanket_clippy_restriction_lints.rs b/tests/ui/blanket_clippy_restriction_lints.rs index d055f17526b9..554745368bca 100644 --- a/tests/ui/blanket_clippy_restriction_lints.rs +++ b/tests/ui/blanket_clippy_restriction_lints.rs @@ -1,3 +1,5 @@ +// compile-flags: -W clippy::restriction + #![warn(clippy::blanket_clippy_restriction_lints)] //! Test that the whole restriction group is not enabled diff --git a/tests/ui/blanket_clippy_restriction_lints.stderr b/tests/ui/blanket_clippy_restriction_lints.stderr index e83eb4d605aa..2bf89ab69a40 100644 --- a/tests/ui/blanket_clippy_restriction_lints.stderr +++ b/tests/ui/blanket_clippy_restriction_lints.stderr @@ -1,27 +1,32 @@ -error: restriction lints are not meant to be all enabled - --> $DIR/blanket_clippy_restriction_lints.rs:4:9 +error: `clippy::restriction` is not meant to be enabled as a group + | + = note: because of the command line `--warn clippy::restriction` + = help: enable the restriction lints you need individually + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + +error: `clippy::restriction` is not meant to be enabled as a group + --> $DIR/blanket_clippy_restriction_lints.rs:6:9 | LL | #![warn(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | - = help: try enabling only the lints you really need - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: enable the restriction lints you need individually -error: restriction lints are not meant to be all enabled - --> $DIR/blanket_clippy_restriction_lints.rs:5:9 +error: `clippy::restriction` is not meant to be enabled as a group + --> $DIR/blanket_clippy_restriction_lints.rs:7:9 | LL | #![deny(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | - = help: try enabling only the lints you really need + = help: enable the restriction lints you need individually -error: restriction lints are not meant to be all enabled - --> $DIR/blanket_clippy_restriction_lints.rs:6:11 +error: `clippy::restriction` is not meant to be enabled as a group + --> $DIR/blanket_clippy_restriction_lints.rs:8:11 | LL | #![forbid(clippy::restriction)] | ^^^^^^^^^^^^^^^^^^^ | - = help: try enabling only the lints you really need + = help: enable the restriction lints you need individually -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/bool_assert_comparison.fixed b/tests/ui/bool_assert_comparison.fixed new file mode 100644 index 000000000000..95f35a61bb28 --- /dev/null +++ b/tests/ui/bool_assert_comparison.fixed @@ -0,0 +1,161 @@ +// run-rustfix + +#![allow(unused, clippy::assertions_on_constants)] +#![warn(clippy::bool_assert_comparison)] + +use std::ops::Not; + +macro_rules! a { + () => { + true + }; +} +macro_rules! b { + () => { + true + }; +} + +// Implements the Not trait but with an output type +// that's not bool. Should not suggest a rewrite +#[derive(Debug, Clone, Copy)] +enum ImplNotTraitWithoutBool { + VariantX(bool), + VariantY(u32), +} + +impl PartialEq for ImplNotTraitWithoutBool { + fn eq(&self, other: &bool) -> bool { + match *self { + ImplNotTraitWithoutBool::VariantX(b) => b == *other, + _ => false, + } + } +} + +impl Not for ImplNotTraitWithoutBool { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b), + ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1), + ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0), + } + } +} + +// This type implements the Not trait with an Output of +// type bool. Using assert!(..) must be suggested +#[derive(Debug, Clone, Copy)] +struct ImplNotTraitWithBool; + +impl PartialEq for ImplNotTraitWithBool { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for ImplNotTraitWithBool { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + +#[derive(Debug)] +struct NonCopy; + +impl PartialEq for NonCopy { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for NonCopy { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + +fn main() { + let a = ImplNotTraitWithoutBool::VariantX(true); + let b = ImplNotTraitWithBool; + + assert_eq!("a".len(), 1); + assert!("a".is_empty()); + assert!("".is_empty()); + assert!("".is_empty()); + assert_eq!(a!(), b!()); + assert_eq!(a!(), "".is_empty()); + assert_eq!("".is_empty(), b!()); + assert_eq!(a, true); + assert!(b); + + assert_ne!("a".len(), 1); + assert!("a".is_empty()); + assert!("".is_empty()); + assert!("".is_empty()); + assert_ne!(a!(), b!()); + assert_ne!(a!(), "".is_empty()); + assert_ne!("".is_empty(), b!()); + assert_ne!(a, true); + assert!(b); + + debug_assert_eq!("a".len(), 1); + debug_assert!("a".is_empty()); + debug_assert!("".is_empty()); + debug_assert!("".is_empty()); + debug_assert_eq!(a!(), b!()); + debug_assert_eq!(a!(), "".is_empty()); + debug_assert_eq!("".is_empty(), b!()); + debug_assert_eq!(a, true); + debug_assert!(b); + + debug_assert_ne!("a".len(), 1); + debug_assert!("a".is_empty()); + debug_assert!("".is_empty()); + debug_assert!("".is_empty()); + debug_assert_ne!(a!(), b!()); + debug_assert_ne!(a!(), "".is_empty()); + debug_assert_ne!("".is_empty(), b!()); + debug_assert_ne!(a, true); + debug_assert!(b); + + // assert with error messages + assert_eq!("a".len(), 1, "tadam {}", 1); + assert_eq!("a".len(), 1, "tadam {}", true); + assert!("a".is_empty(), "tadam {}", 1); + assert!("a".is_empty(), "tadam {}", true); + assert!("a".is_empty(), "tadam {}", true); + assert_eq!(a, true, "tadam {}", false); + + debug_assert_eq!("a".len(), 1, "tadam {}", 1); + debug_assert_eq!("a".len(), 1, "tadam {}", true); + debug_assert!("a".is_empty(), "tadam {}", 1); + debug_assert!("a".is_empty(), "tadam {}", true); + debug_assert!("a".is_empty(), "tadam {}", true); + debug_assert_eq!(a, true, "tadam {}", false); + + assert!(a!()); + assert!(b!()); + + use debug_assert_eq as renamed; + renamed!(a, true); + debug_assert!(b); + + let non_copy = NonCopy; + assert_eq!(non_copy, true); + // changing the above to `assert!(non_copy)` would cause a `borrow of moved value` + println!("{non_copy:?}"); + + macro_rules! in_macro { + ($v:expr) => {{ + assert_eq!($v, true); + }}; + } + in_macro!(a); +} diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index ec4d6f3ff840..88e7560b4f98 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -1,3 +1,6 @@ +// run-rustfix + +#![allow(unused, clippy::assertions_on_constants)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; @@ -15,7 +18,7 @@ macro_rules! b { // Implements the Not trait but with an output type // that's not bool. Should not suggest a rewrite -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum ImplNotTraitWithoutBool { VariantX(bool), VariantY(u32), @@ -44,7 +47,7 @@ impl Not for ImplNotTraitWithoutBool { // This type implements the Not trait with an Output of // type bool. Using assert!(..) must be suggested -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] struct ImplNotTraitWithBool; impl PartialEq for ImplNotTraitWithBool { @@ -61,6 +64,23 @@ impl Not for ImplNotTraitWithBool { } } +#[derive(Debug)] +struct NonCopy; + +impl PartialEq for NonCopy { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for NonCopy { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + fn main() { let a = ImplNotTraitWithoutBool::VariantX(true); let b = ImplNotTraitWithBool; @@ -119,4 +139,23 @@ fn main() { debug_assert_eq!("a".is_empty(), false, "tadam {}", true); debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); debug_assert_eq!(a, true, "tadam {}", false); + + assert_eq!(a!(), true); + assert_eq!(true, b!()); + + use debug_assert_eq as renamed; + renamed!(a, true); + renamed!(b, true); + + let non_copy = NonCopy; + assert_eq!(non_copy, true); + // changing the above to `assert!(non_copy)` would cause a `borrow of moved value` + println!("{non_copy:?}"); + + macro_rules! in_macro { + ($v:expr) => {{ + assert_eq!($v, true); + }}; + } + in_macro!(a); } diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index 377d51be4cde..3d9f8573e617 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -1,136 +1,303 @@ error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:69:5 + --> $DIR/bool_assert_comparison.rs:89:5 | LL | assert_eq!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` +help: replace it with `assert!(..)` + | +LL - assert_eq!("a".is_empty(), false); +LL + assert!("a".is_empty()); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:70:5 + --> $DIR/bool_assert_comparison.rs:90:5 | LL | assert_eq!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("".is_empty(), true); +LL + assert!("".is_empty()); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:71:5 + --> $DIR/bool_assert_comparison.rs:91:5 | LL | assert_eq!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(true, "".is_empty()); +LL + assert!("".is_empty()); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:76:5 + --> $DIR/bool_assert_comparison.rs:96:5 | LL | assert_eq!(b, true); - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(b, true); +LL + assert!(b); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:79:5 + --> $DIR/bool_assert_comparison.rs:99:5 | LL | assert_ne!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("a".is_empty(), false); +LL + assert!("a".is_empty()); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:80:5 + --> $DIR/bool_assert_comparison.rs:100:5 | LL | assert_ne!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("".is_empty(), true); +LL + assert!("".is_empty()); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:81:5 + --> $DIR/bool_assert_comparison.rs:101:5 | LL | assert_ne!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!(true, "".is_empty()); +LL + assert!("".is_empty()); + | error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:86:5 + --> $DIR/bool_assert_comparison.rs:106:5 | LL | assert_ne!(b, true); - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!(b, true); +LL + assert!(b); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:89:5 + --> $DIR/bool_assert_comparison.rs:109:5 | LL | debug_assert_eq!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("a".is_empty(), false); +LL + debug_assert!("a".is_empty()); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:90:5 + --> $DIR/bool_assert_comparison.rs:110:5 | LL | debug_assert_eq!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("".is_empty(), true); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:91:5 + --> $DIR/bool_assert_comparison.rs:111:5 | LL | debug_assert_eq!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!(true, "".is_empty()); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:96:5 + --> $DIR/bool_assert_comparison.rs:116:5 | LL | debug_assert_eq!(b, true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!(b, true); +LL + debug_assert!(b); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:99:5 + --> $DIR/bool_assert_comparison.rs:119:5 | LL | debug_assert_ne!("a".is_empty(), false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("a".is_empty(), false); +LL + debug_assert!("a".is_empty()); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:100:5 + --> $DIR/bool_assert_comparison.rs:120:5 | LL | debug_assert_ne!("".is_empty(), true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("".is_empty(), true); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:101:5 + --> $DIR/bool_assert_comparison.rs:121:5 | LL | debug_assert_ne!(true, "".is_empty()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!(true, "".is_empty()); +LL + debug_assert!("".is_empty()); + | error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:106:5 + --> $DIR/bool_assert_comparison.rs:126:5 | LL | debug_assert_ne!(b, true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!(b, true); +LL + debug_assert!(b); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:111:5 + --> $DIR/bool_assert_comparison.rs:131:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("a".is_empty(), false, "tadam {}", 1); +LL + assert!("a".is_empty(), "tadam {}", 1); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:112:5 + --> $DIR/bool_assert_comparison.rs:132:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("a".is_empty(), false, "tadam {}", true); +LL + assert!("a".is_empty(), "tadam {}", true); + | error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:113:5 + --> $DIR/bool_assert_comparison.rs:133:5 | LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(false, "a".is_empty(), "tadam {}", true); +LL + assert!("a".is_empty(), "tadam {}", true); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:118:5 + --> $DIR/bool_assert_comparison.rs:138:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); +LL + debug_assert!("a".is_empty(), "tadam {}", 1); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:119:5 + --> $DIR/bool_assert_comparison.rs:139:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true); +LL + debug_assert!("a".is_empty(), "tadam {}", true); + | error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:120:5 + --> $DIR/bool_assert_comparison.rs:140:5 | LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); +LL + debug_assert!("a".is_empty(), "tadam {}", true); + | + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:143:5 + | +LL | assert_eq!(a!(), true); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(a!(), true); +LL + assert!(a!()); + | + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:144:5 + | +LL | assert_eq!(true, b!()); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(true, b!()); +LL + assert!(b!()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:148:5 + | +LL | renamed!(b, true); + | ^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - renamed!(b, true); +LL + debug_assert!(b); + | -error: aborting due to 22 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/bool_to_int_with_if.fixed b/tests/ui/bool_to_int_with_if.fixed index 2c8339cdd7f8..37d3e3286a4b 100644 --- a/tests/ui/bool_to_int_with_if.fixed +++ b/tests/ui/bool_to_int_with_if.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -76,6 +77,8 @@ fn main() { 123 }; + pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 }; + some_fn(a); } @@ -89,3 +92,22 @@ fn side_effect() {} fn cond(a: bool, b: bool) -> bool { a || b } + +enum Enum { + A, + B, +} + +fn if_let(a: Enum, b: Enum) { + if let Enum::A = a { + 1 + } else { + 0 + }; + + if let Enum::A = a && let Enum::B = b { + 1 + } else { + 0 + }; +} diff --git a/tests/ui/bool_to_int_with_if.rs b/tests/ui/bool_to_int_with_if.rs index 5d9496f01775..ebdf86fd1856 100644 --- a/tests/ui/bool_to_int_with_if.rs +++ b/tests/ui/bool_to_int_with_if.rs @@ -1,5 +1,6 @@ // run-rustfix +#![feature(let_chains)] #![warn(clippy::bool_to_int_with_if)] #![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)] @@ -108,6 +109,8 @@ fn main() { 123 }; + pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 }; + some_fn(a); } @@ -121,3 +124,22 @@ fn side_effect() {} fn cond(a: bool, b: bool) -> bool { a || b } + +enum Enum { + A, + B, +} + +fn if_let(a: Enum, b: Enum) { + if let Enum::A = a { + 1 + } else { + 0 + }; + + if let Enum::A = a && let Enum::B = b { + 1 + } else { + 0 + }; +} diff --git a/tests/ui/bool_to_int_with_if.stderr b/tests/ui/bool_to_int_with_if.stderr index 4cb5531bef6a..5cfb75cc0dfc 100644 --- a/tests/ui/bool_to_int_with_if.stderr +++ b/tests/ui/bool_to_int_with_if.stderr @@ -1,5 +1,5 @@ error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:15:5 + --> $DIR/bool_to_int_with_if.rs:16:5 | LL | / if a { LL | | 1 @@ -12,7 +12,7 @@ LL | | }; = note: `-D clippy::bool-to-int-with-if` implied by `-D warnings` error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:20:5 + --> $DIR/bool_to_int_with_if.rs:21:5 | LL | / if a { LL | | 0 @@ -24,7 +24,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:25:5 + --> $DIR/bool_to_int_with_if.rs:26:5 | LL | / if !a { LL | | 1 @@ -36,7 +36,7 @@ LL | | }; = note: `!a as i32` or `(!a).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:30:5 + --> $DIR/bool_to_int_with_if.rs:31:5 | LL | / if a || b { LL | | 1 @@ -48,7 +48,7 @@ LL | | }; = note: `(a || b) as i32` or `(a || b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:35:5 + --> $DIR/bool_to_int_with_if.rs:36:5 | LL | / if cond(a, b) { LL | | 1 @@ -60,7 +60,7 @@ LL | | }; = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:40:5 + --> $DIR/bool_to_int_with_if.rs:41:5 | LL | / if x + y < 4 { LL | | 1 @@ -72,7 +72,7 @@ LL | | }; = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:49:12 + --> $DIR/bool_to_int_with_if.rs:50:12 | LL | } else if b { | ____________^ @@ -85,7 +85,7 @@ LL | | }; = note: `b as i32` or `b.into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:58:12 + --> $DIR/bool_to_int_with_if.rs:59:12 | LL | } else if b { | ____________^ @@ -98,7 +98,7 @@ LL | | }; = note: `!b as i32` or `(!b).into()` can also be valid options error: boolean to int conversion using if - --> $DIR/bool_to_int_with_if.rs:116:5 + --> $DIR/bool_to_int_with_if.rs:119:5 | LL | if a { 1 } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)` diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index eefeb1decb69..7c57864245a9 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -42,7 +42,7 @@ impl StaticRef { impl std::ops::Deref for StaticRef { type Target = T; - fn deref(&self) -> &'static T { + fn deref(&self) -> &T { unsafe { &*self.ptr } } } diff --git a/tests/ui/box_default.fixed b/tests/ui/box_default.fixed index 7fbb272ce5a3..7e9f074fdcab 100644 --- a/tests/ui/box_default.fixed +++ b/tests/ui/box_default.fixed @@ -21,16 +21,16 @@ macro_rules! outer { fn main() { let _string: Box = Box::default(); let _byte = Box::::default(); - let _vec = Box::>::default(); + let _vec = Box::>::default(); let _impl = Box::::default(); let _impl2 = Box::::default(); let _impl3: Box = Box::default(); let _own = Box::new(OwnDefault::default()); // should not lint - let _in_macro = outer!(Box::::default()); - let _string_default = outer!(Box::::default()); + let _in_macro = outer!(Box::::default()); + let _string_default = outer!(Box::::default()); let _vec2: Box> = Box::default(); let _vec3: Box> = Box::default(); - let _vec4: Box<_> = Box::>::default(); + let _vec4: Box<_> = Box::>::default(); let _more = ret_ty_fn(); call_ty_fn(Box::default()); } @@ -40,4 +40,28 @@ fn ret_ty_fn() -> Box { } #[allow(clippy::boxed_local)] -fn call_ty_fn(_b: Box) {} +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::::default(); + issue_10089(); +} + +fn issue_10089() { + let _closure = || { + #[derive(Default)] + struct WeirdPathed; + + let _ = Box::::default(); + }; +} diff --git a/tests/ui/box_default.rs b/tests/ui/box_default.rs index 64c4f3887af7..5c8d0b8354cc 100644 --- a/tests/ui/box_default.rs +++ b/tests/ui/box_default.rs @@ -40,4 +40,28 @@ fn ret_ty_fn() -> Box { } #[allow(clippy::boxed_local)] -fn call_ty_fn(_b: Box) {} +fn call_ty_fn(_b: Box) { + issue_9621_dyn_trait(); +} + +use std::io::{Read, Result}; + +impl Read for ImplementsDefault { + fn read(&mut self, _: &mut [u8]) -> Result { + Ok(0) + } +} + +fn issue_9621_dyn_trait() { + let _: Box = Box::new(ImplementsDefault::default()); + issue_10089(); +} + +fn issue_10089() { + let _closure = || { + #[derive(Default)] + struct WeirdPathed; + + let _ = Box::new(WeirdPathed::default()); + }; +} diff --git a/tests/ui/box_default.stderr b/tests/ui/box_default.stderr index 313255fc950e..249eb340f96c 100644 --- a/tests/ui/box_default.stderr +++ b/tests/ui/box_default.stderr @@ -16,7 +16,7 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:24:16 | LL | let _vec = Box::new(Vec::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:25:17 @@ -40,13 +40,13 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:29:28 | LL | let _in_macro = outer!(Box::new(String::new())); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:30:34 | LL | let _string_default = outer!(Box::new(String::from(""))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:31:46 @@ -64,7 +64,7 @@ error: `Box::new(_)` of default value --> $DIR/box_default.rs:33:25 | LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0])); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::>::default()` error: `Box::new(_)` of default value --> $DIR/box_default.rs:35:16 @@ -78,5 +78,17 @@ error: `Box::new(_)` of default value LL | Box::new(bool::default()) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` -error: aborting due to 13 previous errors +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:56:28 + | +LL | let _: Box = Box::new(ImplementsDefault::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:65:17 + | +LL | let _ = Box::new(WeirdPathed::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::::default()` + +error: aborting due to 15 previous errors diff --git a/tests/ui/box_default_no_std.rs b/tests/ui/box_default_no_std.rs new file mode 100644 index 000000000000..4326abc9a541 --- /dev/null +++ b/tests/ui/box_default_no_std.rs @@ -0,0 +1,33 @@ +#![feature(lang_items, start, libc)] +#![warn(clippy::box_default)] +#![no_std] + +pub struct NotBox { + _value: T, +} + +impl NotBox { + pub fn new(value: T) -> Self { + Self { _value: value } + } +} + +impl Default for NotBox { + fn default() -> Self { + Self::new(T::default()) + } +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _p = NotBox::new(isize::default()); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.fixed b/tests/ui/case_sensitive_file_extension_comparisons.fixed new file mode 100644 index 000000000000..5fbaa64db39e --- /dev/null +++ b/tests/ui/case_sensitive_file_extension_comparisons.fixed @@ -0,0 +1,67 @@ +// run-rustfix +#![warn(clippy::case_sensitive_file_extension_comparisons)] + +use std::string::String; + +struct TestStruct; + +impl TestStruct { + fn ends_with(self, _arg: &str) {} +} + +#[allow(dead_code)] +fn is_rust_file(filename: &str) -> bool { + std::path::Path::new(filename) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) +} + +fn main() { + // std::string::String and &str should trigger the lint failure with .ext12 + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + + // The fixup should preserve the indentation level + { + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + } + + // The test struct should not trigger the lint failure with .ext12 + TestStruct {}.ends_with(".ext12"); + + // std::string::String and &str should trigger the lint failure with .EXT12 + let _ = std::path::Path::new(&String::new()) + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + let _ = std::path::Path::new("str") + .extension() + .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + + // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase + let _ = String::new().to_lowercase().ends_with(".EXT12"); + let _ = String::new().to_uppercase().ends_with(".EXT12"); + + // The test struct should not trigger the lint failure with .EXT12 + TestStruct {}.ends_with(".EXT12"); + + // Should not trigger the lint failure with .eXT12 + let _ = String::new().ends_with(".eXT12"); + let _ = "str".ends_with(".eXT12"); + TestStruct {}.ends_with(".eXT12"); + + // Should not trigger the lint failure with .EXT123 (too long) + let _ = String::new().ends_with(".EXT123"); + let _ = "str".ends_with(".EXT123"); + TestStruct {}.ends_with(".EXT123"); + + // Shouldn't fail if it doesn't start with a dot + let _ = String::new().ends_with("a.ext"); + let _ = "str".ends_with("a.extA"); + TestStruct {}.ends_with("a.ext"); +} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs index 6f0485b5279b..3c0d4821f9f3 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::case_sensitive_file_extension_comparisons)] use std::string::String; @@ -5,9 +6,10 @@ use std::string::String; struct TestStruct; impl TestStruct { - fn ends_with(self, arg: &str) {} + fn ends_with(self, _arg: &str) {} } +#[allow(dead_code)] fn is_rust_file(filename: &str) -> bool { filename.ends_with(".rs") } @@ -17,6 +19,11 @@ fn main() { let _ = String::new().ends_with(".ext12"); let _ = "str".ends_with(".ext12"); + // The fixup should preserve the indentation level + { + let _ = "str".ends_with(".ext12"); + } + // The test struct should not trigger the lint failure with .ext12 TestStruct {}.ends_with(".ext12"); @@ -24,6 +31,10 @@ fn main() { let _ = String::new().ends_with(".EXT12"); let _ = "str".ends_with(".EXT12"); + // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase + let _ = String::new().to_lowercase().ends_with(".EXT12"); + let _ = String::new().to_uppercase().ends_with(".EXT12"); + // The test struct should not trigger the lint failure with .EXT12 TestStruct {}.ends_with(".EXT12"); diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr index a28dd8bd5ad3..44c8e3fdf740 100644 --- a/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -1,43 +1,87 @@ error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14 + --> $DIR/case_sensitive_file_extension_comparisons.rs:14:5 | LL | filename.ends_with(".rs") - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead = note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings` +help: use std::path::Path + | +LL ~ std::path::Path::new(filename) +LL + .extension() +LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27 + --> $DIR/case_sensitive_file_extension_comparisons.rs:19:13 | LL | let _ = String::new().ends_with(".ext12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19 + --> $DIR/case_sensitive_file_extension_comparisons.rs:20:13 | LL | let _ = "str".ends_with(".ext12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27 + --> $DIR/case_sensitive_file_extension_comparisons.rs:24:17 + | +LL | let _ = "str".ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12")); + | + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:31:13 | LL | let _ = String::new().ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new(&String::new()) +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19 + --> $DIR/case_sensitive_file_extension_comparisons.rs:32:13 | LL | let _ = "str".ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead +help: use std::path::Path + | +LL ~ let _ = std::path::Path::new("str") +LL + .extension() +LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12")); + | -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index e6031e9adaeb..8b2673c2a7fd 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -28,6 +28,7 @@ fn main() { 1i32 as u8; 1f64 as isize; 1f64 as usize; + 1f32 as u32 as u16; // Test clippy::cast_possible_wrap 1u8 as i8; 1u16 as i16; diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 0c63b4af3086..4af1de9aa38d 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -42,13 +42,24 @@ error: casting `f32` to `i32` may truncate the value LL | 1f32 as i32; | ^^^^^^^^^^^ | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` +help: ... or use `try_from` and handle the error accordingly + | +LL | i32::try_from(1f32); + | ~~~~~~~~~~~~~~~~~~~ error: casting `f32` to `u32` may truncate the value --> $DIR/cast.rs:25:5 | LL | 1f32 as u32; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u32::try_from(1f32); + | ~~~~~~~~~~~~~~~~~~~ error: casting `f32` to `u32` may lose the sign of the value --> $DIR/cast.rs:25:5 @@ -63,30 +74,60 @@ error: casting `f64` to `f32` may truncate the value | LL | 1f64 as f32; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | f32::try_from(1f64); + | ~~~~~~~~~~~~~~~~~~~ error: casting `i32` to `i8` may truncate the value --> $DIR/cast.rs:27:5 | LL | 1i32 as i8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | i8::try_from(1i32); + | ~~~~~~~~~~~~~~~~~~ error: casting `i32` to `u8` may truncate the value --> $DIR/cast.rs:28:5 | LL | 1i32 as u8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u8::try_from(1i32); + | ~~~~~~~~~~~~~~~~~~ error: casting `f64` to `isize` may truncate the value --> $DIR/cast.rs:29:5 | LL | 1f64 as isize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | isize::try_from(1f64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `f64` to `usize` may truncate the value --> $DIR/cast.rs:30:5 | LL | 1f64 as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | usize::try_from(1f64); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `f64` to `usize` may lose the sign of the value --> $DIR/cast.rs:30:5 @@ -94,8 +135,38 @@ error: casting `f64` to `usize` may lose the sign of the value LL | 1f64 as usize; | ^^^^^^^^^^^^^ +error: casting `u32` to `u16` may truncate the value + --> $DIR/cast.rs:31:5 + | +LL | 1f32 as u32 as u16; + | ^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u16::try_from(1f32 as u32); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: casting `f32` to `u32` may truncate the value + --> $DIR/cast.rs:31:5 + | +LL | 1f32 as u32 as u16; + | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u32::try_from(1f32) as u16; + | ~~~~~~~~~~~~~~~~~~~ + +error: casting `f32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:31:5 + | +LL | 1f32 as u32 as u16; + | ^^^^^^^^^^^ + error: casting `u8` to `i8` may wrap around the value - --> $DIR/cast.rs:32:5 + --> $DIR/cast.rs:33:5 | LL | 1u8 as i8; | ^^^^^^^^^ @@ -103,61 +174,79 @@ LL | 1u8 as i8; = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` error: casting `u16` to `i16` may wrap around the value - --> $DIR/cast.rs:33:5 + --> $DIR/cast.rs:34:5 | LL | 1u16 as i16; | ^^^^^^^^^^^ error: casting `u32` to `i32` may wrap around the value - --> $DIR/cast.rs:34:5 + --> $DIR/cast.rs:35:5 | LL | 1u32 as i32; | ^^^^^^^^^^^ error: casting `u64` to `i64` may wrap around the value - --> $DIR/cast.rs:35:5 + --> $DIR/cast.rs:36:5 | LL | 1u64 as i64; | ^^^^^^^^^^^ error: casting `usize` to `isize` may wrap around the value - --> $DIR/cast.rs:36:5 + --> $DIR/cast.rs:37:5 | LL | 1usize as isize; | ^^^^^^^^^^^^^^^ error: casting `i32` to `u32` may lose the sign of the value - --> $DIR/cast.rs:39:5 + --> $DIR/cast.rs:40:5 | LL | -1i32 as u32; | ^^^^^^^^^^^^ error: casting `isize` to `usize` may lose the sign of the value - --> $DIR/cast.rs:41:5 + --> $DIR/cast.rs:42:5 | LL | -1isize as usize; | ^^^^^^^^^^^^^^^^ error: casting `i64` to `i8` may truncate the value - --> $DIR/cast.rs:108:5 + --> $DIR/cast.rs:109:5 | LL | (-99999999999i64).min(1) as i8; // should be linted because signed | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | i8::try_from((-99999999999i64).min(1)); // should be linted because signed + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u64` to `u8` may truncate the value - --> $DIR/cast.rs:120:5 + --> $DIR/cast.rs:121:5 | LL | 999999u64.clamp(0, 256) as u8; // should still be linted | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E2` to `u8` may truncate the value - --> $DIR/cast.rs:141:21 + --> $DIR/cast.rs:142:21 | LL | let _ = self as u8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = u8::try_from(self); + | ~~~~~~~~~~~~~~~~~~ error: casting `main::E2::B` to `u8` will truncate the value - --> $DIR/cast.rs:142:21 + --> $DIR/cast.rs:143:21 | LL | let _ = Self::B as u8; | ^^^^^^^^^^^^^ @@ -165,46 +254,82 @@ LL | let _ = Self::B as u8; = note: `-D clippy::cast-enum-truncation` implied by `-D warnings` error: casting `main::E5` to `i8` may truncate the value - --> $DIR/cast.rs:178:21 + --> $DIR/cast.rs:179:21 | LL | let _ = self as i8; | ^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = i8::try_from(self); + | ~~~~~~~~~~~~~~~~~~ error: casting `main::E5::A` to `i8` will truncate the value - --> $DIR/cast.rs:179:21 + --> $DIR/cast.rs:180:21 | LL | let _ = Self::A as i8; | ^^^^^^^^^^^^^ error: casting `main::E6` to `i16` may truncate the value - --> $DIR/cast.rs:193:21 + --> $DIR/cast.rs:194:21 | LL | let _ = self as i16; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = i16::try_from(self); + | ~~~~~~~~~~~~~~~~~~~ error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers - --> $DIR/cast.rs:208:21 + --> $DIR/cast.rs:209:21 | LL | let _ = self as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = usize::try_from(self); + | ~~~~~~~~~~~~~~~~~~~~~ error: casting `main::E10` to `u16` may truncate the value - --> $DIR/cast.rs:249:21 + --> $DIR/cast.rs:250:21 | LL | let _ = self as u16; | ^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let _ = u16::try_from(self); + | ~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> $DIR/cast.rs:257:13 + --> $DIR/cast.rs:258:13 | LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let c = u8::try_from((q >> 16)); + | ~~~~~~~~~~~~~~~~~~~~~~~ error: casting `u32` to `u8` may truncate the value - --> $DIR/cast.rs:260:13 + --> $DIR/cast.rs:261:13 | LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +LL | let c = u8::try_from((q / 1000)); + | ~~~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 33 previous errors +error: aborting due to 36 previous errors diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index a37f3fec20f1..8676b562b4f9 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -1,6 +1,7 @@ // run-rustfix + #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +31,15 @@ fn main() { let _ = (x as i64 - y as i64).unsigned_abs() as u32; } + +#[clippy::msrv = "1.50"] +fn msrv_1_50() { + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +#[clippy::msrv = "1.51"] +fn msrv_1_51() { + let x: i32 = 10; + assert_eq!(10u32, x.unsigned_abs()); +} diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 5706930af5a0..5775af874f8f 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -1,6 +1,7 @@ // run-rustfix + #![warn(clippy::cast_abs_to_unsigned)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, unused)] fn main() { let x: i32 = -42; @@ -30,3 +31,15 @@ fn main() { let _ = (x as i64 - y as i64).abs() as u32; } + +#[clippy::msrv = "1.50"] +fn msrv_1_50() { + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} + +#[clippy::msrv = "1.51"] +fn msrv_1_51() { + let x: i32 = 10; + assert_eq!(10u32, x.abs() as u32); +} diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index 7cea11c183d2..4668554f4511 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -1,5 +1,5 @@ error: casting the result of `i32::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:7:18 + --> $DIR/cast_abs_to_unsigned.rs:8:18 | LL | let y: u32 = x.abs() as u32; | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` @@ -7,100 +7,106 @@ LL | let y: u32 = x.abs() as u32; = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:11:20 + --> $DIR/cast_abs_to_unsigned.rs:12:20 | LL | let _: usize = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:12:20 + --> $DIR/cast_abs_to_unsigned.rs:13:20 | LL | let _: usize = a.abs() as _; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i32::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:13:13 + --> $DIR/cast_abs_to_unsigned.rs:14:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:16:13 + --> $DIR/cast_abs_to_unsigned.rs:17:13 | LL | let _ = a.abs() as usize; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:17:13 + --> $DIR/cast_abs_to_unsigned.rs:18:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:18:13 + --> $DIR/cast_abs_to_unsigned.rs:19:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:19:13 + --> $DIR/cast_abs_to_unsigned.rs:20:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:20:13 + --> $DIR/cast_abs_to_unsigned.rs:21:13 | LL | let _ = a.abs() as u64; | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:21:13 + --> $DIR/cast_abs_to_unsigned.rs:22:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to usize - --> $DIR/cast_abs_to_unsigned.rs:24:13 + --> $DIR/cast_abs_to_unsigned.rs:25:13 | LL | let _ = a.abs() as usize; | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u8 - --> $DIR/cast_abs_to_unsigned.rs:25:13 + --> $DIR/cast_abs_to_unsigned.rs:26:13 | LL | let _ = a.abs() as u8; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u16 - --> $DIR/cast_abs_to_unsigned.rs:26:13 + --> $DIR/cast_abs_to_unsigned.rs:27:13 | LL | let _ = a.abs() as u16; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:27:13 + --> $DIR/cast_abs_to_unsigned.rs:28:13 | LL | let _ = a.abs() as u32; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u64 - --> $DIR/cast_abs_to_unsigned.rs:28:13 + --> $DIR/cast_abs_to_unsigned.rs:29:13 | LL | let _ = a.abs() as u64; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `isize::abs()` to u128 - --> $DIR/cast_abs_to_unsigned.rs:29:13 + --> $DIR/cast_abs_to_unsigned.rs:30:13 | LL | let _ = a.abs() as u128; | ^^^^^^^ help: replace with: `a.unsigned_abs()` error: casting the result of `i64::abs()` to u32 - --> $DIR/cast_abs_to_unsigned.rs:31:13 + --> $DIR/cast_abs_to_unsigned.rs:32:13 | LL | let _ = (x as i64 - y as i64).abs() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(x as i64 - y as i64).unsigned_abs()` -error: aborting due to 17 previous errors +error: casting the result of `i32::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:44:23 + | +LL | assert_eq!(10u32, x.abs() as u32); + | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/cast_lossless_bool.fixed b/tests/ui/cast_lossless_bool.fixed index 9e2da45c3785..13b3cf838c9f 100644 --- a/tests/ui/cast_lossless_bool.fixed +++ b/tests/ui/cast_lossless_bool.fixed @@ -40,3 +40,13 @@ mod cast_lossless_in_impl { } } } + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = true as u8; +} + +#[clippy::msrv = "1.28"] +fn msrv_1_28() { + let _ = u8::from(true); +} diff --git a/tests/ui/cast_lossless_bool.rs b/tests/ui/cast_lossless_bool.rs index b6f6c59a01f9..3eed2135562c 100644 --- a/tests/ui/cast_lossless_bool.rs +++ b/tests/ui/cast_lossless_bool.rs @@ -40,3 +40,13 @@ mod cast_lossless_in_impl { } } } + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = true as u8; +} + +#[clippy::msrv = "1.28"] +fn msrv_1_28() { + let _ = true as u8; +} diff --git a/tests/ui/cast_lossless_bool.stderr b/tests/ui/cast_lossless_bool.stderr index 6b148336011d..ce240b70f891 100644 --- a/tests/ui/cast_lossless_bool.stderr +++ b/tests/ui/cast_lossless_bool.stderr @@ -78,5 +78,11 @@ error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` -error: aborting due to 13 previous errors +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` + --> $DIR/cast_lossless_bool.rs:51:13 + | +LL | let _ = true as u8; + | ^^^^^^^^^^ help: try: `u8::from(true)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/cast_lossless_integer.fixed b/tests/ui/cast_lossless_integer.fixed index 72a708b40737..925cbf25368f 100644 --- a/tests/ui/cast_lossless_integer.fixed +++ b/tests/ui/cast_lossless_integer.fixed @@ -45,3 +45,9 @@ mod cast_lossless_in_impl { } } } + +#[derive(PartialEq, Debug)] +#[repr(i64)] +enum Test { + A = u32::MAX as i64 + 1, +} diff --git a/tests/ui/cast_lossless_integer.rs b/tests/ui/cast_lossless_integer.rs index 34bb47181e69..c82bd9108d23 100644 --- a/tests/ui/cast_lossless_integer.rs +++ b/tests/ui/cast_lossless_integer.rs @@ -45,3 +45,9 @@ mod cast_lossless_in_impl { } } } + +#[derive(PartialEq, Debug)] +#[repr(i64)] +enum Test { + A = u32::MAX as i64 + 1, +} diff --git a/tests/ui/cast_size.stderr b/tests/ui/cast_size.stderr index 95552f2e2853..8acf26049f4d 100644 --- a/tests/ui/cast_size.stderr +++ b/tests/ui/cast_size.stderr @@ -4,7 +4,12 @@ error: casting `isize` to `i8` may truncate the value LL | 1isize as i8; | ^^^^^^^^^^^^ | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` +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.rs:15:5 @@ -37,24 +42,48 @@ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wi | LL | 1isize as i32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:20:5 | LL | 1isize as u32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:21:5 | LL | 1usize as u32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:22:5 | LL | 1usize as i32; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:22:5 @@ -69,18 +98,36 @@ error: casting `i64` to `isize` may truncate the value on targets with 32-bit wi | LL | 1i64 as isize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:25:5 | LL | 1i64 as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:26:5 | LL | 1u64 as isize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:26:5 @@ -93,6 +140,12 @@ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wi | LL | 1u64 as usize; | ^^^^^^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... +help: ... or use `try_from` and handle the error accordingly + | +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:28:5 diff --git a/tests/ui/cfg_attr_rustfmt.fixed b/tests/ui/cfg_attr_rustfmt.fixed index 061a4ab9b2ef..b970b1209b65 100644 --- a/tests/ui/cfg_attr_rustfmt.fixed +++ b/tests/ui/cfg_attr_rustfmt.fixed @@ -29,3 +29,15 @@ mod foo { pub fn f() {} } + +#[clippy::msrv = "1.29"] +fn msrv_1_29() { + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +#[clippy::msrv = "1.30"] +fn msrv_1_30() { + #[rustfmt::skip] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.rs b/tests/ui/cfg_attr_rustfmt.rs index 035169fab85b..0a8e6a89d8a0 100644 --- a/tests/ui/cfg_attr_rustfmt.rs +++ b/tests/ui/cfg_attr_rustfmt.rs @@ -29,3 +29,15 @@ mod foo { pub fn f() {} } + +#[clippy::msrv = "1.29"] +fn msrv_1_29() { + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+29; +} + +#[clippy::msrv = "1.30"] +fn msrv_1_30() { + #[cfg_attr(rustfmt, rustfmt::skip)] + 1+30; +} diff --git a/tests/ui/cfg_attr_rustfmt.stderr b/tests/ui/cfg_attr_rustfmt.stderr index c1efd47db90b..524a2bf72484 100644 --- a/tests/ui/cfg_attr_rustfmt.stderr +++ b/tests/ui/cfg_attr_rustfmt.stderr @@ -12,5 +12,11 @@ error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes LL | #[cfg_attr(rustfmt, rustfmt_skip)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` -error: aborting due to 2 previous errors +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:41:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 3 previous errors diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index cb7100bc9efa..e279ba3147bb 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -2,6 +2,7 @@ #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +77,16 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +#[clippy::msrv = "1.34"] +fn msrv_1_34() { + let value: i64 = 34; + let _ = u32::try_from(value).is_ok(); +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index ed4e0692388a..9d7a40995c37 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -2,6 +2,7 @@ #![allow( clippy::cast_lossless, + unused, // Int::max_value will be deprecated in the future deprecated, )] @@ -76,4 +77,16 @@ pub const fn issue_8898(i: u32) -> bool { i <= i32::MAX as u32 } +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + let value: i64 = 33; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +#[clippy::msrv = "1.34"] +fn msrv_1_34() { + let value: i64 = 34; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 2e518040561c..273ead73bda5 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:15:13 + --> $DIR/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,94 +7,100 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:16:13 + --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:20:13 + --> $DIR/checked_conversions.rs:21:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:21:13 + --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:25:13 + --> $DIR/checked_conversions.rs:26:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:26:13 + --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:32:13 + --> $DIR/checked_conversions.rs:33:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:33:13 + --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:37:13 + --> $DIR/checked_conversions.rs:38:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:38:13 + --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:44:13 + --> $DIR/checked_conversions.rs:45:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:45:13 + --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:49:13 + --> $DIR/checked_conversions.rs:50:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:50:13 + --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:54:13 + --> $DIR/checked_conversions.rs:55:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:55:13 + --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: aborting due to 16 previous errors +error: checked cast can be simplified + --> $DIR/checked_conversions.rs:89:13 + | +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + +error: aborting due to 17 previous errors diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 42ae227777c7..862234d204be 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -48,7 +48,7 @@ error: using `clone` on type `i32` which implements the `Copy` trait LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` -error: using `clone` on type `std::option::Option` which implements the `Copy` trait +error: using `clone` on type `Option` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:77:17 | LL | let value = opt.clone()?; // operator precedence needed (*opt)? diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed index 4eb999e18e64..ecbfc1feedf6 100644 --- a/tests/ui/cloned_instead_of_copied.fixed +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -1,5 +1,7 @@ // run-rustfix + #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +15,21 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +#[clippy::msrv = "1.34"] +fn msrv_1_34() { + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +#[clippy::msrv = "1.35"] +fn msrv_1_35() { + let _ = [1].iter().cloned(); + let _ = Some(&1).copied(); // Option::copied needs 1.35 +} + +#[clippy::msrv = "1.36"] +fn msrv_1_36() { + let _ = [1].iter().copied(); // Iterator::copied needs 1.36 + let _ = Some(&1).copied(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs index 894496c0ebbb..163dc3dddce6 100644 --- a/tests/ui/cloned_instead_of_copied.rs +++ b/tests/ui/cloned_instead_of_copied.rs @@ -1,5 +1,7 @@ // run-rustfix + #![warn(clippy::cloned_instead_of_copied)] +#![allow(unused)] fn main() { // yay @@ -13,3 +15,21 @@ fn main() { let _ = [String::new()].iter().cloned(); let _ = Some(&String::new()).cloned(); } + +#[clippy::msrv = "1.34"] +fn msrv_1_34() { + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); +} + +#[clippy::msrv = "1.35"] +fn msrv_1_35() { + let _ = [1].iter().cloned(); + let _ = Some(&1).cloned(); // Option::copied needs 1.35 +} + +#[clippy::msrv = "1.36"] +fn msrv_1_36() { + let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + let _ = Some(&1).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr index e0707d321468..e0361acd9560 100644 --- a/tests/ui/cloned_instead_of_copied.stderr +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -1,5 +1,5 @@ error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:6:24 + --> $DIR/cloned_instead_of_copied.rs:8:24 | LL | let _ = [1].iter().cloned(); | ^^^^^^ help: try: `copied` @@ -7,28 +7,46 @@ LL | let _ = [1].iter().cloned(); = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:7:31 + --> $DIR/cloned_instead_of_copied.rs:9:31 | LL | let _ = vec!["hi"].iter().cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:8:22 + --> $DIR/cloned_instead_of_copied.rs:10:22 | LL | let _ = Some(&1).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:9:34 + --> $DIR/cloned_instead_of_copied.rs:11:34 | LL | let _ = Box::new([1].iter()).cloned(); | ^^^^^^ help: try: `copied` error: used `cloned` where `copied` could be used instead - --> $DIR/cloned_instead_of_copied.rs:10:32 + --> $DIR/cloned_instead_of_copied.rs:12:32 | LL | let _ = Box::new(Some(&1)).cloned(); | ^^^^^^ help: try: `copied` -error: aborting due to 5 previous errors +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:28:22 + | +LL | let _ = Some(&1).cloned(); // Option::copied needs 1.35 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:33:24 + | +LL | let _ = [1].iter().cloned(); // Iterator::copied needs 1.36 + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:34:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 8 previous errors diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 912e6788afdd..07bdaff00dc4 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -393,3 +393,19 @@ impl Moo { } } } + +#[clippy::cognitive_complexity = "1"] +mod issue9300 { + async fn a() { + let a = 0; + if a == 0 {} + } + + pub struct S; + impl S { + pub async fn async_method() { + let a = 0; + if a == 0 {} + } + } +} diff --git a/tests/ui/cognitive_complexity.stderr b/tests/ui/cognitive_complexity.stderr index d7f2f24e52f2..5824631fa83b 100644 --- a/tests/ui/cognitive_complexity.stderr +++ b/tests/ui/cognitive_complexity.stderr @@ -135,5 +135,21 @@ LL | fn moo(&self) { | = help: you could split it up into multiple smaller functions -error: aborting due to 17 previous errors +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:399:14 + | +LL | async fn a() { + | ^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:406:22 + | +LL | pub async fn async_method() { + | ^^^^^^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 19 previous errors diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 7d53e08345d3..1d7a72846419 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -253,6 +253,27 @@ fn negative_cases(res_opt: Result, String>, res_res: Result>, b: () }, + B, +} + +pub fn test_1(x: Issue9647) { + if let Issue9647::A { a, .. } = x { + if let Some(u) = a { + println!("{u:?}") + } + } +} + +pub fn test_2(x: Issue9647) { + if let Issue9647::A { a: Some(a), .. } = x { + if let Some(u) = a { + println!("{u}") + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 2580bef58091..0294be60b43f 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,5 +175,37 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: aborting due to 10 previous errors +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:263:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u:?}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:262:27 + | +LL | if let Issue9647::A { a, .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern, prefixed by a: + +error: this `if let` can be collapsed into the outer `if let` + --> $DIR/collapsible_match.rs:271:9 + | +LL | / if let Some(u) = a { +LL | | println!("{u}") +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> $DIR/collapsible_match.rs:270:35 + | +LL | if let Issue9647::A { a: Some(a), .. } = x { + | ^ replace this binding +LL | if let Some(u) = a { + | ^^^^^^^ with this pattern + +error: aborting due to 12 previous errors diff --git a/tests/ui/collapsible_str_replace.fixed b/tests/ui/collapsible_str_replace.fixed index 49fc9a9629e2..9792ae9ed6b8 100644 --- a/tests/ui/collapsible_str_replace.fixed +++ b/tests/ui/collapsible_str_replace.fixed @@ -1,5 +1,6 @@ // run-rustfix +#![allow(unused)] #![warn(clippy::collapsible_str_replace)] fn get_filter() -> char { @@ -71,3 +72,13 @@ fn main() { .replace('u', iter.next().unwrap()) .replace('s', iter.next().unwrap()); } + +#[clippy::msrv = "1.57"] +fn msrv_1_57() { + let _ = "".replace('a', "1.57").replace('b', "1.57"); +} + +#[clippy::msrv = "1.58"] +fn msrv_1_58() { + let _ = "".replace(['a', 'b'], "1.58"); +} diff --git a/tests/ui/collapsible_str_replace.rs b/tests/ui/collapsible_str_replace.rs index e3e25c4146ff..baee185b79ea 100644 --- a/tests/ui/collapsible_str_replace.rs +++ b/tests/ui/collapsible_str_replace.rs @@ -1,5 +1,6 @@ // run-rustfix +#![allow(unused)] #![warn(clippy::collapsible_str_replace)] fn get_filter() -> char { @@ -74,3 +75,13 @@ fn main() { .replace('u', iter.next().unwrap()) .replace('s', iter.next().unwrap()); } + +#[clippy::msrv = "1.57"] +fn msrv_1_57() { + let _ = "".replace('a', "1.57").replace('b', "1.57"); +} + +#[clippy::msrv = "1.58"] +fn msrv_1_58() { + let _ = "".replace('a', "1.58").replace('b', "1.58"); +} diff --git a/tests/ui/collapsible_str_replace.stderr b/tests/ui/collapsible_str_replace.stderr index 8e3daf3b898a..223358cf53f3 100644 --- a/tests/ui/collapsible_str_replace.stderr +++ b/tests/ui/collapsible_str_replace.stderr @@ -1,5 +1,5 @@ error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:19:27 + --> $DIR/collapsible_str_replace.rs:20:27 | LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` @@ -7,19 +7,19 @@ LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); = note: `-D clippy::collapsible-str-replace` implied by `-D warnings` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:21:27 + --> $DIR/collapsible_str_replace.rs:22:27 | LL | let _ = "hesuo worpd".replace('s', l).replace('u', l); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:23:27 + --> $DIR/collapsible_str_replace.rs:24:27 | LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:26:10 + --> $DIR/collapsible_str_replace.rs:27:10 | LL | .replace('s', "l") | __________^ @@ -29,58 +29,64 @@ LL | | .replace('d', "l"); | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:31:27 + --> $DIR/collapsible_str_replace.rs:32:27 | LL | let _ = "hesuo world".replace(s, "l").replace('u', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:33:27 + --> $DIR/collapsible_str_replace.rs:34:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:35:27 + --> $DIR/collapsible_str_replace.rs:36:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:37:27 + --> $DIR/collapsible_str_replace.rs:38:27 | LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:39:27 + --> $DIR/collapsible_str_replace.rs:40:27 | LL | let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:41:45 + --> $DIR/collapsible_str_replace.rs:42:45 | LL | let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:44:47 + --> $DIR/collapsible_str_replace.rs:45:47 | LL | let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:46:28 + --> $DIR/collapsible_str_replace.rs:47:28 | LL | let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:48:27 + --> $DIR/collapsible_str_replace.rs:49:27 | LL | let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")` -error: aborting due to 13 previous errors +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:86:16 + | +LL | let _ = "".replace('a', "1.58").replace('b', "1.58"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['a', 'b'], "1.58")` + +error: aborting due to 14 previous errors diff --git a/tests/ui/crashes/ice-10044.rs b/tests/ui/crashes/ice-10044.rs new file mode 100644 index 000000000000..65f38fe71188 --- /dev/null +++ b/tests/ui/crashes/ice-10044.rs @@ -0,0 +1,3 @@ +fn main() { + [0; usize::MAX]; +} diff --git a/tests/ui/crashes/ice-10044.stderr b/tests/ui/crashes/ice-10044.stderr new file mode 100644 index 000000000000..731f8265ad6c --- /dev/null +++ b/tests/ui/crashes/ice-10044.stderr @@ -0,0 +1,10 @@ +error: statement with no effect + --> $DIR/ice-10044.rs:2:5 + | +LL | [0; usize::MAX]; + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr index 0c2d48f938fc..c5ea0b16d1be 100644 --- a/tests/ui/crashes/ice-2774.stderr +++ b/tests/ui/crashes/ice-2774.stderr @@ -1,10 +1,15 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/ice-2774.rs:15:1 | LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { +LL + pub fn add_barfoos_to_foos(bars: &HashSet<&Bar>) { + | error: aborting due to previous error diff --git a/tests/ui/crashes/ice-6250.stderr b/tests/ui/crashes/ice-6250.stderr index 878897c410cf..4506d1550bd4 100644 --- a/tests/ui/crashes/ice-6250.stderr +++ b/tests/ui/crashes/ice-6250.stderr @@ -23,6 +23,11 @@ error[E0308]: mismatched types | LL | Some(reference) = cache.data.get(key) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | let Some(reference) = cache.data.get(key) { + | +++ error: aborting due to 3 previous errors diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr index 638e4a548493..efdd56dd47d3 100644 --- a/tests/ui/crashes/ice-6252.stderr +++ b/tests/ui/crashes/ice-6252.stderr @@ -17,9 +17,12 @@ error[E0412]: cannot find type `VAL` in this scope --> $DIR/ice-6252.rs:10:63 | LL | impl TypeVal for Multiply where N: TypeVal {} - | - ^^^ not found in this scope - | | - | help: you might be missing a type parameter: `, VAL` + | ^^^ not found in this scope + | +help: you might be missing a type parameter + | +LL | impl TypeVal for Multiply where N: TypeVal {} + | +++++ error[E0046]: not all trait items implemented, missing: `VAL` --> $DIR/ice-6252.rs:10:1 diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index a2a60a169153..8af60890390e 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -1,4 +1,4 @@ -// originally from ./src/test/ui/pattern/usefulness/consts-opaque.rs +// originally from ./tests/ui/pattern/usefulness/consts-opaque.rs // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 diff --git a/tests/ui/crashes/ice-6255.rs b/tests/ui/crashes/ice-6255.rs index bd4a81d98e2e..ccde6aa2b0f7 100644 --- a/tests/ui/crashes/ice-6255.rs +++ b/tests/ui/crashes/ice-6255.rs @@ -1,4 +1,4 @@ -// originally from rustc ./src/test/ui/macros/issue-78325-inconsistent-resolution.rs +// originally from rustc ./tests/ui/macros/issue-78325-inconsistent-resolution.rs // inconsistent resolution for a macro macro_rules! define_other_core { diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs index 67308263dadd..f9ee3e058c11 100644 --- a/tests/ui/crashes/ice-6256.rs +++ b/tests/ui/crashes/ice-6256.rs @@ -1,4 +1,4 @@ -// originally from rustc ./src/test/ui/regions/issue-78262.rs +// originally from rustc ./tests/ui/regions/issue-78262.rs // ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig() #![allow(clippy::upper_case_acronyms)] diff --git a/tests/ui/crashes/ice-9463.rs b/tests/ui/crashes/ice-9463.rs index 41ef930d3233..9564e77c24b1 100644 --- a/tests/ui/crashes/ice-9463.rs +++ b/tests/ui/crashes/ice-9463.rs @@ -1,4 +1,4 @@ -#![deny(arithmetic_overflow, const_err)] +#![deny(arithmetic_overflow)] fn main() { let _x = -1_i32 >> -1; let _y = 1u32 >> 10000000000000u32; diff --git a/tests/ui/crashes/ice-9463.stderr b/tests/ui/crashes/ice-9463.stderr index b0ce306d6838..2b425e85a27b 100644 --- a/tests/ui/crashes/ice-9463.stderr +++ b/tests/ui/crashes/ice-9463.stderr @@ -7,7 +7,7 @@ LL | let _x = -1_i32 >> -1; note: the lint level is defined here --> $DIR/ice-9463.rs:1:9 | -LL | #![deny(arithmetic_overflow, const_err)] +LL | #![deny(arithmetic_overflow)] | ^^^^^^^^^^^^^^^^^^^ error: this arithmetic operation will overflow diff --git a/tests/ui/crashes/ice-9746.rs b/tests/ui/crashes/ice-9746.rs new file mode 100644 index 000000000000..fbd373c70bf2 --- /dev/null +++ b/tests/ui/crashes/ice-9746.rs @@ -0,0 +1,15 @@ +//! + +trait Trait {} + +struct Struct<'a> { + _inner: &'a Struct<'a>, +} + +impl Trait for Struct<'_> {} + +fn example<'a>(s: &'a Struct) -> Box> { + Box::new(Box::new(Struct { _inner: s })) +} + +fn main() {} diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index d68bbe788021..0b0e0ad2684a 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -1,4 +1,4 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/needless_lifetimes_impl_trait.rs:15:5 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { @@ -9,6 +9,11 @@ note: the lint level is defined here | LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: aborting due to previous error diff --git a/tests/ui/crate_level_checks/no_std_main_recursion.rs b/tests/ui/crate_level_checks/no_std_main_recursion.rs index 4a5c597dda51..e1c9fe30a9df 100644 --- a/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,6 +1,5 @@ // compile-flags: -Clink-arg=-nostartfiles // ignore-macos -// ignore-windows #![feature(lang_items, start, libc)] #![no_std] diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index e6a65b46d975..ddb5f1342e99 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -1,143 +1,143 @@ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:9:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | if n <= 1 { | ~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:10:9 | LL | dbg!(1) | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1 | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:12:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | n * factorial(n - 1) | -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(42); | ^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 42; | ~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:18:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | dbg!(dbg!(42)); | ~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:19:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:20:5 | LL | dbg!(1, 2, dbg!(3, 4)); | ^^^^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, dbg!(3, 4)); | ~~~~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:21:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:41:9 | LL | dbg!(2); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 2; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:47:5 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:52:5 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ -error: `dbg!` macro is intended as a debugging tool +error: the `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:58:9 | LL | dbg!(1); | ^^^^^^^ | -help: ensure to avoid having uses of it in version control +help: remove the invocation before committing it to a version control system | LL | 1; | ~ diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index a7da8f89aa3d..1af77d1a25b2 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -15,7 +15,7 @@ pub trait Copy {} pub unsafe trait Freeze {} #[lang = "start"] -fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize { +fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index a28bff76755b..a370ccc76962 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.0_f64; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index b48435cc7b28..2476fe95141d 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -33,6 +33,7 @@ mod basic_expr { let x: [f64; 3] = [1., 2., 3.]; let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) }; let x: _ = 1.; + const X: f32 = 1.; } } @@ -59,6 +60,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2. }; + + const X: f32 = { + // Should lint this because this literal is not bound to any types. + let y = 1.; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1. + }; } } diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8b6c7746edb..5df2f642388d 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -61,79 +61,85 @@ LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:43:21 + --> $DIR/default_numeric_fallback_f64.rs:44:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:51:21 + --> $DIR/default_numeric_fallback_f64.rs:52:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:57:21 + --> $DIR/default_numeric_fallback_f64.rs:58:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:69:9 + --> $DIR/default_numeric_fallback_f64.rs:66:21 + | +LL | let y = 1.; + | ^^ help: consider adding suffix: `1.0_f64` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_f64.rs:78:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:75:27 + --> $DIR/default_numeric_fallback_f64.rs:84:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:79:29 + --> $DIR/default_numeric_fallback_f64.rs:88:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:21 + --> $DIR/default_numeric_fallback_f64.rs:102:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:96:32 + --> $DIR/default_numeric_fallback_f64.rs:105:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:28 + --> $DIR/default_numeric_fallback_f64.rs:123:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:117:36 + --> $DIR/default_numeric_fallback_f64.rs:126:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:135:24 + --> $DIR/default_numeric_fallback_f64.rs:144:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:155:23 + --> $DIR/default_numeric_fallback_f64.rs:164:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:162:21 + --> $DIR/default_numeric_fallback_f64.rs:171:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` @@ -143,5 +149,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index 55451cf2f7d0..3f4994f0453b 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1_i32; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 62d72f2febaa..2df0e09787f9 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -33,6 +33,8 @@ mod basic_expr { let x: [i32; 3] = [1, 2, 3]; let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; let x: _ = 1; + let x: u64 = 1; + const CONST_X: i8 = 1; } } @@ -59,6 +61,14 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 2 }; + + const CONST_X: i32 = { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; } } diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index f7c5e724c403..6f219c3fc2b0 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -73,79 +73,85 @@ LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:43:21 + --> $DIR/default_numeric_fallback_i32.rs:45:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:51:21 + --> $DIR/default_numeric_fallback_i32.rs:53:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:57:21 + --> $DIR/default_numeric_fallback_i32.rs:59:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:69:9 + --> $DIR/default_numeric_fallback_i32.rs:67:21 + | +LL | let y = 1; + | ^ help: consider adding suffix: `1_i32` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:79:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:27 + --> $DIR/default_numeric_fallback_i32.rs:85:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:29 + --> $DIR/default_numeric_fallback_i32.rs:89:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:93:21 + --> $DIR/default_numeric_fallback_i32.rs:103:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:96:32 + --> $DIR/default_numeric_fallback_i32.rs:106:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:114:28 + --> $DIR/default_numeric_fallback_i32.rs:124:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:117:36 + --> $DIR/default_numeric_fallback_i32.rs:127:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:135:24 + --> $DIR/default_numeric_fallback_i32.rs:145:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:155:23 + --> $DIR/default_numeric_fallback_i32.rs:165:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:162:21 + --> $DIR/default_numeric_fallback_i32.rs:172:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -155,5 +161,5 @@ LL | internal_macro!(); | = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index eedd43619392..5640599d48ae 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -12,17 +12,17 @@ use std::default::Default as D2; use std::string; fn main() { - let s1: String = std::string::String::default(); + let s1: String = String::default(); let s2 = String::default(); - let s3: String = std::string::String::default(); + let s3: String = String::default(); - let s4: String = std::string::String::default(); + let s4: String = String::default(); let s5 = string::String::default(); - let s6: String = std::string::String::default(); + let s6: String = String::default(); let s7 = std::string::String::default(); diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 49b2dde3f1e8..e4f73c08d190 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,8 +1,8 @@ -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:15:22 | LL | let s1: String = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()` | note: the lint level is defined here --> $DIR/default_trait_access.rs:3:9 @@ -10,23 +10,23 @@ note: the lint level is defined here LL | #![deny(clippy::default_trait_access)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:19:22 | LL | let s3: String = D2::default(); - | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^ help: try: `String::default()` -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:21:22 | LL | let s4: String = std::default::Default::default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` -error: calling `std::string::String::default()` is more clear than this expression +error: calling `String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:25:22 | LL | let s6: String = default::Default::default(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()` error: calling `GenericDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:35:46 diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index 7dcdfb0937e8..ee8456f5deb8 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -210,4 +210,25 @@ impl Default for IntOrString { } } +#[derive(Default)] +pub enum SimpleEnum { + Foo, + #[default] + Bar, +} + + + +pub enum NonExhaustiveEnum { + Foo, + #[non_exhaustive] + Bar, +} + +impl Default for NonExhaustiveEnum { + fn default() -> Self { + NonExhaustiveEnum::Bar + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 625cbcdde230..14af419bcad1 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -244,4 +244,27 @@ impl Default for IntOrString { } } +pub enum SimpleEnum { + Foo, + Bar, +} + +impl Default for SimpleEnum { + fn default() -> Self { + SimpleEnum::Bar + } +} + +pub enum NonExhaustiveEnum { + Foo, + #[non_exhaustive] + Bar, +} + +impl Default for NonExhaustiveEnum { + fn default() -> Self { + NonExhaustiveEnum::Bar + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index c1db5a58b1f5..81963c3be5b5 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -113,5 +113,26 @@ help: ...and instead derive it LL | #[derive(Default)] | -error: aborting due to 7 previous errors +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:252:1 + | +LL | / impl Default for SimpleEnum { +LL | | fn default() -> Self { +LL | | SimpleEnum::Bar +LL | | } +LL | | } + | |_^ + | + = help: remove the manual implementation... +help: ...and instead derive it... + | +LL | #[derive(Default)] + | +help: ...and mark the default variant + | +LL ~ #[default] +LL ~ Bar, + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index c629c0e53537..843e1df8bc6b 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -85,4 +85,15 @@ impl Clone for GenericRef<'_, T, U> { } } +// https://github.com/rust-lang/rust-clippy/issues/10188 +#[repr(packed)] +#[derive(Copy)] +struct Packed(T); + +impl Clone for Packed { + fn clone(&self) -> Self { + *self + } +} + fn main() {} diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derive_hash_xor_eq.stderr deleted file mode 100644 index 16c92397804e..000000000000 --- a/tests/ui/derive_hash_xor_eq.stderr +++ /dev/null @@ -1,59 +0,0 @@ -error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:12:10 - | -LL | #[derive(Hash)] - | ^^^^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:15:1 - | -LL | impl PartialEq for Bar { - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:21:10 - | -LL | #[derive(Hash)] - | ^^^^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:24:1 - | -LL | impl PartialEq for Baz { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:33:1 - | -LL | / impl std::hash::Hash for Bah { -LL | | fn hash(&self, _: &mut H) {} -LL | | } - | |_^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:30:10 - | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:51:5 - | -LL | / impl Hash for Foo3 { -LL | | fn hash(&self, _: &mut H) {} -LL | | } - | |_____^ - | -note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:48:14 - | -LL | #[derive(PartialEq)] - | ^^^^^^^^^ - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 4 previous errors - diff --git a/tests/ui/derive_hash_xor_eq.rs b/tests/ui/derived_hash_with_manual_eq.rs similarity index 62% rename from tests/ui/derive_hash_xor_eq.rs rename to tests/ui/derived_hash_with_manual_eq.rs index 813ddc566464..8ad09a8de43d 100644 --- a/tests/ui/derive_hash_xor_eq.rs +++ b/tests/ui/derived_hash_with_manual_eq.rs @@ -27,6 +27,8 @@ impl PartialEq for Baz { } } +// Implementing `Hash` with a derived `PartialEq` is fine. See #2627 + #[derive(PartialEq)] struct Bah; @@ -34,23 +36,4 @@ impl std::hash::Hash for Bah { fn hash(&self, _: &mut H) {} } -#[derive(PartialEq)] -struct Foo2; - -trait Hash {} - -// We don't want to lint on user-defined traits called `Hash` -impl Hash for Foo2 {} - -mod use_hash { - use std::hash::{Hash, Hasher}; - - #[derive(PartialEq)] - struct Foo3; - - impl Hash for Foo3 { - fn hash(&self, _: &mut H) {} - } -} - fn main() {} diff --git a/tests/ui/derived_hash_with_manual_eq.stderr b/tests/ui/derived_hash_with_manual_eq.stderr new file mode 100644 index 000000000000..230940f25fb6 --- /dev/null +++ b/tests/ui/derived_hash_with_manual_eq.stderr @@ -0,0 +1,29 @@ +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derived_hash_with_manual_eq.rs:12:10 + | +LL | #[derive(Hash)] + | ^^^^ + | +note: `PartialEq` implemented here + --> $DIR/derived_hash_with_manual_eq.rs:15:1 + | +LL | impl PartialEq for Bar { + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::derived_hash_with_manual_eq)]` on by default + = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derived_hash_with_manual_eq.rs:21:10 + | +LL | #[derive(Hash)] + | ^^^^ + | +note: `PartialEq` implemented here + --> $DIR/derived_hash_with_manual_eq.rs:24:1 + | +LL | impl PartialEq for Baz { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index c7b616e28970..d74f2dbfe1ba 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,52 +1,40 @@ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:7:1 | -LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub fn pub_fn_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:11:1 | -LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:16:1 | -LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub fn pub_fn_returning_io_result() -> io::Result<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:21:1 | -LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:51:5 | -LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub fn pub_method_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:56:5 | -LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for function returning `Result` missing `# Errors` section --> $DIR/doc_errors.rs:85:5 diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index 904b88eaef62..a86e191370e3 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -1,20 +1,16 @@ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:9:1 | -LL | / pub unsafe fn destroy_the_planet() { -LL | | unimplemented!(); -LL | | } - | |_^ +LL | pub unsafe fn destroy_the_planet() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::missing-safety-doc` implied by `-D warnings` error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:32:5 | -LL | / pub unsafe fn republished() { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub unsafe fn republished() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:40:5 @@ -25,29 +21,23 @@ LL | unsafe fn woefully_underdocumented(self); error: docs for unsafe trait missing `# Safety` section --> $DIR/doc_unsafe.rs:46:1 | -LL | / pub unsafe trait UnsafeTrait { -LL | | fn method(); -LL | | } - | |_^ +LL | pub unsafe trait UnsafeTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:76:5 | -LL | / pub unsafe fn more_undocumented_unsafe() -> Self { -LL | | unimplemented!(); -LL | | } - | |_____^ +LL | pub unsafe fn more_undocumented_unsafe() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsafe function's docs miss `# Safety` section --> $DIR/doc_unsafe.rs:92:9 | -LL | / pub unsafe fn whee() { -LL | | unimplemented!() -LL | | } - | |_________^ +LL | pub unsafe fn whee() { + | ^^^^^^^^^^^^^^^^^^^^ ... -LL | very_unsafe!(); - | -------------- in this macro invocation +LL | very_unsafe!(); + | -------------- in this macro invocation | = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 7de0b0bbdf9a..10044e65f115 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -72,3 +72,26 @@ fn test_owl_result_2() -> Result { produce_half_owl_ok().map(drop)?; Ok(1) } + +#[allow(unused)] +#[allow(clippy::unit_cmp)] +fn issue10122(x: u8) { + // This is a function which returns a reference and has a side-effect, which means + // that calling drop() on the function is considered an idiomatic way of achieving the side-effect + // in a match arm. + fn println_and(t: &T) -> &T { + println!("foo"); + t + } + + match x { + 0 => drop(println_and(&12)), // Don't lint (copy type), we only care about side-effects + 1 => drop(println_and(&String::new())), // Don't lint (no copy type), we only care about side-effects + 2 => { + drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + }, + 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + 4 => drop(&2), // Lint, not a fn/method call + _ => (), + } +} diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 4743cf79b5d3..293b9f6de832 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -107,5 +107,41 @@ note: argument has type `&SomeStruct` LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:91:13 + | +LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:91:18 + | +LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block + | ^^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:93:14 + | +LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:93:19 + | +LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm` + | ^^^^^^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing + --> $DIR/drop_ref.rs:94:14 + | +LL | 4 => drop(&2), // Lint, not a fn/method call + | ^^^^^^^^ + | +note: argument has type `&i32` + --> $DIR/drop_ref.rs:94:19 + | +LL | 4 => drop(&2), // Lint, not a fn/method call + | ^^ + +error: aborting due to 12 previous errors diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index e43635abcd11..79c29c04e059 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index d999b3b7dc80..2d7985457d8b 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -1,3 +1,4 @@ +// needs-asm-support // run-rustfix #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 2ef9966525ce..2c4c49d2522c 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -1,5 +1,5 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:24:5 + --> $DIR/entry.rs:25:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::map-entry` implied by `-D warnings` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:29:5 + --> $DIR/entry.rs:30:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -32,7 +32,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:38:5 + --> $DIR/entry.rs:39:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -55,7 +55,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:47:5 + --> $DIR/entry.rs:48:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -79,7 +79,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:57:5 + --> $DIR/entry.rs:58:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -96,7 +96,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:63:5 + --> $DIR/entry.rs:64:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -122,7 +122,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:75:5 + --> $DIR/entry.rs:76:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -146,7 +146,7 @@ LL + } | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:85:5 + --> $DIR/entry.rs:86:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -187,7 +187,7 @@ LL + }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:119:5 + --> $DIR/entry.rs:120:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); @@ -195,7 +195,7 @@ LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:151:5 + --> $DIR/entry.rs:152:5 | LL | / if !m.contains_key(&k) { LL | | let x = (String::new(), String::new()); diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 422f9486503d..e73795502652 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -2,6 +2,7 @@ #![warn(clippy::eq_op)] #![allow(clippy::double_parens, clippy::identity_op, clippy::nonminimal_bool)] +#![allow(clippy::suspicious_xor_used_as_pow)] fn main() { // simple values and comparisons diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 313ceed2b41f..d365ab27edc2 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -1,5 +1,5 @@ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:8:13 + --> $DIR/eq_op.rs:9:13 | LL | let _ = 1 == 1; | ^^^^^^ @@ -7,163 +7,163 @@ LL | let _ = 1 == 1; = note: `-D clippy::eq-op` implied by `-D warnings` error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:9:13 + --> $DIR/eq_op.rs:10:13 | LL | let _ = "no" == "no"; | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:11:13 + --> $DIR/eq_op.rs:12:13 | LL | let _ = false != false; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:12:13 + --> $DIR/eq_op.rs:13:13 | LL | let _ = 1.5 < 1.5; | ^^^^^^^^^ error: equal expressions as operands to `>=` - --> $DIR/eq_op.rs:13:13 + --> $DIR/eq_op.rs:14:13 | LL | let _ = 1u64 >= 1u64; | ^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:16:13 + --> $DIR/eq_op.rs:17:13 | LL | let _ = (1u32 as u64) & (1u32 as u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `^` - --> $DIR/eq_op.rs:19:17 + --> $DIR/eq_op.rs:20:17 | LL | let _ = 1 ^ ((((((1)))))); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:23:13 + --> $DIR/eq_op.rs:24:13 | LL | let _ = (-(2) < -(2)); | ^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:24:13 + --> $DIR/eq_op.rs:25:13 | LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:14 + --> $DIR/eq_op.rs:25:14 | LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:35 + --> $DIR/eq_op.rs:25:35 | LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:25:13 + --> $DIR/eq_op.rs:26:13 | LL | let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:28:13 + --> $DIR/eq_op.rs:29:13 | LL | let _ = ([1] != [1]); | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:29:13 + --> $DIR/eq_op.rs:30:13 | LL | let _ = ((1, 2) != (1, 2)); | ^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:33:13 + --> $DIR/eq_op.rs:34:13 | LL | let _ = 1 + 1 == 2; | ^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:34:13 + --> $DIR/eq_op.rs:35:13 | LL | let _ = 1 - 1 == 0; | ^^^^^^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:34:13 + --> $DIR/eq_op.rs:35:13 | LL | let _ = 1 - 1 == 0; | ^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:36:13 + --> $DIR/eq_op.rs:37:13 | LL | let _ = 1 - 1; | ^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:37:13 + --> $DIR/eq_op.rs:38:13 | LL | let _ = 1 / 1; | ^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:38:13 + --> $DIR/eq_op.rs:39:13 | LL | let _ = true && true; | ^^^^^^^^^^^^ error: equal expressions as operands to `||` - --> $DIR/eq_op.rs:40:13 + --> $DIR/eq_op.rs:41:13 | LL | let _ = true || true; | ^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:45:13 + --> $DIR/eq_op.rs:46:13 | LL | let _ = a == b && b == a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:46:13 + --> $DIR/eq_op.rs:47:13 | LL | let _ = a != b && b != a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:47:13 + --> $DIR/eq_op.rs:48:13 | LL | let _ = a < b && b > a; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:48:13 + --> $DIR/eq_op.rs:49:13 | LL | let _ = a <= b && b >= a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:51:13 + --> $DIR/eq_op.rs:52:13 | LL | let _ = a == a; | ^^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:61:20 + --> $DIR/eq_op.rs:62:20 | LL | const D: u32 = A / A; | ^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:92:5 + --> $DIR/eq_op.rs:93:5 | LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 687efdada6e3..9af2ba962720 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -66,10 +72,11 @@ fn main() { if let Some(3 | 4) = c {} if let Struct { a, b: false } = d {} if let Struct { a: 2, b: x } = d {} - if let NotPartialEq::A = f {} + if matches!(f, NotPartialEq::A) {} if g == NotStructuralEq::A {} - if let Some(NotPartialEq::A) = Some(f) {} + if matches!(Some(f), Some(NotPartialEq::A)) {} if Some(g) == Some(NotStructuralEq::A) {} + if matches!(h, NoPartialEqStruct { a: 2, b: false }) {} macro_rules! m1 { (x) => { diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 8c467d14d2a9..c3626c081dd5 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -23,6 +23,11 @@ struct Struct { b: bool, } +struct NoPartialEqStruct { + a: i32, + b: bool, +} + enum NotPartialEq { A, B, @@ -47,6 +52,7 @@ fn main() { let e = Enum::UnitVariant; let f = NotPartialEq::A; let g = NotStructuralEq::A; + let h = NoPartialEqStruct { a: 2, b: false }; // true @@ -70,6 +76,7 @@ fn main() { if let NotStructuralEq::A = g {} if let Some(NotPartialEq::A) = Some(f) {} if let Some(NotStructuralEq::A) = Some(g) {} + if let NoPartialEqStruct { a: 2, b: false } = h {} macro_rules! m1 { (x) => { diff --git a/tests/ui/equatable_if_let.stderr b/tests/ui/equatable_if_let.stderr index 9c4c3cc3682e..40ca75b8da22 100644 --- a/tests/ui/equatable_if_let.stderr +++ b/tests/ui/equatable_if_let.stderr @@ -1,5 +1,5 @@ error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:53:8 + --> $DIR/equatable_if_let.rs:59:8 | LL | if let 2 = a {} | ^^^^^^^^^ help: try: `a == 2` @@ -7,64 +7,82 @@ LL | if let 2 = a {} = note: `-D clippy::equatable-if-let` implied by `-D warnings` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:54:8 + --> $DIR/equatable_if_let.rs:60:8 | LL | if let Ordering::Greater = a.cmp(&b) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:55:8 + --> $DIR/equatable_if_let.rs:61:8 | LL | if let Some(2) = c {} | ^^^^^^^^^^^^^^^ help: try: `c == Some(2)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:56:8 + --> $DIR/equatable_if_let.rs:62:8 | LL | if let Struct { a: 2, b: false } = d {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:57:8 + --> $DIR/equatable_if_let.rs:63:8 | LL | if let Enum::TupleVariant(32, 64) = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:58:8 + --> $DIR/equatable_if_let.rs:64:8 | LL | if let Enum::RecordVariant { a: 64, b: 32 } = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:59:8 + --> $DIR/equatable_if_let.rs:65:8 | LL | if let Enum::UnitVariant = e {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant` error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:60:8 + --> $DIR/equatable_if_let.rs:66:8 | LL | if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:75:8 + | +LL | if let NotPartialEq::A = f {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:70:8 + --> $DIR/equatable_if_let.rs:76:8 | LL | if let NotStructuralEq::A = g {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A` +error: this pattern matching can be expressed using `matches!` + --> $DIR/equatable_if_let.rs:77:8 + | +LL | if let Some(NotPartialEq::A) = Some(f) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))` + error: this pattern matching can be expressed using equality - --> $DIR/equatable_if_let.rs:72:8 + --> $DIR/equatable_if_let.rs:78:8 | LL | if let Some(NotStructuralEq::A) = Some(g) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)` -error: this pattern matching can be expressed using equality +error: this pattern matching can be expressed using `matches!` --> $DIR/equatable_if_let.rs:79:8 | +LL | if let NoPartialEqStruct { a: 2, b: false } = h {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })` + +error: this pattern matching can be expressed using equality + --> $DIR/equatable_if_let.rs:86:8 + | LL | if let m1!(x) = "abc" { | ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed index 7e18d70bae40..b63cbd8a8e6b 100644 --- a/tests/ui/err_expect.fixed +++ b/tests/ui/err_expect.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +14,15 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +#[clippy::msrv = "1.16"] +fn msrv_1_16() { + let x: Result = Ok(16); + x.err().expect("16"); +} + +#[clippy::msrv = "1.17"] +fn msrv_1_17() { + let x: Result = Ok(17); + x.expect_err("17"); +} diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs index bf8c3c9fb8c9..c081a745fb40 100644 --- a/tests/ui/err_expect.rs +++ b/tests/ui/err_expect.rs @@ -1,5 +1,7 @@ // run-rustfix +#![allow(unused)] + struct MyTypeNonDebug; #[derive(Debug)] @@ -12,3 +14,15 @@ fn main() { let test_non_debug: Result = Ok(MyTypeNonDebug); test_non_debug.err().expect("Testing non debug type"); } + +#[clippy::msrv = "1.16"] +fn msrv_1_16() { + let x: Result = Ok(16); + x.err().expect("16"); +} + +#[clippy::msrv = "1.17"] +fn msrv_1_17() { + let x: Result = Ok(17); + x.err().expect("17"); +} diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr index ffd97e00a5c0..82c0754cfb00 100644 --- a/tests/ui/err_expect.stderr +++ b/tests/ui/err_expect.stderr @@ -1,10 +1,16 @@ error: called `.err().expect()` on a `Result` value - --> $DIR/err_expect.rs:10:16 + --> $DIR/err_expect.rs:12:16 | LL | test_debug.err().expect("Testing debug type"); | ^^^^^^^^^^^^ help: try: `expect_err` | = note: `-D clippy::err-expect` implied by `-D warnings` -error: aborting due to previous error +error: called `.err().expect()` on a `Result` value + --> $DIR/err_expect.rs:27:7 + | +LL | x.err().expect("17"); + | ^^^^^^^^^^^^ help: try: `expect_err` + +error: aborting due to 2 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index a9cc80aaaf62..dc129591eac4 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) - move || takes_fn_mut(&mut f_used_once) } + +impl dyn TestTrait + '_ { + fn method_on_dyn(&self) -> bool { + false + } +} + +// https://github.com/rust-lang/rust-clippy/issues/7746 +fn angle_brackets_and_substs() { + let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]); + array_opt.map(<[u8; 3]>::as_slice); + + let slice_opt: Option<&[u8]> = Some(b"slice"); + slice_opt.map(<[u8]>::len); + + let ptr_opt: Option<*const usize> = Some(&487); + ptr_opt.map(<*const usize>::is_null); + + let test_struct = TestStruct { some_ref: &487 }; + let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct); + dyn_opt.map(::method_on_dyn); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index cc99906ccd66..025fd6a0b7af 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -316,3 +316,25 @@ pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) - move || takes_fn_mut(|| f_used_once()) } + +impl dyn TestTrait + '_ { + fn method_on_dyn(&self) -> bool { + false + } +} + +// https://github.com/rust-lang/rust-clippy/issues/7746 +fn angle_brackets_and_substs() { + let array_opt: Option<&[u8; 3]> = Some(&[4, 8, 7]); + array_opt.map(|a| a.as_slice()); + + let slice_opt: Option<&[u8]> = Some(b"slice"); + slice_opt.map(|s| s.len()); + + let ptr_opt: Option<*const usize> = Some(&487); + ptr_opt.map(|p| p.is_null()); + + let test_struct = TestStruct { some_ref: &487 }; + let dyn_opt: Option<&dyn TestTrait> = Some(&test_struct); + dyn_opt.map(|d| d.method_on_dyn()); +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 434706b7e258..a521fb868607 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -134,5 +134,29 @@ error: redundant closure LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` -error: aborting due to 22 previous errors +error: redundant closure + --> $DIR/eta.rs:329:19 + | +LL | array_opt.map(|a| a.as_slice()); + | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` + +error: redundant closure + --> $DIR/eta.rs:332:19 + | +LL | slice_opt.map(|s| s.len()); + | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` + +error: redundant closure + --> $DIR/eta.rs:335:17 + | +LL | ptr_opt.map(|p| p.is_null()); + | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` + +error: redundant closure + --> $DIR/eta.rs:339:17 + | +LL | dyn_opt.map(|d| d.method_on_dyn()); + | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` + +error: aborting due to 26 previous errors diff --git a/tests/ui/expect.stderr b/tests/ui/expect.stderr index f6738865cac1..c08e0dbbf744 100644 --- a/tests/ui/expect.stderr +++ b/tests/ui/expect.stderr @@ -1,4 +1,4 @@ -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/expect.rs:5:13 | LL | let _ = opt.expect(""); @@ -7,7 +7,7 @@ LL | let _ = opt.expect(""); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/expect.rs:10:13 | LL | let _ = res.expect(""); @@ -15,7 +15,7 @@ LL | let _ = res.expect(""); | = help: if this value is an `Err`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/expect.rs:11:13 | LL | let _ = res.expect_err(""); diff --git a/tests/ui/explicit_auto_deref.fixed b/tests/ui/explicit_auto_deref.fixed index d1d35e5c0eb4..475fae5e823b 100644 --- a/tests/ui/explicit_auto_deref.fixed +++ b/tests/ui/explicit_auto_deref.fixed @@ -266,4 +266,19 @@ fn main() { } x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); + + // Issue #9901 + fn takes_ref(_: &i32) {} + takes_ref(*Box::new(&0i32)); } diff --git a/tests/ui/explicit_auto_deref.rs b/tests/ui/explicit_auto_deref.rs index deedafad153b..c1894258f4d8 100644 --- a/tests/ui/explicit_auto_deref.rs +++ b/tests/ui/explicit_auto_deref.rs @@ -266,4 +266,19 @@ fn main() { } *x }; + + trait WithAssoc { + type Assoc: ?Sized; + } + impl WithAssoc for String { + type Assoc = str; + } + fn takes_assoc(_: &T::Assoc) -> T { + unimplemented!() + } + let _: String = takes_assoc(&*String::new()); + + // Issue #9901 + fn takes_ref(_: &i32) {} + takes_ref(*Box::new(&0i32)); } diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index 6eddc01e2c47..46565a97f005 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -189,3 +189,33 @@ mod issue_7920 { } } } + +mod issue_10058 { + pub fn test() { + // should not lint since we are increasing counter potentially more than once in the loop + let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1]; + let mut counter = 0; + for value in values { + counter += 1; + + if value == 0 { + continue; + } + + counter += 1; + } + } + + pub fn test2() { + // should not lint since we are increasing counter potentially more than once in the loop + let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1]; + let mut counter = 0; + for value in values { + counter += 1; + + if value != 0 { + counter += 1; + } + } + } +} diff --git a/tests/ui/extra_unused_type_parameters.rs b/tests/ui/extra_unused_type_parameters.rs new file mode 100644 index 000000000000..5cb80cb6233f --- /dev/null +++ b/tests/ui/extra_unused_type_parameters.rs @@ -0,0 +1,69 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::extra_unused_type_parameters)] + +fn unused_ty(x: u8) {} + +fn unused_multi(x: u8) {} + +fn unused_with_lt<'a, T>(x: &'a u8) {} + +fn used_ty(x: T, y: u8) {} + +fn used_ref<'a, T>(x: &'a T) {} + +fn used_ret(x: u8) -> T { + T::default() +} + +fn unused_bounded(x: U) {} + +fn unused_where_clause(x: U) +where + T: Default, +{ +} + +fn some_unused, E>(b: B, c: C) {} + +fn used_opaque
(iter: impl Iterator) -> usize { + iter.count() +} + +fn used_ret_opaque() -> impl Iterator { + std::iter::empty() +} + +fn used_vec_box(x: Vec>) {} + +fn used_body() -> String { + T::default().to_string() +} + +fn used_closure() -> impl Fn() { + || println!("{}", T::default().to_string()) +} + +struct S; + +impl S { + fn unused_ty_impl(&self) {} +} + +// Don't lint on trait methods +trait Foo { + fn bar(&self); +} + +impl Foo for S { + fn bar(&self) {} +} + +fn skip_index(iter: Iter, index: usize) -> impl Iterator +where + Iter: Iterator, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} + +fn main() {} diff --git a/tests/ui/extra_unused_type_parameters.stderr b/tests/ui/extra_unused_type_parameters.stderr new file mode 100644 index 000000000000..1c8dd53e6385 --- /dev/null +++ b/tests/ui/extra_unused_type_parameters.stderr @@ -0,0 +1,59 @@ +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:4:13 + | +LL | fn unused_ty(x: u8) {} + | ^^^ + | + = help: consider removing the parameter + = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings` + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:6:16 + | +LL | fn unused_multi(x: u8) {} + | ^^^^^^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:8:23 + | +LL | fn unused_with_lt<'a, T>(x: &'a u8) {} + | ^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:18:19 + | +LL | fn unused_bounded(x: U) {} + | ^^^^^^^^^^^ + | + = help: consider removing the parameter + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:20:24 + | +LL | fn unused_where_clause(x: U) + | ^^ + | + = help: consider removing the parameter + +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:26:16 + | +LL | fn some_unused, E>(b: B, c: C) {} + | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:49:22 + | +LL | fn unused_ty_impl(&self) {} + | ^^^ + | + = help: consider removing the parameter + +error: aborting due to 7 previous errors + diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 7367910eaa12..1f989bb12205 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -247,3 +247,24 @@ mod issue6312 { } } } + +struct Collection { + items: Vec, + len: usize, +} + +impl Default for Collection { + fn default() -> Self { + Self { + items: vec![1, 2, 3], + len: 0, + } + } +} + +#[allow(clippy::redundant_closure_call)] +fn issue10136() { + let mut c = Collection::default(); + // don't lint, since c.items was used to calculate this value + c.len = (|| c.items.len())(); +} diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed index c3992d7e92cf..462d46169fcb 100644 --- a/tests/ui/filter_map_next_fixable.fixed +++ b/tests/ui/filter_map_next_fixable.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +9,15 @@ fn main() { let element: Option = a.iter().find_map(|s| s.parse().ok()); assert_eq!(element, Some(1)); } + +#[clippy::msrv = "1.29"] +fn msrv_1_29() { + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +#[clippy::msrv = "1.30"] +fn msrv_1_30() { + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().find_map(|s| s.parse().ok()); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs index 447219a96839..2ea00cf73072 100644 --- a/tests/ui/filter_map_next_fixable.rs +++ b/tests/ui/filter_map_next_fixable.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(unused)] fn main() { let a = ["1", "lol", "3", "NaN", "5"]; @@ -8,3 +9,15 @@ fn main() { let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); assert_eq!(element, Some(1)); } + +#[clippy::msrv = "1.29"] +fn msrv_1_29() { + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} + +#[clippy::msrv = "1.30"] +fn msrv_1_30() { + let a = ["1", "lol", "3", "NaN", "5"]; + let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index 3bb062ffd7a3..a9fc6abe88fa 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,10 +1,16 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead - --> $DIR/filter_map_next_fixable.rs:8:32 + --> $DIR/filter_map_next_fixable.rs:9:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` -error: aborting due to previous error +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead + --> $DIR/filter_map_next_fixable.rs:22:26 + | +LL | let _: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + +error: aborting due to 2 previous errors diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 884d05fed71b..8ffd4cc51379 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -14,6 +14,15 @@ fn main() { let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); + + let _ = (x - 1.0).mul_add(x - 1.0, -y); + let _ = (x - 1.0).mul_add(x - 1.0, -y) + 3.0; + let _ = (x - 1.0).mul_add(x - 1.0, -(y + 3.0)); + let _ = (y + 1.0).mul_add(-(y + 1.0), x); + let _ = (3.0 * y).mul_add(-(3.0 * y), x); + let _ = (y + 1.0 + x).mul_add(-(y + 1.0 + x), x); + let _ = (y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index e6a1c895371b..9ae3455a1346 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -14,6 +14,15 @@ fn main() { let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); + + let _ = (x - 1.0).powi(2) - y; + let _ = (x - 1.0).powi(2) - y + 3.0; + let _ = (x - 1.0).powi(2) - (y + 3.0); + let _ = x - (y + 1.0).powi(2); + let _ = x - (3.0 * y).powi(2); + let _ = x - (y + 1.0 + x).powi(2); + let _ = x - (y + 1.0 + 2.0).powi(2); + // Cases where the lint shouldn't be applied let _ = x.powi(2); let _ = x.powi(1 + 1); diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index 5df0de1fef22..fdf6d088052e 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -42,5 +42,47 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 7 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:18:13 + | +LL | let _ = (x - 1.0).powi(2) - y; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:19:13 + | +LL | let _ = (x - 1.0).powi(2) - y + 3.0; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:20:13 + | +LL | let _ = (x - 1.0).powi(2) - (y + 3.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x - 1.0).mul_add(x - 1.0, -(y + 3.0))` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:21:13 + | +LL | let _ = x - (y + 1.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0).mul_add(-(y + 1.0), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:22:13 + | +LL | let _ = x - (3.0 * y).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(3.0 * y).mul_add(-(3.0 * y), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:23:13 + | +LL | let _ = x - (y + 1.0 + x).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + x).mul_add(-(y + 1.0 + x), x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:24:13 + | +LL | let _ = x - (y + 1.0 + 2.0).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y + 1.0 + 2.0).mul_add(-(y + 1.0 + 2.0), x)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/fn_null_check.rs b/tests/ui/fn_null_check.rs new file mode 100644 index 000000000000..df5bc8420d57 --- /dev/null +++ b/tests/ui/fn_null_check.rs @@ -0,0 +1,21 @@ +#![allow(unused)] +#![warn(clippy::fn_null_check)] +#![allow(clippy::cmp_null)] +#![allow(clippy::ptr_eq)] +#![allow(clippy::zero_ptr)] + +pub const ZPTR: *const () = 0 as *const _; +pub const NOT_ZPTR: *const () = 1 as *const _; + +fn main() { + let fn_ptr = main; + + if (fn_ptr as *mut ()).is_null() {} + if (fn_ptr as *const u8).is_null() {} + if (fn_ptr as *const ()) == std::ptr::null() {} + if (fn_ptr as *const ()) == (0 as *const ()) {} + if (fn_ptr as *const ()) == ZPTR {} + + // no lint + if (fn_ptr as *const ()) == NOT_ZPTR {} +} diff --git a/tests/ui/fn_null_check.stderr b/tests/ui/fn_null_check.stderr new file mode 100644 index 000000000000..660dd3239792 --- /dev/null +++ b/tests/ui/fn_null_check.stderr @@ -0,0 +1,43 @@ +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:13:8 + | +LL | if (fn_ptr as *mut ()).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + = note: `-D clippy::fn-null-check` implied by `-D warnings` + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:14:8 + | +LL | if (fn_ptr as *const u8).is_null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:15:8 + | +LL | if (fn_ptr as *const ()) == std::ptr::null() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:16:8 + | +LL | if (fn_ptr as *const ()) == (0 as *const ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: function pointer assumed to be nullable, even though it isn't + --> $DIR/fn_null_check.rs:17:8 + | +LL | if (fn_ptr as *const ()) == ZPTR {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: try wrapping your function pointer type in `Option` instead, and using `is_none` to check for null pointer value + +error: aborting due to 5 previous errors + diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs index f805bcc9ba8a..f53e531629aa 100644 --- a/tests/ui/fn_params_excessive_bools.rs +++ b/tests/ui/fn_params_excessive_bools.rs @@ -2,6 +2,7 @@ #![allow(clippy::too_many_arguments)] extern "C" { + // Should not lint, most of the time users have no control over extern function signatures fn f(_: bool, _: bool, _: bool, _: bool); } @@ -22,8 +23,12 @@ fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} struct S; trait Trait { + // should warn for trait functions with and without body fn f(_: bool, _: bool, _: bool, _: bool); fn g(_: bool, _: bool, _: bool, _: Vec); + #[allow(clippy::fn_params_excessive_bools)] + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool); + fn i(_: bool, _: bool, _: bool, _: bool) {} } impl S { @@ -34,8 +39,11 @@ impl S { } impl Trait for S { + // Should not lint because the trait might not be changeable by the user + // We only lint in the trait definition fn f(_: bool, _: bool, _: bool, _: bool) {} fn g(_: bool, _: bool, _: bool, _: Vec) {} + fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool) {} } fn main() { diff --git a/tests/ui/fn_params_excessive_bools.stderr b/tests/ui/fn_params_excessive_bools.stderr index 11627105691b..43363b46972c 100644 --- a/tests/ui/fn_params_excessive_bools.stderr +++ b/tests/ui/fn_params_excessive_bools.stderr @@ -1,5 +1,5 @@ error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:18:1 + --> $DIR/fn_params_excessive_bools.rs:19:1 | LL | fn g(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | fn g(_: bool, _: bool, _: bool, _: bool) {} = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings` error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:21:1 + --> $DIR/fn_params_excessive_bools.rs:22:1 | LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:25:5 + --> $DIR/fn_params_excessive_bools.rs:27:5 | LL | fn f(_: bool, _: bool, _: bool, _: bool); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,15 @@ LL | fn f(_: bool, _: bool, _: bool, _: bool); = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:30:5 + --> $DIR/fn_params_excessive_bools.rs:31:5 + | +LL | fn i(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:35:5 | LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +40,7 @@ LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:42:5 + --> $DIR/fn_params_excessive_bools.rs:50:5 | LL | / fn n(_: bool, _: u32, _: bool, _: Box, _: bool, _: bool) { LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} @@ -42,12 +50,12 @@ LL | | } = help: consider refactoring bools into two-variant enums error: more than 3 bools in function parameters - --> $DIR/fn_params_excessive_bools.rs:43:9 + --> $DIR/fn_params_excessive_bools.rs:51:9 | LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider refactoring bools into two-variant enums -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index efcaffce24ea..55fb3788a8b1 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -8,6 +8,7 @@ clippy::for_kv_map )] #[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)] +#[allow(for_loops_over_fallibles)] fn main() { let vec = vec![1, 2, 3, 4]; diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index f769b4bdc941..50a86eaa68f7 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,5 +1,5 @@ error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_unfixable.rs:14:15 + --> $DIR/for_loop_unfixable.rs:15:15 | LL | for _v in vec.iter().next() {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs deleted file mode 100644 index 4b2a9297d084..000000000000 --- a/tests/ui/for_loops_over_fallibles.rs +++ /dev/null @@ -1,73 +0,0 @@ -#![warn(clippy::for_loops_over_fallibles)] -#![allow(clippy::uninlined_format_args)] - -fn for_loops_over_fallibles() { - let option = Some(1); - let mut result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check over an `Option` - for x in option { - println!("{}", x); - } - - // check over an `Option` - for x in option.iter() { - println!("{}", x); - } - - // check over a `Result` - for x in result { - println!("{}", x); - } - - // check over a `Result` - for x in result.iter_mut() { - println!("{}", x); - } - - // check over a `Result` - for x in result.into_iter() { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr deleted file mode 100644 index f09adccabd1a..000000000000 --- a/tests/ui/for_loops_over_fallibles.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:10:14 - | -LL | for x in option { - | ^^^^^^ - | - = help: consider replacing `for x in option` with `if let Some(x) = option` - = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` - -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:15:14 - | -LL | for x in option.iter() { - | ^^^^^^ - | - = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:20:14 - | -LL | for x in result { - | ^^^^^^ - | - = help: consider replacing `for x in result` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:25:14 - | -LL | for x in result.iter_mut() { - | ^^^^^^ - | - = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:30:14 - | -LL | for x in result.into_iter() { - | ^^^^^^ - | - = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` - -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:34:14 - | -LL | for x in option.ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` - -error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:40:14 - | -LL | for x in v.iter().next() { - | ^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::iter_next_loop)]` on by default - -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:45:14 - | -LL | for x in v.iter().next().and(Some(0)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` - -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:49:14 - | -LL | for x in v.iter().next().ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:61:5 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:67:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 11 previous errors - diff --git a/tests/ui/from_over_into.fixed b/tests/ui/from_over_into.fixed new file mode 100644 index 000000000000..72d635c2ccd6 --- /dev/null +++ b/tests/ui/from_over_into.fixed @@ -0,0 +1,91 @@ +// run-rustfix + +#![feature(type_alias_impl_trait)] +#![warn(clippy::from_over_into)] +#![allow(unused)] + +// this should throw an error +struct StringWrapper(String); + +impl From for StringWrapper { + fn from(val: String) -> Self { + StringWrapper(val) + } +} + +struct SelfType(String); + +impl From for SelfType { + fn from(val: String) -> Self { + SelfType(String::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl From for SelfKeywords { + fn from(val: X) -> Self { + let _ = X::default(); + let _ = X::FOO; + let _: X = val; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::From for bool { + fn from(mut val: crate::ExplicitPaths) -> Self { + let in_closure = || val.0; + + val.0 = false; + val.0 + } +} + +// this is fine +struct A(String); + +impl From for A { + fn from(s: String) -> A { + A(s) + } +} + +#[clippy::msrv = "1.40"] +fn msrv_1_40() { + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +#[clippy::msrv = "1.41"] +fn msrv_1_41() { + struct FromOverInto(Vec); + + impl From> for FromOverInto { + fn from(val: Vec) -> Self { + FromOverInto(val) + } + } +} + +type Opaque = impl Sized; +struct IntoOpaque; +impl Into for IntoOpaque { + fn into(self) -> Opaque {} +} + +fn main() {} diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs index 292d0924fb17..965f4d5d7859 100644 --- a/tests/ui/from_over_into.rs +++ b/tests/ui/from_over_into.rs @@ -1,4 +1,8 @@ +// run-rustfix + +#![feature(type_alias_impl_trait)] #![warn(clippy::from_over_into)] +#![allow(unused)] // this should throw an error struct StringWrapper(String); @@ -9,6 +13,44 @@ impl Into for String { } } +struct SelfType(String); + +impl Into for String { + fn into(self) -> SelfType { + SelfType(Self::new()) + } +} + +#[derive(Default)] +struct X; + +impl X { + const FOO: &'static str = "a"; +} + +struct SelfKeywords; + +impl Into for X { + fn into(self) -> SelfKeywords { + let _ = Self::default(); + let _ = Self::FOO; + let _: Self = self; + + SelfKeywords + } +} + +struct ExplicitPaths(bool); + +impl core::convert::Into for crate::ExplicitPaths { + fn into(mut self) -> bool { + let in_closure = || self.0; + + self.0 = false; + self.0 + } +} + // this is fine struct A(String); @@ -18,4 +60,32 @@ impl From for A { } } +#[clippy::msrv = "1.40"] +fn msrv_1_40() { + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +#[clippy::msrv = "1.41"] +fn msrv_1_41() { + struct FromOverInto(Vec); + + impl Into> for Vec { + fn into(self) -> FromOverInto { + FromOverInto(self) + } + } +} + +type Opaque = impl Sized; +struct IntoOpaque; +impl Into for IntoOpaque { + fn into(self) -> Opaque {} +} + fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 469adadd2196..3c4d011d6fb4 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,11 +1,75 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true - --> $DIR/from_over_into.rs:6:1 + --> $DIR/from_over_into.rs:10:1 | LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider to implement `From` instead = note: `-D clippy::from-over-into` implied by `-D warnings` +help: replace the `Into` implentation with `From` + | +LL ~ impl From for StringWrapper { +LL ~ fn from(val: String) -> Self { +LL ~ StringWrapper(val) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:18:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfType { +LL ~ fn from(val: String) -> Self { +LL ~ SelfType(String::new()) + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:33:1 + | +LL | impl Into for X { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From` + | +LL ~ impl From for SelfKeywords { +LL ~ fn from(val: X) -> Self { +LL ~ let _ = X::default(); +LL ~ let _ = X::FOO; +LL ~ let _: X = val; + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:45:1 + | +LL | impl core::convert::Into for crate::ExplicitPaths { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence +help: replace the `Into` implentation with `From` + | +LL ~ impl core::convert::From for bool { +LL ~ fn from(mut val: crate::ExplicitPaths) -> Self { +LL ~ let in_closure = || val.0; +LL | +LL ~ val.0 = false; +LL ~ val.0 + | + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:78:5 + | +LL | impl Into> for Vec { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace the `Into` implentation with `From>` + | +LL ~ impl From> for FromOverInto { +LL ~ fn from(val: Vec) -> Self { +LL ~ FromOverInto(val) + | -error: aborting due to previous error +error: aborting due to 5 previous errors diff --git a/tests/ui/from_over_into_unfixable.rs b/tests/ui/from_over_into_unfixable.rs new file mode 100644 index 000000000000..3b280b7488ae --- /dev/null +++ b/tests/ui/from_over_into_unfixable.rs @@ -0,0 +1,35 @@ +#![warn(clippy::from_over_into)] + +struct InMacro(String); + +macro_rules! in_macro { + ($e:ident) => { + $e + }; +} + +impl Into for String { + fn into(self) -> InMacro { + InMacro(in_macro!(self)) + } +} + +struct WeirdUpperSelf; + +impl Into for &'static [u8] { + fn into(self) -> WeirdUpperSelf { + let _ = Self::default(); + WeirdUpperSelf + } +} + +struct ContainsVal; + +impl Into for ContainsVal { + fn into(self) -> u8 { + let val = 1; + val + 1 + } +} + +fn main() {} diff --git a/tests/ui/from_over_into_unfixable.stderr b/tests/ui/from_over_into_unfixable.stderr new file mode 100644 index 000000000000..6f6ce351921b --- /dev/null +++ b/tests/ui/from_over_into_unfixable.stderr @@ -0,0 +1,29 @@ +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:11:1 + | +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From` + = note: `-D clippy::from-over-into` implied by `-D warnings` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:19:1 + | +LL | impl Into for &'static [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace the `Into` implentation with `From<&'static [u8]>` + +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into_unfixable.rs:28:1 + | +LL | impl Into for ContainsVal { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `impl From for Foreign` is allowed by the orphan rules, for more information see + https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence + = help: replace the `Into` implentation with `From` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/from_raw_with_void_ptr.rs b/tests/ui/from_raw_with_void_ptr.rs new file mode 100644 index 000000000000..8484da2415ab --- /dev/null +++ b/tests/ui/from_raw_with_void_ptr.rs @@ -0,0 +1,34 @@ +#![warn(clippy::from_raw_with_void_ptr)] + +use std::ffi::c_void; +use std::rc::Rc; +use std::sync::Arc; + +fn main() { + // must lint + let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + let _ = unsafe { Box::from_raw(ptr) }; + + // shouldn't be linted + let _ = unsafe { Box::from_raw(ptr as *mut usize) }; + + // shouldn't be linted + let should_not_lint_ptr = Box::into_raw(Box::new(12u8)) as *mut u8; + let _ = unsafe { Box::from_raw(should_not_lint_ptr as *mut u8) }; + + // must lint + let ptr = Rc::into_raw(Rc::new(42usize)) as *mut c_void; + let _ = unsafe { Rc::from_raw(ptr) }; + + // must lint + let ptr = Arc::into_raw(Arc::new(42usize)) as *mut c_void; + let _ = unsafe { Arc::from_raw(ptr) }; + + // must lint + let ptr = std::rc::Weak::into_raw(Rc::downgrade(&Rc::new(42usize))) as *mut c_void; + let _ = unsafe { std::rc::Weak::from_raw(ptr) }; + + // must lint + let ptr = std::sync::Weak::into_raw(Arc::downgrade(&Arc::new(42usize))) as *mut c_void; + let _ = unsafe { std::sync::Weak::from_raw(ptr) }; +} diff --git a/tests/ui/from_raw_with_void_ptr.stderr b/tests/ui/from_raw_with_void_ptr.stderr new file mode 100644 index 000000000000..96e4af12ba38 --- /dev/null +++ b/tests/ui/from_raw_with_void_ptr.stderr @@ -0,0 +1,63 @@ +error: creating a `Box` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:10:22 + | +LL | let _ = unsafe { Box::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:10:36 + | +LL | let _ = unsafe { Box::from_raw(ptr) }; + | ^^^ + = note: `-D clippy::from-raw-with-void-ptr` implied by `-D warnings` + +error: creating a `Rc` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:21:22 + | +LL | let _ = unsafe { Rc::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:21:35 + | +LL | let _ = unsafe { Rc::from_raw(ptr) }; + | ^^^ + +error: creating a `Arc` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:25:22 + | +LL | let _ = unsafe { Arc::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:25:36 + | +LL | let _ = unsafe { Arc::from_raw(ptr) }; + | ^^^ + +error: creating a `Weak` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:29:22 + | +LL | let _ = unsafe { std::rc::Weak::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:29:46 + | +LL | let _ = unsafe { std::rc::Weak::from_raw(ptr) }; + | ^^^ + +error: creating a `Weak` from a void raw pointer + --> $DIR/from_raw_with_void_ptr.rs:33:22 + | +LL | let _ = unsafe { std::sync::Weak::from_raw(ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: cast this to a pointer of the appropriate type + --> $DIR/from_raw_with_void_ptr.rs:33:48 + | +LL | let _ = unsafe { std::sync::Weak::from_raw(ptr) }; + | ^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 937f85904083..6dee4d5b4b62 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); @@ -25,7 +25,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); @@ -39,7 +39,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); @@ -53,7 +53,7 @@ error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); @@ -67,7 +67,7 @@ error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); @@ -81,7 +81,7 @@ error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); @@ -95,7 +95,7 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more co LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); @@ -109,7 +109,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; @@ -123,7 +123,7 @@ error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and mor LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; @@ -137,7 +137,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; @@ -151,7 +151,7 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; @@ -165,7 +165,7 @@ error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more conc LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); @@ -179,7 +179,7 @@ error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed index e7b9a78c5dbc..cac69ef42c41 100644 --- a/tests/ui/identity_op.fixed +++ b/tests/ui/identity_op.fixed @@ -65,7 +65,7 @@ fn main() { 42; 1; 42; - &x; + x; x; let mut a = A(String::new()); @@ -112,6 +112,10 @@ fn main() { 2 * { a }; (({ a } + 4)); 1; + + // Issue #9904 + let x = 0i32; + let _: i32 = x; } pub fn decide(a: bool, b: bool) -> u32 { diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 9a435cdbb753..33201aad4f64 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -112,6 +112,10 @@ fn main() { 2 * (0 + { a }); 1 * ({ a } + 4); 1 * 1; + + // Issue #9904 + let x = 0i32; + let _: i32 = &x + 0; } pub fn decide(a: bool, b: bool) -> u32 { diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 1a104a20b841..3ba557d18b24 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -70,7 +70,7 @@ error: this operation has no effect --> $DIR/identity_op.rs:68:5 | LL | &x >> 0; - | ^^^^^^^ help: consider reducing it to: `&x` + | ^^^^^^^ help: consider reducing it to: `x` error: this operation has no effect --> $DIR/identity_op.rs:69:5 @@ -229,10 +229,16 @@ LL | 1 * 1; | ^^^^^ help: consider reducing it to: `1` error: this operation has no effect - --> $DIR/identity_op.rs:118:5 + --> $DIR/identity_op.rs:118:18 + | +LL | let _: i32 = &x + 0; + | ^^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:122:5 | LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` -error: aborting due to 39 previous errors +error: aborting due to 40 previous errors diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 3bc3a0395244..0e89fdb0dfa2 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -1,5 +1,4 @@ #![warn(clippy::if_then_some_else_none)] -#![feature(custom_inner_attributes)] fn main() { // Should issue an error. @@ -66,8 +65,8 @@ fn main() { let _ = if foo() { into_some("foo") } else { None }; } +#[clippy::msrv = "1.49"] fn _msrv_1_49() { - #![clippy::msrv = "1.49"] // `bool::then` was stabilized in 1.50. Do not lint this let _ = if foo() { println!("true!"); @@ -77,8 +76,8 @@ fn _msrv_1_49() { }; } +#[clippy::msrv = "1.50"] fn _msrv_1_50() { - #![clippy::msrv = "1.50"] let _ = if foo() { println!("true!"); Some(150) diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index 24e0b5947f19..d728a3c31a3b 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -1,5 +1,5 @@ error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:6:13 + --> $DIR/if_then_some_else_none.rs:5:13 | LL | let _ = if foo() { | _____________^ @@ -14,7 +14,7 @@ LL | | }; = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:14:13 + --> $DIR/if_then_some_else_none.rs:13:13 | LL | let _ = if matches!(true, true) { | _____________^ @@ -28,7 +28,7 @@ LL | | }; = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` error: this could be simplified with `bool::then_some` - --> $DIR/if_then_some_else_none.rs:23:28 + --> $DIR/if_then_some_else_none.rs:22:28 | LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); = help: consider using `bool::then_some` like: `(o < 32).then_some(o)` error: this could be simplified with `bool::then_some` - --> $DIR/if_then_some_else_none.rs:27:13 + --> $DIR/if_then_some_else_none.rs:26:13 | LL | let _ = if !x { Some(0) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = if !x { Some(0) } else { None }; = help: consider using `bool::then_some` like: `(!x).then_some(0)` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:82:13 + --> $DIR/if_then_some_else_none.rs:81:13 | LL | let _ = if foo() { | _____________^ diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed index 33770fc2a2cf..51b1afbe5ac8 100644 --- a/tests/ui/implicit_clone.fixed +++ b/tests/ui/implicit_clone.fixed @@ -115,4 +115,14 @@ fn main() { let pathbuf_ref = &pathbuf_ref; let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` let _ = (**pathbuf_ref).clone(); + + struct NoClone; + impl ToOwned for NoClone { + type Owned = Self; + fn to_owned(&self) -> Self { + NoClone + } + } + let no_clone = &NoClone; + let _ = no_clone.to_owned(); } diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index fc896525bd27..8a9027433d95 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -115,4 +115,14 @@ fn main() { let pathbuf_ref = &pathbuf_ref; let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` let _ = pathbuf_ref.to_path_buf(); + + struct NoClone; + impl ToOwned for NoClone { + type Owned = Self; + fn to_owned(&self) -> Self { + NoClone + } + } + let no_clone = &NoClone; + let _ = no_clone.to_owned(); } diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 7ebf6ee993cb..26abc9edb5e4 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -3,10 +3,10 @@ // We also check the out_of_bounds_indexing lint here, because it lints similar things and // we want to avoid false positives. #![warn(clippy::out_of_bounds_indexing)] -#![allow(const_err, unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)] const ARR: [i32; 2] = [1, 2]; -const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr. +const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. const fn idx() -> usize { @@ -27,8 +27,8 @@ fn main() { x[3]; // Ok, should not produce stderr. x[const { idx() }]; // Ok, should not produce stderr. x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays. - const { &ARR[idx()] }; // Ok, should not produce stderr. - const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. + const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. let y = &x; y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index a8d8b38163d0..8fd77913a3fd 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,14 +1,33 @@ +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:9:20 + | +LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + | ^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + error[E0080]: evaluation of `main::{constant#3}` failed --> $DIR/indexing_slicing_index.rs:31:14 | -LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 -error[E0080]: erroneous constant used +note: erroneous constant used --> $DIR/indexing_slicing_index.rs:31:5 | -LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. - | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + | ^^^^^^^^^^^^^^^^^^^^^^ error: indexing may panic --> $DIR/indexing_slicing_index.rs:22:5 @@ -17,7 +36,24 @@ LL | x[index]; | ^^^^^^^^ | = help: consider using `.get(n)` or `.get_mut(n)` instead - = note: `-D clippy::indexing-slicing` implied by `-D warnings` + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:30:14 + | +LL | const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + | ^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks + +error: indexing may panic + --> $DIR/indexing_slicing_index.rs:31:14 + | +LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false. + | ^^^^^^^^^^^ + | + = help: consider using `.get(n)` or `.get_mut(n)` instead + = note: the suggestion might not be applicable in constant blocks error: indexing may panic --> $DIR/indexing_slicing_index.rs:38:5 @@ -59,6 +95,12 @@ LL | v[M]; | = help: consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 8 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/indexing_slicing_index.rs:10:24 + | +LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts. + | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4 + +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/infallible_destructuring_match.fixed b/tests/ui/infallible_destructuring_match.fixed index b8e40d995531..61985e56b769 100644 --- a/tests/ui/infallible_destructuring_match.fixed +++ b/tests/ui/infallible_destructuring_match.fixed @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -71,6 +74,15 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let TupleStructWithNonCopy(ref data) = wrapper; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.rs b/tests/ui/infallible_destructuring_match.rs index 106cd438b90e..f2768245bbc4 100644 --- a/tests/ui/infallible_destructuring_match.rs +++ b/tests/ui/infallible_destructuring_match.rs @@ -9,6 +9,9 @@ enum SingleVariantEnum { struct TupleStruct(i32); +struct NonCopy; +struct TupleStructWithNonCopy(NonCopy); + enum EmptyEnum {} macro_rules! match_enum { @@ -75,6 +78,17 @@ fn infallible_destructuring_match_struct() { let TupleStruct(data) = wrapper; } +fn infallible_destructuring_match_struct_with_noncopy() { + let wrapper = TupleStructWithNonCopy(NonCopy); + + // This should lint! (keeping `ref` in the suggestion) + let data = match wrapper { + TupleStructWithNonCopy(ref n) => n, + }; + + let TupleStructWithNonCopy(ref data) = wrapper; +} + macro_rules! match_never_enum { ($param:expr) => { let data = match $param { diff --git a/tests/ui/infallible_destructuring_match.stderr b/tests/ui/infallible_destructuring_match.stderr index 1b78db42014a..f8a50f0223d6 100644 --- a/tests/ui/infallible_destructuring_match.stderr +++ b/tests/ui/infallible_destructuring_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:26:5 + --> $DIR/infallible_destructuring_match.rs:29:5 | LL | / let data = match wrapper { LL | | SingleVariantEnum::Variant(i) => i, @@ -9,7 +9,7 @@ LL | | }; = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:58:5 + --> $DIR/infallible_destructuring_match.rs:61:5 | LL | / let data = match wrapper { LL | | TupleStruct(i) => i, @@ -17,12 +17,20 @@ LL | | }; | |______^ help: try this: `let TupleStruct(data) = wrapper;` error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` - --> $DIR/infallible_destructuring_match.rs:90:5 + --> $DIR/infallible_destructuring_match.rs:85:5 + | +LL | / let data = match wrapper { +LL | | TupleStructWithNonCopy(ref n) => n, +LL | | }; + | |______^ help: try this: `let TupleStructWithNonCopy(ref data) = wrapper;` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:104:5 | LL | / let data = match wrapper { LL | | Ok(i) => i, LL | | }; | |______^ help: try this: `let Ok(data) = wrapper;` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/issue_4266.stderr b/tests/ui/issue_4266.stderr index fb2a93c9580e..fd553aa4538a 100644 --- a/tests/ui/issue_4266.stderr +++ b/tests/ui/issue_4266.stderr @@ -1,4 +1,4 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/issue_4266.rs:4:1 | LL | async fn sink1<'a>(_: &'a str) {} // lint @@ -6,7 +6,7 @@ LL | async fn sink1<'a>(_: &'a str) {} // lint | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) +error: the following explicit lifetimes could be elided: 'a --> $DIR/issue_4266.rs:8:1 | LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 83fee04080fa..f2a4c284cb16 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -1,14 +1,15 @@ // run-rustfix #![warn(clippy::iter_kv_map)] -#![allow(clippy::redundant_clone)] -#![allow(clippy::suspicious_map)] -#![allow(clippy::map_identity)] +#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] use std::collections::{BTreeMap, HashMap}; fn main() { let get_key = |(key, _val)| key; + fn ref_acceptor(v: &u32) -> u32 { + *v + } let map: HashMap = HashMap::new(); @@ -36,6 +37,20 @@ fn main() { let _ = map.keys().map(|key| key * 9).count(); let _ = map.values().map(|value| value * 17).count(); + // Preserve the ref in the fix. + let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone().into_values().map(|mut val| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_values().count(); + let map: BTreeMap = BTreeMap::new(); let _ = map.keys().collect::>(); @@ -61,4 +76,18 @@ fn main() { // Lint let _ = map.keys().map(|key| key * 9).count(); let _ = map.values().map(|value| value * 17).count(); + + // Preserve the ref in the fix. + let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone().into_values().map(|mut val| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_values().count(); } diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index 7a1f1fb0198c..ad6564df4084 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -1,14 +1,15 @@ // run-rustfix #![warn(clippy::iter_kv_map)] -#![allow(clippy::redundant_clone)] -#![allow(clippy::suspicious_map)] -#![allow(clippy::map_identity)] +#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] use std::collections::{BTreeMap, HashMap}; fn main() { let get_key = |(key, _val)| key; + fn ref_acceptor(v: &u32) -> u32 { + *v + } let map: HashMap = HashMap::new(); @@ -36,6 +37,22 @@ fn main() { let _ = map.iter().map(|(key, _value)| key * 9).count(); let _ = map.iter().map(|(_key, value)| value * 17).count(); + // Preserve the ref in the fix. + let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone() + .into_iter() + .map(|(_, mut val)| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + let map: BTreeMap = BTreeMap::new(); let _ = map.iter().map(|(key, _)| key).collect::>(); @@ -61,4 +78,20 @@ fn main() { // Lint let _ = map.iter().map(|(key, _value)| key * 9).count(); let _ = map.iter().map(|(_key, value)| value * 17).count(); + + // Preserve the ref in the fix. + let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + + // Preserve the mut in the fix. + let _ = map + .clone() + .into_iter() + .map(|(_, mut val)| { + val += 2; + val + }) + .count(); + + // Don't let a mut interfere. + let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); } diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index 9b9b04c97d81..e00da223b4dd 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -1,5 +1,5 @@ error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:15:13 + --> $DIR/iter_kv_map.rs:16:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` @@ -7,130 +7,198 @@ LL | let _ = map.iter().map(|(key, _)| key).collect::>(); = note: `-D clippy::iter-kv-map` implied by `-D warnings` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:16:13 + --> $DIR/iter_kv_map.rs:17:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:17:13 + --> $DIR/iter_kv_map.rs:18:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:19:13 + --> $DIR/iter_kv_map.rs:20:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:20:13 + --> $DIR/iter_kv_map.rs:21:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:22:13 + --> $DIR/iter_kv_map.rs:23:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:23:13 + --> $DIR/iter_kv_map.rs:24:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:25:13 + --> $DIR/iter_kv_map.rs:26:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:26:13 + --> $DIR/iter_kv_map.rs:27:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:36:13 + --> $DIR/iter_kv_map.rs:37:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:37:13 + --> $DIR/iter_kv_map.rs:38:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` -error: iterating on a map's keys +error: iterating on a map's values --> $DIR/iter_kv_map.rs:41:13 | +LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:44:13 + | +LL | let _ = map + | _____________^ +LL | | .clone() +LL | | .into_iter() +LL | | .map(|(_, mut val)| { +LL | | val += 2; +LL | | val +LL | | }) + | |__________^ + | +help: try + | +LL ~ let _ = map +LL + .clone().into_values().map(|mut val| { +LL + val += 2; +LL + val +LL + }) + | + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:54:13 + | +LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: iterating on a map's keys + --> $DIR/iter_kv_map.rs:58:13 + | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:42:13 + --> $DIR/iter_kv_map.rs:59:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:43:13 + --> $DIR/iter_kv_map.rs:60:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:45:13 + --> $DIR/iter_kv_map.rs:62:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:46:13 + --> $DIR/iter_kv_map.rs:63:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:48:13 + --> $DIR/iter_kv_map.rs:65:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:49:13 + --> $DIR/iter_kv_map.rs:66:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:51:13 + --> $DIR/iter_kv_map.rs:68:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:52:13 + --> $DIR/iter_kv_map.rs:69:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> $DIR/iter_kv_map.rs:62:13 + --> $DIR/iter_kv_map.rs:79:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> $DIR/iter_kv_map.rs:63:13 + --> $DIR/iter_kv_map.rs:80:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` -error: aborting due to 22 previous errors +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:83:13 + | +LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:86:13 + | +LL | let _ = map + | _____________^ +LL | | .clone() +LL | | .into_iter() +LL | | .map(|(_, mut val)| { +LL | | val += 2; +LL | | val +LL | | }) + | |__________^ + | +help: try + | +LL ~ let _ = map +LL + .clone().into_values().map(|mut val| { +LL + val += 2; +LL + val +LL + }) + | + +error: iterating on a map's values + --> $DIR/iter_kv_map.rs:96:13 + | +LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` + +error: aborting due to 28 previous errors diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 78397c2af346..b5dec6c46bdd 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -282,4 +282,50 @@ impl AsyncLen { } } +// issue #9520 +pub struct NonStandardLenAndIsEmptySignature; +impl NonStandardLenAndIsEmptySignature { + // don't lint + pub fn len(&self, something: usize) -> usize { + something + } + + pub fn is_empty(&self, something: usize) -> bool { + something == 0 + } +} + +// test case for #9520 with generics in the function signature +pub trait TestResource { + type NonStandardSignatureWithGenerics: Copy; + fn lookup_content(&self, item: Self::NonStandardSignatureWithGenerics) -> Result, String>; +} +pub struct NonStandardSignatureWithGenerics(u32); +impl NonStandardSignatureWithGenerics { + pub fn is_empty(self, resource: &T) -> bool + where + T: TestResource, + U: Copy + From, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.is_empty() + } else { + true + } + } + + // test case for #9520 with generics in the function signature + pub fn len(self, resource: &T) -> usize + where + T: TestResource, + U: Copy + From, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.len() + } else { + 0_usize + } + } +} + fn main() {} diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 1f3b8ac99b19..c1c0b5ae40f6 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -3,6 +3,9 @@ #![warn(clippy::len_zero)] #![allow(dead_code, unused, clippy::len_without_is_empty)] +extern crate core; +use core::ops::Deref; + pub struct One; struct Wither; @@ -56,6 +59,26 @@ impl WithIsEmpty for Wither { } } +struct DerefToDerefToString; + +impl Deref for DerefToDerefToString { + type Target = DerefToString; + + fn deref(&self) -> &Self::Target { + &DerefToString {} + } +} + +struct DerefToString; + +impl Deref for DerefToString { + type Target = str; + + fn deref(&self) -> &Self::Target { + "Hello, world!" + } +} + fn main() { let x = [1, 2]; if x.is_empty() { @@ -64,6 +87,23 @@ fn main() { if "".is_empty() {} + let s = "Hello, world!"; + let s1 = &s; + let s2 = &s1; + let s3 = &s2; + let s4 = &s3; + let s5 = &s4; + let s6 = &s5; + println!("{}", s1.is_empty()); + println!("{}", s2.is_empty()); + println!("{}", s3.is_empty()); + println!("{}", s4.is_empty()); + println!("{}", s5.is_empty()); + println!("{}", (s6).is_empty()); + + let d2s = DerefToDerefToString {}; + println!("{}", (**d2s).is_empty()); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index dc21de0001b6..cc2eb05b6bfd 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -3,6 +3,9 @@ #![warn(clippy::len_zero)] #![allow(dead_code, unused, clippy::len_without_is_empty)] +extern crate core; +use core::ops::Deref; + pub struct One; struct Wither; @@ -56,6 +59,26 @@ impl WithIsEmpty for Wither { } } +struct DerefToDerefToString; + +impl Deref for DerefToDerefToString { + type Target = DerefToString; + + fn deref(&self) -> &Self::Target { + &DerefToString {} + } +} + +struct DerefToString; + +impl Deref for DerefToString { + type Target = str; + + fn deref(&self) -> &Self::Target { + "Hello, world!" + } +} + fn main() { let x = [1, 2]; if x.len() == 0 { @@ -64,6 +87,23 @@ fn main() { if "".len() == 0 {} + let s = "Hello, world!"; + let s1 = &s; + let s2 = &s1; + let s3 = &s2; + let s4 = &s3; + let s5 = &s4; + let s6 = &s5; + println!("{}", *s1 == ""); + println!("{}", **s2 == ""); + println!("{}", ***s3 == ""); + println!("{}", ****s4 == ""); + println!("{}", *****s5 == ""); + println!("{}", ******(s6) == ""); + + let d2s = DerefToDerefToString {}; + println!("{}", &**d2s == ""); + let y = One; if y.len() == 0 { // No error; `One` does not have `.is_empty()`. diff --git a/tests/ui/len_zero.stderr b/tests/ui/len_zero.stderr index 6c71f1beeac6..b6f13780253c 100644 --- a/tests/ui/len_zero.stderr +++ b/tests/ui/len_zero.stderr @@ -1,5 +1,5 @@ error: length comparison to zero - --> $DIR/len_zero.rs:61:8 + --> $DIR/len_zero.rs:84:8 | LL | if x.len() == 0 { | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` @@ -7,82 +7,126 @@ LL | if x.len() == 0 { = note: `-D clippy::len-zero` implied by `-D warnings` error: length comparison to zero - --> $DIR/len_zero.rs:65:8 + --> $DIR/len_zero.rs:88:8 | LL | if "".len() == 0 {} | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` +error: comparison to empty slice + --> $DIR/len_zero.rs:97:20 + | +LL | println!("{}", *s1 == ""); + | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()` + | + = note: `-D clippy::comparison-to-empty` implied by `-D warnings` + +error: comparison to empty slice + --> $DIR/len_zero.rs:98:20 + | +LL | println!("{}", **s2 == ""); + | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:99:20 + | +LL | println!("{}", ***s3 == ""); + | ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:100:20 + | +LL | println!("{}", ****s4 == ""); + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:101:20 + | +LL | println!("{}", *****s5 == ""); + | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:102:20 + | +LL | println!("{}", ******(s6) == ""); + | ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()` + +error: comparison to empty slice + --> $DIR/len_zero.rs:105:20 + | +LL | println!("{}", &**d2s == ""); + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` + error: length comparison to zero - --> $DIR/len_zero.rs:80:8 + --> $DIR/len_zero.rs:120:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:83:8 + --> $DIR/len_zero.rs:123:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:86:8 + --> $DIR/len_zero.rs:126:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:89:8 + --> $DIR/len_zero.rs:129:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:92:8 + --> $DIR/len_zero.rs:132:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:103:8 + --> $DIR/len_zero.rs:143:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:106:8 + --> $DIR/len_zero.rs:146:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:109:8 + --> $DIR/len_zero.rs:149:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:112:8 + --> $DIR/len_zero.rs:152:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> $DIR/len_zero.rs:115:8 + --> $DIR/len_zero.rs:155:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:129:8 + --> $DIR/len_zero.rs:169:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> $DIR/len_zero.rs:142:8 + --> $DIR/len_zero.rs:182:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` -error: aborting due to 14 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs deleted file mode 100644 index 11b50492ab29..000000000000 --- a/tests/ui/let_underscore_drop.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![warn(clippy::let_underscore_drop)] -#![allow(clippy::let_unit_value)] - -struct Droppable; - -impl Drop for Droppable { - fn drop(&mut self) {} -} - -fn main() { - let unit = (); - let boxed = Box::new(()); - let droppable = Droppable; - let optional = Some(Droppable); - - let _ = (); - let _ = Box::new(()); - let _ = Droppable; - let _ = Some(Droppable); - - // no lint for reference - let _ = droppable_ref(); -} - -#[must_use] -fn droppable_ref() -> &'static mut Droppable { - unimplemented!() -} diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr deleted file mode 100644 index 324b7cd431d4..000000000000 --- a/tests/ui/let_underscore_drop.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:17:5 - | -LL | let _ = Box::new(()); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - -error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:18:5 - | -LL | let _ = Droppable; - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:19:5 - | -LL | let _ = Some(Droppable); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 3 previous errors - diff --git a/tests/ui/let_underscore_future.rs b/tests/ui/let_underscore_future.rs new file mode 100644 index 000000000000..d8f54cdca912 --- /dev/null +++ b/tests/ui/let_underscore_future.rs @@ -0,0 +1,20 @@ +use std::future::Future; + +async fn some_async_fn() {} + +fn sync_side_effects() {} +fn custom() -> impl Future { + sync_side_effects(); + async {} +} + +fn do_something_to_future(future: &mut impl Future) {} + +fn main() { + let _ = some_async_fn(); + let _ = custom(); + + let mut future = some_async_fn(); + do_something_to_future(&mut future); + let _ = future; +} diff --git a/tests/ui/let_underscore_future.stderr b/tests/ui/let_underscore_future.stderr new file mode 100644 index 000000000000..33a748736a88 --- /dev/null +++ b/tests/ui/let_underscore_future.stderr @@ -0,0 +1,27 @@ +error: non-binding `let` on a future + --> $DIR/let_underscore_future.rs:14:5 + | +LL | let _ = some_async_fn(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider awaiting the future or dropping explicitly with `std::mem::drop` + = note: `-D clippy::let-underscore-future` implied by `-D warnings` + +error: non-binding `let` on a future + --> $DIR/let_underscore_future.rs:15:5 + | +LL | let _ = custom(); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider awaiting the future or dropping explicitly with `std::mem::drop` + +error: non-binding `let` on a future + --> $DIR/let_underscore_future.rs:19:5 + | +LL | let _ = future; + | ^^^^^^^^^^^^^^^ + | + = help: consider awaiting the future or dropping explicitly with `std::mem::drop` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/let_underscore_lock.rs b/tests/ui/let_underscore_lock.rs index 7a7c4e924995..4dff4d766bcc 100644 --- a/tests/ui/let_underscore_lock.rs +++ b/tests/ui/let_underscore_lock.rs @@ -3,20 +3,6 @@ extern crate parking_lot; fn main() { - let m = std::sync::Mutex::new(()); - let rw = std::sync::RwLock::new(()); - - let _ = m.lock(); - let _ = rw.read(); - let _ = rw.write(); - let _ = m.try_lock(); - let _ = rw.try_read(); - let _ = rw.try_write(); - - // These shouldn't throw an error. - let _ = m; - let _ = rw; - use parking_lot::{lock_api::RawMutex, Mutex, RwLock}; let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ()); @@ -34,3 +20,20 @@ fn main() { let _ = p_m1; let _ = p_rw; } + +fn uplifted() { + // shouldn't lint std locks as they were uplifted as rustc's `let_underscore_lock` + + let m = std::sync::Mutex::new(()); + let rw = std::sync::RwLock::new(()); + + let _ = m.lock(); + let _ = rw.read(); + let _ = rw.write(); + let _ = m.try_lock(); + let _ = rw.try_read(); + let _ = rw.try_write(); + + let _ = m; + let _ = rw; +} diff --git a/tests/ui/let_underscore_lock.stderr b/tests/ui/let_underscore_lock.stderr index d7779e7b6c48..f137d4112092 100644 --- a/tests/ui/let_underscore_lock.stderr +++ b/tests/ui/let_underscore_lock.stderr @@ -1,83 +1,35 @@ -error: non-binding let on a synchronization lock +error: non-binding `let` on a synchronization lock --> $DIR/let_underscore_lock.rs:9:5 | -LL | let _ = m.lock(); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - = note: `-D clippy::let-underscore-lock` implied by `-D warnings` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:10:5 - | -LL | let _ = rw.read(); - | ^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:11:5 - | -LL | let _ = rw.write(); - | ^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:12:5 - | -LL | let _ = m.try_lock(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:13:5 - | -LL | let _ = rw.try_read(); - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:14:5 - | -LL | let _ = rw.try_write(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:23:5 - | LL | let _ = p_m.lock(); | ^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + = note: `-D clippy::let-underscore-lock` implied by `-D warnings` -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:26:5 +error: non-binding `let` on a synchronization lock + --> $DIR/let_underscore_lock.rs:12:5 | LL | let _ = p_m1.lock(); | ^^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:29:5 +error: non-binding `let` on a synchronization lock + --> $DIR/let_underscore_lock.rs:15:5 | LL | let _ = p_rw.read(); | ^^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a synchronization lock - --> $DIR/let_underscore_lock.rs:30:5 +error: non-binding `let` on a synchronization lock + --> $DIR/let_underscore_lock.rs:16:5 | LL | let _ = p_rw.write(); | ^^^^^^^^^^^^^^^^^^^^^ | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: aborting due to 10 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/let_underscore_must_use.stderr b/tests/ui/let_underscore_must_use.stderr index bae60f2ff9b7..28d760eb46ec 100644 --- a/tests/ui/let_underscore_must_use.stderr +++ b/tests/ui/let_underscore_must_use.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:67:5 | LL | let _ = f(); @@ -7,7 +7,7 @@ LL | let _ = f(); = help: consider explicitly using function result = note: `-D clippy::let-underscore-must-use` implied by `-D warnings` -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:68:5 | LL | let _ = g(); @@ -15,7 +15,7 @@ LL | let _ = g(); | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:70:5 | LL | let _ = l(0_u32); @@ -23,7 +23,7 @@ LL | let _ = l(0_u32); | = help: consider explicitly using function result -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:74:5 | LL | let _ = s.f(); @@ -31,7 +31,7 @@ LL | let _ = s.f(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:75:5 | LL | let _ = s.g(); @@ -39,7 +39,7 @@ LL | let _ = s.g(); | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:78:5 | LL | let _ = S::h(); @@ -47,7 +47,7 @@ LL | let _ = S::h(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:79:5 | LL | let _ = S::p(); @@ -55,7 +55,7 @@ LL | let _ = S::p(); | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:81:5 | LL | let _ = S::a(); @@ -63,7 +63,7 @@ LL | let _ = S::a(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:83:5 | LL | let _ = if true { Ok(()) } else { Err(()) }; @@ -71,7 +71,7 @@ LL | let _ = if true { Ok(()) } else { Err(()) }; | = help: consider explicitly using expression value -error: non-binding let on a result of a `#[must_use]` function +error: non-binding `let` on a result of a `#[must_use]` function --> $DIR/let_underscore_must_use.rs:87:5 | LL | let _ = a.is_ok(); @@ -79,7 +79,7 @@ LL | let _ = a.is_ok(); | = help: consider explicitly using function result -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:89:5 | LL | let _ = a.map(|_| ()); @@ -87,7 +87,7 @@ LL | let _ = a.map(|_| ()); | = help: consider explicitly using expression value -error: non-binding let on an expression with `#[must_use]` type +error: non-binding `let` on an expression with `#[must_use]` type --> $DIR/let_underscore_must_use.rs:91:5 | LL | let _ = a; diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 0cadd5a3da19..1a646e49ce3a 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -40,3 +40,10 @@ fn main() { let ok26 = 0x6_A0_BF; let ok27 = 0b1_0010_0101; } + +fn issue9651() { + // lint but octal form is not possible here + let _ = 08; + let _ = 09; + let _ = 089; +} diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 365b24074735..603d47bacca8 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -135,5 +135,38 @@ error: digits of hex or binary literal not grouped by four LL | let fail25 = 0b01_100_101; | ^^^^^^^^^^^^ help: consider: `0b0110_0101` -error: aborting due to 18 previous errors +error: this is a decimal constant + --> $DIR/literals.rs:46:13 + | +LL | let _ = 08; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 8; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:47:13 + | +LL | let _ = 09; + | ^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 9; + | ~ + +error: this is a decimal constant + --> $DIR/literals.rs:48:13 + | +LL | let _ = 089; + | ^^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let _ = 89; + | ~~ + +error: aborting due to 21 previous errors diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index bf7b6edd0e31..61843124ccd9 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -1,8 +1,8 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:23:5 + --> $DIR/macro_use_imports.rs:25:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` | = note: `-D clippy::macro-use-imports` implied by `-D warnings` @@ -13,10 +13,10 @@ LL | #[macro_use] | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:25:5 + --> $DIR/macro_use_imports.rs:23:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` error: `macro_use` attributes are no longer needed in the Rust 2018 edition --> $DIR/macro_use_imports.rs:19:5 diff --git a/tests/ui/manual_assert.edition2018.fixed b/tests/ui/manual_assert.edition2018.fixed index c9a819ba5354..8c7e919bf62a 100644 --- a/tests/ui/manual_assert.edition2018.fixed +++ b/tests/ui/manual_assert.edition2018.fixed @@ -29,9 +29,7 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - if !a.is_empty() { - panic!("qwqwq"); - } + assert!(a.is_empty(), "qwqwq"); if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -46,32 +44,26 @@ fn main() { println!("qwq"); } let b = vec![1, 2, 3]; - if b.is_empty() { - panic!("panic1"); - } - if b.is_empty() && a.is_empty() { - panic!("panic2"); - } - if a.is_empty() && !b.is_empty() { - panic!("panic3"); - } - if b.is_empty() || a.is_empty() { - panic!("panic4"); - } - if a.is_empty() || !b.is_empty() { - panic!("panic5"); - } + assert!(!b.is_empty(), "panic1"); + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { // Suggestion should preserve comment - if a > 2 { - // comment - /* this is a + // comment +/* this is a multiline comment */ - /// Doc comment - panic!("panic with comment") // comment after `panic!` - } +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); } diff --git a/tests/ui/manual_assert.edition2018.stderr b/tests/ui/manual_assert.edition2018.stderr index c4e6bcd92fc1..3555ac29243a 100644 --- a/tests/ui/manual_assert.edition2018.stderr +++ b/tests/ui/manual_assert.edition2018.stderr @@ -4,26 +4,82 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:34:5 | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); +LL | / if !a.is_empty() { +LL | | panic!("qwqwq"); +LL | | } + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:51:5 | +LL | / if b.is_empty() { +LL | | panic!("panic1"); +LL | | } + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:54:5 + | +LL | / if b.is_empty() && a.is_empty() { +LL | | panic!("panic2"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:57:5 + | +LL | / if a.is_empty() && !b.is_empty() { +LL | | panic!("panic3"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:60:5 + | +LL | / if b.is_empty() || a.is_empty() { +LL | | panic!("panic4"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:63:5 + | +LL | / if a.is_empty() || !b.is_empty() { +LL | | panic!("panic5"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) +LL | | } + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:78:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` LL | | } | |_____^ | help: try instead | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); +LL | assert!(!(a > 2), "panic with comment"); | -error: aborting due to 2 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_assert.edition2021.fixed b/tests/ui/manual_assert.edition2021.fixed index 2f62de51cadc..8c7e919bf62a 100644 --- a/tests/ui/manual_assert.edition2021.fixed +++ b/tests/ui/manual_assert.edition2021.fixed @@ -50,6 +50,11 @@ fn main() { assert!(!(b.is_empty() || a.is_empty()), "panic4"); assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { diff --git a/tests/ui/manual_assert.edition2021.stderr b/tests/ui/manual_assert.edition2021.stderr index 7718588fdf6f..3555ac29243a 100644 --- a/tests/ui/manual_assert.edition2021.stderr +++ b/tests/ui/manual_assert.edition2021.stderr @@ -4,13 +4,9 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qaqaq{:?}", a); LL | | } - | |_____^ + | |_____^ help: try instead: `assert!(a.is_empty(), "qaqaq{:?}", a);` | = note: `-D clippy::manual-assert` implied by `-D warnings` -help: try instead - | -LL | assert!(a.is_empty(), "qaqaq{:?}", a); - | error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:34:5 @@ -18,12 +14,7 @@ error: only a `panic!` in `if`-then statement LL | / if !a.is_empty() { LL | | panic!("qwqwq"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(a.is_empty(), "qwqwq"); - | + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:51:5 @@ -31,12 +22,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() { LL | | panic!("panic1"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!b.is_empty(), "panic1"); - | + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:54:5 @@ -44,12 +30,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() && a.is_empty() { LL | | panic!("panic2"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() && a.is_empty()), "panic2"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:57:5 @@ -57,12 +38,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() && !b.is_empty() { LL | | panic!("panic3"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() && !b.is_empty()), "panic3"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:60:5 @@ -70,12 +46,7 @@ error: only a `panic!` in `if`-then statement LL | / if b.is_empty() || a.is_empty() { LL | | panic!("panic4"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(b.is_empty() || a.is_empty()), "panic4"); - | + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:63:5 @@ -83,12 +54,7 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() || !b.is_empty() { LL | | panic!("panic5"); LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!(a.is_empty() || !b.is_empty()), "panic5"); - | + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 @@ -96,15 +62,10 @@ error: only a `panic!` in `if`-then statement LL | / if a.is_empty() { LL | | panic!("with expansion {}", one!()) LL | | } - | |_____^ - | -help: try instead - | -LL | assert!(!a.is_empty(), "with expansion {}", one!()); - | + | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` error: only a `panic!` in `if`-then statement - --> $DIR/manual_assert.rs:73:5 + --> $DIR/manual_assert.rs:78:5 | LL | / if a > 2 { LL | | // comment diff --git a/tests/ui/manual_assert.rs b/tests/ui/manual_assert.rs index 6a4cc2468d41..f037c5b8405c 100644 --- a/tests/ui/manual_assert.rs +++ b/tests/ui/manual_assert.rs @@ -66,6 +66,11 @@ fn main() { if a.is_empty() { panic!("with expansion {}", one!()) } + if a.is_empty() { + let _ = 0; + } else if a.len() == 1 { + panic!("panic6"); + } } fn issue7730(a: u8) { diff --git a/tests/ui/manual_clamp.rs b/tests/ui/manual_clamp.rs index 54fd888af99f..f7902e6fd538 100644 --- a/tests/ui/manual_clamp.rs +++ b/tests/ui/manual_clamp.rs @@ -302,3 +302,27 @@ fn dont_tell_me_what_to_do() { fn cmp_min_max(input: i32) -> i32 { input * 3 } + +#[clippy::msrv = "1.49"] +fn msrv_1_49() { + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} + +#[clippy::msrv = "1.50"] +fn msrv_1_50() { + let (input, min, max) = (0, -1, 2); + let _ = if input < min { + min + } else if input > max { + max + } else { + input + }; +} diff --git a/tests/ui/manual_clamp.stderr b/tests/ui/manual_clamp.stderr index 0604f8606c3f..988ad1527e83 100644 --- a/tests/ui/manual_clamp.stderr +++ b/tests/ui/manual_clamp.stderr @@ -371,5 +371,20 @@ LL | | } | = note: clamp will panic if max < min -error: aborting due to 34 previous errors +error: clamp-like pattern without using clamp function + --> $DIR/manual_clamp.rs:321:13 + | +LL | let _ = if input < min { + | _____________^ +LL | | min +LL | | } else if input > max { +LL | | max +LL | | } else { +LL | | input +LL | | }; + | |_____^ help: replace with clamp: `input.clamp(min, max)` + | + = note: clamp will panic if max < min + +error: aborting due to 35 previous errors diff --git a/tests/ui/manual_filter.fixed b/tests/ui/manual_filter.fixed index 3553291b87df..ef6780dc96d9 100644 --- a/tests/ui/manual_filter.fixed +++ b/tests/ui/manual_filter.fixed @@ -116,4 +116,45 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; +} + +fn maybe_some() -> Option { + Some(0) } diff --git a/tests/ui/manual_filter.rs b/tests/ui/manual_filter.rs index aa9f90f752b1..ea0ce83172b7 100644 --- a/tests/ui/manual_filter.rs +++ b/tests/ui/manual_filter.rs @@ -240,4 +240,45 @@ fn main() { }, None => None, }; + + match Some(20) { + // Don't Lint, because `Some(3*x)` is not `None` + None => None, + Some(x) => { + if x > 0 { + Some(3 * x) + } else { + Some(x) + } + }, + }; + + // Don't lint: https://github.com/rust-lang/rust-clippy/issues/10088 + let result = if let Some(a) = maybe_some() { + if let Some(b) = maybe_some() { + Some(a + b) + } else { + Some(a) + } + } else { + None + }; + + let allowed_integers = vec![3, 4, 5, 6]; + // Don't lint, since there's a side effect in the else branch + match Some(21) { + Some(x) => { + if allowed_integers.contains(&x) { + Some(x) + } else { + println!("Invalid integer: {x:?}"); + None + } + }, + None => None, + }; +} + +fn maybe_some() -> Option { + Some(0) } diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index 96cd87c0e19a..552213a7ff22 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -10,7 +10,7 @@ fn main() { } } - // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement + // Test for loop over implicitly adjusted `Iterator` with `if let` statement let y: Vec> = vec![]; for n in y.clone() { if let Ok(n) = n { diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed index 0fa776b7b2e4..85a91543c893 100644 --- a/tests/ui/manual_instant_elapsed.fixed +++ b/tests/ui/manual_instant_elapsed.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs index 5b11b84535dd..c98cb15b9164 100644 --- a/tests/ui/manual_instant_elapsed.rs +++ b/tests/ui/manual_instant_elapsed.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::manual_instant_elapsed)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::unchecked_duration_subtraction)] #![allow(unused_variables)] #![allow(unused_must_use)] diff --git a/tests/ui/manual_instant_elapsed.stderr b/tests/ui/manual_instant_elapsed.stderr index 5537f5642a23..4ce1f689107e 100644 --- a/tests/ui/manual_instant_elapsed.stderr +++ b/tests/ui/manual_instant_elapsed.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:17:20 + --> $DIR/manual_instant_elapsed.rs:18:20 | LL | let duration = Instant::now() - prev_instant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `prev_instant.elapsed()` @@ -7,7 +7,7 @@ LL | let duration = Instant::now() - prev_instant; = note: `-D clippy::manual-instant-elapsed` implied by `-D warnings` error: manual implementation of `Instant::elapsed` - --> $DIR/manual_instant_elapsed.rs:26:5 + --> $DIR/manual_instant_elapsed.rs:27:5 | LL | Instant::now() - *ref_to_instant; // to ensure parens are added correctly | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*ref_to_instant).elapsed()` diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed new file mode 100644 index 000000000000..5b2b44c2fdb2 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.fixed @@ -0,0 +1,55 @@ +// run-rustfix + +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!('x'.is_ascii_lowercase()); + assert!('X'.is_ascii_uppercase()); + assert!(b'x'.is_ascii_lowercase()); + assert!(b'X'.is_ascii_uppercase()); + + let num = '2'; + assert!(num.is_ascii_digit()); + assert!(b'1'.is_ascii_digit()); + assert!('x'.is_ascii_alphabetic()); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + + b'0'.is_ascii_digit(); + b'a'.is_ascii_lowercase(); + b'A'.is_ascii_uppercase(); + + '0'.is_ascii_digit(); + 'a'.is_ascii_lowercase(); + 'A'.is_ascii_uppercase(); + + let cool_letter = &'g'; + cool_letter.is_ascii_digit(); + cool_letter.is_ascii_lowercase(); + cool_letter.is_ascii_uppercase(); +} + +#[clippy::msrv = "1.23"] +fn msrv_1_23() { + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +#[clippy::msrv = "1.24"] +fn msrv_1_24() { + assert!(b'1'.is_ascii_digit()); + assert!('X'.is_ascii_uppercase()); + assert!('x'.is_ascii_alphabetic()); +} + +#[clippy::msrv = "1.46"] +fn msrv_1_46() { + const FOO: bool = matches!('x', '0'..='9'); +} + +#[clippy::msrv = "1.47"] +fn msrv_1_47() { + const FOO: bool = 'x'.is_ascii_digit(); +} diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs new file mode 100644 index 000000000000..c9433f33a1b6 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.rs @@ -0,0 +1,55 @@ +// run-rustfix + +#![allow(unused, dead_code)] +#![warn(clippy::manual_is_ascii_check)] + +fn main() { + assert!(matches!('x', 'a'..='z')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!(b'x', b'a'..=b'z')); + assert!(matches!(b'X', b'A'..=b'Z')); + + let num = '2'; + assert!(matches!(num, '0'..='9')); + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + + assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_')); + + (b'0'..=b'9').contains(&b'0'); + (b'a'..=b'z').contains(&b'a'); + (b'A'..=b'Z').contains(&b'A'); + + ('0'..='9').contains(&'0'); + ('a'..='z').contains(&'a'); + ('A'..='Z').contains(&'A'); + + let cool_letter = &'g'; + ('0'..='9').contains(cool_letter); + ('a'..='z').contains(cool_letter); + ('A'..='Z').contains(cool_letter); +} + +#[clippy::msrv = "1.23"] +fn msrv_1_23() { + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +#[clippy::msrv = "1.24"] +fn msrv_1_24() { + assert!(matches!(b'1', b'0'..=b'9')); + assert!(matches!('X', 'A'..='Z')); + assert!(matches!('x', 'A'..='Z' | 'a'..='z')); +} + +#[clippy::msrv = "1.46"] +fn msrv_1_46() { + const FOO: bool = matches!('x', '0'..='9'); +} + +#[clippy::msrv = "1.47"] +fn msrv_1_47() { + const FOO: bool = matches!('x', '0'..='9'); +} diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr new file mode 100644 index 000000000000..ee60188506d6 --- /dev/null +++ b/tests/ui/manual_is_ascii_check.stderr @@ -0,0 +1,124 @@ +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:7:13 + | +LL | assert!(matches!('x', 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()` + | + = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:8:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:9:13 + | +LL | assert!(matches!(b'x', b'a'..=b'z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:10:13 + | +LL | assert!(matches!(b'X', b'A'..=b'Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:13:13 + | +LL | assert!(matches!(num, '0'..='9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:14:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:15:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:19:5 + | +LL | (b'0'..=b'9').contains(&b'0'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'0'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:20:5 + | +LL | (b'a'..=b'z').contains(&b'a'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'a'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:21:5 + | +LL | (b'A'..=b'Z').contains(&b'A'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'A'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:23:5 + | +LL | ('0'..='9').contains(&'0'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'0'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:24:5 + | +LL | ('a'..='z').contains(&'a'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'a'.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:25:5 + | +LL | ('A'..='Z').contains(&'A'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'A'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:28:5 + | +LL | ('0'..='9').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:29:5 + | +LL | ('a'..='z').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_lowercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:30:5 + | +LL | ('A'..='Z').contains(cool_letter); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:42:13 + | +LL | assert!(matches!(b'1', b'0'..=b'9')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:43:13 + | +LL | assert!(matches!('X', 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:44:13 + | +LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()` + +error: manual check for common ascii range + --> $DIR/manual_is_ascii_check.rs:54:23 + | +LL | const FOO: bool = matches!('x', '0'..='9'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()` + +error: aborting due to 20 previous errors + diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs new file mode 100644 index 000000000000..48a162c13602 --- /dev/null +++ b/tests/ui/manual_let_else.rs @@ -0,0 +1,251 @@ +#![allow(unused_braces, unused_variables, dead_code)] +#![allow( + clippy::collapsible_else_if, + clippy::unused_unit, + clippy::let_unit_value, + clippy::match_single_binding, + clippy::never_loop +)] +#![warn(clippy::manual_let_else)] + +fn g() -> Option<()> { + None +} + +fn main() {} + +fn fire() { + let v = if let Some(v_some) = g() { v_some } else { return }; + let v = if let Some(v_some) = g() { + v_some + } else { + return; + }; + + let v = if let Some(v) = g() { + // Blocks around the identity should have no impact + { + { v } + } + } else { + // Some computation should still make it fire + g(); + return; + }; + + // continue and break diverge + loop { + let v = if let Some(v_some) = g() { v_some } else { continue }; + let v = if let Some(v_some) = g() { v_some } else { break }; + } + + // panic also diverges + let v = if let Some(v_some) = g() { v_some } else { panic!() }; + + // abort also diverges + let v = if let Some(v_some) = g() { + v_some + } else { + std::process::abort() + }; + + // If whose two branches diverge also diverges + let v = if let Some(v_some) = g() { + v_some + } else { + if true { return } else { panic!() } + }; + + // Diverging after an if still makes the block diverge: + let v = if let Some(v_some) = g() { + v_some + } else { + if true {} + panic!(); + }; + + // A match diverges if all branches diverge: + // Note: the corresponding let-else requires a ; at the end of the match + // as otherwise the type checker does not turn it into a ! type. + let v = if let Some(v_some) = g() { + v_some + } else { + match () { + _ if panic!() => {}, + _ => panic!(), + } + }; + + // An if's expression can cause divergence: + let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; + + // An expression of a match can cause divergence: + let v = if let Some(v_some) = g() { + v_some + } else { + match panic!() { + _ => {}, + } + }; + + // Top level else if + let v = if let Some(v_some) = g() { + v_some + } else if true { + return; + } else { + panic!("diverge"); + }; + + // All match arms diverge + let v = if let Some(v_some) = g() { + v_some + } else { + match (g(), g()) { + (Some(_), None) => return, + (None, Some(_)) => { + if true { + return; + } else { + panic!(); + } + }, + _ => return, + } + }; + + // Tuples supported for the declared variables + let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { + v_some + } else { + return; + }; + + // Tuples supported for the identity block and pattern + let v = if let (Some(v_some), w_some) = (g(), 0) { + (w_some, v_some) + } else { + return; + }; + + // entirely inside macro lints + macro_rules! create_binding_if_some { + ($n:ident, $e:expr) => { + let $n = if let Some(v) = $e { v } else { return }; + }; + } + create_binding_if_some!(w, g()); +} + +fn not_fire() { + let v = if let Some(v_some) = g() { + // Nothing returned. Should not fire. + } else { + return; + }; + + let w = 0; + let v = if let Some(v_some) = g() { + // Different variable than v_some. Should not fire. + w + } else { + return; + }; + + let v = if let Some(v_some) = g() { + // Computation in then clause. Should not fire. + g(); + v_some + } else { + return; + }; + + let v = if let Some(v_some) = g() { + v_some + } else { + if false { + return; + } + // This doesn't diverge. Should not fire. + () + }; + + let v = if let Some(v_some) = g() { + v_some + } else { + // There is one match arm that doesn't diverge. Should not fire. + match (g(), g()) { + (Some(_), None) => return, + (None, Some(_)) => return, + (Some(_), Some(_)) => (), + _ => return, + } + }; + + let v = if let Some(v_some) = g() { + v_some + } else { + // loop with a break statement inside does not diverge. + loop { + break; + } + }; + + enum Uninhabited {} + fn un() -> Uninhabited { + panic!() + } + let v = if let Some(v_some) = None { + v_some + } else { + // Don't lint if the type is uninhabited but not ! + un() + }; + + fn question_mark() -> Option<()> { + let v = if let Some(v) = g() { + v + } else { + // Question mark does not diverge + g()? + }; + Some(v) + } + + // Macro boundary inside let + macro_rules! some_or_return { + ($e:expr) => { + if let Some(v) = $e { v } else { return } + }; + } + let v = some_or_return!(g()); + + // Also macro boundary inside let, but inside a macro + macro_rules! create_binding_if_some_nf { + ($n:ident, $e:expr) => { + let $n = some_or_return!($e); + }; + } + create_binding_if_some_nf!(v, g()); + + // Already a let-else + let Some(a) = (if let Some(b) = Some(Some(())) { b } else { return }) else { panic!() }; + + // If a type annotation is present, don't lint as + // expressing the type might be too hard + let v: () = if let Some(v_some) = g() { v_some } else { panic!() }; + + // Issue 9940 + // Suggestion should not expand macros + macro_rules! macro_call { + () => { + return () + }; + } + + let ff = Some(1); + let _ = match ff { + Some(value) => value, + _ => macro_call!(), + }; +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr new file mode 100644 index 000000000000..52aac6bc673d --- /dev/null +++ b/tests/ui/manual_let_else.stderr @@ -0,0 +1,272 @@ +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:18:5 + | +LL | let v = if let Some(v_some) = g() { v_some } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { return };` + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:19:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:25:5 + | +LL | / let v = if let Some(v) = g() { +LL | | // Blocks around the identity should have no impact +LL | | { +LL | | { v } +... | +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v) = g() else { +LL + // Some computation should still make it fire +LL + g(); +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:38:9 + | +LL | let v = if let Some(v_some) = g() { v_some } else { continue }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { continue };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:39:9 + | +LL | let v = if let Some(v_some) = g() { v_some } else { break }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { break };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:43:5 + | +LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { panic!() };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:46:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | std::process::abort() +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + std::process::abort() +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:53:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | if true { return } else { panic!() } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + if true { return } else { panic!() } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:60:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | if true {} +LL | | panic!(); +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + if true {} +LL + panic!(); +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:70:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | match () { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + match () { +LL + _ if panic!() => {}, +LL + _ => panic!(), +LL + } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:80:5 + | +LL | let v = if let Some(v_some) = g() { v_some } else { if panic!() {} }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { if panic!() {} };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:83:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | match panic!() { +LL | | _ => {}, +LL | | } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + match panic!() { +LL + _ => {}, +LL + } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:92:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else if true { +LL | | return; +LL | | } else { +LL | | panic!("diverge"); +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { if true { +LL + return; +LL + } else { +LL + panic!("diverge"); +LL + } }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:101:5 + | +LL | / let v = if let Some(v_some) = g() { +LL | | v_some +LL | | } else { +LL | | match (g(), g()) { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g() else { +LL + match (g(), g()) { +LL + (Some(_), None) => return, +LL + (None, Some(_)) => { +LL + if true { +LL + return; +LL + } else { +LL + panic!(); +LL + } +LL + }, +LL + _ => return, +LL + } +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:118:5 + | +LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { +LL | | v_some +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let Some(v_some) = g().map(|v| (v, 42)) else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:125:5 + | +LL | / let v = if let (Some(v_some), w_some) = (g(), 0) { +LL | | (w_some, v_some) +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let (Some(v_some), w_some) = (g(), 0) else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:134:13 + | +LL | let $n = if let Some(v) = $e { v } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` +... +LL | create_binding_if_some!(w, g()); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:247:5 + | +LL | / let _ = match ff { +LL | | Some(value) => value, +LL | | _ => macro_call!(), +LL | | }; + | |______^ help: consider writing: `let Some(value) = ff else { macro_call!() };` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs new file mode 100644 index 000000000000..28caed9d79df --- /dev/null +++ b/tests/ui/manual_let_else_match.rs @@ -0,0 +1,128 @@ +#![allow(unused_braces, unused_variables, dead_code)] +#![allow(clippy::collapsible_else_if, clippy::let_unit_value)] +#![warn(clippy::manual_let_else)] +// Ensure that we don't conflict with match -> if let lints +#![warn(clippy::single_match_else, clippy::single_match)] + +fn f() -> Result { + Ok(0) +} + +fn g() -> Option<()> { + None +} + +fn h() -> (Option<()>, Option<()>) { + (None, None) +} + +enum Variant { + Foo, + Bar(u32), + Baz(u32), +} + +fn build_enum() -> Variant { + Variant::Foo +} + +fn main() {} + +fn fire() { + let v = match g() { + Some(v_some) => v_some, + None => return, + }; + + let v = match g() { + Some(v_some) => v_some, + _ => return, + }; + + loop { + // More complex pattern for the identity arm and diverging arm + let v = match h() { + (Some(_), Some(_)) | (None, None) => continue, + (Some(v), None) | (None, Some(v)) => v, + }; + // Custom enums are supported as long as the "else" arm is a simple _ + let v = match build_enum() { + _ => continue, + Variant::Bar(v) | Variant::Baz(v) => v, + }; + } + + // There is a _ in the diverging arm + // TODO also support unused bindings aka _v + let v = match f() { + Ok(v) => v, + Err(_) => return, + }; + + // Err(()) is an allowed pattern + let v = match f().map_err(|_| ()) { + Ok(v) => v, + Err(()) => return, + }; + + let f = Variant::Bar(1); + + let _value = match f { + Variant::Bar(_) | Variant::Baz(_) => (), + _ => return, + }; +} + +fn not_fire() { + // Multiple diverging arms + let v = match h() { + _ => panic!(), + (None, Some(_v)) => return, + (Some(v), None) => v, + }; + + // Multiple identity arms + let v = match h() { + _ => panic!(), + (None, Some(v)) => v, + (Some(v), None) => v, + }; + + // No diverging arm at all, only identity arms. + // This is no case for let else, but destructuring assignment. + let v = match f() { + Ok(v) => v, + Err(e) => e, + }; + + // The identity arm has a guard + let v = match g() { + Some(v) if g().is_none() => v, + _ => return, + }; + + // The diverging arm has a guard + let v = match f() { + Err(v) if v > 0 => panic!(), + Ok(v) | Err(v) => v, + }; + + // The diverging arm creates a binding + let v = match f() { + Ok(v) => v, + Err(e) => panic!("error: {e}"), + }; + + // Custom enum where the diverging arm + // explicitly mentions the variant + let v = match build_enum() { + Variant::Foo => return, + Variant::Bar(v) | Variant::Baz(v) => v, + }; + + // The custom enum is surrounded by an Err() + let v = match Err(build_enum()) { + Ok(v) | Err(Variant::Bar(v) | Variant::Baz(v)) => v, + Err(Variant::Foo) => return, + }; +} diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr new file mode 100644 index 000000000000..cd5e9a9ac39c --- /dev/null +++ b/tests/ui/manual_let_else_match.stderr @@ -0,0 +1,67 @@ +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:32:5 + | +LL | / let v = match g() { +LL | | Some(v_some) => v_some, +LL | | None => return, +LL | | }; + | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + | + = note: `-D clippy::manual-let-else` implied by `-D warnings` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:37:5 + | +LL | / let v = match g() { +LL | | Some(v_some) => v_some, +LL | | _ => return, +LL | | }; + | |______^ help: consider writing: `let Some(v_some) = g() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:44:9 + | +LL | / let v = match h() { +LL | | (Some(_), Some(_)) | (None, None) => continue, +LL | | (Some(v), None) | (None, Some(v)) => v, +LL | | }; + | |__________^ help: consider writing: `let ((Some(v), None) | (None, Some(v))) = h() else { continue };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:49:9 + | +LL | / let v = match build_enum() { +LL | | _ => continue, +LL | | Variant::Bar(v) | Variant::Baz(v) => v, +LL | | }; + | |__________^ help: consider writing: `let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:57:5 + | +LL | / let v = match f() { +LL | | Ok(v) => v, +LL | | Err(_) => return, +LL | | }; + | |______^ help: consider writing: `let Ok(v) = f() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:63:5 + | +LL | / let v = match f().map_err(|_| ()) { +LL | | Ok(v) => v, +LL | | Err(()) => return, +LL | | }; + | |______^ help: consider writing: `let Ok(v) = f().map_err(|_| ()) else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:70:5 + | +LL | / let _value = match f { +LL | | Variant::Bar(_) | Variant::Baz(_) => (), +LL | | _ => return, +LL | | }; + | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index a59da4ae10bc..e12ea7ec1450 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 0bdbefa51e8b..325a6db06c4e 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,7 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, - clippy::for_loops_over_fallibles, + for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed index d864f8554534..fc8511626b3d 100644 --- a/tests/ui/manual_ok_or.fixed +++ b/tests/ui/manual_ok_or.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::manual_ok_or)] +#![allow(clippy::or_fun_call)] #![allow(clippy::disallowed_names)] #![allow(clippy::redundant_closure)] #![allow(dead_code)] diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs index 6264768460ef..b5303d33f5fd 100644 --- a/tests/ui/manual_ok_or.rs +++ b/tests/ui/manual_ok_or.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::manual_ok_or)] +#![allow(clippy::or_fun_call)] #![allow(clippy::disallowed_names)] #![allow(clippy::redundant_closure)] #![allow(dead_code)] diff --git a/tests/ui/manual_ok_or.stderr b/tests/ui/manual_ok_or.stderr index 65459a097384..b4a17f143e3f 100644 --- a/tests/ui/manual_ok_or.stderr +++ b/tests/ui/manual_ok_or.stderr @@ -1,5 +1,5 @@ error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:11:5 + --> $DIR/manual_ok_or.rs:12:5 | LL | foo.map_or(Err("error"), |v| Ok(v)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` @@ -7,19 +7,19 @@ LL | foo.map_or(Err("error"), |v| Ok(v)); = note: `-D clippy::manual-ok-or` implied by `-D warnings` error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:14:5 + --> $DIR/manual_ok_or.rs:15:5 | LL | foo.map_or(Err("error"), Ok); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:17:5 + --> $DIR/manual_ok_or.rs:18:5 | LL | None::.map_or(Err("error"), |v| Ok(v)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::.ok_or("error")` error: this pattern reimplements `Option::ok_or` - --> $DIR/manual_ok_or.rs:21:5 + --> $DIR/manual_ok_or.rs:22:5 | LL | / foo.map_or(Err::( LL | | &format!( diff --git a/tests/ui/manual_rem_euclid.fixed b/tests/ui/manual_rem_euclid.fixed index 5601c96c10b2..4cdc0546a744 100644 --- a/tests/ui/manual_rem_euclid.fixed +++ b/tests/ui/manual_rem_euclid.fixed @@ -53,3 +53,28 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { num.rem_euclid(4) } + +#[clippy::msrv = "1.37"] +pub fn msrv_1_37() { + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +#[clippy::msrv = "1.38"] +pub fn msrv_1_38() { + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} + +// For const fns: +#[clippy::msrv = "1.51"] +pub const fn msrv_1_51() { + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +#[clippy::msrv = "1.52"] +pub const fn msrv_1_52() { + let x: i32 = 10; + let _: i32 = x.rem_euclid(4); +} diff --git a/tests/ui/manual_rem_euclid.rs b/tests/ui/manual_rem_euclid.rs index 52135be26b73..58a9e20f38b1 100644 --- a/tests/ui/manual_rem_euclid.rs +++ b/tests/ui/manual_rem_euclid.rs @@ -53,3 +53,28 @@ pub fn rem_euclid_4(num: i32) -> i32 { pub const fn const_rem_euclid_4(num: i32) -> i32 { ((num % 4) + 4) % 4 } + +#[clippy::msrv = "1.37"] +pub fn msrv_1_37() { + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +#[clippy::msrv = "1.38"] +pub fn msrv_1_38() { + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +// For const fns: +#[clippy::msrv = "1.51"] +pub const fn msrv_1_51() { + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} + +#[clippy::msrv = "1.52"] +pub const fn msrv_1_52() { + let x: i32 = 10; + let _: i32 = ((x % 4) + 4) % 4; +} diff --git a/tests/ui/manual_rem_euclid.stderr b/tests/ui/manual_rem_euclid.stderr index a237fd0213c1..e3122a588b64 100644 --- a/tests/ui/manual_rem_euclid.stderr +++ b/tests/ui/manual_rem_euclid.stderr @@ -53,5 +53,17 @@ error: manual `rem_euclid` implementation LL | ((num % 4) + 4) % 4 | ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)` -error: aborting due to 8 previous errors +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:66:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: manual `rem_euclid` implementation + --> $DIR/manual_rem_euclid.rs:79:18 + | +LL | let _: i32 = ((x % 4) + 4) % 4; + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index fba503a20667..8f25fea678f1 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -1,7 +1,6 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; @@ -216,8 +215,8 @@ fn vec_deque_retain() { bar = foobar.into_iter().filter(|x| x % 2 == 0).collect(); } +#[clippy::msrv = "1.52"] fn _msrv_153() { - #![clippy::msrv = "1.52"] let mut btree_map: BTreeMap = (0..8).map(|x| (x, x * 10)).collect(); btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); @@ -225,14 +224,14 @@ fn _msrv_153() { btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); } +#[clippy::msrv = "1.25"] fn _msrv_126() { - #![clippy::msrv = "1.25"] let mut s = String::from("foobar"); s = s.chars().filter(|&c| c != 'o').to_owned().collect(); } +#[clippy::msrv = "1.17"] fn _msrv_118() { - #![clippy::msrv = "1.17"] let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]); hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 81a849fe7684..e6b3995a689b 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -1,7 +1,6 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_retain)] -#![allow(unused)] +#![allow(unused, clippy::redundant_clone)] use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::BinaryHeap; @@ -222,8 +221,8 @@ fn vec_deque_retain() { bar = foobar.into_iter().filter(|x| x % 2 == 0).collect(); } +#[clippy::msrv = "1.52"] fn _msrv_153() { - #![clippy::msrv = "1.52"] let mut btree_map: BTreeMap = (0..8).map(|x| (x, x * 10)).collect(); btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); @@ -231,14 +230,14 @@ fn _msrv_153() { btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); } +#[clippy::msrv = "1.25"] fn _msrv_126() { - #![clippy::msrv = "1.25"] let mut s = String::from("foobar"); s = s.chars().filter(|&c| c != 'o').to_owned().collect(); } +#[clippy::msrv = "1.17"] fn _msrv_118() { - #![clippy::msrv = "1.17"] let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]); hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); let mut hash_map: HashMap = (0..8).map(|x| (x, x * 10)).collect(); diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index ec635919b48f..89316ce1d998 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:52:5 + --> $DIR/manual_retain.rs:51:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` @@ -7,13 +7,13 @@ LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect() = note: `-D clippy::manual-retain` implied by `-D warnings` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:53:5 + --> $DIR/manual_retain.rs:52:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:54:5 + --> $DIR/manual_retain.rs:53:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -22,37 +22,37 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:76:5 + --> $DIR/manual_retain.rs:75:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:77:5 + --> $DIR/manual_retain.rs:76:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:78:5 + --> $DIR/manual_retain.rs:77:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:108:5 + --> $DIR/manual_retain.rs:107:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:109:5 + --> $DIR/manual_retain.rs:108:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:110:5 + --> $DIR/manual_retain.rs:109:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -61,61 +61,61 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:131:5 + --> $DIR/manual_retain.rs:130:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:132:5 + --> $DIR/manual_retain.rs:131:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:133:5 + --> $DIR/manual_retain.rs:132:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:162:5 + --> $DIR/manual_retain.rs:161:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:174:5 + --> $DIR/manual_retain.rs:173:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:175:5 + --> $DIR/manual_retain.rs:174:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:176:5 + --> $DIR/manual_retain.rs:175:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:198:5 + --> $DIR/manual_retain.rs:197:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:199:5 + --> $DIR/manual_retain.rs:198:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> $DIR/manual_retain.rs:200:5 + --> $DIR/manual_retain.rs:199:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index c7ca770434a3..50b02019cc27 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] @@ -127,8 +126,8 @@ fn indirect() -> Option<()> { None } +#[clippy::msrv = "1.51"] fn _msrv_1_51() { - #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); @@ -137,8 +136,8 @@ fn _msrv_1_51() { let b = iter.next().unwrap(); } +#[clippy::msrv = "1.52"] fn _msrv_1_52() { - #![clippy::msrv = "1.52"] let _ = "key=value".split_once('=').unwrap().1; let (a, b) = "a.b.c".split_once('.').unwrap(); diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index ee2848a251ee..e1e8b71a9def 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] @@ -127,8 +126,8 @@ fn indirect() -> Option<()> { None } +#[clippy::msrv = "1.51"] fn _msrv_1_51() { - #![clippy::msrv = "1.51"] // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); @@ -137,8 +136,8 @@ fn _msrv_1_51() { let b = iter.next().unwrap(); } +#[clippy::msrv = "1.52"] fn _msrv_1_52() { - #![clippy::msrv = "1.52"] let _ = "key=value".splitn(2, '=').nth(1).unwrap(); let mut iter = "a.b.c".splitn(2, '.'); diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index 2696694680ad..78da5a16cc52 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,5 +1,5 @@ error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:14:13 + --> $DIR/manual_split_once.rs:13:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` @@ -7,79 +7,79 @@ LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); = note: `-D clippy::manual-split-once` implied by `-D warnings` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:15:13 + --> $DIR/manual_split_once.rs:14:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:16:18 + --> $DIR/manual_split_once.rs:15:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:19:13 + --> $DIR/manual_split_once.rs:18:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:22:13 + --> $DIR/manual_split_once.rs:21:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:25:13 + --> $DIR/manual_split_once.rs:24:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:28:17 + --> $DIR/manual_split_once.rs:27:17 | LL | let _ = s.splitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:29:17 + --> $DIR/manual_split_once.rs:28:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:30:17 + --> $DIR/manual_split_once.rs:29:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:31:17 + --> $DIR/manual_split_once.rs:30:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:39:13 + --> $DIR/manual_split_once.rs:38:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:40:18 + --> $DIR/manual_split_once.rs:39:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:41:13 + --> $DIR/manual_split_once.rs:40:13 | LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:45:5 + --> $DIR/manual_split_once.rs:44:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,7 @@ LL + | error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:49:5 + --> $DIR/manual_split_once.rs:48:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL + | error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:53:5 + --> $DIR/manual_split_once.rs:52:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL + | error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:57:5 + --> $DIR/manual_split_once.rs:56:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,13 +179,13 @@ LL + | error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:142:13 + --> $DIR/manual_split_once.rs:141:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:144:5 + --> $DIR/manual_split_once.rs:143:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index 0704ba2f933e..3d56f2a0dedb 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; @@ -54,13 +53,13 @@ fn main() { let _: String = repeat(x).take(count).collect(); } +#[clippy::msrv = "1.15"] fn _msrv_1_15() { - #![clippy::msrv = "1.15"] // `str::repeat` was stabilized in 1.16. Do not lint this let _: String = std::iter::repeat("test").take(10).collect(); } +#[clippy::msrv = "1.16"] fn _msrv_1_16() { - #![clippy::msrv = "1.16"] let _: String = "test".repeat(10); } diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index f522be439aa0..e8240a949dbc 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; @@ -54,13 +53,13 @@ fn main() { let _: String = repeat(x).take(count).collect(); } +#[clippy::msrv = "1.15"] fn _msrv_1_15() { - #![clippy::msrv = "1.15"] // `str::repeat` was stabilized in 1.16. Do not lint this let _: String = std::iter::repeat("test").take(10).collect(); } +#[clippy::msrv = "1.16"] fn _msrv_1_16() { - #![clippy::msrv = "1.16"] let _: String = std::iter::repeat("test").take(10).collect(); } diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr index c65116897164..bdfee7cab261 100644 --- a/tests/ui/manual_str_repeat.stderr +++ b/tests/ui/manual_str_repeat.stderr @@ -1,5 +1,5 @@ error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:10:21 + --> $DIR/manual_str_repeat.rs:9:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` @@ -7,55 +7,55 @@ LL | let _: String = std::iter::repeat("test").take(10).collect(); = note: `-D clippy::manual-str-repeat` implied by `-D warnings` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:11:21 + --> $DIR/manual_str_repeat.rs:10:21 | LL | let _: String = std::iter::repeat('x').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:12:21 + --> $DIR/manual_str_repeat.rs:11:21 | LL | let _: String = std::iter::repeat('/'').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:13:21 + --> $DIR/manual_str_repeat.rs:12:21 | LL | let _: String = std::iter::repeat('"').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:17:13 + --> $DIR/manual_str_repeat.rs:16:13 | LL | let _ = repeat(x).take(count + 2).collect::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:26:21 + --> $DIR/manual_str_repeat.rs:25:21 | LL | let _: String = repeat(*x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:35:21 + --> $DIR/manual_str_repeat.rs:34:21 | LL | let _: String = repeat(x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:47:21 + --> $DIR/manual_str_repeat.rs:46:21 | LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:50:21 + --> $DIR/manual_str_repeat.rs:49:21 | LL | let _: String = repeat(x).take(count).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:65:21 + --> $DIR/manual_str_repeat.rs:64:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index cbb84eb5c7e3..b0b1c262aeed 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -64,3 +64,19 @@ fn main() { s4[2..].to_string(); } } + +#[clippy::msrv = "1.44"] +fn msrv_1_44() { + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} + +#[clippy::msrv = "1.45"] +fn msrv_1_45() { + let s = "abc"; + if s.starts_with('a') { + s[1..].to_string(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr index 2191ccb85dd5..f592e898fc92 100644 --- a/tests/ui/manual_strip.stderr +++ b/tests/ui/manual_strip.stderr @@ -128,5 +128,22 @@ LL ~ if let Some() = s1.strip_prefix("ab") { LL ~ .to_uppercase(); | -error: aborting due to 7 previous errors +error: stripping a prefix manually + --> $DIR/manual_strip.rs:80:9 + | +LL | s[1..].to_string(); + | ^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:79:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL ~ if let Some() = s.strip_prefix('a') { +LL ~ .to_string(); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/map_flatten_fixable.fixed b/tests/ui/map_flatten_fixable.fixed index 312819a0a2cf..53628ef6531a 100644 --- a/tests/ui/map_flatten_fixable.fixed +++ b/tests/ui/map_flatten_fixable.fixed @@ -1,7 +1,6 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::redundant_closure)] diff --git a/tests/ui/map_flatten_fixable.rs b/tests/ui/map_flatten_fixable.rs index 3fbf4f9a1b04..76016c8ed3cd 100644 --- a/tests/ui/map_flatten_fixable.rs +++ b/tests/ui/map_flatten_fixable.rs @@ -1,7 +1,6 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::redundant_closure)] diff --git a/tests/ui/map_flatten_fixable.stderr b/tests/ui/map_flatten_fixable.stderr index c91f0b9ae94f..b6b0c4d09c37 100644 --- a/tests/ui/map_flatten_fixable.stderr +++ b/tests/ui/map_flatten_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:18:47 + --> $DIR/map_flatten_fixable.rs:17:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)` @@ -7,43 +7,43 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:19:47 + --> $DIR/map_flatten_fixable.rs:18:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:20:47 + --> $DIR/map_flatten_fixable.rs:19:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:21:47 + --> $DIR/map_flatten_fixable.rs:20:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:24:47 + --> $DIR/map_flatten_fixable.rs:23:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)` error: called `map(..).flatten()` on `Option` - --> $DIR/map_flatten_fixable.rs:27:40 + --> $DIR/map_flatten_fixable.rs:26:40 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Result` - --> $DIR/map_flatten_fixable.rs:30:42 + --> $DIR/map_flatten_fixable.rs:29:42 | LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:39:10 + --> $DIR/map_flatten_fixable.rs:38:10 | LL | .map(|n| match n { | __________^ @@ -72,7 +72,7 @@ LL ~ }); | error: called `map(..).flatten()` on `Option` - --> $DIR/map_flatten_fixable.rs:59:10 + --> $DIR/map_flatten_fixable.rs:58:10 | LL | .map(|_| { | __________^ diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 5429fb4e454e..32631024ca5d 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,6 +1,7 @@ // aux-build:option_helpers.rs + #![warn(clippy::map_unwrap_or)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)] #[macro_use] extern crate option_helpers; @@ -79,3 +80,17 @@ fn main() { option_methods(); result_methods(); } + +#[clippy::msrv = "1.40"] +fn msrv_1_40() { + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} + +#[clippy::msrv = "1.41"] +fn msrv_1_41() { + let res: Result = Ok(1); + + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index abc9c1ece327..41781b050fa2 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:16:13 + --> $DIR/map_unwrap_or.rs:17:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -15,7 +15,7 @@ LL + let _ = opt.map_or(0, |x| x + 1); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap_or.rs:21:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -33,7 +33,7 @@ LL ~ ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:24:13 + --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -50,7 +50,7 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:29:13 + --> $DIR/map_unwrap_or.rs:30:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + let _ = opt.and_then(|x| Some(x + 1)); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:32:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -80,7 +80,7 @@ LL ~ ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:35:13 + --> $DIR/map_unwrap_or.rs:36:13 | LL | let _ = opt | _____________^ @@ -95,7 +95,7 @@ LL + .and_then(|x| Some(x + 1)); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:46:13 + --> $DIR/map_unwrap_or.rs:47:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap_or.rs:51:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -117,7 +117,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:54:13 + --> $DIR/map_unwrap_or.rs:55:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -127,7 +127,7 @@ LL | | ); | |_________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:66:13 + --> $DIR/map_unwrap_or.rs:67:13 | LL | let _ = res.map(|x| { | _____________^ @@ -137,7 +137,7 @@ LL | | ).unwrap_or_else(|_e| 0); | |____________________________^ error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:70:13 + --> $DIR/map_unwrap_or.rs:71:13 | LL | let _ = res.map(|x| x + 1) | _____________^ @@ -146,5 +146,11 @@ LL | | 0 LL | | }); | |__________^ -error: aborting due to 11 previous errors +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:95:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 12 previous errors diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 95ca571d07bf..55cd15bd5c38 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); @@ -193,3 +198,16 @@ fn main() { _ => false, }; } + +#[clippy::msrv = "1.41"] +fn msrv_1_41() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +#[clippy::msrv = "1.42"] +fn msrv_1_42() { + let _y = matches!(Some(5), Some(0)); +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 3b9c8cadadcc..5d645e108e51 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,7 +1,12 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] -#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)] +#![allow( + unreachable_patterns, + dead_code, + clippy::equatable_if_let, + clippy::needless_borrowed_reference +)] fn main() { let x = Some(5); @@ -234,3 +239,19 @@ fn main() { _ => false, }; } + +#[clippy::msrv = "1.41"] +fn msrv_1_41() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +#[clippy::msrv = "1.42"] +fn msrv_1_42() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index e94555e27448..46f67ef4900f 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:10:14 + --> $DIR/match_expr_like_matches_macro.rs:15:14 | LL | let _y = match x { | ______________^ @@ -11,7 +11,7 @@ LL | | }; = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:16:14 + --> $DIR/match_expr_like_matches_macro.rs:21:14 | LL | let _w = match x { | ______________^ @@ -21,7 +21,7 @@ LL | | }; | |_____^ help: try this: `matches!(x, Some(_))` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:22:14 + --> $DIR/match_expr_like_matches_macro.rs:27:14 | LL | let _z = match x { | ______________^ @@ -33,7 +33,7 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:28:15 + --> $DIR/match_expr_like_matches_macro.rs:33:15 | LL | let _zz = match x { | _______________^ @@ -43,13 +43,13 @@ LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:34:16 + --> $DIR/match_expr_like_matches_macro.rs:39:16 | LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:58:20 + --> $DIR/match_expr_like_matches_macro.rs:63:20 | LL | let _ans = match x { | ____________________^ @@ -60,7 +60,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:68:20 + --> $DIR/match_expr_like_matches_macro.rs:73:20 | LL | let _ans = match x { | ____________________^ @@ -73,7 +73,7 @@ LL | | }; | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:78:20 + --> $DIR/match_expr_like_matches_macro.rs:83:20 | LL | let _ans = match x { | ____________________^ @@ -84,7 +84,7 @@ LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:138:18 + --> $DIR/match_expr_like_matches_macro.rs:143:18 | LL | let _z = match &z { | __________________^ @@ -94,7 +94,7 @@ LL | | }; | |_________^ help: try this: `matches!(z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:147:18 + --> $DIR/match_expr_like_matches_macro.rs:152:18 | LL | let _z = match &z { | __________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: try this: `matches!(&z, Some(3))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:164:21 + --> $DIR/match_expr_like_matches_macro.rs:169:21 | LL | let _ = match &z { | _____________________^ @@ -114,7 +114,7 @@ LL | | }; | |_____________^ help: try this: `matches!(&z, AnEnum::X)` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:178:20 + --> $DIR/match_expr_like_matches_macro.rs:183:20 | LL | let _res = match &val { | ____________________^ @@ -124,7 +124,7 @@ LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:190:20 + --> $DIR/match_expr_like_matches_macro.rs:195:20 | LL | let _res = match &val { | ____________________^ @@ -133,5 +133,15 @@ LL | | _ => false, LL | | }; | |_________^ help: try this: `matches!(&val, &Some(ref _a))` -error: aborting due to 13 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:253:14 + | +LL | let _y = match Some(5) { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(Some(5), Some(0))` + +error: aborting due to 14 previous errors diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 2f85e6357135..b4097fa96045 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -1,5 +1,4 @@ #![feature(exclusive_range_pattern)] -#![feature(half_open_range_patterns)] #![warn(clippy::match_overlapping_arm)] #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let)] diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index a72becbeb669..b98d4799e42c 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -1,96 +1,96 @@ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:13:9 + --> $DIR/match_overlapping_arm.rs:12:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:14:9 + --> $DIR/match_overlapping_arm.rs:13:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:19:9 + --> $DIR/match_overlapping_arm.rs:18:9 | LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:21:9 + --> $DIR/match_overlapping_arm.rs:20:9 | LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:56:9 + --> $DIR/match_overlapping_arm.rs:55:9 | LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:57:9 + --> $DIR/match_overlapping_arm.rs:56:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:81:9 + --> $DIR/match_overlapping_arm.rs:80:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:80:9 + --> $DIR/match_overlapping_arm.rs:79:9 | LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:86:9 + --> $DIR/match_overlapping_arm.rs:85:9 | LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:87:9 + --> $DIR/match_overlapping_arm.rs:86:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:98:9 + --> $DIR/match_overlapping_arm.rs:97:9 | LL | ..=23 => println!("..=23"), | ^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:99:9 + --> $DIR/match_overlapping_arm.rs:98:9 | LL | ..26 => println!("..26"), | ^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:107:9 + --> $DIR/match_overlapping_arm.rs:106:9 | LL | 21..=30 => (), | ^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:108:9 + --> $DIR/match_overlapping_arm.rs:107:9 | LL | 21..=40 => (), | ^^^^^^^ error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:121:9 + --> $DIR/match_overlapping_arm.rs:120:9 | LL | 0..=0x0000_0000_0000_00ff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: overlaps with this - --> $DIR/match_overlapping_arm.rs:122:9 + --> $DIR/match_overlapping_arm.rs:121:9 | LL | 0..=0x0000_0000_0000_ffff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index a6e315e4773a..6cfb6661a039 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -133,3 +133,16 @@ fn issue_9575() { println!("Needs curlies"); }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + let x = r; + match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index cecbd703e566..f188aeb5f2ff 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -148,3 +148,17 @@ fn issue_9575() { _ => println!("Needs curlies"), }; } + +#[allow(dead_code)] +fn issue_9725(r: Option) { + match r { + x => match x { + Some(_) => { + println!("Some"); + }, + None => { + println!("None"); + }, + }, + }; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 2b9ec7ee7026..e960d64ad2b0 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -213,5 +213,30 @@ LL + println!("Needs curlies"); LL ~ }; | -error: aborting due to 14 previous errors +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:154:5 + | +LL | / match r { +LL | | x => match x { +LL | | Some(_) => { +LL | | println!("Some"); +... | +LL | | }, +LL | | }; + | |_____^ + | +help: consider using a `let` statement + | +LL ~ let x = r; +LL + match x { +LL + Some(_) => { +LL + println!("Some"); +LL + }, +LL + None => { +LL + println!("None"); +LL + }, +LL ~ }; + | + +error: aborting due to 15 previous errors diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index e675c183ea71..9fd3739b69c2 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -123,7 +123,7 @@ fn main() { Enum::A => (), Enum::B => (), Enum::C => (), - _ => (), + Enum::__Private => (), } match Enum::A { Enum::A => (), @@ -132,3 +132,25 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + Foo::B => 2, + }; + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 38c3ffc00c71..9a5c849e6ec9 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -132,3 +132,25 @@ fn main() { } } } + +mod issue9993 { + enum Foo { + A(bool), + B, + } + + fn test() { + let _ = match Foo::A(true) { + _ if false => 0, + Foo::A(true) => 1, + Foo::A(false) => 2, + Foo::B => 3, + }; + + let _ = match Foo::B { + _ if false => 0, + Foo::A(_) => 1, + _ => 2, + }; + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 34538dea8e5f..105b4c4b41d1 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -48,5 +48,17 @@ error: wildcard matches only a single variant and will also match any future add LL | _ => (), | ^ help: try this: `Color::Blue` -error: aborting due to 8 previous errors +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:126:13 + | +LL | _ => (), + | ^ help: try this: `Enum::__Private` + +error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:153:13 + | +LL | _ => 2, + | ^ help: try this: `Foo::B` + +error: aborting due to 10 previous errors diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index b609ba659467..874d55843303 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -1,5 +1,6 @@ // run-rustfix -#![allow(unused_imports)] + +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +78,15 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +#[clippy::msrv = "1.39"] +fn msrv_1_39() { + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +#[clippy::msrv = "1.40"] +fn msrv_1_40() { + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); +} diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 93f6dcdec83b..f4f3bff51446 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -1,5 +1,6 @@ // run-rustfix -#![allow(unused_imports)] + +#![allow(unused)] #![warn( clippy::all, clippy::style, @@ -77,3 +78,15 @@ fn main() { replace_with_default(); dont_lint_primitive(); } + +#[clippy::msrv = "1.39"] +fn msrv_1_39() { + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} + +#[clippy::msrv = "1.40"] +fn msrv_1_40() { + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); +} diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 90dc6c95f858..caa127f76eef 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:15:13 + --> $DIR/mem_replace.rs:16:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:17:13 + --> $DIR/mem_replace.rs:18:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:22:13 + --> $DIR/mem_replace.rs:23:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,100 +21,106 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:25:13 + --> $DIR/mem_replace.rs:26:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:26:13 + --> $DIR/mem_replace.rs:27:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:29:13 + --> $DIR/mem_replace.rs:30:13 | LL | let _ = std::mem::replace(&mut v, Vec::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:30:13 + --> $DIR/mem_replace.rs:31:13 | LL | let _ = std::mem::replace(&mut v, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:31:13 + --> $DIR/mem_replace.rs:32:13 | LL | let _ = std::mem::replace(&mut v, Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:32:13 + --> $DIR/mem_replace.rs:33:13 | LL | let _ = std::mem::replace(&mut v, vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:35:13 + --> $DIR/mem_replace.rs:36:13 | LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:38:13 + --> $DIR/mem_replace.rs:39:13 | LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:41:13 + --> $DIR/mem_replace.rs:42:13 | LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:44:13 + --> $DIR/mem_replace.rs:45:13 | LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:47:13 + --> $DIR/mem_replace.rs:48:13 | LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:50:13 + --> $DIR/mem_replace.rs:51:13 | LL | let _ = std::mem::replace(&mut list, LinkedList::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:53:13 + --> $DIR/mem_replace.rs:54:13 | LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:56:13 + --> $DIR/mem_replace.rs:57:13 | LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:59:13 + --> $DIR/mem_replace.rs:60:13 | LL | let _ = std::mem::replace(&mut refstr, ""); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:62:13 + --> $DIR/mem_replace.rs:63:13 | LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` -error: aborting due to 19 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:91:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index c4c6391bb4c1..955e7eb72763 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -1,240 +1,62 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0.0"] -use std::ops::{Deref, RangeFrom}; +fn main() {} -fn approx_const() { +#[clippy::msrv = "1.42.0"] +fn just_under_msrv() { let log2_10 = 3.321928094887362; - let log10_2 = 0.301029995663981; } -fn cloned_instead_of_copied() { - let _ = [1].iter().cloned(); -} - -fn option_as_ref_deref() { - let mut opt = Some(String::from("123")); - - let _ = opt.as_ref().map(String::as_str); - let _ = opt.as_ref().map(|x| x.as_str()); - let _ = opt.as_mut().map(String::as_mut_str); - let _ = opt.as_mut().map(|x| x.as_mut_str()); -} - -fn match_like_matches() { - let _y = match Some(5) { - Some(0) => true, - _ => false, - }; -} - -fn match_same_arms() { - match (1, 2, 3) { - (1, .., 3) => 42, - (.., 3) => 42, //~ ERROR match arms have same body - _ => 0, - }; -} - -fn match_same_arms2() { - let _ = match Some(42) { - Some(_) => 24, - None => 24, //~ ERROR match arms have same body - }; -} - -pub fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -pub fn redundant_fieldnames() { - let start = 0; - let _ = RangeFrom { start: start }; +#[clippy::msrv = "1.43.0"] +fn meets_msrv() { + let log2_10 = 3.321928094887362; } -pub fn redundant_static_lifetime() { - const VAR_ONE: &'static str = "Test constant #1"; +#[clippy::msrv = "1.44.0"] +fn just_above_msrv() { + let log2_10 = 3.321928094887362; } -pub fn checked_conversion() { - let value: i64 = 42; - let _ = value <= (u32::max_value() as i64) && value >= 0; - let _ = value <= (u32::MAX as i64) && value >= 0; +#[clippy::msrv = "1.42"] +fn no_patch_under() { + let log2_10 = 3.321928094887362; } -pub struct FromOverInto(String); - -impl Into for String { - fn into(self) -> FromOverInto { - FromOverInto(self) - } +#[clippy::msrv = "1.43"] +fn no_patch_meets() { + let log2_10 = 3.321928094887362; } -pub fn filter_map_next() { - let a = ["1", "lol", "3", "NaN", "5"]; - - #[rustfmt::skip] - let _: Option = vec![1, 2, 3, 4, 5, 6] - .into_iter() - .filter_map(|x| { - if x == 2 { - Some(x * 2) - } else { - None - } - }) - .next(); +fn inner_attr_under() { + #![clippy::msrv = "1.42"] + let log2_10 = 3.321928094887362; } -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] -pub fn manual_range_contains() { - let x = 5; - x >= 8 && x < 12; +fn inner_attr_meets() { + #![clippy::msrv = "1.43"] + let log2_10 = 3.321928094887362; } -pub fn use_self() { - struct Foo; - - impl Foo { - fn new() -> Foo { - Foo {} - } - fn test() -> Foo { - Foo::new() - } +// https://github.com/rust-lang/rust-clippy/issues/6920 +fn scoping() { + mod m { + #![clippy::msrv = "1.42.0"] } -} -fn replace_with_default() { - let mut s = String::from("foo"); - let _ = std::mem::replace(&mut s, String::default()); -} - -fn map_unwrap_or() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt - .map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); -} - -// Could be const -fn missing_const_for_fn() -> i32 { - 1 -} - -fn unnest_or_patterns() { - struct TS(u8, u8); - if let TS(0, x) | TS(1, x) = TS(0, 0) {} -} - -#[cfg_attr(rustfmt, rustfmt_skip)] -fn deprecated_cfg_attr() {} - -#[warn(clippy::cast_lossless)] -fn int_from_bool() -> u8 { - true as u8 -} - -fn err_expect() { - let x: Result = Ok(10); - x.err().expect("Testing expect_err"); -} - -fn cast_abs_to_unsigned() { - let x: i32 = 10; - assert_eq!(10u32, x.abs() as u32); -} - -fn manual_rem_euclid() { - let x: i32 = 10; - let _: i32 = ((x % 4) + 4) % 4; -} - -fn manual_clamp() { - let (input, min, max) = (0, -1, 2); - let _ = if input < min { - min - } else if input > max { - max - } else { - input - }; -} - -fn main() { - filter_map_next(); - checked_conversion(); - redundant_fieldnames(); - redundant_static_lifetime(); - option_as_ref_deref(); - match_like_matches(); - match_same_arms(); - match_same_arms2(); - manual_strip_msrv(); - manual_range_contains(); - use_self(); - replace_with_default(); - map_unwrap_or(); - missing_const_for_fn(); - unnest_or_patterns(); - int_from_bool(); - err_expect(); - cast_abs_to_unsigned(); - manual_rem_euclid(); - manual_clamp(); -} + // Should warn + let log2_10 = 3.321928094887362; -mod just_under_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.44.0"] + mod a { + #![clippy::msrv = "1.42.0"] - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + fn should_warn() { + #![clippy::msrv = "1.43.0"] + let log2_10 = 3.321928094887362; } - } -} - -mod meets_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.45.0"] - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + fn should_not_warn() { + let log2_10 = 3.321928094887362; } } } - -mod just_above_msrv { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.46.0"] - - fn main() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } - } -} - -mod const_rem_euclid { - #![feature(custom_inner_attributes)] - #![clippy::msrv = "1.50.0"] - - pub const fn const_rem_euclid_4(num: i32) -> i32 { - ((num % 4) + 4) % 4 - } -} diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d1cffc26a831..7e2135584efd 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,51 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:216:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:13:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:215:9 + = help: consider using the constant directly + = note: `#[deny(clippy::approx_constant)]` on by default + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:18:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `-D clippy::manual-strip` implied by `-D warnings` -help: try using the `strip_prefix` method + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:28:19 | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:228:24 +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:38:19 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:227:9 + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:48:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::LOG2_10` found + --> $DIR/min_rust_version_attr.rs:55:27 | -LL ~ if let Some() = s.strip_prefix("hello, ") { -LL ~ assert_eq!(.to_uppercase(), "WORLD!"); +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ | + = help: consider using the constant directly -error: aborting due to 2 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs index f20841891a74..02892f329af6 100644 --- a/tests/ui/min_rust_version_invalid_attr.rs +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -2,3 +2,17 @@ #![clippy::msrv = "invalid.version"] fn main() {} + +#[clippy::msrv = "invalid.version"] +fn outer_attr() {} + +mod multiple { + #![clippy::msrv = "1.40"] + #![clippy::msrv = "=1.35.0"] + #![clippy::msrv = "1.10.1"] + + mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] + } +} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr index 6ff88ca56f8b..675b78031525 100644 --- a/tests/ui/min_rust_version_invalid_attr.stderr +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -4,5 +4,47 @@ error: `invalid.version` is not a valid Rust version LL | #![clippy::msrv = "invalid.version"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: `invalid.version` is not a valid Rust version + --> $DIR/min_rust_version_invalid_attr.rs:6:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:11:5 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:12:5 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:10:5 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_invalid_attr.rs:16:9 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_invalid_attr.rs:15:9 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs deleted file mode 100644 index e882d5ccf91a..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.40"] -#![clippy::msrv = "=1.35.0"] -#![clippy::msrv = "1.10.1"] - -mod foo { - #![clippy::msrv = "1"] - #![clippy::msrv = "1.0.0"] -} - -fn main() {} diff --git a/tests/ui/min_rust_version_multiple_inner_attr.stderr b/tests/ui/min_rust_version_multiple_inner_attr.stderr deleted file mode 100644 index e3ff6605cde8..000000000000 --- a/tests/ui/min_rust_version_multiple_inner_attr.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1 - | -LL | #![clippy::msrv = "=1.35.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1 - | -LL | #![clippy::msrv = "1.10.1"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 - | -LL | #![clippy::msrv = "1.40"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `msrv` is defined multiple times - --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5 - | -LL | #![clippy::msrv = "1.0.0"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: first definition found here - --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5 - | -LL | #![clippy::msrv = "1"] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs deleted file mode 100644 index 98fffe1e3512..000000000000 --- a/tests/ui/min_rust_version_no_patch.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![allow(clippy::redundant_clone)] -#![feature(custom_inner_attributes)] -#![clippy::msrv = "1.0"] - -fn manual_strip_msrv() { - let s = "hello, world!"; - if s.starts_with("hello, ") { - assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - } -} - -fn main() { - manual_strip_msrv() -} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs deleted file mode 100644 index 551948bd72ef..000000000000 --- a/tests/ui/min_rust_version_outer_attr.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![feature(custom_inner_attributes)] - -#[clippy::msrv = "invalid.version"] -fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr deleted file mode 100644 index 579ee7a87d23..000000000000 --- a/tests/ui/min_rust_version_outer_attr.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: `msrv` cannot be an outer attribute - --> $DIR/min_rust_version_outer_attr.rs:3:1 - | -LL | #[clippy::msrv = "invalid.version"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/tests/ui/misnamed_getters.rs b/tests/ui/misnamed_getters.rs new file mode 100644 index 000000000000..03e7dac7df94 --- /dev/null +++ b/tests/ui/misnamed_getters.rs @@ -0,0 +1,124 @@ +#![allow(unused)] +#![warn(clippy::misnamed_getters)] + +struct A { + a: u8, + b: u8, + c: u8, +} + +impl A { + fn a(&self) -> &u8 { + &self.b + } + fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + fn b(self) -> u8 { + self.a + } + + fn b_mut(&mut self) -> &mut u8 { + &mut self.a + } + + fn c(&self) -> &u8 { + &self.b + } + + fn c_mut(&mut self) -> &mut u8 { + &mut self.a + } +} + +union B { + a: u8, + b: u8, +} + +impl B { + unsafe fn a(&self) -> &u8 { + &self.b + } + unsafe fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + unsafe fn b(self) -> u8 { + self.a + } + + unsafe fn b_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn c(&self) -> &u8 { + &self.b + } + + unsafe fn c_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn a_unchecked(&self) -> &u8 { + &self.b + } + unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { + &mut self.b + } + + unsafe fn b_unchecked(self) -> u8 { + self.a + } + + unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { + &mut self.a + } + + unsafe fn c_unchecked(&self) -> &u8 { + &self.b + } + + unsafe fn c_unchecked_mut(&mut self) -> &mut u8 { + &mut self.a + } +} + +struct D { + d: u8, + inner: A, +} + +impl core::ops::Deref for D { + type Target = A; + fn deref(&self) -> &A { + &self.inner + } +} + +impl core::ops::DerefMut for D { + fn deref_mut(&mut self) -> &mut A { + &mut self.inner + } +} + +impl D { + fn a(&self) -> &u8 { + &self.b + } + fn a_mut(&mut self) -> &mut u8 { + &mut self.b + } + + fn d(&self) -> &u8 { + &self.b + } + fn d_mut(&mut self) -> &mut u8 { + &mut self.b + } +} + +fn main() { + // test code goes here +} diff --git a/tests/ui/misnamed_getters.stderr b/tests/ui/misnamed_getters.stderr new file mode 100644 index 000000000000..1e38a83d019a --- /dev/null +++ b/tests/ui/misnamed_getters.stderr @@ -0,0 +1,166 @@ +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:11:5 + | +LL | / fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + | + = note: `-D clippy::misnamed-getters` implied by `-D warnings` + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:14:5 + | +LL | / fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:18:5 + | +LL | / fn b(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:22:5 + | +LL | / fn b_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:26:5 + | +LL | / fn c(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.c` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:30:5 + | +LL | / fn c_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.c` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:41:5 + | +LL | / unsafe fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:44:5 + | +LL | / unsafe fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:48:5 + | +LL | / unsafe fn b(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:52:5 + | +LL | / unsafe fn b_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:64:5 + | +LL | / unsafe fn a_unchecked(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:67:5 + | +LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:71:5 + | +LL | / unsafe fn b_unchecked(self) -> u8 { +LL | | self.a + | | ------ help: consider using: `self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:75:5 + | +LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { +LL | | &mut self.a + | | ----------- help: consider using: `&mut self.b` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:107:5 + | +LL | / fn a(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:110:5 + | +LL | / fn a_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.a` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:114:5 + | +LL | / fn d(&self) -> &u8 { +LL | | &self.b + | | ------- help: consider using: `&self.d` +LL | | } + | |_____^ + +error: getter function appears to return the wrong field + --> $DIR/misnamed_getters.rs:117:5 + | +LL | / fn d_mut(&mut self) -> &mut u8 { +LL | | &mut self.b + | | ----------- help: consider using: `&mut self.d` +LL | | } + | |_____^ + +error: aborting due to 18 previous errors + diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index b950248ef942..75cace181675 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -7,7 +7,6 @@ #![warn(clippy::missing_const_for_fn)] #![feature(start)] -#![feature(custom_inner_attributes)] extern crate helper; extern crate proc_macro_with_span; @@ -115,9 +114,8 @@ fn unstably_const_fn() { helper::unstably_const_fn() } +#[clippy::msrv = "1.46.0"] mod const_fn_stabilized_after_msrv { - #![clippy::msrv = "1.46.0"] - // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0. fn const_fn_stabilized_after_msrv(byte: u8) { byte.is_ascii_digit(); diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index 88f6935d224a..0246c8622ed3 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features, clippy::let_and_return)] -#![feature(custom_inner_attributes)] use std::mem::transmute; @@ -68,14 +67,23 @@ mod with_drop { } } +#[clippy::msrv = "1.47.0"] mod const_fn_stabilized_before_msrv { - #![clippy::msrv = "1.47.0"] - // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47. fn const_fn_stabilized_before_msrv(byte: u8) { byte.is_ascii_digit(); } } +#[clippy::msrv = "1.45"] +fn msrv_1_45() -> i32 { + 45 +} + +#[clippy::msrv = "1.46"] +fn msrv_1_46() -> i32 { + 46 +} + // Should not be const fn main() {} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 3eb52b682747..955e1ed26340 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:13:5 + --> $DIR/could_be_const.rs:12:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:17:5 + --> $DIR/could_be_const.rs:16:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | b @@ -17,7 +17,7 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:23:1 + --> $DIR/could_be_const.rs:22:1 | LL | / fn one() -> i32 { LL | | 1 @@ -25,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:28:1 + --> $DIR/could_be_const.rs:27:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -34,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:34:1 + --> $DIR/could_be_const.rs:33:1 | LL | / fn string() -> String { LL | | String::new() @@ -42,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:39:1 + --> $DIR/could_be_const.rs:38:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -50,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:44:1 + --> $DIR/could_be_const.rs:43:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -58,7 +58,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:52:1 + --> $DIR/could_be_const.rs:51:1 | LL | / fn generic_arr(t: [T; 1]) -> T { LL | | t[0] @@ -66,7 +66,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:65:9 + --> $DIR/could_be_const.rs:64:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B @@ -74,12 +74,20 @@ LL | | } | |_________^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:75:5 + --> $DIR/could_be_const.rs:73:5 | LL | / fn const_fn_stabilized_before_msrv(byte: u8) { LL | | byte.is_ascii_digit(); LL | | } | |_____^ -error: aborting due to 10 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:84:1 + | +LL | / fn msrv_1_46() -> i32 { +LL | | 46 +LL | | } + | |_^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/missing_doc.rs b/tests/ui/missing_doc.rs index 29cc026a8fd3..590ad63c90be 100644 --- a/tests/ui/missing_doc.rs +++ b/tests/ui/missing_doc.rs @@ -1,3 +1,4 @@ +// needs-asm-support // aux-build: proc_macro_with_span.rs #![warn(clippy::missing_docs_in_private_items)] diff --git a/tests/ui/missing_doc.stderr b/tests/ui/missing_doc.stderr index 6c8e66f46437..d3bef28bf64c 100644 --- a/tests/ui/missing_doc.stderr +++ b/tests/ui/missing_doc.stderr @@ -1,5 +1,5 @@ error: missing documentation for a type alias - --> $DIR/missing_doc.rs:15:1 + --> $DIR/missing_doc.rs:16:1 | LL | type Typedef = String; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,37 +7,37 @@ LL | type Typedef = String; = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` error: missing documentation for a type alias - --> $DIR/missing_doc.rs:16:1 + --> $DIR/missing_doc.rs:17:1 | LL | pub type PubTypedef = String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:18:1 + --> $DIR/missing_doc.rs:19:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:19:1 + --> $DIR/missing_doc.rs:20:1 | LL | pub mod pub_module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:23:1 + --> $DIR/missing_doc.rs:24:1 | LL | pub fn foo2() {} | ^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:24:1 + --> $DIR/missing_doc.rs:25:1 | LL | fn foo3() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:38:1 + --> $DIR/missing_doc.rs:39:1 | LL | / enum Baz { LL | | BazA { a: isize, b: isize }, @@ -46,31 +46,31 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:39:5 + --> $DIR/missing_doc.rs:40:5 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:12 + --> $DIR/missing_doc.rs:40:12 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:39:22 + --> $DIR/missing_doc.rs:40:22 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:40:5 + --> $DIR/missing_doc.rs:41:5 | LL | BarB, | ^^^^ error: missing documentation for an enum - --> $DIR/missing_doc.rs:43:1 + --> $DIR/missing_doc.rs:44:1 | LL | / pub enum PubBaz { LL | | PubBazA { a: isize }, @@ -78,43 +78,43 @@ LL | | } | |_^ error: missing documentation for a variant - --> $DIR/missing_doc.rs:44:5 + --> $DIR/missing_doc.rs:45:5 | LL | PubBazA { a: isize }, | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/missing_doc.rs:44:15 + --> $DIR/missing_doc.rs:45:15 | LL | PubBazA { a: isize }, | ^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:64:1 + --> $DIR/missing_doc.rs:65:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a constant - --> $DIR/missing_doc.rs:71:1 + --> $DIR/missing_doc.rs:72:1 | LL | pub const FOO4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:73:1 + --> $DIR/missing_doc.rs:74:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/missing_doc.rs:80:1 + --> $DIR/missing_doc.rs:81:1 | LL | pub static BAR4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/missing_doc.rs:82:1 + --> $DIR/missing_doc.rs:83:1 | LL | / mod internal_impl { LL | | /// dox @@ -126,31 +126,31 @@ LL | | } | |_^ error: missing documentation for a function - --> $DIR/missing_doc.rs:85:5 + --> $DIR/missing_doc.rs:86:5 | LL | pub fn undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:86:5 + --> $DIR/missing_doc.rs:87:5 | LL | pub fn undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:87:5 + --> $DIR/missing_doc.rs:88:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:92:9 + --> $DIR/missing_doc.rs:93:9 | LL | pub fn also_undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/missing_doc.rs:93:9 + --> $DIR/missing_doc.rs:94:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index c9ded7f1ad03..183c262ce0b5 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -1,11 +1,8 @@ error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:6:1 | -LL | / pub fn unwrap() { -LL | | let result = Err("Hi"); -LL | | result.unwrap() -LL | | } - | |_^ +LL | pub fn unwrap() { + | ^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:8:5 @@ -17,10 +14,8 @@ LL | result.unwrap() error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:12:1 | -LL | / pub fn panic() { -LL | | panic!("This function panics") -LL | | } - | |_^ +LL | pub fn panic() { + | ^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:13:5 @@ -31,10 +26,8 @@ LL | panic!("This function panics") error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:17:1 | -LL | / pub fn todo() { -LL | | todo!() -LL | | } - | |_^ +LL | pub fn todo() { + | ^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:18:5 @@ -45,14 +38,8 @@ LL | todo!() error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:22:1 | -LL | / pub fn inner_body(opt: Option) { -LL | | opt.map(|x| { -LL | | if x == 10 { -LL | | panic!() -LL | | } -LL | | }); -LL | | } - | |_^ +LL | pub fn inner_body(opt: Option) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:25:13 @@ -63,10 +50,8 @@ LL | panic!() error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:31:1 | -LL | / pub fn unreachable_and_panic() { -LL | | if true { unreachable!() } else { panic!() } -LL | | } - | |_^ +LL | pub fn unreachable_and_panic() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:32:39 @@ -77,11 +62,8 @@ LL | if true { unreachable!() } else { panic!() } error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:36:1 | -LL | / pub fn assert_eq() { -LL | | let x = 0; -LL | | assert_eq!(x, 0); -LL | | } - | |_^ +LL | pub fn assert_eq() { + | ^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:38:5 @@ -92,11 +74,8 @@ LL | assert_eq!(x, 0); error: docs for function which may panic missing `# Panics` section --> $DIR/missing_panics_doc.rs:42:1 | -LL | / pub fn assert_ne() { -LL | | let x = 0; -LL | | assert_ne!(x, 0); -LL | | } - | |_^ +LL | pub fn assert_ne() { + | ^^^^^^^^^^^^^^^^^^ | note: first possible panic found here --> $DIR/missing_panics_doc.rs:44:5 diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs new file mode 100644 index 000000000000..8df885919a3e --- /dev/null +++ b/tests/ui/missing_trait_methods.rs @@ -0,0 +1,50 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::missing_trait_methods)] + +trait A { + fn provided() {} +} + +trait B { + fn required(); + + fn a(_: usize) -> usize { + 1 + } + + fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + a.as_ref() + } +} + +struct Partial; + +impl A for Partial {} + +impl B for Partial { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } +} + +struct Complete; + +impl A for Complete { + fn provided() {} +} + +impl B for Complete { + fn required() {} + + fn a(_: usize) -> usize { + 2 + } + + fn b>(a: &T) -> &[u8] { + a.as_ref() + } +} + +fn main() {} diff --git a/tests/ui/missing_trait_methods.stderr b/tests/ui/missing_trait_methods.stderr new file mode 100644 index 000000000000..0c5205e19657 --- /dev/null +++ b/tests/ui/missing_trait_methods.stderr @@ -0,0 +1,27 @@ +error: missing trait method provided by default: `provided` + --> $DIR/missing_trait_methods.rs:22:1 + | +LL | impl A for Partial {} + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:5:5 + | +LL | fn provided() {} + | ^^^^^^^^^^^^^ + = note: `-D clippy::missing-trait-methods` implied by `-D warnings` + +error: missing trait method provided by default: `b` + --> $DIR/missing_trait_methods.rs:24:1 + | +LL | impl B for Partial { + | ^^^^^^^^^^^^^^^^^^ + | +help: implement the method + --> $DIR/missing_trait_methods.rs:15:5 + | +LL | fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/module_name_repetitions.stderr b/tests/ui/module_name_repetitions.stderr index 3f343a3e4301..277801194a1d 100644 --- a/tests/ui/module_name_repetitions.stderr +++ b/tests/ui/module_name_repetitions.stderr @@ -1,34 +1,34 @@ error: item name starts with its containing module's name - --> $DIR/module_name_repetitions.rs:8:5 + --> $DIR/module_name_repetitions.rs:8:12 | LL | pub fn foo_bar() {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::module-name-repetitions` implied by `-D warnings` error: item name ends with its containing module's name - --> $DIR/module_name_repetitions.rs:9:5 + --> $DIR/module_name_repetitions.rs:9:12 | LL | pub fn bar_foo() {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: item name starts with its containing module's name - --> $DIR/module_name_repetitions.rs:10:5 + --> $DIR/module_name_repetitions.rs:10:16 | LL | pub struct FooCake; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: item name ends with its containing module's name - --> $DIR/module_name_repetitions.rs:11:5 + --> $DIR/module_name_repetitions.rs:11:14 | LL | pub enum CakeFoo {} - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: item name starts with its containing module's name - --> $DIR/module_name_repetitions.rs:12:5 + --> $DIR/module_name_repetitions.rs:12:16 | LL | pub struct Foo7Bar; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ error: aborting due to 5 previous errors diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs new file mode 100644 index 000000000000..4511bc99c3c7 --- /dev/null +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -0,0 +1,119 @@ +// aux-build:macro_rules.rs +#![allow(unused)] +#![allow(deref_nullptr)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::drop_copy)] +#![warn(clippy::multiple_unsafe_ops_per_block)] + +#[macro_use] +extern crate macro_rules; + +use core::arch::asm; + +fn raw_ptr() -> *const () { + core::ptr::null() +} + +unsafe fn not_very_safe() {} + +struct Sample; + +impl Sample { + unsafe fn not_very_safe(&self) {} +} + +#[allow(non_upper_case_globals)] +const sample: Sample = Sample; + +union U { + i: i32, + u: u32, +} + +static mut STATIC: i32 = 0; + +fn test1() { + unsafe { + STATIC += 1; + not_very_safe(); + } +} + +fn test2() { + let u = U { i: 0 }; + + unsafe { + drop(u.u); + *raw_ptr(); + } +} + +fn test3() { + unsafe { + asm!("nop"); + sample.not_very_safe(); + STATIC = 0; + } +} + +fn test_all() { + let u = U { i: 0 }; + unsafe { + drop(u.u); + drop(STATIC); + sample.not_very_safe(); + not_very_safe(); + *raw_ptr(); + asm!("nop"); + } +} + +// no lint +fn correct1() { + unsafe { + STATIC += 1; + } +} + +// no lint +fn correct2() { + unsafe { + STATIC += 1; + } + + unsafe { + *raw_ptr(); + } +} + +// no lint +fn correct3() { + let u = U { u: 0 }; + + unsafe { + not_very_safe(); + } + + unsafe { + drop(u.i); + } +} + +// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064) + +unsafe fn read_char_bad(ptr: *const u8) -> char { + unsafe { char::from_u32_unchecked(*ptr.cast::()) } +} + +// no lint +unsafe fn read_char_good(ptr: *const u8) -> char { + let int_value = unsafe { *ptr.cast::() }; + unsafe { core::char::from_u32_unchecked(int_value) } +} + +// no lint +fn issue10259() { + unsafe_macro!(); +} + +fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr new file mode 100644 index 000000000000..303aeb7aee0c --- /dev/null +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -0,0 +1,129 @@ +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:36:5 + | +LL | / unsafe { +LL | | STATIC += 1; +LL | | not_very_safe(); +LL | | } + | |_____^ + | +note: modification of a mutable static occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:37:9 + | +LL | STATIC += 1; + | ^^^^^^^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:38:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ + = note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings` + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:45:5 + | +LL | / unsafe { +LL | | drop(u.u); +LL | | *raw_ptr(); +LL | | } + | |_____^ + | +note: union field access occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:46:14 + | +LL | drop(u.u); + | ^^^ +note: raw pointer dereference occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:47:9 + | +LL | *raw_ptr(); + | ^^^^^^^^^^ + +error: this `unsafe` block contains 3 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:52:5 + | +LL | / unsafe { +LL | | asm!("nop"); +LL | | sample.not_very_safe(); +LL | | STATIC = 0; +LL | | } + | |_____^ + | +note: inline assembly used here + --> $DIR/multiple_unsafe_ops_per_block.rs:53:9 + | +LL | asm!("nop"); + | ^^^^^^^^^^^ +note: unsafe method call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:54:9 + | +LL | sample.not_very_safe(); + | ^^^^^^^^^^^^^^^^^^^^^^ +note: modification of a mutable static occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:55:9 + | +LL | STATIC = 0; + | ^^^^^^^^^^ + +error: this `unsafe` block contains 6 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:61:5 + | +LL | / unsafe { +LL | | drop(u.u); +LL | | drop(STATIC); +LL | | sample.not_very_safe(); +... | +LL | | asm!("nop"); +LL | | } + | |_____^ + | +note: union field access occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:62:14 + | +LL | drop(u.u); + | ^^^ +note: access of a mutable static occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:63:14 + | +LL | drop(STATIC); + | ^^^^^^ +note: unsafe method call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:64:9 + | +LL | sample.not_very_safe(); + | ^^^^^^^^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:65:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: raw pointer dereference occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:66:9 + | +LL | *raw_ptr(); + | ^^^^^^^^^^ +note: inline assembly used here + --> $DIR/multiple_unsafe_ops_per_block.rs:67:9 + | +LL | asm!("nop"); + | ^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> $DIR/multiple_unsafe_ops_per_block.rs:105:5 + | +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: unsafe function call occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:105:14 + | +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: raw pointer dereference occurs here + --> $DIR/multiple_unsafe_ops_per_block.rs:105:39 + | +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index 370dbd588216..7de153305947 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused, clippy::needless_lifetimes)] #![warn(clippy::mut_from_ref)] struct Foo; diff --git a/tests/ui/mut_mut.rs b/tests/ui/mut_mut.rs index ac8fd9d8fb09..ee3a856566cc 100644 --- a/tests/ui/mut_mut.rs +++ b/tests/ui/mut_mut.rs @@ -57,3 +57,20 @@ fn issue6922() { // do not lint from an external macro mut_mut!(); } + +mod issue9035 { + use std::fmt::Display; + + struct Foo<'a> { + inner: &'a mut dyn Display, + } + + impl Foo<'_> { + fn foo(&mut self) { + let hlp = &mut self.inner; + bar(hlp); + } + } + + fn bar(_: &mut impl Display) {} +} diff --git a/tests/ui/mut_range_bound.rs b/tests/ui/mut_range_bound.rs index e1ae1ef92822..7fdeb27ed988 100644 --- a/tests/ui/mut_range_bound.rs +++ b/tests/ui/mut_range_bound.rs @@ -76,7 +76,7 @@ fn mut_range_bound_no_immediate_break() { let mut n = 3; for i in n..10 { if n == 4 { - n = 1; // FIXME: warning because is is not immediately followed by break + n = 1; // FIXME: warning because it is not immediately followed by break let _ = 2; break; } diff --git a/tests/ui/mut_range_bound.stderr b/tests/ui/mut_range_bound.stderr index e0c8dced382e..b679b7a0aaf8 100644 --- a/tests/ui/mut_range_bound.stderr +++ b/tests/ui/mut_range_bound.stderr @@ -50,7 +50,7 @@ LL | m = 2; // warning because it is not immediately followed by break error: attempt to mutate range bound within loop --> $DIR/mut_range_bound.rs:79:13 | -LL | n = 1; // FIXME: warning because is is not immediately followed by break +LL | n = 1; // FIXME: warning because it is not immediately followed by break | ^ | = note: the range of the loop is unchanged diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 340e89d2db1d..4cb7f6b687f1 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,13 +1,13 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons)] - -#[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables)] -#[allow( +#![feature(lint_reasons)] +#![allow( + unused, clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned )] +#![warn(clippy::needless_borrow)] + fn main() { let a = 5; let ref_a = &a; @@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] let _ = x(&&a); } -#[allow(dead_code)] mod issue9160 { pub struct S { f: F, @@ -267,7 +265,6 @@ where } // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -#[allow(dead_code)] mod copyable_iterator { #[derive(Clone, Copy)] struct Iter; @@ -287,25 +284,20 @@ mod copyable_iterator { } } +#[clippy::msrv = "1.52.0"] mod under_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.52.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } +#[clippy::msrv = "1.53.0"] mod meets_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.53.0"] - fn foo() { let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); } } -#[allow(unused)] fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -334,7 +326,6 @@ fn issue9383() { } } -#[allow(dead_code)] fn closure_test() { let env = "env".to_owned(); let arg = "arg".to_owned(); @@ -348,7 +339,6 @@ fn closure_test() { f(arg); } -#[allow(dead_code)] mod significant_drop { #[derive(Debug)] struct X; @@ -368,7 +358,6 @@ mod significant_drop { fn debug(_: impl std::fmt::Debug) {} } -#[allow(dead_code)] mod used_exactly_once { fn foo(x: String) { use_x(x); @@ -376,7 +365,6 @@ mod used_exactly_once { fn use_x(_: impl AsRef) {} } -#[allow(dead_code)] mod used_more_than_once { fn foo(x: String) { use_x(&x); @@ -385,3 +373,121 @@ mod used_more_than_once { fn use_x(_: impl AsRef) {} fn use_x_again(_: impl AsRef) {} } + +// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 +mod issue_9111 { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + fn main() { + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } +} + +mod issue_9710 { + fn main() { + let string = String::new(); + for _i in 0..10 { + f(&string); + } + } + + fn f>(_: T) {} +} + +mod issue_9739 { + fn foo(_it: impl IntoIterator) {} + + fn main() { + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +mod issue_9739_method_variant { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + fn main() { + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +mod issue_9782 { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(a); + } +} + +mod issue_9782_type_relative_variant { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S::foo::<&[u8; 100]>(&a); + } +} + +mod issue_9782_method_variant { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S.foo::<&[u8; 100]>(&a); + } +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index c93711ac8e28..9a01190ed8db 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,13 +1,13 @@ // run-rustfix -#![feature(custom_inner_attributes, lint_reasons)] - -#[warn(clippy::all, clippy::needless_borrow)] -#[allow(unused_variables)] -#[allow( +#![feature(lint_reasons)] +#![allow( + unused, clippy::uninlined_format_args, clippy::unnecessary_mut_passed, clippy::unnecessary_to_owned )] +#![warn(clippy::needless_borrow)] + fn main() { let a = 5; let ref_a = &a; @@ -171,14 +171,12 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] let _ = x(&&a); } -#[allow(dead_code)] mod issue9160 { pub struct S { f: F, @@ -267,7 +265,6 @@ where } // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 -#[allow(dead_code)] mod copyable_iterator { #[derive(Clone, Copy)] struct Iter; @@ -287,25 +284,20 @@ mod copyable_iterator { } } +#[clippy::msrv = "1.52.0"] mod under_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.52.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } +#[clippy::msrv = "1.53.0"] mod meets_msrv { - #![allow(dead_code)] - #![clippy::msrv = "1.53.0"] - fn foo() { let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); } } -#[allow(unused)] fn issue9383() { // Should not lint because unions need explicit deref when accessing field use std::mem::ManuallyDrop; @@ -334,7 +326,6 @@ fn issue9383() { } } -#[allow(dead_code)] fn closure_test() { let env = "env".to_owned(); let arg = "arg".to_owned(); @@ -348,7 +339,6 @@ fn closure_test() { f(arg); } -#[allow(dead_code)] mod significant_drop { #[derive(Debug)] struct X; @@ -368,7 +358,6 @@ mod significant_drop { fn debug(_: impl std::fmt::Debug) {} } -#[allow(dead_code)] mod used_exactly_once { fn foo(x: String) { use_x(&x); @@ -376,7 +365,6 @@ mod used_exactly_once { fn use_x(_: impl AsRef) {} } -#[allow(dead_code)] mod used_more_than_once { fn foo(x: String) { use_x(&x); @@ -385,3 +373,121 @@ mod used_more_than_once { fn use_x(_: impl AsRef) {} fn use_x_again(_: impl AsRef) {} } + +// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280 +mod issue_9111 { + struct A; + + impl Extend for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + impl<'a> Extend<&'a u8> for A { + fn extend>(&mut self, _: T) { + unimplemented!() + } + } + + fn main() { + let mut a = A; + a.extend(&[]); // vs a.extend([]); + } +} + +mod issue_9710 { + fn main() { + let string = String::new(); + for _i in 0..10 { + f(&string); + } + } + + fn f>(_: T) {} +} + +mod issue_9739 { + fn foo(_it: impl IntoIterator) {} + + fn main() { + foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +mod issue_9739_method_variant { + struct S; + + impl S { + fn foo(&self, _it: impl IntoIterator) {} + } + + fn main() { + S.foo(if std::env::var_os("HI").is_some() { + &[0] + } else { + &[] as &[u32] + }); + } +} + +mod issue_9782 { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + // 100 + foo::<[u8; 100]>(a); + foo(a); + + // 16 + foo::<&[u8]>(&a); + foo(a.as_slice()); + + // 8 + foo::<&[u8; 100]>(&a); + foo(&a); + } +} + +mod issue_9782_type_relative_variant { + struct S; + + impl S { + fn foo>(t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S::foo::<&[u8; 100]>(&a); + } +} + +mod issue_9782_method_variant { + struct S; + + impl S { + fn foo>(&self, t: T) { + println!("{}", std::mem::size_of::()); + let _t: &[u8] = t.as_ref(); + } + } + + fn main() { + let a: [u8; 100] = [0u8; 100]; + + S.foo::<&[u8; 100]>(&a); + } +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 8b593268bec2..d26c317124b8 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -163,52 +163,58 @@ LL | let _ = std::fs::write("x", &"".to_string()); | ^^^^^^^^^^^^^^^ help: change this to: `"".to_string()` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:192:13 + --> $DIR/needless_borrow.rs:190:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:201:13 + --> $DIR/needless_borrow.rs:199:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:286:20 + --> $DIR/needless_borrow.rs:283:20 | LL | takes_iter(&mut x) | ^^^^^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:304:55 + --> $DIR/needless_borrow.rs:297:55 | LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:344:37 + --> $DIR/needless_borrow.rs:335:37 | LL | let _ = std::fs::write("x", &arg); | ^^^^ help: change this to: `arg` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:345:37 + --> $DIR/needless_borrow.rs:336:37 | LL | let _ = std::fs::write("x", &loc); | ^^^^ help: change this to: `loc` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:364:15 + --> $DIR/needless_borrow.rs:354:15 | LL | debug(&x); | ^^ help: change this to: `x` error: the borrowed expression implements the required traits - --> $DIR/needless_borrow.rs:374:15 + --> $DIR/needless_borrow.rs:363:15 | LL | use_x(&x); | ^^ help: change this to: `x` -error: aborting due to 35 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:457:13 + | +LL | foo(&a); + | ^^ help: change this to: `a` + +error: aborting due to 36 previous errors diff --git a/tests/ui/needless_borrowed_ref.fixed b/tests/ui/needless_borrowed_ref.fixed index bcb4eb2dd48a..0c47ceb7b679 100644 --- a/tests/ui/needless_borrowed_ref.fixed +++ b/tests/ui/needless_borrowed_ref.fixed @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let [a, b, ..] = slice {} if let [a, .., b] = slice {} if let [.., a, b] = slice {} + + if let [a, _] = slice {} + + if let (a, b, c) = &tuple {} + if let (a, _, c) = &tuple {} + if let (a, ..) = &tuple {} + + if let TupleStruct(a, ..) = &tuple_struct {} + + if let Struct { + a, + b: b, + c: renamed, + } = &s + {} + + if let Struct { a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.rs b/tests/ui/needless_borrowed_ref.rs index f6de1a6d83d1..f883bb0c8891 100644 --- a/tests/ui/needless_borrowed_ref.rs +++ b/tests/ui/needless_borrowed_ref.rs @@ -1,11 +1,32 @@ // run-rustfix #![warn(clippy::needless_borrowed_reference)] -#![allow(unused, clippy::needless_borrow)] +#![allow( + unused, + irrefutable_let_patterns, + non_shorthand_field_patterns, + clippy::needless_borrow +)] fn main() {} -fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +struct Struct { + a: usize, + b: usize, + c: usize, +} + +struct TupleStruct(u8, u8, u8); + +fn should_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { let mut v = Vec::::new(); let _ = v.iter_mut().filter(|&ref a| a.is_empty()); @@ -24,16 +45,54 @@ fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec if let &[ref a, ref b, ..] = slice {} if let &[ref a, .., ref b] = slice {} if let &[.., ref a, ref b] = slice {} + + if let &[ref a, _] = slice {} + + if let &(ref a, ref b, ref c) = &tuple {} + if let &(ref a, _, ref c) = &tuple {} + if let &(ref a, ..) = &tuple {} + + if let &TupleStruct(ref a, ..) = &tuple_struct {} + + if let &Struct { + ref a, + b: ref b, + c: ref renamed, + } = &s + {} + + if let &Struct { ref a, b: _, .. } = &s {} } -fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec) { +fn should_not_lint( + array: [u8; 4], + slice: &[u8], + slice_of_refs: &[&u8], + vec: Vec, + tuple: (u8, u8, u8), + tuple_struct: TupleStruct, + s: Struct, +) { if let [ref a] = slice {} if let &[ref a, b] = slice {} if let &[ref a, .., b] = slice {} + if let &(ref a, b, ..) = &tuple {} + if let &TupleStruct(ref a, b, ..) = &tuple_struct {} + if let &Struct { ref a, b, .. } = &s {} + // must not be removed as variables must be bound consistently across | patterns if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {} + // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match + // the lint name + if let &[] = slice {} + if let &[_] = slice {} + if let &[..] = slice {} + if let &(..) = &tuple {} + if let &TupleStruct(..) = &tuple_struct {} + if let &Struct { .. } = &s {} + let mut var2 = 5; let thingy2 = Some(&mut var2); if let Some(&mut ref mut v) = thingy2 { @@ -59,6 +118,6 @@ fn foo(a: &Animal, b: &Animal) { // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63 (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // ^ and ^ should **not** be linted - (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + (Animal::Dog(a), &Animal::Dog(_)) => (), } } diff --git a/tests/ui/needless_borrowed_ref.stderr b/tests/ui/needless_borrowed_ref.stderr index 7453542e673f..8d0f0c258dd2 100644 --- a/tests/ui/needless_borrowed_ref.stderr +++ b/tests/ui/needless_borrowed_ref.stderr @@ -1,5 +1,5 @@ error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:10:34 + --> $DIR/needless_borrowed_ref.rs:31:34 | LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); | ^^^^^^ @@ -12,7 +12,7 @@ LL + let _ = v.iter_mut().filter(|a| a.is_empty()); | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:14:17 + --> $DIR/needless_borrowed_ref.rs:35:17 | LL | if let Some(&ref v) = thingy {} | ^^^^^^ @@ -24,7 +24,7 @@ LL + if let Some(v) = thingy {} | error: this pattern takes a reference on something that is being dereferenced - --> $DIR/needless_borrowed_ref.rs:16:14 + --> $DIR/needless_borrowed_ref.rs:37:14 | LL | if let &[&ref a, ref b] = slice_of_refs {} | ^^^^^^ @@ -36,7 +36,7 @@ LL + if let &[a, ref b] = slice_of_refs {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:18:9 + --> $DIR/needless_borrowed_ref.rs:39:9 | LL | let &[ref a, ..] = &array; | ^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + let [a, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:19:9 + --> $DIR/needless_borrowed_ref.rs:40:9 | LL | let &[ref a, ref b, ..] = &array; | ^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL + let [a, b, ..] = &array; | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:21:12 + --> $DIR/needless_borrowed_ref.rs:42:12 | LL | if let &[ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL + if let [a, b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:22:12 + --> $DIR/needless_borrowed_ref.rs:43:12 | LL | if let &[ref a, ref b] = &vec[..] {} | ^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL + if let [a, b] = &vec[..] {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:24:12 + --> $DIR/needless_borrowed_ref.rs:45:12 | LL | if let &[ref a, ref b, ..] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL + if let [a, b, ..] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:25:12 + --> $DIR/needless_borrowed_ref.rs:46:12 | LL | if let &[ref a, .., ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL + if let [a, .., b] = slice {} | error: dereferencing a slice pattern where every element takes a reference - --> $DIR/needless_borrowed_ref.rs:26:12 + --> $DIR/needless_borrowed_ref.rs:47:12 | LL | if let &[.., ref a, ref b] = slice {} | ^^^^^^^^^^^^^^^^^^^ @@ -119,5 +119,96 @@ LL - if let &[.., ref a, ref b] = slice {} LL + if let [.., a, b] = slice {} | -error: aborting due to 10 previous errors +error: dereferencing a slice pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:49:12 + | +LL | if let &[ref a, _] = slice {} + | ^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &[ref a, _] = slice {} +LL + if let [a, _] = slice {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:51:12 + | +LL | if let &(ref a, ref b, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ref b, ref c) = &tuple {} +LL + if let (a, b, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:52:12 + | +LL | if let &(ref a, _, ref c) = &tuple {} + | ^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, _, ref c) = &tuple {} +LL + if let (a, _, c) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:53:12 + | +LL | if let &(ref a, ..) = &tuple {} + | ^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &(ref a, ..) = &tuple {} +LL + if let (a, ..) = &tuple {} + | + +error: dereferencing a tuple pattern where every element takes a reference + --> $DIR/needless_borrowed_ref.rs:55:12 + | +LL | if let &TupleStruct(ref a, ..) = &tuple_struct {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &TupleStruct(ref a, ..) = &tuple_struct {} +LL + if let TupleStruct(a, ..) = &tuple_struct {} + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:57:12 + | +LL | if let &Struct { + | ____________^ +LL | | ref a, +LL | | b: ref b, +LL | | c: ref renamed, +LL | | } = &s + | |_____^ + | +help: try removing the `&` and `ref` parts + | +LL ~ if let Struct { +LL ~ a, +LL ~ b: b, +LL ~ c: renamed, + | + +error: dereferencing a struct pattern where every field's pattern takes a reference + --> $DIR/needless_borrowed_ref.rs:64:12 + | +LL | if let &Struct { ref a, b: _, .. } = &s {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try removing the `&` and `ref` parts + | +LL - if let &Struct { ref a, b: _, .. } = &s {} +LL + if let Struct { a, b: _, .. } = &s {} + | + +error: aborting due to 17 previous errors diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 6ecbbcb62495..2659ad384885 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().count(); sample.iter().next().is_none(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().next().is_none(); + let _ = sample.iter().any(|x| x == &0); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 8dc69bcf5b38..535ec82982b1 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -33,4 +33,33 @@ fn main() { // `BinaryHeap` doesn't have `contains` method sample.iter().collect::>().len(); sample.iter().collect::>().is_empty(); + + // Don't lint string from str + let _ = ["", ""].into_iter().collect::().is_empty(); + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); + + struct VecWrapper(Vec); + impl core::ops::Deref for VecWrapper { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl IntoIterator for VecWrapper { + type IntoIter = as IntoIterator>::IntoIter; + type Item = as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + impl FromIterator for VecWrapper { + fn from_iter>(iter: I) -> Self { + Self(Vec::from_iter(iter)) + } + } + + let _ = sample.iter().collect::>().is_empty(); + let _ = sample.iter().collect::>().contains(&&0); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 039091627a8d..584d2a1d8356 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -66,5 +66,29 @@ error: avoid using `collect()` when not needed LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 11 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:40:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:41:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:63:27 + | +LL | let _ = sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:64:27 + | +LL | let _ = sample.iter().collect::>().contains(&&0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` + +error: aborting due to 15 previous errors diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 6d213b46c20c..fe4209e99b2f 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,5 @@ #![allow(clippy::uninlined_format_args)] +#![warn(clippy::needless_collect)] use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 99e1b91d8fea..790d725907f3 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:39 + --> $DIR/needless_collect_indirect.rs:8:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -14,7 +14,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:38 + --> $DIR/needless_collect_indirect.rs:10:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -28,7 +28,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:40 + --> $DIR/needless_collect_indirect.rs:12:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -42,7 +42,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:13:43 + --> $DIR/needless_collect_indirect.rs:14:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -56,7 +56,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:25:48 + --> $DIR/needless_collect_indirect.rs:26:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -70,7 +70,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:54:51 + --> $DIR/needless_collect_indirect.rs:55:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -84,7 +84,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:59:55 + --> $DIR/needless_collect_indirect.rs:60:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -98,7 +98,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:64:57 + --> $DIR/needless_collect_indirect.rs:65:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -112,7 +112,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:69:57 + --> $DIR/needless_collect_indirect.rs:70:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -126,7 +126,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:129:59 + --> $DIR/needless_collect_indirect.rs:130:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -143,7 +143,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:154:59 + --> $DIR/needless_collect_indirect.rs:155:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -160,7 +160,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:183:63 + --> $DIR/needless_collect_indirect.rs:184:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -177,7 +177,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:219:59 + --> $DIR/needless_collect_indirect.rs:220:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -195,7 +195,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:244:26 + --> $DIR/needless_collect_indirect.rs:245:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -211,7 +211,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:266:30 + --> $DIR/needless_collect_indirect.rs:267:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -227,7 +227,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:288:30 + --> $DIR/needless_collect_indirect.rs:289:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/needless_lifetimes.fixed b/tests/ui/needless_lifetimes.fixed new file mode 100644 index 000000000000..d286ef4ba378 --- /dev/null +++ b/tests/ui/needless_lifetimes.fixed @@ -0,0 +1,538 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::needless_lifetimes)] +#![allow( + unused, + clippy::boxed_local, + clippy::extra_unused_type_parameters, + clippy::needless_pass_by_value, + clippy::unnecessary_wraps, + dyn_drop, + clippy::get_first +)] + +#[macro_use] +extern crate macro_rules; + +fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + +fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + +// No error; same lifetime on two params. +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {} + +// No error; static involved. +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {} + +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {} + +fn in_and_out(x: &u8, _y: u8) -> &u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 +// ^^^ +fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 +// ^^^ +fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + y +} + +// No error; multiple input refs +async fn func<'a>(args: &[&'a str]) -> Option<&'a str> { + args.get(0).cloned() +} + +// No error; static involved. +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> +// ^^^ +fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> +// ^^^ +fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + Ok(y) +} + +// No error; two input refs. +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { + x.unwrap() +} + +fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + Ok(x) +} + +// Where-clause, but without lifetimes. +fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> +where + T: Copy, +{ + Ok(x) +} + +type Ref<'r> = &'r u8; + +// No error; same lifetime on two params. +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {} + +fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} + +// No error; bounded lifetime. +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) +where + 'b: 'a, +{ +} + +struct Lt<'a, I: 'static> { + x: &'a I, +} + +// No error; fn bound references `'a`. +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + F: Fn(Lt<'a, I>) -> Lt<'a, I>, +{ + unreachable!() +} + +fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> +where + for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, +{ + unreachable!() +} + +// No error; see below. +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) { + f(x); +} + +fn fn_bound_3_cannot_elide() { + let x = 42; + let p = &x; + let mut q = &x; + // This will fail if we elide lifetimes of `fn_bound_3`. + fn_bound_3(p, |y| q = y); +} + +// No error; multiple input refs. +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { + if cond { x } else { f() } +} + +struct X { + x: u8, +} + +impl X { + fn self_and_out(&self) -> &u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 + // ^^^ + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 + // ^^^^^ + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + x + } + + fn distinct_self_and_in(&self, _x: &u8) {} + + // No error; same lifetimes on two params. + fn self_and_same_in<'s>(&'s self, _x: &'s u8) {} +} + +struct Foo<'a>(&'a u8); + +impl<'a> Foo<'a> { + // No error; lifetime `'a` not defined in method. + fn self_shared_lifetime(&self, _: &'a u8) {} + // No error; bounds exist. + fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {} +} + +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 { + unimplemented!() +} + +fn struct_with_lt(_foo: Foo<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`). +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`). +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str +// ^^ +fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str +// ^^^^ +fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + unimplemented!() +} + +trait WithLifetime<'a> {} + +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>; + +// Should not warn because it won't build without the lifetime. +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str { + unimplemented!() +} + +// Should warn because there is no lifetime on `Drop`, so this would be +// unambiguous if we elided the lifetime. +fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + unimplemented!() +} + +type FooAlias<'a> = Foo<'a>; + +fn alias_with_lt(_foo: FooAlias<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`). +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`). +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str +// ^^ +fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str +// ^^^^^^^^^ +fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + unimplemented!() +} + +fn named_input_elided_output(_arg: &str) -> &str { + unimplemented!() +} + +fn elided_input_named_output<'a>(_arg: &str) -> &'a str { + unimplemented!() +} + +fn trait_bound_ok>(_: &u8, _: T) { + unimplemented!() +} +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { + unimplemented!() +} + +// Don't warn on these; see issue #292. +fn trait_bound_bug<'a, T: WithLifetime<'a>>() { + unimplemented!() +} + +// See issue #740. +struct Test { + vec: Vec, +} + +impl Test { + fn iter<'a>(&'a self) -> Box + 'a> { + unimplemented!() + } +} + +trait LintContext<'a> {} + +fn f<'a, T: LintContext<'a>>(_: &T) {} + +fn test<'a>(x: &'a [u8]) -> u8 { + let y: &'a u8 = &x[5]; + *y +} + +// Issue #3284: give hint regarding lifetime in return type. +struct Cow<'a> { + x: &'a str, +} +fn out_return_type_lts(e: &str) -> Cow<'_> { + unimplemented!() +} + +// Make sure we still warn on implementations +mod issue4291 { + trait BadTrait { + fn needless_lt(x: &u8) {} + } + + impl BadTrait for () { + fn needless_lt(_x: &u8) {} + } +} + +mod issue2944 { + trait Foo {} + struct Bar; + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz(&self) -> impl Foo + '_ { + Baz { bar: self } + } + } +} + +mod nested_elision_sites { + // issue #issue2944 + + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + // lint + fn where_clause_elidadable(i: &i32, f: T) -> &i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } + + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } + // lint + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + |f| () + } +} + +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + +mod issue7296 { + use std::rc::Rc; + use std::sync::Arc; + + struct Foo; + impl Foo { + fn implicit(&self) -> &() { + &() + } + fn implicit_mut(&mut self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc) -> &'a () { + &() + } + fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box, here: &()) -> &() { + &() + } + } + + trait Bar { + fn implicit(&self) -> &(); + fn implicit_provided(&self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc) -> &'a (); + fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box, here: &()) -> &(); + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { + &() + } + } +} + +mod pr_9743_false_negative_fix { + #![allow(unused)] + + fn foo(x: &u8, y: &'_ u8) {} + + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} +} + +mod pr_9743_output_lifetime_checks { + #![allow(unused)] + + // lint: only one input + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + + // lint: multiple inputs, output would not be elided + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + unimplemented!() + } + + // don't lint: multiple inputs, output would be elided (which would create an ambiguity) + fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 { + unimplemented!() + } +} + +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + +fn main() {} diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index fc686b1dac0e..409528b291db 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,13 +1,20 @@ +// run-rustfix +// aux-build:macro_rules.rs + #![warn(clippy::needless_lifetimes)] #![allow( - dead_code, + unused, clippy::boxed_local, + clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop, clippy::get_first )] +#[macro_use] +extern crate macro_rules; + fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} @@ -29,11 +36,20 @@ fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { x } -// No error; multiple input refs. -fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 +// ^^^ +fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { x } +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 +// ^^^ +fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { + y +} + // No error; multiple input refs async fn func<'a>(args: &[&'a str]) -> Option<&'a str> { args.get(0).cloned() @@ -44,11 +60,20 @@ fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { x } -// No error. -fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> +// ^^^ +fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { Ok(x) } +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> +// ^^^ +fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { + Ok(y) +} + // No error; two input refs. fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { x.unwrap() @@ -129,11 +154,20 @@ impl X { &self.x } - // No error; multiple input refs. - fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 + // ^^^ + fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { &self.x } + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 + // ^^^^^ + fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { + x + } + fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} // No error; same lifetimes on two params. @@ -167,8 +201,19 @@ fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { unimplemented!() } -// No warning; two input lifetimes. -fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str +// ^^ +fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str +// ^^^^ +fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { unimplemented!() } @@ -203,8 +248,19 @@ fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { unimplemented!() } -// No warning; two input lifetimes. -fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str +// ^^ +fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str +// ^^^^^^^^^ +fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { unimplemented!() } @@ -419,4 +475,64 @@ mod issue7296 { } } +mod pr_9743_false_negative_fix { + #![allow(unused)] + + fn foo<'a>(x: &'a u8, y: &'_ u8) {} + + fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} +} + +mod pr_9743_output_lifetime_checks { + #![allow(unused)] + + // lint: only one input + fn one_input<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + + // lint: multiple inputs, output would not be elided + fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { + unimplemented!() + } + + // don't lint: multiple inputs, output would be elided (which would create an ambiguity) + fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 { + unimplemented!() + } +} + +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index 3c428fd4674c..4e3c8f20d8c5 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,190 +1,559 @@ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:11:1 +error: the following explicit lifetimes could be elided: 'a, 'b + --> $DIR/needless_lifetimes.rs:18:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} +LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:13:1 +error: the following explicit lifetimes could be elided: 'a, 'b + --> $DIR/needless_lifetimes.rs:20:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} +LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:23:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:30:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { +LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { + | + +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:42:1 + | +LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { +LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:57:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:49:1 + | +LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { +LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + | + +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:66:1 + | +LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:73:1 + | +LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { +LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:82:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:62:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:87:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> +LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:74:1 +error: the following explicit lifetimes could be elided: 'a, 'b + --> $DIR/needless_lifetimes.rs:99:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} +LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:98:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:123:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +LL + fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:128:5 +error: the following explicit lifetimes could be elided: 's + --> $DIR/needless_lifetimes.rs:153:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_out<'s>(&'s self) -> &'s u8 { +LL + fn self_and_out(&self) -> &u8 { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:137:5 +error: the following explicit lifetimes could be elided: 't + --> $DIR/needless_lifetimes.rs:160:5 + | +LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { +LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + | + +error: the following explicit lifetimes could be elided: 's + --> $DIR/needless_lifetimes.rs:167:5 + | +LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { +LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + | + +error: the following explicit lifetimes could be elided: 's, 't + --> $DIR/needless_lifetimes.rs:171:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} +LL + fn distinct_self_and_in(&self, _x: &u8) {} + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:156:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:190:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { +LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { + | + +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:208:1 + | +LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { +LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:216:1 + | +LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { +LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:186:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:231:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { +LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:192:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:237:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { +LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { + | + +error: the following explicit lifetimes could be elided: 'b + --> $DIR/needless_lifetimes.rs:255:1 + | +LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { +LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:263:1 + | +LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { +LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:211:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:267:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn named_input_elided_output<'a>(_arg: &'a str) -> &str { +LL + fn named_input_elided_output(_arg: &str) -> &str { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:219:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:275:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { +LL + fn trait_bound_ok>(_: &u8, _: T) { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:255:1 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:311:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { +LL + fn out_return_type_lts(e: &str) -> Cow<'_> { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:262:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:318:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(x: &'a u8) {} +LL + fn needless_lt(x: &u8) {} + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:266:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:322:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(_x: &'a u8) {} +LL + fn needless_lt(_x: &u8) {} + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:279:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:335:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:311:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:367:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { +LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:320:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:376:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { +LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:332:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:388:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 +LL + fn where_clause_elidadable(i: &i32, f: T) -> &i32 + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:347:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:403:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { +LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:360:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:416:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { +LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:363:5 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:419:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { +LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:385:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:441:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a () { +LL + fn implicit(&self) -> &() { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:388:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:444:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_mut<'a>(&'a mut self) -> &'a () { +LL + fn implicit_mut(&mut self) -> &() { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:399:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:455:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:405:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:461:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a (); +LL + fn implicit(&self) -> &(); + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:406:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:462:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_provided<'a>(&'a self) -> &'a () { +LL + fn implicit_provided(&self) -> &() { + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:415:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:471:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); +LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); + | -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:416:9 +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:472:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:481:5 + | +LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn foo<'a>(x: &'a u8, y: &'_ u8) {} +LL + fn foo(x: &u8, y: &'_ u8) {} + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:483:5 + | +LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} +LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:490:5 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:495:5 + | +LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { +LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:508:13 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | local_one_input_macro!(); + | ------------------------ in this macro invocation + | + = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | -error: aborting due to 31 previous errors +error: aborting due to 46 previous errors diff --git a/tests/ui/needless_parens_on_range_literals.fixed b/tests/ui/needless_parens_on_range_literals.fixed index 1bd75c806bc9..f11330a8916d 100644 --- a/tests/ui/needless_parens_on_range_literals.fixed +++ b/tests/ui/needless_parens_on_range_literals.fixed @@ -2,7 +2,7 @@ // edition:2018 #![warn(clippy::needless_parens_on_range_literals)] -#![allow(clippy::almost_complete_letter_range)] +#![allow(clippy::almost_complete_range)] fn main() { let _ = 'a'..='z'; diff --git a/tests/ui/needless_parens_on_range_literals.rs b/tests/ui/needless_parens_on_range_literals.rs index 7abb8a1adc1b..671c0009e23b 100644 --- a/tests/ui/needless_parens_on_range_literals.rs +++ b/tests/ui/needless_parens_on_range_literals.rs @@ -2,7 +2,7 @@ // edition:2018 #![warn(clippy::needless_parens_on_range_literals)] -#![allow(clippy::almost_complete_letter_range)] +#![allow(clippy::almost_complete_range)] fn main() { let _ = ('a')..=('z'); diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index ba9d15e59d0e..7eaca571992f 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -8,7 +8,6 @@ dead_code, unused_must_use )] -#![feature(custom_inner_attributes)] struct TO { magic: Option, diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 3a6523e8fe87..960bc7b78983 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -8,7 +8,6 @@ dead_code, unused_must_use )] -#![feature(custom_inner_attributes)] struct TO { magic: Option, diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index f8308e24e771..d1f89e326c67 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,5 +1,5 @@ error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:23:12 + --> $DIR/needless_question_mark.rs:22:12 | LL | return Some(to.magic?); | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` @@ -7,67 +7,67 @@ LL | return Some(to.magic?); = note: `-D clippy::needless-question-mark` implied by `-D warnings` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:31:12 + --> $DIR/needless_question_mark.rs:30:12 | LL | return Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:36:5 + --> $DIR/needless_question_mark.rs:35:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:41:21 + --> $DIR/needless_question_mark.rs:40:21 | LL | to.and_then(|t| Some(t.magic?)) | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:50:9 + --> $DIR/needless_question_mark.rs:49:9 | LL | Some(t.magic?) | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:55:12 + --> $DIR/needless_question_mark.rs:54:12 | LL | return Ok(tr.magic?); | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:62:12 + --> $DIR/needless_question_mark.rs:61:12 | LL | return Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:66:5 + --> $DIR/needless_question_mark.rs:65:5 | LL | Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:70:21 + --> $DIR/needless_question_mark.rs:69:21 | LL | tr.and_then(|t| Ok(t.magic?)) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:78:9 + --> $DIR/needless_question_mark.rs:77:9 | LL | Ok(t.magic?) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:85:16 + --> $DIR/needless_question_mark.rs:84:16 | LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:120:27 + --> $DIR/needless_question_mark.rs:119:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)` @@ -78,13 +78,13 @@ LL | let _x = some_and_qmark_in_macro!(x?); = note: this error originates in the macro `some_and_qmark_in_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:131:5 + --> $DIR/needless_question_mark.rs:130:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:139:5 + --> $DIR/needless_question_mark.rs:138:5 | LL | Ok(s.magic?) | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic` diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index b31544ec334a..cffa19bec3a6 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -49,7 +49,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ @@ -126,7 +126,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,7 +137,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..10 { | ^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -148,7 +148,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, ) in vec.iter_mut().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d2163b14fcad..079e3531def1 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -30,6 +31,16 @@ fn test_no_semicolon() -> bool { true } +#[rustfmt::skip] +fn test_multiple_semicolon() -> bool { + true +} + +#[rustfmt::skip] +fn test_multiple_semicolon_with_spaces() -> bool { + true +} + fn test_if_block() -> bool { if true { true @@ -59,14 +70,11 @@ fn test_macro_call() -> i32 { } fn test_void_fun() { - } fn test_void_if_fun(b: bool) { if b { - } else { - } } @@ -82,7 +90,6 @@ fn test_nested_match(x: u32) { 0 => (), 1 => { let _ = 42; - }, _ => (), } @@ -126,7 +133,6 @@ mod issue6501 { fn test_closure() { let _ = || { - }; let _ = || {}; } @@ -179,14 +185,11 @@ async fn async_test_macro_call() -> i32 { } async fn async_test_void_fun() { - } async fn async_test_void_if_fun(b: bool) { if b { - } else { - } } @@ -269,4 +272,29 @@ fn issue9503(x: usize) -> isize { } } +mod issue9416 { + pub fn with_newline() { + let _ = 42; + } + + #[rustfmt::skip] + pub fn oneline() { + let _ = 42; + } +} + +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + +// without anyhow, but triggers the same bug I believe +#[expect(clippy::useless_format)] +fn issue10051() -> Result { + if true { + Ok(format!("ok!")) + } else { + Err(format!("err!")) + } +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 114414b5fac7..c1c48284f086 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,6 +1,7 @@ // run-rustfix #![feature(lint_reasons)] +#![feature(yeet_expr)] #![allow(unused)] #![allow( clippy::if_same_then_else, @@ -30,6 +31,16 @@ fn test_no_semicolon() -> bool { return true; } +#[rustfmt::skip] +fn test_multiple_semicolon() -> bool { + return true;;; +} + +#[rustfmt::skip] +fn test_multiple_semicolon_with_spaces() -> bool { + return true;; ; ; +} + fn test_if_block() -> bool { if true { return true; @@ -269,4 +280,31 @@ fn issue9503(x: usize) -> isize { }; } +mod issue9416 { + pub fn with_newline() { + let _ = 42; + + return; + } + + #[rustfmt::skip] + pub fn oneline() { + let _ = 42; return; + } +} + +fn issue9947() -> Result<(), String> { + do yeet "hello"; +} + +// without anyhow, but triggers the same bug I believe +#[expect(clippy::useless_format)] +fn issue10051() -> Result { + if true { + return Ok(format!("ok!")); + } else { + return Err(format!("err!")); + } +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 047fb6c2311a..08b04bfe9d8b 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -1,5 +1,5 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:26:5 + --> $DIR/needless_return.rs:27:5 | LL | return true; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:30:5 + --> $DIR/needless_return.rs:31:5 | LL | return true; | ^^^^^^^^^^^ @@ -16,7 +16,23 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:35:9 + --> $DIR/needless_return.rs:36:5 + | +LL | return true;;; + | ^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:41:5 + | +LL | return true;; ; ; + | ^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:46:9 | LL | return true; | ^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:9 + --> $DIR/needless_return.rs:48:9 | LL | return false; | ^^^^^^^^^^^^ @@ -32,7 +48,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:43:17 + --> $DIR/needless_return.rs:54:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -40,7 +56,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:45:13 + --> $DIR/needless_return.rs:56:13 | LL | return true; | ^^^^^^^^^^^ @@ -48,7 +64,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:52:9 + --> $DIR/needless_return.rs:63:9 | LL | return true; | ^^^^^^^^^^^ @@ -56,7 +72,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:16 + --> $DIR/needless_return.rs:65:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -64,7 +80,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:58:5 + --> $DIR/needless_return.rs:69:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -72,31 +88,37 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:62:5 + --> $DIR/needless_return.rs:72:21 | -LL | return; - | ^^^^^^ +LL | fn test_void_fun() { + | _____________________^ +LL | | return; + | |__________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:67:9 + --> $DIR/needless_return.rs:77:11 | -LL | return; - | ^^^^^^ +LL | if b { + | ___________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:69:9 + --> $DIR/needless_return.rs:79:13 | -LL | return; - | ^^^^^^ +LL | } else { + | _____________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:76:14 + --> $DIR/needless_return.rs:87:14 | LL | _ => return, | ^^^^^^ @@ -104,15 +126,17 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:85:13 + --> $DIR/needless_return.rs:95:24 | -LL | return; - | ^^^^^^ +LL | let _ = 42; + | ________________________^ +LL | | return; + | |__________________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:87:14 + --> $DIR/needless_return.rs:98:14 | LL | _ => return, | ^^^^^^ @@ -120,7 +144,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:100:9 + --> $DIR/needless_return.rs:111:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,7 +152,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:102:9 + --> $DIR/needless_return.rs:113:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +160,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:124:32 + --> $DIR/needless_return.rs:135:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ @@ -144,15 +168,17 @@ LL | bar.unwrap_or_else(|_| return) = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:129:13 + --> $DIR/needless_return.rs:139:21 | -LL | return; - | ^^^^^^ +LL | let _ = || { + | _____________________^ +LL | | return; + | |__________________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:131:20 + --> $DIR/needless_return.rs:142:20 | LL | let _ = || return; | ^^^^^^ @@ -160,7 +186,7 @@ LL | let _ = || return; = help: replace `return` with an empty block error: unneeded `return` statement - --> $DIR/needless_return.rs:137:32 + --> $DIR/needless_return.rs:148:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ @@ -168,7 +194,7 @@ LL | res.unwrap_or_else(|_| return Foo) = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:146:5 + --> $DIR/needless_return.rs:157:5 | LL | return true; | ^^^^^^^^^^^ @@ -176,7 +202,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:150:5 + --> $DIR/needless_return.rs:161:5 | LL | return true; | ^^^^^^^^^^^ @@ -184,7 +210,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:155:9 + --> $DIR/needless_return.rs:166:9 | LL | return true; | ^^^^^^^^^^^ @@ -192,7 +218,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:157:9 + --> $DIR/needless_return.rs:168:9 | LL | return false; | ^^^^^^^^^^^^ @@ -200,7 +226,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:163:17 + --> $DIR/needless_return.rs:174:17 | LL | true => return false, | ^^^^^^^^^^^^ @@ -208,7 +234,7 @@ LL | true => return false, = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:165:13 + --> $DIR/needless_return.rs:176:13 | LL | return true; | ^^^^^^^^^^^ @@ -216,7 +242,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:172:9 + --> $DIR/needless_return.rs:183:9 | LL | return true; | ^^^^^^^^^^^ @@ -224,7 +250,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:174:16 + --> $DIR/needless_return.rs:185:16 | LL | let _ = || return true; | ^^^^^^^^^^^ @@ -232,7 +258,7 @@ LL | let _ = || return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:178:5 + --> $DIR/needless_return.rs:189:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ @@ -240,31 +266,37 @@ LL | return the_answer!(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:182:5 + --> $DIR/needless_return.rs:192:33 | -LL | return; - | ^^^^^^ +LL | async fn async_test_void_fun() { + | _________________________________^ +LL | | return; + | |__________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:187:9 + --> $DIR/needless_return.rs:197:11 | -LL | return; - | ^^^^^^ +LL | if b { + | ___________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:189:9 + --> $DIR/needless_return.rs:199:13 | -LL | return; - | ^^^^^^ +LL | } else { + | _____________^ +LL | | return; + | |______________^ | = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:196:14 + --> $DIR/needless_return.rs:207:14 | LL | _ => return, | ^^^^^^ @@ -272,7 +304,7 @@ LL | _ => return, = help: replace `return` with a unit value error: unneeded `return` statement - --> $DIR/needless_return.rs:209:9 + --> $DIR/needless_return.rs:220:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -280,7 +312,7 @@ LL | return String::from("test"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:211:9 + --> $DIR/needless_return.rs:222:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ @@ -288,7 +320,7 @@ LL | return String::new(); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:227:5 + --> $DIR/needless_return.rs:238:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +328,7 @@ LL | return format!("Hello {}", "world!"); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:238:9 + --> $DIR/needless_return.rs:249:9 | LL | return true; | ^^^^^^^^^^^ @@ -304,7 +336,7 @@ LL | return true; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:240:9 + --> $DIR/needless_return.rs:251:9 | LL | return false; | ^^^^^^^^^^^^ @@ -312,7 +344,7 @@ LL | return false; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:247:13 + --> $DIR/needless_return.rs:258:13 | LL | return 10; | ^^^^^^^^^ @@ -320,7 +352,7 @@ LL | return 10; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:250:13 + --> $DIR/needless_return.rs:261:13 | LL | return 100; | ^^^^^^^^^^ @@ -328,7 +360,7 @@ LL | return 100; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:258:9 + --> $DIR/needless_return.rs:269:9 | LL | return 0; | ^^^^^^^^ @@ -336,7 +368,7 @@ LL | return 0; = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:265:13 + --> $DIR/needless_return.rs:276:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -344,12 +376,47 @@ LL | return *(x as *const isize); = help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:267:13 + --> $DIR/needless_return.rs:278:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: remove `return` -error: aborting due to 44 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:285:20 + | +LL | let _ = 42; + | ____________________^ +LL | | +LL | | return; + | |______________^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:292:20 + | +LL | let _ = 42; return; + | ^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:304:9 + | +LL | return Ok(format!("ok!")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:306:9 + | +LL | return Err(format!("err!")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: aborting due to 50 previous errors diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed index 61f5fc4e679e..5496031fefab 100644 --- a/tests/ui/needless_splitn.fixed +++ b/tests/ui/needless_splitn.fixed @@ -1,7 +1,6 @@ // run-rustfix // edition:2018 -#![feature(custom_inner_attributes)] #![warn(clippy::needless_splitn)] #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)] @@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> { Some(()) } +#[clippy::msrv = "1.51"] fn _test_msrv() { - #![clippy::msrv = "1.51"] // `manual_split_once` MSRV shouldn't apply to `needless_splitn` let _ = "key=value".split('=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs index 71d9a7077faa..35c2465bae13 100644 --- a/tests/ui/needless_splitn.rs +++ b/tests/ui/needless_splitn.rs @@ -1,7 +1,6 @@ // run-rustfix // edition:2018 -#![feature(custom_inner_attributes)] #![warn(clippy::needless_splitn)] #![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)] @@ -40,8 +39,8 @@ fn _question_mark(s: &str) -> Option<()> { Some(()) } +#[clippy::msrv = "1.51"] fn _test_msrv() { - #![clippy::msrv = "1.51"] // `manual_split_once` MSRV shouldn't apply to `needless_splitn` let _ = "key=value".splitn(2, '=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr index f112b29e7f20..f607d8e1ab5f 100644 --- a/tests/ui/needless_splitn.stderr +++ b/tests/ui/needless_splitn.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:15:13 + --> $DIR/needless_splitn.rs:14:13 | LL | let _ = str.splitn(2, '=').next(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` @@ -7,73 +7,73 @@ LL | let _ = str.splitn(2, '=').next(); = note: `-D clippy::needless-splitn` implied by `-D warnings` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:16:13 + --> $DIR/needless_splitn.rs:15:13 | LL | let _ = str.splitn(2, '=').nth(0); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:19:18 + --> $DIR/needless_splitn.rs:18:18 | LL | let (_, _) = str.splitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:22:13 + --> $DIR/needless_splitn.rs:21:13 | LL | let _ = str.rsplitn(2, '=').next(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:23:13 + --> $DIR/needless_splitn.rs:22:13 | LL | let _ = str.rsplitn(2, '=').nth(0); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:26:18 + --> $DIR/needless_splitn.rs:25:18 | LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:28:13 + --> $DIR/needless_splitn.rs:27:13 | LL | let _ = str.splitn(5, '=').next(); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:29:13 + --> $DIR/needless_splitn.rs:28:13 | LL | let _ = str.splitn(5, '=').nth(3); | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:35:13 + --> $DIR/needless_splitn.rs:34:13 | LL | let _ = s.splitn(2, '=').next()?; | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:36:13 + --> $DIR/needless_splitn.rs:35:13 | LL | let _ = s.splitn(2, '=').nth(0)?; | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:37:13 + --> $DIR/needless_splitn.rs:36:13 | LL | let _ = s.rsplitn(2, '=').next()?; | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` error: unnecessary use of `rsplitn` - --> $DIR/needless_splitn.rs:38:13 + --> $DIR/needless_splitn.rs:37:13 | LL | let _ = s.rsplitn(2, '=').nth(0)?; | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` error: unnecessary use of `splitn` - --> $DIR/needless_splitn.rs:46:13 + --> $DIR/needless_splitn.rs:45:13 | LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 3dbef19890e9..28e8f459d442 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -229,6 +229,27 @@ pub fn test18() { }; } +// Issue #9831: unconditional break to internal labeled block +pub fn test19() { + fn thing(iter: impl Iterator) { + for _ in iter { + 'b: { + break 'b; + } + } + } +} + +pub fn test20() { + 'a: loop { + 'b: { + break 'b 'c: { + break 'a; + }; + } + } +} + fn main() { test1(); test2(); diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 3033f019244a..b7029bf8bed4 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -114,5 +114,17 @@ LL | | break x; LL | | }; | |_____^ -error: aborting due to 10 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:244:5 + | +LL | / 'a: loop { +LL | | 'b: { +LL | | break 'b 'c: { +LL | | break 'a; +LL | | }; +LL | | } +LL | | } + | |_____^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index 2f315ffe2983..beec42f08bb0 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -1,3 +1,4 @@ +#![feature(type_alias_impl_trait)] #![warn(clippy::new_ret_no_self)] #![allow(dead_code)] @@ -350,3 +351,75 @@ impl RetOtherSelf { RetOtherSelf(RetOtherSelfWrapper(t)) } } + +mod issue7344 { + struct RetImplTraitSelf(T); + + impl RetImplTraitSelf { + // should not trigger lint + fn new(t: T) -> impl Into { + Self(t) + } + } + + struct RetImplTraitNoSelf(T); + + impl RetImplTraitNoSelf { + // should trigger lint + fn new(t: T) -> impl Into { + 1 + } + } + + trait Trait2 {} + impl Trait2 for () {} + + struct RetImplTraitSelf2(T); + + impl RetImplTraitSelf2 { + // should not trigger lint + fn new(t: T) -> impl Trait2<(), Self> { + unimplemented!() + } + } + + struct RetImplTraitNoSelf2(T); + + impl RetImplTraitNoSelf2 { + // should trigger lint + fn new(t: T) -> impl Trait2<(), i32> { + unimplemented!() + } + } + + struct RetImplTraitSelfAdt<'a>(&'a str); + + impl<'a> RetImplTraitSelfAdt<'a> { + // should not trigger lint + fn new<'b: 'a>(s: &'b str) -> impl Into> { + RetImplTraitSelfAdt(s) + } + } +} + +mod issue10041 { + struct Bomb; + + impl Bomb { + // Hidden default generic paramter. + pub fn new() -> impl PartialOrd { + 0i32 + } + } + + // TAIT with self-referencing bounds + type X = impl std::ops::Add; + + struct Bomb2; + + impl Bomb2 { + pub fn new() -> X { + 0i32 + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index 8217bc6187f9..2eaebfb5cac5 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:49:5 + --> $DIR/new_ret_no_self.rs:50:5 | LL | / pub fn new(_: String) -> impl R { LL | | S3 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:81:5 + --> $DIR/new_ret_no_self.rs:82:5 | LL | / pub fn new() -> u32 { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:90:5 + --> $DIR/new_ret_no_self.rs:91:5 | LL | / pub fn new(_: String) -> u32 { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:126:5 + --> $DIR/new_ret_no_self.rs:127:5 | LL | / pub fn new() -> (u32, u32) { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:153:5 + --> $DIR/new_ret_no_self.rs:154:5 | LL | / pub fn new() -> *mut V { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:171:5 + --> $DIR/new_ret_no_self.rs:172:5 | LL | / pub fn new() -> Option { LL | | unimplemented!(); @@ -49,19 +49,19 @@ LL | | } | |_____^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:224:9 + --> $DIR/new_ret_no_self.rs:225:9 | LL | fn new() -> String; | ^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:236:9 + --> $DIR/new_ret_no_self.rs:237:9 | LL | fn new(_: String) -> String; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:271:9 + --> $DIR/new_ret_no_self.rs:272:9 | LL | / fn new() -> (u32, u32) { LL | | unimplemented!(); @@ -69,12 +69,44 @@ LL | | } | |_________^ error: methods called `new` usually return `Self` - --> $DIR/new_ret_no_self.rs:298:9 + --> $DIR/new_ret_no_self.rs:299:9 | LL | / fn new() -> *mut V { LL | | unimplemented!(); LL | | } | |_________^ -error: aborting due to 10 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:369:9 + | +LL | / fn new(t: T) -> impl Into { +LL | | 1 +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:390:9 + | +LL | / fn new(t: T) -> impl Trait2<(), i32> { +LL | | unimplemented!() +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:410:9 + | +LL | / pub fn new() -> impl PartialOrd { +LL | | 0i32 +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:421:9 + | +LL | / pub fn new() -> X { +LL | | 0i32 +LL | | } + | |_________^ + +error: aborting due to 14 previous errors diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 65809023f8df..7803418cb047 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,4 +1,9 @@ -#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] +#![allow( + dead_code, + clippy::missing_safety_doc, + clippy::extra_unused_lifetimes, + clippy::extra_unused_type_parameters +)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 212a69ab94e6..583dd327d6a5 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:7:5 + --> $DIR/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | Foo @@ -17,7 +17,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> $DIR/new_without_default.rs:15:5 + --> $DIR/new_without_default.rs:20:5 | LL | / pub fn new() -> Self { LL | | Bar @@ -34,7 +34,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> $DIR/new_without_default.rs:79:5 + --> $DIR/new_without_default.rs:84:5 | LL | / pub fn new() -> LtKo<'c> { LL | | unimplemented!() @@ -51,7 +51,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> $DIR/new_without_default.rs:172:5 + --> $DIR/new_without_default.rs:177:5 | LL | / pub fn new() -> Self { LL | | NewNotEqualToDerive { foo: 1 } @@ -68,7 +68,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics` - --> $DIR/new_without_default.rs:180:5 + --> $DIR/new_without_default.rs:185:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -85,7 +85,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics` - --> $DIR/new_without_default.rs:187:5 + --> $DIR/new_without_default.rs:192:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -102,7 +102,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:198:9 + --> $DIR/new_without_default.rs:203:9 | LL | / pub fn new() -> Self { LL | | todo!() diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 07d7f0b45b0c..d124d133faa2 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -42,3 +42,15 @@ fn main() { // Issue #5927 let _ = opt.as_deref(); } + +#[clippy::msrv = "1.39"] +fn msrv_1_39() { + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +#[clippy::msrv = "1.40"] +fn msrv_1_40() { + let opt = Some(String::from("123")); + let _ = opt.as_deref(); +} diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 6ae059c9425d..86e354c6716b 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports, clippy::redundant_clone)] +#![allow(unused, clippy::redundant_clone)] #![warn(clippy::option_as_ref_deref)] use std::ffi::{CString, OsString}; @@ -45,3 +45,15 @@ fn main() { // Issue #5927 let _ = opt.as_ref().map(std::ops::Deref::deref); } + +#[clippy::msrv = "1.39"] +fn msrv_1_39() { + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} + +#[clippy::msrv = "1.40"] +fn msrv_1_40() { + let opt = Some(String::from("123")); + let _ = opt.as_ref().map(String::as_str); +} diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index 62f282324752..e471b56eea81 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -106,5 +106,11 @@ error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This c LL | let _ = opt.as_ref().map(std::ops::Deref::deref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` -error: aborting due to 17 previous errors +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:58:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 18 previous errors diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index f15ac551bb3c..0456005dce49 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -189,3 +189,12 @@ fn main() { let _ = res.map_or(1, |a| a + 1); let _ = res.map_or(5, |a| a + 1); } + +#[allow(dead_code)] +fn issue9742() -> Option<&'static str> { + // should not lint because of guards + match Some("foo ") { + Some(name) if name.starts_with("foo") => Some(name.trim()), + _ => None, + } +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 9eeaea12d3bc..23b148752cbf 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -230,3 +230,12 @@ fn main() { }; let _ = if let Ok(a) = res { a + 1 } else { 5 }; } + +#[allow(dead_code)] +fn issue9742() -> Option<&'static str> { + // should not lint because of guards + match Some("foo ") { + Some(name) if name.starts_with("foo") => Some(name.trim()), + _ => None, + } +} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 23b1aa8bebd5..be9a65506e13 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -236,4 +236,20 @@ mod issue9608 { } } +mod issue8993 { + fn g() -> i32 { + 3 + } + + fn f(n: i32) -> i32 { + n + } + + fn test_map_or() { + let _ = Some(4).map_or_else(g, |v| v); + let _ = Some(4).map_or_else(g, f); + let _ = Some(4).map_or(0, f); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 039998f22dd7..628c97046389 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -236,4 +236,20 @@ mod issue9608 { } } +mod issue8993 { + fn g() -> i32 { + 3 + } + + fn f(n: i32) -> i32 { + n + } + + fn test_map_or() { + let _ = Some(4).map_or(g(), |v| v); + let _ = Some(4).map_or(g(), f); + let _ = Some(4).map_or(0, f); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 113ba150c619..ba3001db7a5f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -156,5 +156,17 @@ error: use of `unwrap_or` followed by a call to `new` LL | .unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` -error: aborting due to 26 previous errors +error: use of `map_or` followed by a function call + --> $DIR/or_fun_call.rs:249:25 + | +LL | let _ = Some(4).map_or(g(), |v| v); + | ^^^^^^^^^^^^^^^^^^ help: try this: `map_or_else(g, |v| v)` + +error: use of `map_or` followed by a function call + --> $DIR/or_fun_call.rs:250:25 + | +LL | let _ = Some(4).map_or(g(), f); + | ^^^^^^^^^^^^^^ help: try this: `map_or_else(g, f)` + +error: aborting due to 28 previous errors diff --git a/tests/ui/out_of_bounds_indexing/issue-3102.rs b/tests/ui/out_of_bounds_indexing/issue-3102.rs index f20a0ede1137..edd2123d48a5 100644 --- a/tests/ui/out_of_bounds_indexing/issue-3102.rs +++ b/tests/ui/out_of_bounds_indexing/issue-3102.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, const_err)] +#![allow(clippy::no_effect)] fn main() { let x = [1, 2, 3, 4]; diff --git a/tests/ui/out_of_bounds_indexing/simple.rs b/tests/ui/out_of_bounds_indexing/simple.rs index 590e578d758e..4c541c23f5f4 100644 --- a/tests/ui/out_of_bounds_indexing/simple.rs +++ b/tests/ui/out_of_bounds_indexing/simple.rs @@ -1,5 +1,5 @@ #![warn(clippy::out_of_bounds_indexing)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] fn main() { let x = [1, 2, 3, 4]; diff --git a/tests/ui/partial_pub_fields.rs b/tests/ui/partial_pub_fields.rs new file mode 100644 index 000000000000..668545da8441 --- /dev/null +++ b/tests/ui/partial_pub_fields.rs @@ -0,0 +1,40 @@ +#![allow(unused)] +#![warn(clippy::partial_pub_fields)] + +fn main() { + use std::collections::HashMap; + + #[derive(Default)] + pub struct FileSet { + files: HashMap, + pub paths: HashMap, + } + + pub struct Color { + pub r: u8, + pub g: u8, + b: u8, + } + + pub struct Point(i32, pub i32); + + pub struct Visibility { + r#pub: bool, + pub pos: u32, + } + + // Don't lint on empty structs; + pub struct Empty1; + pub struct Empty2(); + pub struct Empty3 {}; + + // Don't lint on structs with one field. + pub struct Single1(i32); + pub struct Single2(pub i32); + pub struct Single3 { + v1: i32, + } + pub struct Single4 { + pub v1: i32, + } +} diff --git a/tests/ui/partial_pub_fields.stderr b/tests/ui/partial_pub_fields.stderr new file mode 100644 index 000000000000..84cfc1a91940 --- /dev/null +++ b/tests/ui/partial_pub_fields.stderr @@ -0,0 +1,35 @@ +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:10:9 + | +LL | pub paths: HashMap, + | ^^^ + | + = help: consider using private field here + = note: `-D clippy::partial-pub-fields` implied by `-D warnings` + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:16:9 + | +LL | b: u8, + | ^ + | + = help: consider using public field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:19:27 + | +LL | pub struct Point(i32, pub i32); + | ^^^ + | + = help: consider using private field here + +error: mixed usage of pub and non-pub fields + --> $DIR/partial_pub_fields.rs:23:9 + | +LL | pub pos: u32, + | ^^^ + | + = help: consider using private field here + +error: aborting due to 4 previous errors + diff --git a/tests/ui/permissions_set_readonly_false.rs b/tests/ui/permissions_set_readonly_false.rs new file mode 100644 index 000000000000..28c00d100942 --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.rs @@ -0,0 +1,29 @@ +#![allow(unused)] +#![warn(clippy::permissions_set_readonly_false)] + +use std::fs::File; + +struct A; + +impl A { + pub fn set_readonly(&mut self, b: bool) {} +} + +fn set_readonly(b: bool) {} + +fn main() { + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + // lint here + permissions.set_readonly(false); + // no lint + permissions.set_readonly(true); + + let mut a = A; + // no lint here - a is not of type std::fs::Permissions + a.set_readonly(false); + + // no lint here - plain function + set_readonly(false); +} diff --git a/tests/ui/permissions_set_readonly_false.stderr b/tests/ui/permissions_set_readonly_false.stderr new file mode 100644 index 000000000000..e7a8ee6cb19b --- /dev/null +++ b/tests/ui/permissions_set_readonly_false.stderr @@ -0,0 +1,13 @@ +error: call to `set_readonly` with argument `false` + --> $DIR/permissions_set_readonly_false.rs:19:5 + | +LL | permissions.set_readonly(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: on Unix platforms this results in the file being world writable + = help: you can set the desired permissions using `PermissionsExt`. For more information, see + https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html + = note: `-D clippy::permissions-set-readonly-false` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index fd15001e540c..5f54101ca15a 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -3,7 +3,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn do_vec(x: &Vec) { //Nothing here @@ -207,3 +207,31 @@ fn cow_conditional_to_mut(a: &mut Cow) { a.to_mut().push_str("foo"); } } + +// Issue #9542 +fn dyn_trait_ok(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for String {} + impl T for PathBuf {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} + +fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + trait T {} + impl T for Vec {} + impl T for [U] {} + impl T for String {} + impl T for str {} + impl T for PathBuf {} + impl T for Path {} + fn takes_dyn(_: &mut dyn T) {} + + takes_dyn(a); + takes_dyn(b); + takes_dyn(c); +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index d64b5f454a5a..6b4de98ce88c 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -162,5 +162,23 @@ error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a sl LL | fn mut_vec_slice_methods(v: &mut Vec) { | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` -error: aborting due to 17 previous errors +error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:17 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^^ help: change this to: `&mut [u32]` + +error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:35 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^ help: change this to: `&mut str` + +error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do + --> $DIR/ptr_arg.rs:224:51 + | +LL | fn dyn_trait(a: &mut Vec, b: &mut String, c: &mut PathBuf) { + | ^^^^^^^^^^^^ help: change this to: `&mut Path` + +error: aborting due to 20 previous errors diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index bea6be66a8e0..df36a9b842bf 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -2,7 +2,6 @@ // aux-build:macro_rules.rs #![warn(clippy::ptr_as_ptr)] -#![feature(custom_inner_attributes)] extern crate macro_rules; @@ -45,8 +44,8 @@ fn main() { let _ = macro_rules::ptr_as_ptr_cast!(ptr); } +#[clippy::msrv = "1.37"] fn _msrv_1_37() { - #![clippy::msrv = "1.37"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -55,8 +54,8 @@ fn _msrv_1_37() { let _ = mut_ptr as *mut i32; } +#[clippy::msrv = "1.38"] fn _msrv_1_38() { - #![clippy::msrv = "1.38"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index ca2616b0069a..302c66462d9b 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -2,7 +2,6 @@ // aux-build:macro_rules.rs #![warn(clippy::ptr_as_ptr)] -#![feature(custom_inner_attributes)] extern crate macro_rules; @@ -45,8 +44,8 @@ fn main() { let _ = macro_rules::ptr_as_ptr_cast!(ptr); } +#[clippy::msrv = "1.37"] fn _msrv_1_37() { - #![clippy::msrv = "1.37"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; @@ -55,8 +54,8 @@ fn _msrv_1_37() { let _ = mut_ptr as *mut i32; } +#[clippy::msrv = "1.38"] fn _msrv_1_38() { - #![clippy::msrv = "1.38"] let ptr: *const u32 = &42_u32; let mut_ptr: *mut u32 = &mut 42_u32; diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index c58c55cfd83a..a68e1cab6d35 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:19:13 + --> $DIR/ptr_as_ptr.rs:18:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` @@ -7,31 +7,31 @@ LL | let _ = ptr as *const i32; = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:20:13 + --> $DIR/ptr_as_ptr.rs:19:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:25:17 + --> $DIR/ptr_as_ptr.rs:24:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:38:25 + --> $DIR/ptr_as_ptr.rs:37:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:39:23 + --> $DIR/ptr_as_ptr.rs:38:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:11:9 + --> $DIR/ptr_as_ptr.rs:10:9 | LL | $ptr as *const i32 | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::()` @@ -42,13 +42,13 @@ LL | let _ = cast_it!(ptr); = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info) error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:63:13 + --> $DIR/ptr_as_ptr.rs:62:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:64:13 + --> $DIR/ptr_as_ptr.rs:63:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 993389232cc2..5c49d46da726 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -134,6 +134,9 @@ fn result_func(x: Result) -> Result { return func_returning_result(); } + // no warning + let _ = if let Err(e) = x { Err(e) } else { Ok(0) }; + Ok(y) } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 9ae0d88829af..d057df6a9b35 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -166,6 +166,9 @@ fn result_func(x: Result) -> Result { return func_returning_result(); } + // no warning + let _ = if let Err(e) = x { Err(e) } else { Ok(0) }; + Ok(y) } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 1b6cd524b2f2..23172d7e535d 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -115,7 +115,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:193:5 + --> $DIR/question_mark.rs:196:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); @@ -123,7 +123,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:200:5 + --> $DIR/question_mark.rs:203:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 85d021b2f25e..4923731fe45e 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -1,10 +1,11 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +63,15 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +#[clippy::msrv = "1.34"] +fn msrv_1_34() { + let x = 5; + x >= 8 && x < 34; +} + +#[clippy::msrv = "1.35"] +fn msrv_1_35() { + let x = 5; + (8..35).contains(&x); +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9a7a75dc1325..d623ccb5da63 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -1,10 +1,11 @@ // run-rustfix -#[warn(clippy::manual_range_contains)] -#[allow(unused)] -#[allow(clippy::no_effect)] -#[allow(clippy::short_circuit_statement)] -#[allow(clippy::unnecessary_operation)] +#![warn(clippy::manual_range_contains)] +#![allow(unused)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::unnecessary_operation)] + fn main() { let x = 9_i32; @@ -62,3 +63,15 @@ fn main() { pub const fn in_range(a: i32) -> bool { 3 <= a && a <= 20 } + +#[clippy::msrv = "1.34"] +fn msrv_1_34() { + let x = 5; + x >= 8 && x < 34; +} + +#[clippy::msrv = "1.35"] +fn msrv_1_35() { + let x = 5; + x >= 8 && x < 35; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 936859db5a12..ea34023a4664 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:12:5 + --> $DIR/range_contains.rs:13:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,118 +7,124 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:13:5 + --> $DIR/range_contains.rs:14:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:15:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:17:5 + --> $DIR/range_contains.rs:18:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:18:5 + --> $DIR/range_contains.rs:19:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:20:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:22:5 + --> $DIR/range_contains.rs:23:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:23:5 + --> $DIR/range_contains.rs:24:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:25:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:27:5 + --> $DIR/range_contains.rs:28:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:28:5 + --> $DIR/range_contains.rs:29:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:30:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:44:5 + --> $DIR/range_contains.rs:45:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:45:5 + --> $DIR/range_contains.rs:46:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:48:5 + --> $DIR/range_contains.rs:49:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:50:5 + --> $DIR/range_contains.rs:51:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:30 + --> $DIR/range_contains.rs:56:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:55:5 + --> $DIR/range_contains.rs:56:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:29 + --> $DIR/range_contains.rs:57:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:56:5 + --> $DIR/range_contains.rs:57:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` -error: aborting due to 20 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:76:5 + | +LL | x >= 8 && x < 35; + | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` + +error: aborting due to 21 previous errors diff --git a/tests/ui/redundant_closure_call_fixable.fixed b/tests/ui/redundant_closure_call_fixable.fixed index 7cd687c95a00..c0e49ff4caa7 100644 --- a/tests/ui/redundant_closure_call_fixable.fixed +++ b/tests/ui/redundant_closure_call_fixable.fixed @@ -25,4 +25,16 @@ fn main() { x * y }; let d = async { something().await }; + + macro_rules! m { + () => { + 0 + }; + } + macro_rules! m2 { + () => { + m!() + }; + } + m2!(); } diff --git a/tests/ui/redundant_closure_call_fixable.rs b/tests/ui/redundant_closure_call_fixable.rs index 37e4d2238641..9e6e54348a8c 100644 --- a/tests/ui/redundant_closure_call_fixable.rs +++ b/tests/ui/redundant_closure_call_fixable.rs @@ -25,4 +25,16 @@ fn main() { x * y })(); let d = (async || something().await)(); + + macro_rules! m { + () => { + (|| 0)() + }; + } + macro_rules! m2 { + () => { + (|| m!())() + }; + } + m2!(); } diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 56a8e57c0c36..d71bcba2a820 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -52,5 +52,27 @@ error: try not to call a closure in the expression where it is declared LL | let d = (async || something().await)(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }` -error: aborting due to 4 previous errors +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_fixable.rs:36:13 + | +LL | (|| m!())() + | ^^^^^^^^^^^ help: try doing something like: `m!()` +... +LL | m2!(); + | ----- in this macro invocation + | + = note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: try not to call a closure in the expression where it is declared + --> $DIR/redundant_closure_call_fixable.rs:31:13 + | +LL | (|| 0)() + | ^^^^^^^^ help: try doing something like: `0` +... +LL | m2!(); + | ----- in this macro invocation + | + = note: this error originates in the macro `m` which comes from the expansion of the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors diff --git a/tests/ui/redundant_field_names.fixed b/tests/ui/redundant_field_names.fixed index 5b4b8eeedd46..276266a2dd80 100644 --- a/tests/ui/redundant_field_names.fixed +++ b/tests/ui/redundant_field_names.fixed @@ -1,6 +1,7 @@ // run-rustfix + #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; @@ -69,3 +70,15 @@ fn issue_3476() { S { foo: foo:: }; } + +#[clippy::msrv = "1.16"] +fn msrv_1_16() { + let start = 0; + let _ = RangeFrom { start: start }; +} + +#[clippy::msrv = "1.17"] +fn msrv_1_17() { + let start = 0; + let _ = RangeFrom { start }; +} diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 3f97b80c5682..f674141c138e 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -1,6 +1,7 @@ // run-rustfix + #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; @@ -69,3 +70,15 @@ fn issue_3476() { S { foo: foo:: }; } + +#[clippy::msrv = "1.16"] +fn msrv_1_16() { + let start = 0; + let _ = RangeFrom { start: start }; +} + +#[clippy::msrv = "1.17"] +fn msrv_1_17() { + let start = 0; + let _ = RangeFrom { start: start }; +} diff --git a/tests/ui/redundant_field_names.stderr b/tests/ui/redundant_field_names.stderr index 7976292df224..00a72c50cf7d 100644 --- a/tests/ui/redundant_field_names.stderr +++ b/tests/ui/redundant_field_names.stderr @@ -1,5 +1,5 @@ error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:34:9 + --> $DIR/redundant_field_names.rs:35:9 | LL | gender: gender, | ^^^^^^^^^^^^^^ help: replace it with: `gender` @@ -7,40 +7,46 @@ LL | gender: gender, = note: `-D clippy::redundant-field-names` implied by `-D warnings` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:35:9 + --> $DIR/redundant_field_names.rs:36:9 | LL | age: age, | ^^^^^^^^ help: replace it with: `age` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:56:25 + --> $DIR/redundant_field_names.rs:57:25 | LL | let _ = RangeFrom { start: start }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:57:23 + --> $DIR/redundant_field_names.rs:58:23 | LL | let _ = RangeTo { end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:21 + --> $DIR/redundant_field_names.rs:59:21 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^^^^^ help: replace it with: `start` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:58:35 + --> $DIR/redundant_field_names.rs:59:35 | LL | let _ = Range { start: start, end: end }; | ^^^^^^^^ help: replace it with: `end` error: redundant field names in struct initialization - --> $DIR/redundant_field_names.rs:60:32 + --> $DIR/redundant_field_names.rs:61:32 | LL | let _ = RangeToInclusive { end: end }; | ^^^^^^^^ help: replace it with: `end` -error: aborting due to 7 previous errors +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:83:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: aborting due to 8 previous errors diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed index acc8f1e25b6e..bca777a890c3 100644 --- a/tests/ui/redundant_static_lifetimes.fixed +++ b/tests/ui/redundant_static_lifetimes.fixed @@ -39,8 +39,14 @@ static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static. +static mut STATIC_MUT_SLICE: &mut [u32] = &mut [0]; + fn main() { let false_positive: &'static str = "test"; + + unsafe { + STATIC_MUT_SLICE[0] = 0; + } } trait Bar { @@ -54,3 +60,13 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +#[clippy::msrv = "1.16"] +fn msrv_1_16() { + static V: &'static u8 = &16; +} + +#[clippy::msrv = "1.17"] +fn msrv_1_17() { + static V: &u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs index f2f0f78659c9..afe7644816d2 100644 --- a/tests/ui/redundant_static_lifetimes.rs +++ b/tests/ui/redundant_static_lifetimes.rs @@ -39,8 +39,14 @@ static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. +static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0]; + fn main() { let false_positive: &'static str = "test"; + + unsafe { + STATIC_MUT_SLICE[0] = 0; + } } trait Bar { @@ -54,3 +60,13 @@ impl Foo { impl Bar for Foo { const TRAIT_VAR: &'static str = "foo"; } + +#[clippy::msrv = "1.16"] +fn msrv_1_16() { + static V: &'static u8 = &16; +} + +#[clippy::msrv = "1.17"] +fn msrv_1_17() { + static V: &'static u8 = &17; +} diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 649831f9c069..b2cbd2d9d01b 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -96,5 +96,17 @@ error: statics have by default a `'static` lifetime LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: aborting due to 16 previous errors +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:42:31 + | +LL | static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0]; + | -^^^^^^^---------- help: consider removing `'static`: `&mut [u32]` + +error: statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:71:16 + | +LL | static V: &'static u8 = &17; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: aborting due to 18 previous errors diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 2df45c927d71..e487799e1522 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -45,3 +45,8 @@ impl RefOptTrait for u32 { fn main() { let x: &Option<&u32> = &None; } + +fn issue9682(arg: &Option<&mut String>) { + // Should not lint, as the inner ref is mutable making it non `Copy` + println!("{arg:?}"); +} diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index f0e1a8128d7c..ab8ac97a0e70 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -36,6 +36,10 @@ fn syntax_error() { let raw_string_error = Regex::new(r"[...\/...]"); let raw_string_error = Regex::new(r#"[...\/...]"#); + + let escaped_string_span = Regex::new("\\b\\c"); + + let aux_span = Regex::new("(?ixi)"); } fn trivial_regex() { diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 2424644c6f6b..c2440f39e0a0 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -29,7 +29,10 @@ error: regex syntax error: invalid character class range, the start must be <= t LL | let some_unicode = Regex::new("[é-è]"); | ^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:18:33 | LL | let some_regex = Regex::new(OPENING_PAREN); @@ -43,25 +46,37 @@ LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | = help: the regex is unlikely to be useful as it is -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:21:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:22:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:34:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:35:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); @@ -79,8 +94,25 @@ error: regex syntax error: unrecognized escape sequence LL | let raw_string_error = Regex::new(r#"[...//...]"#); | ^^ +error: regex parse error: + /b/c + ^^ + error: unrecognized escape sequence + --> $DIR/regex.rs:40:42 + | +LL | let escaped_string_span = Regex::new("/b/c"); + | ^^^^^^^^ + | + = help: consider using a raw string literal: `r".."` + +error: regex syntax error: duplicate flag + --> $DIR/regex.rs:42:34 + | +LL | let aux_span = Regex::new("(?ixi)"); + | ^ ^ + error: trivial regex - --> $DIR/regex.rs:42:33 + --> $DIR/regex.rs:46:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:44:48 + --> $DIR/regex.rs:48:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:46:42 + --> $DIR/regex.rs:50:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -104,7 +136,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:48:40 + --> $DIR/regex.rs:52:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -112,7 +144,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:50:39 + --> $DIR/regex.rs:54:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -120,7 +152,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:52:39 + --> $DIR/regex.rs:56:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -128,7 +160,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:54:40 + --> $DIR/regex.rs:58:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -136,7 +168,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:57:36 + --> $DIR/regex.rs:61:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -144,7 +176,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:59:36 + --> $DIR/regex.rs:63:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -152,7 +184,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:61:36 + --> $DIR/regex.rs:65:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -160,12 +192,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:63:44 + --> $DIR/regex.rs:67:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index a6e7bdba77c6..5076f61334d6 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -4,15 +4,16 @@ // run-rustfix +#![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_if_conditions)] #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -27,26 +28,28 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] #![allow(drop_bounds)] +#![allow(for_loops_over_fallibles)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] +#![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![warn(clippy::almost_complete_range)] #![warn(clippy::disallowed_names)] #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::box_collection)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] +#![warn(clippy::derived_hash_with_manual_eq)] #![warn(clippy::disallowed_methods)] #![warn(clippy::disallowed_types)] #![warn(clippy::mixed_read_write_in_expression)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] #![warn(clippy::match_result_ok)] #![warn(clippy::overly_complex_bool_expr)] @@ -65,9 +68,13 @@ #![warn(clippy::recursive_format_impl)] #![warn(clippy::invisible_characters)] #![warn(drop_bounds)] +#![warn(for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] +#![warn(for_loops_over_fallibles)] #![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] #![warn(invalid_value)] +#![warn(let_underscore_drop)] #![warn(enum_intrinsics_non_enums)] #![warn(non_fmt_panics)] #![warn(named_arguments_used_positionally)] diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index e8f57597d02b..64bc1ca7116c 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -4,15 +4,16 @@ // run-rustfix +#![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_if_conditions)] #![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::derived_hash_with_manual_eq)] #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] #![allow(clippy::mixed_read_write_in_expression)] -#![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] #![allow(clippy::match_result_ok)] #![allow(clippy::overly_complex_bool_expr)] @@ -27,26 +28,28 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] #![allow(drop_bounds)] +#![allow(for_loops_over_fallibles)] #![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] +#![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] #![allow(temporary_cstring_as_ptr)] #![allow(unknown_lints)] #![allow(unused_labels)] +#![warn(clippy::almost_complete_letter_range)] #![warn(clippy::blacklisted_name)] #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] #![warn(clippy::box_vec)] #![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::derive_hash_xor_eq)] #![warn(clippy::disallowed_method)] #![warn(clippy::disallowed_type)] #![warn(clippy::eval_order_dependence)] -#![warn(clippy::for_loop_over_option)] -#![warn(clippy::for_loop_over_result)] #![warn(clippy::identity_conversion)] #![warn(clippy::if_let_some_result)] #![warn(clippy::logic_bug)] @@ -65,9 +68,13 @@ #![warn(clippy::to_string_in_display)] #![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] +#![warn(clippy::for_loop_over_option)] +#![warn(clippy::for_loop_over_result)] +#![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] #![warn(clippy::invalid_ref)] +#![warn(clippy::let_underscore_drop)] #![warn(clippy::mem_discriminant_non_enum)] #![warn(clippy::panic_params)] #![warn(clippy::positional_named_format_parameters)] diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 31865a7f66d6..27a0263292ef 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,232 +1,256 @@ +error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` + --> $DIR/rename.rs:42:9 + | +LL | #![warn(clippy::almost_complete_letter_range)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:43:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:40:9 + --> $DIR/rename.rs:44: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:41:9 + --> $DIR/rename.rs:45: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:42:9 + --> $DIR/rename.rs:46: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:43:9 + --> $DIR/rename.rs:47: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:44:9 + --> $DIR/rename.rs:48: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:49: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:45:9 + --> $DIR/rename.rs:50: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:46:9 + --> $DIR/rename.rs:51: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:47:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:48:9 - | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` - -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` - --> $DIR/rename.rs:49:9 - | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` - error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:50:9 + --> $DIR/rename.rs:53: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:51:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:55: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:53:9 + --> $DIR/rename.rs:56: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:54:9 + --> $DIR/rename.rs:57: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:55:9 + --> $DIR/rename.rs:58: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:56:9 + --> $DIR/rename.rs:59: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:57:9 + --> $DIR/rename.rs:60: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:58:9 + --> $DIR/rename.rs:61: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:59:9 + --> $DIR/rename.rs:62: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:60:9 + --> $DIR/rename.rs:63: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:61:9 + --> $DIR/rename.rs:64: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:62:9 + --> $DIR/rename.rs:65: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:63:9 + --> $DIR/rename.rs:66: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:64:9 + --> $DIR/rename.rs:67: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:65:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` +error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` + --> $DIR/rename.rs:71: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:72: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:73:9 + | +LL | #![warn(clippy::for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` + error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:74: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:69:9 + --> $DIR/rename.rs:75: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:70:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` + --> $DIR/rename.rs:77: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:71:9 + --> $DIR/rename.rs:78: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:72:9 + --> $DIR/rename.rs:79: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:73:9 + --> $DIR/rename.rs:80: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:74:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:82: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:76:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 38 previous errors +error: aborting due to 42 previous errors diff --git a/tests/ui/result_large_err.rs b/tests/ui/result_large_err.rs index f7df3b856550..1c12cebfd971 100644 --- a/tests/ui/result_large_err.rs +++ b/tests/ui/result_large_err.rs @@ -50,6 +50,18 @@ impl LargeErrorVariants<()> { } } +enum MultipleLargeVariants { + _Biggest([u8; 1024]), + _AlsoBig([u8; 512]), + _Ok(usize), +} + +impl MultipleLargeVariants { + fn large_enum_error() -> Result<(), Self> { + Ok(()) + } +} + trait TraitForcesLargeError { fn large_error() -> Result<(), [u8; 512]> { Ok(()) @@ -96,4 +108,10 @@ pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { Ok(()) } +// Issue #10005 +enum Empty {} +fn _empty_error() -> Result<(), Empty> { + Ok(()) +} + fn main() {} diff --git a/tests/ui/result_large_err.stderr b/tests/ui/result_large_err.stderr index bea101fe20bf..c386edfd2157 100644 --- a/tests/ui/result_large_err.stderr +++ b/tests/ui/result_large_err.stderr @@ -42,13 +42,29 @@ LL | pub fn param_large_error() -> Result<(), (u128, R, FullyDefinedLargeErro error: the `Err`-variant returned from this function is very large --> $DIR/result_large_err.rs:48:34 | +LL | _Omg([u8; 512]), + | --------------- the largest variant contains at least 512 bytes +... LL | pub fn large_enum_error() -> Result<(), Self> { - | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes + | ^^^^^^^^^^^^^^^^ | = 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:54:25 + --> $DIR/result_large_err.rs:60:30 + | +LL | _Biggest([u8; 1024]), + | -------------------- the largest variant contains at least 1024 bytes +LL | _AlsoBig([u8; 512]), + | ------------------- the variant `_AlsoBig` contains at least 512 bytes +... +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:66:25 | LL | fn large_error() -> Result<(), [u8; 512]> { | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -56,7 +72,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:73:29 + --> $DIR/result_large_err.rs:85:29 | LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -64,7 +80,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:82:40 + --> $DIR/result_large_err.rs:94:40 | LL | pub fn param_large_union() -> Result<(), UnionError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes @@ -72,7 +88,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:91:34 + --> $DIR/result_large_err.rs:103:34 | LL | pub fn array_error_subst() -> Result<(), ArrayError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes @@ -80,12 +96,12 @@ 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:95:31 + --> $DIR/result_large_err.rs:107:31 | LL | pub fn array_error() -> Result<(), ArrayError<(i32, T), U>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes | = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box>` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index 88e4efdb0f05..d0e534f63568 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -19,15 +19,12 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | -LL | x.field.map(|value| { - | _____^ - | |_____| - | || +LL | // x.field.map(|value| { LL | || do_nothing(value); LL | || do_nothing(value) LL | || }); | ||______^- help: try this: `if let Ok(value) = x.field { ... }` - | |_______| + | |______| | error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` diff --git a/tests/ui/seek_from_current.fixed b/tests/ui/seek_from_current.fixed new file mode 100644 index 000000000000..1309c91b81c9 --- /dev/null +++ b/tests/ui/seek_from_current.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![warn(clippy::seek_from_current)] + +use std::fs::File; +use std::io::{self, Seek, SeekFrom, Write}; + +#[clippy::msrv = "1.50"] +fn _msrv_1_50() -> io::Result<()> { + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.seek(SeekFrom::Current(0))?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +#[clippy::msrv = "1.51"] +fn _msrv_1_51() -> io::Result<()> { + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.stream_position()?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/seek_from_current.rs b/tests/ui/seek_from_current.rs new file mode 100644 index 000000000000..5d9b1424cf68 --- /dev/null +++ b/tests/ui/seek_from_current.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![warn(clippy::seek_from_current)] + +use std::fs::File; +use std::io::{self, Seek, SeekFrom, Write}; + +#[clippy::msrv = "1.50"] +fn _msrv_1_50() -> io::Result<()> { + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.seek(SeekFrom::Current(0))?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +#[clippy::msrv = "1.51"] +fn _msrv_1_51() -> io::Result<()> { + let mut f = File::create("foo.txt")?; + f.write_all(b"Hi!")?; + f.seek(SeekFrom::Current(0))?; + f.seek(SeekFrom::Current(1))?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/seek_from_current.stderr b/tests/ui/seek_from_current.stderr new file mode 100644 index 000000000000..c079f3611929 --- /dev/null +++ b/tests/ui/seek_from_current.stderr @@ -0,0 +1,10 @@ +error: using `SeekFrom::Current` to start from current position + --> $DIR/seek_from_current.rs:20:5 + | +LL | f.seek(SeekFrom::Current(0))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()` + | + = note: `-D clippy::seek-from-current` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/seek_to_start_instead_of_rewind.fixed b/tests/ui/seek_to_start_instead_of_rewind.fixed new file mode 100644 index 000000000000..dc24d447c607 --- /dev/null +++ b/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -0,0 +1,140 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::seek_to_start_instead_of_rewind)] + +use std::fs::OpenOptions; +use std::io::{Read, Seek, SeekFrom, Write}; + +struct StructWithSeekMethod {} + +impl StructWithSeekMethod { + fn seek(&mut self, from: SeekFrom) {} +} + +trait MySeekTrait { + fn seek(&mut self, from: SeekFrom) {} +} + +struct StructWithSeekTrait {} +impl MySeekTrait for StructWithSeekTrait {} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_bound(t: &mut T) { + t.seek(SeekFrom::Start(0)); +} + +// This should trigger clippy warning +fn seek_to_start(t: &mut T) { + t.rewind(); +} + +// This should trigger clippy warning +fn owned_seek_to_start(mut t: T) { + t.rewind(); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_5(t: &mut T) { + t.seek(SeekFrom::Start(5)); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_end(t: &mut T) { + t.seek(SeekFrom::End(0)); +} + +// This should NOT trigger clippy warning because +// expr is used here +fn seek_to_start_in_let(t: &mut T) { + let a = t.seek(SeekFrom::Start(0)).unwrap(); +} + +fn main() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let mut my_struct_trait = StructWithSeekTrait {}; + seek_to_start_false_trait_bound(&mut my_struct_trait); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + seek_to_5(&mut f); + seek_to_end(&mut f); + seek_to_start(&mut f); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +#[clippy::msrv = "1.54"] +fn msrv_1_54() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.seek(SeekFrom::Start(0)); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +#[clippy::msrv = "1.55"] +fn msrv_1_55() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.rewind(); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} diff --git a/tests/ui/seek_to_start_instead_of_rewind.rs b/tests/ui/seek_to_start_instead_of_rewind.rs new file mode 100644 index 000000000000..4adde2c40182 --- /dev/null +++ b/tests/ui/seek_to_start_instead_of_rewind.rs @@ -0,0 +1,140 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::seek_to_start_instead_of_rewind)] + +use std::fs::OpenOptions; +use std::io::{Read, Seek, SeekFrom, Write}; + +struct StructWithSeekMethod {} + +impl StructWithSeekMethod { + fn seek(&mut self, from: SeekFrom) {} +} + +trait MySeekTrait { + fn seek(&mut self, from: SeekFrom) {} +} + +struct StructWithSeekTrait {} +impl MySeekTrait for StructWithSeekTrait {} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// StructWithSeekMethod does not implement std::io::Seek; +fn seek_to_start_false_trait_bound(t: &mut T) { + t.seek(SeekFrom::Start(0)); +} + +// This should trigger clippy warning +fn seek_to_start(t: &mut T) { + t.seek(SeekFrom::Start(0)); +} + +// This should trigger clippy warning +fn owned_seek_to_start(mut t: T) { + t.seek(SeekFrom::Start(0)); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_5(t: &mut T) { + t.seek(SeekFrom::Start(5)); +} + +// This should NOT trigger clippy warning because +// it does not seek to start +fn seek_to_end(t: &mut T) { + t.seek(SeekFrom::End(0)); +} + +// This should NOT trigger clippy warning because +// expr is used here +fn seek_to_start_in_let(t: &mut T) { + let a = t.seek(SeekFrom::Start(0)).unwrap(); +} + +fn main() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let mut my_struct_trait = StructWithSeekTrait {}; + seek_to_start_false_trait_bound(&mut my_struct_trait); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + seek_to_5(&mut f); + seek_to_end(&mut f); + seek_to_start(&mut f); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +#[clippy::msrv = "1.54"] +fn msrv_1_54() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.seek(SeekFrom::Start(0)); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} + +#[clippy::msrv = "1.55"] +fn msrv_1_55() { + let mut f = OpenOptions::new() + .write(true) + .read(true) + .create(true) + .open("foo.txt") + .unwrap(); + + let hello = "Hello!\n"; + write!(f, "{hello}").unwrap(); + + f.seek(SeekFrom::Start(0)); + + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + + assert_eq!(&buf, hello); +} diff --git a/tests/ui/seek_to_start_instead_of_rewind.stderr b/tests/ui/seek_to_start_instead_of_rewind.stderr new file mode 100644 index 000000000000..342ec00fe729 --- /dev/null +++ b/tests/ui/seek_to_start_instead_of_rewind.stderr @@ -0,0 +1,22 @@ +error: used `seek` to go to the start of the stream + --> $DIR/seek_to_start_instead_of_rewind.rs:53:7 + | +LL | t.seek(SeekFrom::Start(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` + | + = note: `-D clippy::seek-to-start-instead-of-rewind` implied by `-D warnings` + +error: used `seek` to go to the start of the stream + --> $DIR/seek_to_start_instead_of_rewind.rs:58:7 + | +LL | t.seek(SeekFrom::Start(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` + +error: used `seek` to go to the start of the stream + --> $DIR/seek_to_start_instead_of_rewind.rs:134:7 + | +LL | f.seek(SeekFrom::Start(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed new file mode 100644 index 000000000000..42e97e1ca358 --- /dev/null +++ b/tests/ui/semicolon_inside_block.fixed @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_inside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()); } + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs new file mode 100644 index 000000000000..f40848f702e1 --- /dev/null +++ b/tests/ui/semicolon_inside_block.rs @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_inside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr new file mode 100644 index 000000000000..48d3690e2bde --- /dev/null +++ b/tests/ui/semicolon_inside_block.stderr @@ -0,0 +1,54 @@ +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:39:5 + | +LL | { unit_fn_block() }; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::semicolon-inside-block` implied by `-D warnings` +help: put the `;` here + | +LL - { unit_fn_block() }; +LL + { unit_fn_block(); } + | + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:40:5 + | +LL | unsafe { unit_fn_block() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: put the `;` here + | +LL - unsafe { unit_fn_block() }; +LL + unsafe { unit_fn_block(); } + | + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:48:5 + | +LL | / { +LL | | unit_fn_block(); +LL | | unit_fn_block() +LL | | }; + | |______^ + | +help: put the `;` here + | +LL ~ unit_fn_block(); +LL ~ } + | + +error: consider moving the `;` inside the block for consistent formatting + --> $DIR/semicolon_inside_block.rs:61:5 + | +LL | { m!(()) }; + | ^^^^^^^^^^^ + | +help: put the `;` here + | +LL - { m!(()) }; +LL + { m!(()); } + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/semicolon_outside_block.fixed b/tests/ui/semicolon_outside_block.fixed new file mode 100644 index 000000000000..091eaa7518e9 --- /dev/null +++ b/tests/ui/semicolon_outside_block.fixed @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_outside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()) }; + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_outside_block.rs b/tests/ui/semicolon_outside_block.rs new file mode 100644 index 000000000000..7ce46431fac9 --- /dev/null +++ b/tests/ui/semicolon_outside_block.rs @@ -0,0 +1,85 @@ +// run-rustfix +#![allow( + unused, + clippy::unused_unit, + clippy::unnecessary_operation, + clippy::no_effect, + clippy::single_element_loop +)] +#![warn(clippy::semicolon_outside_block)] + +macro_rules! m { + (()) => { + () + }; + (0) => {{ + 0 + };}; + (1) => {{ + 1; + }}; + (2) => {{ + 2; + }}; +} + +fn unit_fn_block() { + () +} + +#[rustfmt::skip] +fn main() { + { unit_fn_block() } + unsafe { unit_fn_block() } + + { + unit_fn_block() + } + + { unit_fn_block() }; + unsafe { unit_fn_block() }; + + { unit_fn_block(); } + unsafe { unit_fn_block(); } + + { unit_fn_block(); }; + unsafe { unit_fn_block(); }; + + { + unit_fn_block(); + unit_fn_block() + }; + { + unit_fn_block(); + unit_fn_block(); + } + { + unit_fn_block(); + unit_fn_block(); + }; + + { m!(()) }; + { m!(()); } + { m!(()); }; + m!(0); + m!(1); + m!(2); + + for _ in [()] { + unit_fn_block(); + } + for _ in [()] { + unit_fn_block() + } + + let _d = || { + unit_fn_block(); + }; + let _d = || { + unit_fn_block() + }; + + { unit_fn_block(); }; + + unit_fn_block() +} diff --git a/tests/ui/semicolon_outside_block.stderr b/tests/ui/semicolon_outside_block.stderr new file mode 100644 index 000000000000..dcc102e60994 --- /dev/null +++ b/tests/ui/semicolon_outside_block.stderr @@ -0,0 +1,54 @@ +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:42:5 + | +LL | { unit_fn_block(); } + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::semicolon-outside-block` implied by `-D warnings` +help: put the `;` here + | +LL - { unit_fn_block(); } +LL + { unit_fn_block() }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:43:5 + | +LL | unsafe { unit_fn_block(); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: put the `;` here + | +LL - unsafe { unit_fn_block(); } +LL + unsafe { unit_fn_block() }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:52:5 + | +LL | / { +LL | | unit_fn_block(); +LL | | unit_fn_block(); +LL | | } + | |_____^ + | +help: put the `;` here + | +LL ~ unit_fn_block() +LL ~ }; + | + +error: consider moving the `;` outside the block for consistent formatting + --> $DIR/semicolon_outside_block.rs:62:5 + | +LL | { m!(()); } + | ^^^^^^^^^^^ + | +help: put the `;` here + | +LL - { m!(()); } +LL + { m!(()) }; + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 509c88ac256a..71dcc25d6e5b 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -1,16 +1,16 @@ error: this import is redundant - --> $DIR/single_component_path_imports.rs:23:5 + --> $DIR/single_component_path_imports.rs:5:1 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely | = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant - --> $DIR/single_component_path_imports.rs:5:1 + --> $DIR/single_component_path_imports.rs:23:5 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely error: aborting due to 2 previous errors diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr index 633546f6419a..330f285202d0 100644 --- a/tests/ui/single_component_path_imports_nested_first.stderr +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -1,3 +1,11 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:4:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + error: this import is redundant --> $DIR/single_component_path_imports_nested_first.rs:13:10 | @@ -5,7 +13,6 @@ LL | use {regex, serde}; | ^^^^^ | = help: remove this import - = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant --> $DIR/single_component_path_imports_nested_first.rs:13:17 @@ -15,11 +22,5 @@ LL | use {regex, serde}; | = help: remove this import -error: this import is redundant - --> $DIR/single_component_path_imports_nested_first.rs:4:1 - | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely - error: aborting due to 3 previous errors diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index 63d31ff83f9b..a0dcc0172e8b 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -33,4 +33,31 @@ fn main() { let item = 0..5; dbg!(item); } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + continue; + } + } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + break; + } + } + + // should lint (issue #10018) + { + let _ = 42; + let _f = |n: u32| { + for i in 0..n { + if i > 10 { + dbg!(i); + break; + } + } + }; + } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 2cda5a329d25..bc014035c98a 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -27,4 +27,30 @@ fn main() { for item in [0..5].into_iter() { dbg!(item); } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + continue; + } + } + + // should not lint (issue #10018) + for e in [42] { + if e > 0 { + break; + } + } + + // should lint (issue #10018) + for _ in [42] { + let _f = |n: u32| { + for i in 0..n { + if i > 10 { + dbg!(i); + break; + } + } + }; + } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index 0aeb8da1a2e2..14437a59745e 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -95,5 +95,32 @@ LL + dbg!(item); LL + } | -error: aborting due to 6 previous errors +error: for loop over a single element + --> $DIR/single_element_loop.rs:46:5 + | +LL | / for _ in [42] { +LL | | let _f = |n: u32| { +LL | | for i in 0..n { +LL | | if i > 10 { +... | +LL | | }; +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let _ = 42; +LL + let _f = |n: u32| { +LL + for i in 0..n { +LL + if i > 10 { +LL + dbg!(i); +LL + break; +LL + } +LL + } +LL + }; +LL + } + | + +error: aborting due to 7 previous errors diff --git a/tests/ui/size_of_ref.rs b/tests/ui/size_of_ref.rs new file mode 100644 index 000000000000..1e83ab82907d --- /dev/null +++ b/tests/ui/size_of_ref.rs @@ -0,0 +1,27 @@ +#![allow(unused)] +#![warn(clippy::size_of_ref)] + +use std::mem::size_of_val; + +fn main() { + let x = 5; + let y = &x; + + size_of_val(&x); // no lint + size_of_val(y); // no lint + + size_of_val(&&x); + size_of_val(&y); +} + +struct S { + field: u32, + data: Vec, +} + +impl S { + /// Get size of object including `self`, in bytes. + pub fn size(&self) -> usize { + std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + } +} diff --git a/tests/ui/size_of_ref.stderr b/tests/ui/size_of_ref.stderr new file mode 100644 index 000000000000..d4c13ac3290b --- /dev/null +++ b/tests/ui/size_of_ref.stderr @@ -0,0 +1,27 @@ +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:13:5 + | +LL | size_of_val(&&x); + | ^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + = note: `-D clippy::size-of-ref` implied by `-D warnings` + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:14:5 + | +LL | size_of_val(&y); + | ^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: argument to `std::mem::size_of_val()` is a reference to a reference + --> $DIR/size_of_ref.rs:25:9 + | +LL | std::mem::size_of_val(&self) + (std::mem::size_of::() * self.data.capacity()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: dereference the argument to `std::mem::size_of_val()` to get the size of the value instead of the size of the reference-type + +error: aborting due to 3 previous errors + diff --git a/tests/ui/string_extend.fixed b/tests/ui/string_extend.fixed index 1883a9f83257..d200d7310fca 100644 --- a/tests/ui/string_extend.fixed +++ b/tests/ui/string_extend.fixed @@ -29,4 +29,7 @@ fn main() { let f = HasChars; s.extend(f.chars()); + + // issue #9735 + s.push_str(&abc[0..2]); } diff --git a/tests/ui/string_extend.rs b/tests/ui/string_extend.rs index 07d0baa1be6c..0dd96a3b2103 100644 --- a/tests/ui/string_extend.rs +++ b/tests/ui/string_extend.rs @@ -29,4 +29,7 @@ fn main() { let f = HasChars; s.extend(f.chars()); + + // issue #9735 + s.extend(abc[0..2].chars()); } diff --git a/tests/ui/string_extend.stderr b/tests/ui/string_extend.stderr index 6af8c9e1662b..b35c77fd9611 100644 --- a/tests/ui/string_extend.stderr +++ b/tests/ui/string_extend.stderr @@ -18,5 +18,11 @@ error: calling `.extend(_.chars())` LL | s.extend(def.chars()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)` -error: aborting due to 3 previous errors +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:34:5 + | +LL | s.extend(abc[0..2].chars()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&abc[0..2])` + +error: aborting due to 4 previous errors diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index df2256e4f97d..506187fc1257 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -25,6 +25,12 @@ fn str_lit_as_bytes() { let includestr = include_bytes!("string_lit_as_bytes.rs"); let _ = b"string with newline\t\n"; + + let _ = match "x".as_bytes() { + b"xx" => 0, + [b'x', ..] => 1, + _ => 2, + }; } fn main() {} diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index c6bf8f732ed9..2c339f1ddb81 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -25,6 +25,12 @@ fn str_lit_as_bytes() { let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); let _ = "string with newline\t\n".as_bytes(); + + let _ = match "x".as_bytes() { + b"xx" => 0, + [b'x', ..] => 1, + _ => 2, + }; } fn main() {} diff --git a/tests/ui/suspicious_to_owned.stderr b/tests/ui/suspicious_to_owned.stderr index ae1aec34d82e..dec3f50d6f1b 100644 --- a/tests/ui/suspicious_to_owned.stderr +++ b/tests/ui/suspicious_to_owned.stderr @@ -1,4 +1,4 @@ -error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned +error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:16:13 | LL | let _ = cow.to_owned(); @@ -6,19 +6,19 @@ LL | let _ = cow.to_owned(); | = note: `-D clippy::suspicious-to-owned` implied by `-D warnings` -error: this `to_owned` call clones the std::borrow::Cow<'_, [char; 3]> itself and does not cause the std::borrow::Cow<'_, [char; 3]> contents to become owned +error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned --> $DIR/suspicious_to_owned.rs:26:13 | LL | let _ = cow.to_owned(); | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` -error: this `to_owned` call clones the std::borrow::Cow<'_, std::vec::Vec> itself and does not cause the std::borrow::Cow<'_, std::vec::Vec> contents to become owned +error: this `to_owned` call clones the Cow<'_, Vec> itself and does not cause the Cow<'_, Vec> contents to become owned --> $DIR/suspicious_to_owned.rs:36:13 | LL | let _ = cow.to_owned(); | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` -error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned +error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:46:13 | LL | let _ = cow.to_owned(); diff --git a/tests/ui/suspicious_xor_used_as_pow.rs b/tests/ui/suspicious_xor_used_as_pow.rs new file mode 100644 index 000000000000..eb9fc63fb1d4 --- /dev/null +++ b/tests/ui/suspicious_xor_used_as_pow.rs @@ -0,0 +1,34 @@ +#![allow(unused)] +#![warn(clippy::suspicious_xor_used_as_pow)] +#![allow(clippy::eq_op)] + +macro_rules! macro_test { + () => { + 13 + }; +} + +macro_rules! macro_test_inside { + () => { + 1 ^ 2 // should warn even if inside macro + }; +} + +fn main() { + // Should warn: + let _ = 2 ^ 5; + let _ = 2i32 ^ 9i32; + let _ = 2i32 ^ 2i32; + let _ = 50i32 ^ 3i32; + let _ = 5i32 ^ 8i32; + let _ = 2i32 ^ 32i32; + macro_test_inside!(); + + // Should not warn: + let x = 0x02; + let _ = x ^ 2; + let _ = 2 ^ x; + let _ = x ^ 5; + let _ = 10 ^ 0b0101; + let _ = 2i32 ^ macro_test!(); +} diff --git a/tests/ui/suspicious_xor_used_as_pow.stderr b/tests/ui/suspicious_xor_used_as_pow.stderr new file mode 100644 index 000000000000..8bb3c8fbeebd --- /dev/null +++ b/tests/ui/suspicious_xor_used_as_pow.stderr @@ -0,0 +1,51 @@ +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:19:13 + | +LL | let _ = 2 ^ 5; + | ^^^^^ help: did you mean to write: `2.pow(5)` + | + = note: `-D clippy::suspicious-xor-used-as-pow` implied by `-D warnings` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:20:13 + | +LL | let _ = 2i32 ^ 9i32; + | ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(9_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:21:13 + | +LL | let _ = 2i32 ^ 2i32; + | ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(2_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:22:13 + | +LL | let _ = 50i32 ^ 3i32; + | ^^^^^^^^^^^^ help: did you mean to write: `50_i32.pow(3_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:23:13 + | +LL | let _ = 5i32 ^ 8i32; + | ^^^^^^^^^^^ help: did you mean to write: `5_i32.pow(8_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:24:13 + | +LL | let _ = 2i32 ^ 32i32; + | ^^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(32_i32)` + +error: `^` is not the exponentiation operator + --> $DIR/suspicious_xor_used_as_pow.rs:13:9 + | +LL | 1 ^ 2 // should warn even if inside macro + | ^^^^^ help: did you mean to write: `1.pow(2)` +... +LL | macro_test_inside!(); + | -------------------- in this macro invocation + | + = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 7 previous errors + diff --git a/tests/ui/swap.fixed b/tests/ui/swap.fixed index 24b229235d33..805a2ba5a598 100644 --- a/tests/ui/swap.fixed +++ b/tests/ui/swap.fixed @@ -155,3 +155,12 @@ fn issue_8154() { let s = S3(&mut s); std::mem::swap(&mut s.0.x, &mut s.0.y); } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} diff --git a/tests/ui/swap.rs b/tests/ui/swap.rs index a318c27919c8..a8c878479523 100644 --- a/tests/ui/swap.rs +++ b/tests/ui/swap.rs @@ -179,3 +179,12 @@ fn issue_8154() { s.0.x = s.0.y; s.0.y = t; } + +const fn issue_9864(mut u: u32) -> u32 { + let mut v = 10; + + let temp = u; + u = v; + v = temp; + u + v +} diff --git a/tests/ui/track-diagnostics.rs b/tests/ui/track-diagnostics.rs new file mode 100644 index 000000000000..fa9221ed02d7 --- /dev/null +++ b/tests/ui/track-diagnostics.rs @@ -0,0 +1,12 @@ +// compile-flags: -Z track-diagnostics +// error-pattern: created at + +// Normalize the emitted location so this doesn't need +// updating everytime someone adds or removes a line. +// normalize-stderr-test ".rs:\d+:\d+" -> ".rs:LL:CC" + +struct A; +struct B; +const S: A = B; + +fn main() {} diff --git a/tests/ui/track-diagnostics.stderr b/tests/ui/track-diagnostics.stderr new file mode 100644 index 000000000000..ec3031862531 --- /dev/null +++ b/tests/ui/track-diagnostics.stderr @@ -0,0 +1,10 @@ +error[E0308]: mismatched types + --> $DIR/track-diagnostics.rs:LL:CC + | +LL | const S: A = B; + | ^ expected struct `A`, found struct `B` +-Ztrack-diagnostics: created at compiler/rustc_infer/src/infer/error_reporting/mod.rs:LL:CC + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 001c910239af..1cbacf0feab5 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::borrow_as_ptr)] +#![allow(dead_code, clippy::borrow_as_ptr, clippy::needless_lifetimes)] extern crate core; diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs new file mode 100644 index 000000000000..b3ea3d9039e0 --- /dev/null +++ b/tests/ui/transmute_null_to_fn.rs @@ -0,0 +1,28 @@ +#![allow(dead_code)] +#![warn(clippy::transmute_null_to_fn)] +#![allow(clippy::zero_ptr)] + +// Easy to lint because these only span one line. +fn one_liners() { + unsafe { + let _: fn() = std::mem::transmute(0 as *const ()); + let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + } +} + +pub const ZPTR: *const usize = 0 as *const _; +pub const NOT_ZPTR: *const usize = 1 as *const _; + +fn transmute_const() { + unsafe { + // Should raise a lint. + let _: fn() = std::mem::transmute(ZPTR); + // Should NOT raise a lint. + let _: fn() = std::mem::transmute(NOT_ZPTR); + } +} + +fn main() { + one_liners(); + transmute_const(); +} diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr new file mode 100644 index 000000000000..f0c65497d750 --- /dev/null +++ b/tests/ui/transmute_null_to_fn.stderr @@ -0,0 +1,27 @@ +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:8:23 + | +LL | let _: fn() = std::mem::transmute(0 as *const ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + = note: `-D clippy::transmute-null-to-fn` implied by `-D warnings` + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:9:23 + | +LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: transmuting a known null pointer into a function pointer + --> $DIR/transmute_null_to_fn.rs:19:23 + | +LL | let _: fn() = std::mem::transmute(ZPTR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior + | + = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value + +error: aborting due to 3 previous errors + diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index e5fe9133f975..074dae5fb286 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::transmute_ptr_to_ref)] #![allow(clippy::match_single_binding)] @@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } } +#[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.38"] let a = 0u32; let a = &a as *const u32; let _: &u32 = &*a; @@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +#[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.37"] let a = 0u32; let a = &a as *const u32; let _: &u32 = &*a; diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index fe49cdc324fd..2edc122cf471 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -1,6 +1,5 @@ // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::transmute_ptr_to_ref)] #![allow(clippy::match_single_binding)] @@ -51,8 +50,8 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } } +#[clippy::msrv = "1.38"] unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.38"] let a = 0u32; let a = &a as *const u32; let _: &u32 = std::mem::transmute(a); @@ -63,8 +62,8 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +#[clippy::msrv = "1.37"] unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { - #![clippy::msrv = "1.37"] let a = 0u32; let a = &a as *const u32; let _: &u32 = std::mem::transmute(a); diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 10117ee9182a..b3e6c09d2d7a 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:8:17 + --> $DIR/transmute_ptr_to_ref.rs:7:17 | LL | let _: &T = std::mem::transmute(p); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` @@ -7,127 +7,127 @@ LL | let _: &T = std::mem::transmute(p); = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:11:21 + --> $DIR/transmute_ptr_to_ref.rs:10:21 | LL | let _: &mut T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:14:17 + --> $DIR/transmute_ptr_to_ref.rs:13:17 | LL | let _: &T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:17:21 + --> $DIR/transmute_ptr_to_ref.rs:16:21 | LL | let _: &mut T = std::mem::transmute(p as *mut T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:20:17 + --> $DIR/transmute_ptr_to_ref.rs:19:17 | LL | let _: &T = std::mem::transmute(o); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:23:21 + --> $DIR/transmute_ptr_to_ref.rs:22:21 | LL | let _: &mut T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:26:17 + --> $DIR/transmute_ptr_to_ref.rs:25:17 | LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) - --> $DIR/transmute_ptr_to_ref.rs:36:32 + --> $DIR/transmute_ptr_to_ref.rs:35:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) - --> $DIR/transmute_ptr_to_ref.rs:38:33 + --> $DIR/transmute_ptr_to_ref.rs:37:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> $DIR/transmute_ptr_to_ref.rs:42:14 + --> $DIR/transmute_ptr_to_ref.rs:41:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:47:14 + --> $DIR/transmute_ptr_to_ref.rs:46:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:48:14 + --> $DIR/transmute_ptr_to_ref.rs:47:14 | LL | 1 => std::mem::transmute(y), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:49:14 + --> $DIR/transmute_ptr_to_ref.rs:48:14 | LL | 2 => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:50:14 + --> $DIR/transmute_ptr_to_ref.rs:49:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(y), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:58:19 + --> $DIR/transmute_ptr_to_ref.rs:57:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:59:19 + --> $DIR/transmute_ptr_to_ref.rs:58:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:61:14 + --> $DIR/transmute_ptr_to_ref.rs:60:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:62:14 + --> $DIR/transmute_ptr_to_ref.rs:61:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:70:19 + --> $DIR/transmute_ptr_to_ref.rs:69:19 | LL | let _: &u32 = std::mem::transmute(a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) - --> $DIR/transmute_ptr_to_ref.rs:71:19 + --> $DIR/transmute_ptr_to_ref.rs:70:19 | LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:73:14 + --> $DIR/transmute_ptr_to_ref.rs:72:14 | LL | 0 => std::mem::transmute(x), | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) - --> $DIR/transmute_ptr_to_ref.rs:74:14 + --> $DIR/transmute_ptr_to_ref.rs:73:14 | LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 7263abac15df..55307506eb3c 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -51,6 +51,8 @@ fn main() { // e is a function pointer type and U is an integer; fptr-addr-cast let _usize_from_fn_ptr_transmute = unsafe { foo as usize }; let _usize_from_fn_ptr = foo as *const usize; + + let _usize_from_ref = unsafe { &1u32 as *const u32 as usize }; } // If a ref-to-ptr cast of this form where the pointer type points to a type other diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index d8e4421d4c18..e7360f3f9dcb 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -51,6 +51,8 @@ fn main() { // e is a function pointer type and U is an integer; fptr-addr-cast let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; let _usize_from_fn_ptr = foo as *const usize; + + let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; } // If a ref-to-ptr cast of this form where the pointer type points to a type other diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index de9418c8d1ad..e862fcb67a4a 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -46,11 +46,17 @@ error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` +error: transmute from `*const u32` to `usize` which could be expressed as a pointer cast instead + --> $DIR/transmutes_expressible_as_ptr_casts.rs:55:36 + | +LL | let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&1u32 as *const u32 as usize` + error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:66:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index af4f3b18443b..c0af011d33d0 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -3,6 +3,7 @@ #![deny(clippy::trivially_copy_pass_by_ref)] #![allow( clippy::disallowed_names, + clippy::needless_lifetimes, clippy::redundant_field_names, clippy::uninlined_format_args )] diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index 6a8eca965534..8c5cfa8a0f17 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,5 +1,5 @@ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:50:11 + --> $DIR/trivially_copy_pass_by_ref.rs:51:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` @@ -11,103 +11,103 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:50:20 + --> $DIR/trivially_copy_pass_by_ref.rs:51:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:50:29 + --> $DIR/trivially_copy_pass_by_ref.rs:51:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:12 + --> $DIR/trivially_copy_pass_by_ref.rs:58:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:22 + --> $DIR/trivially_copy_pass_by_ref.rs:58:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:31 + --> $DIR/trivially_copy_pass_by_ref.rs:58:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:57:40 + --> $DIR/trivially_copy_pass_by_ref.rs:58:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:59:16 + --> $DIR/trivially_copy_pass_by_ref.rs:60:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:59:25 + --> $DIR/trivially_copy_pass_by_ref.rs:60:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:59:34 + --> $DIR/trivially_copy_pass_by_ref.rs:60:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:61:35 + --> $DIR/trivially_copy_pass_by_ref.rs:62:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:73:16 + --> $DIR/trivially_copy_pass_by_ref.rs:74:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:73:25 + --> $DIR/trivially_copy_pass_by_ref.rs:74:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:73:34 + --> $DIR/trivially_copy_pass_by_ref.rs:74:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:77:34 + --> $DIR/trivially_copy_pass_by_ref.rs:78:34 | LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:109:21 + --> $DIR/trivially_copy_pass_by_ref.rs:110:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:114:15 + --> $DIR/trivially_copy_pass_by_ref.rs:115:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:141:37 + --> $DIR/trivially_copy_pass_by_ref.rs:142:37 | LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { | ^^^^^^^ help: consider passing by value instead: `u32` diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 2eca1f4701c9..8b4613b3f6ec 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -1,4 +1,5 @@ #![deny(clippy::type_repetition_in_bounds)] +#![allow(clippy::extra_unused_type_parameters)] use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 70d700c1cc46..a90df03c04ff 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:8:5 + --> $DIR/type_repetition_in_bounds.rs:9:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:25:5 + --> $DIR/type_repetition_in_bounds.rs:26:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:85:5 + --> $DIR/type_repetition_in_bounds.rs:86:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:90:5 + --> $DIR/type_repetition_in_bounds.rs:91:5 | LL | T: ?Sized, | ^^^^^^^^^ diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed new file mode 100644 index 000000000000..a0e49a8beb1e --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.fixed @@ -0,0 +1,17 @@ +// run-rustfix +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first.checked_sub(second).unwrap(); + + let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); + + let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); + + let _ = Instant::now().checked_sub(second).unwrap(); +} diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs new file mode 100644 index 000000000000..a14a7ea57cc5 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.rs @@ -0,0 +1,17 @@ +// run-rustfix +#![warn(clippy::unchecked_duration_subtraction)] + +use std::time::{Duration, Instant}; + +fn main() { + let _first = Instant::now(); + let second = Duration::from_secs(3); + + let _ = _first - second; + + let _ = Instant::now() - Duration::from_secs(5); + + let _ = _first - Duration::from_secs(5); + + let _ = Instant::now() - second; +} diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr new file mode 100644 index 000000000000..a2e0aa1d7c08 --- /dev/null +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -0,0 +1,28 @@ +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:10:13 + | +LL | let _ = _first - second; + | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` + | + = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:12:13 + | +LL | let _ = Instant::now() - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:14:13 + | +LL | let _ = _first - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` + +error: unchecked subtraction of a 'Duration' from an 'Instant' + --> $DIR/unchecked_duration_subtraction.rs:16:13 + | +LL | let _ = Instant::now() - second; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 08aee4332151..c05eb447b2eb 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,6 +1,6 @@ // aux-build:proc_macro_unsafe.rs -#![warn(clippy::undocumented_unsafe_blocks)] +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] #![allow(clippy::let_unit_value, clippy::missing_safety_doc)] extern crate proc_macro_unsafe; @@ -490,4 +490,23 @@ unsafe impl CrateRoot for () {} // SAFETY: ok unsafe impl CrateRoot for (i32) {} +fn issue_9142() { + // SAFETY: ok + let _ = + // we need this comment to avoid rustfmt putting + // it all on one line + unsafe {}; + + // SAFETY: this is more than one level away, so it should warn + let _ = { + if unsafe { true } { + todo!(); + } else { + let bar = unsafe {}; + todo!(); + bar + } + }; +} + fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 2c466ff5c733..d1c1bb5ffeac 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -239,6 +239,19 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: | = help: consider adding a safety comment on the preceding line +error: constant item has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:471:5 + | +LL | const BIG_NUMBER: i32 = 1000000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:470:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + error: unsafe impl missing a safety comment --> $DIR/undocumented_unsafe_blocks.rs:472:5 | @@ -263,5 +276,47 @@ LL | unsafe impl CrateRoot for () {} | = help: consider adding a safety comment on the preceding line -error: aborting due to 31 previous errors +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:498:9 + | +LL | unsafe {}; + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: statement has unnecessary safety comment + --> $DIR/undocumented_unsafe_blocks.rs:501:5 + | +LL | / let _ = { +LL | | if unsafe { true } { +LL | | todo!(); +LL | | } else { +... | +LL | | } +LL | | }; + | |______^ + | +help: consider removing the safety comment + --> $DIR/undocumented_unsafe_blocks.rs:500:5 + | +LL | // SAFETY: this is more than one level away, so it should warn + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:502:12 + | +LL | if unsafe { true } { + | ^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe block missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:505:23 + | +LL | let bar = unsafe {}; + | ^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 36 previous errors diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 106274479751..9d08e80cf9a5 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -1,6 +1,5 @@ // aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] @@ -44,9 +43,7 @@ fn tester(fn_arg: i32) { println!("val='{local_i32}'"); // space+tab println!("val='{local_i32}'"); // tab+space println!( - "val='{ - }'", - local_i32 + "val='{local_i32}'" ); println!("{local_i32}"); println!("{fn_arg}"); @@ -57,11 +54,11 @@ fn tester(fn_arg: i32) { println!("{local_i32:<3}"); println!("{local_i32:#010x}"); println!("{local_f64:.1}"); - println!("Hello {} is {local_f64:.local_i32$}", "x"); - println!("Hello {local_i32} is {local_f64:.*}", 5); - println!("Hello {local_i32} is {local_f64:.*}", 5); + println!("Hello {} is {:.*}", "x", local_i32, local_f64); + println!("Hello {} is {:.*}", local_i32, 5, local_f64); + println!("Hello {} is {2:.*}", local_i32, 5, local_f64); println!("{local_i32} {local_f64}"); - println!("{local_i32}, {}", local_opt.unwrap()); + println!("{}, {}", local_i32, local_opt.unwrap()); println!("{val}"); println!("{val}"); println!("{} {1}", local_i32, 42); @@ -110,8 +107,7 @@ fn tester(fn_arg: i32) { println!("{local_f64:width$.prec$}"); println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); println!( - "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", - local_i32, width, prec, + "{local_i32:width$.prec$} {local_i32:prec$.width$} {width:local_i32$.prec$} {width:prec$.local_i32$} {prec:local_i32$.width$} {prec:width$.local_i32$}", ); println!( "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}", @@ -142,9 +138,7 @@ fn tester(fn_arg: i32) { println!(no_param_str!(), local_i32); println!( - "{}", - // comment with a comma , in it - val, + "{val}", ); println!("{val}"); @@ -169,14 +163,14 @@ fn main() { tester(42); } +#[clippy::msrv = "1.57"] fn _under_msrv() { - #![clippy::msrv = "1.57"] let local_i32 = 1; println!("don't expand='{}'", local_i32); } +#[clippy::msrv = "1.58"] fn _meets_msrv() { - #![clippy::msrv = "1.58"] let local_i32 = 1; println!("expand='{local_i32}'"); } diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index 8e495ebd083a..35b3677a8968 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -1,6 +1,5 @@ // aux-build:proc_macro_with_span.rs // run-rustfix -#![feature(custom_inner_attributes)] #![warn(clippy::uninlined_format_args)] #![allow(named_arguments_used_positionally, unused_imports, unused_macros, unused_variables)] #![allow(clippy::eq_op, clippy::format_in_format_args, clippy::print_literal)] @@ -169,14 +168,14 @@ fn main() { tester(42); } +#[clippy::msrv = "1.57"] fn _under_msrv() { - #![clippy::msrv = "1.57"] let local_i32 = 1; println!("don't expand='{}'", local_i32); } +#[clippy::msrv = "1.58"] fn _meets_msrv() { - #![clippy::msrv = "1.58"] let local_i32 = 1; println!("expand='{}'", local_i32); } diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 2ce3b7fa960c..a12abf8bef8a 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:41:5 + --> $DIR/uninlined_format_args.rs:40:5 | LL | println!("val='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:42:5 + --> $DIR/uninlined_format_args.rs:41:5 | LL | println!("val='{ }'", local_i32); // 3 spaces | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL + println!("val='{local_i32}'"); // 3 spaces | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:43:5 + --> $DIR/uninlined_format_args.rs:42:5 | LL | println!("val='{ }'", local_i32); // tab | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL + println!("val='{local_i32}'"); // tab | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:44:5 + --> $DIR/uninlined_format_args.rs:43:5 | LL | println!("val='{ }'", local_i32); // space+tab | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL + println!("val='{local_i32}'"); // space+tab | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:45:5 + --> $DIR/uninlined_format_args.rs:44:5 | LL | println!("val='{ }'", local_i32); // tab+space | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,17 @@ LL + println!("val='{local_i32}'"); // tab+space | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:51:5 + --> $DIR/uninlined_format_args.rs:45:5 + | +LL | / println!( +LL | | "val='{ +LL | | }'", +LL | | local_i32 +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:50:5 | LL | println!("{}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +82,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:52:5 + --> $DIR/uninlined_format_args.rs:51:5 | LL | println!("{}", fn_arg); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +94,7 @@ LL + println!("{fn_arg}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:53:5 + --> $DIR/uninlined_format_args.rs:52:5 | LL | println!("{:?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +106,7 @@ LL + println!("{local_i32:?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:54:5 + --> $DIR/uninlined_format_args.rs:53:5 | LL | println!("{:#?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +118,7 @@ LL + println!("{local_i32:#?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:55:5 + --> $DIR/uninlined_format_args.rs:54:5 | LL | println!("{:4}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +130,7 @@ LL + println!("{local_i32:4}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:56:5 + --> $DIR/uninlined_format_args.rs:55:5 | LL | println!("{:04}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +142,7 @@ LL + println!("{local_i32:04}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:57:5 + --> $DIR/uninlined_format_args.rs:56:5 | LL | println!("{:<3}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +154,7 @@ LL + println!("{local_i32:<3}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:58:5 + --> $DIR/uninlined_format_args.rs:57:5 | LL | println!("{:#010x}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +166,7 @@ LL + println!("{local_i32:#010x}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:59:5 + --> $DIR/uninlined_format_args.rs:58:5 | LL | println!("{:.1}", local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -167,45 +177,9 @@ LL - println!("{:.1}", local_f64); LL + println!("{local_f64:.1}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:60:5 - | -LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); -LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:61:5 - | -LL | println!("Hello {} is {:.*}", local_i32, 5, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {:.*}", local_i32, 5, local_f64); -LL + println!("Hello {local_i32} is {local_f64:.*}", 5); - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:62:5 | -LL | println!("Hello {} is {2:.*}", local_i32, 5, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {2:.*}", local_i32, 5, local_f64); -LL + println!("Hello {local_i32} is {local_f64:.*}", 5); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:63:5 - | LL | println!("{} {}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -218,18 +192,6 @@ LL + println!("{local_i32} {local_f64}"); error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:64:5 | -LL | println!("{}, {}", local_i32, local_opt.unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}, {}", local_i32, local_opt.unwrap()); -LL + println!("{local_i32}, {}", local_opt.unwrap()); - | - -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:65:5 - | LL | println!("{}", val); | ^^^^^^^^^^^^^^^^^^^ | @@ -240,7 +202,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:66:5 + --> $DIR/uninlined_format_args.rs:65:5 | LL | println!("{}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -252,7 +214,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:68:5 + --> $DIR/uninlined_format_args.rs:67:5 | LL | println!("val='{/t }'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -264,7 +226,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:69:5 + --> $DIR/uninlined_format_args.rs:68:5 | LL | println!("val='{/n }'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -276,7 +238,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:70:5 + --> $DIR/uninlined_format_args.rs:69:5 | LL | println!("val='{local_i32}'", local_i32 = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -288,7 +250,7 @@ LL + println!("val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:71:5 + --> $DIR/uninlined_format_args.rs:70:5 | LL | println!("val='{local_i32}'", local_i32 = fn_arg); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -300,7 +262,7 @@ LL + println!("val='{fn_arg}'"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:72:5 + --> $DIR/uninlined_format_args.rs:71:5 | LL | println!("{0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -312,7 +274,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:73:5 + --> $DIR/uninlined_format_args.rs:72:5 | LL | println!("{0:?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -324,7 +286,7 @@ LL + println!("{local_i32:?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:74:5 + --> $DIR/uninlined_format_args.rs:73:5 | LL | println!("{0:#?}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -336,7 +298,7 @@ LL + println!("{local_i32:#?}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:75:5 + --> $DIR/uninlined_format_args.rs:74:5 | LL | println!("{0:04}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -348,7 +310,7 @@ LL + println!("{local_i32:04}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:76:5 + --> $DIR/uninlined_format_args.rs:75:5 | LL | println!("{0:<3}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -360,7 +322,7 @@ LL + println!("{local_i32:<3}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:77:5 + --> $DIR/uninlined_format_args.rs:76:5 | LL | println!("{0:#010x}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -372,7 +334,7 @@ LL + println!("{local_i32:#010x}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:78:5 + --> $DIR/uninlined_format_args.rs:77:5 | LL | println!("{0:.1}", local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -384,7 +346,7 @@ LL + println!("{local_f64:.1}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:79:5 + --> $DIR/uninlined_format_args.rs:78:5 | LL | println!("{0} {0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -396,7 +358,7 @@ LL + println!("{local_i32} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:80:5 + --> $DIR/uninlined_format_args.rs:79:5 | LL | println!("{1} {} {0} {}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -408,7 +370,7 @@ LL + println!("{local_f64} {local_i32} {local_i32} {local_f64}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:81:5 + --> $DIR/uninlined_format_args.rs:80:5 | LL | println!("{0} {1}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -420,7 +382,7 @@ LL + println!("{local_i32} {local_f64}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:82:5 + --> $DIR/uninlined_format_args.rs:81:5 | LL | println!("{1} {0}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -432,7 +394,7 @@ LL + println!("{local_f64} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:83:5 + --> $DIR/uninlined_format_args.rs:82:5 | LL | println!("{1} {0} {1} {0}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -444,7 +406,7 @@ LL + println!("{local_f64} {local_i32} {local_f64} {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:85:5 + --> $DIR/uninlined_format_args.rs:84:5 | LL | println!("{v}", v = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -456,7 +418,7 @@ LL + println!("{local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:86:5 + --> $DIR/uninlined_format_args.rs:85:5 | LL | println!("{local_i32:0$}", width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -468,7 +430,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:87:5 + --> $DIR/uninlined_format_args.rs:86:5 | LL | println!("{local_i32:w$}", w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -480,7 +442,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:88:5 + --> $DIR/uninlined_format_args.rs:87:5 | LL | println!("{local_i32:.0$}", prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -492,7 +454,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:89:5 + --> $DIR/uninlined_format_args.rs:88:5 | LL | println!("{local_i32:.p$}", p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -504,7 +466,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:90:5 + --> $DIR/uninlined_format_args.rs:89:5 | LL | println!("{:0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -516,7 +478,7 @@ LL + println!("{val:val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:91:5 + --> $DIR/uninlined_format_args.rs:90:5 | LL | println!("{0:0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -528,7 +490,7 @@ LL + println!("{val:val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:92:5 + --> $DIR/uninlined_format_args.rs:91:5 | LL | println!("{:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -540,7 +502,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:93:5 + --> $DIR/uninlined_format_args.rs:92:5 | LL | println!("{0:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -552,7 +514,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:94:5 + --> $DIR/uninlined_format_args.rs:93:5 | LL | println!("{0:0$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -564,7 +526,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:95:5 + --> $DIR/uninlined_format_args.rs:94:5 | LL | println!("{0:v$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -576,7 +538,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:96:5 + --> $DIR/uninlined_format_args.rs:95:5 | LL | println!("{v:0$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -588,7 +550,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:97:5 + --> $DIR/uninlined_format_args.rs:96:5 | LL | println!("{v:v$.0$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -600,7 +562,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:98:5 + --> $DIR/uninlined_format_args.rs:97:5 | LL | println!("{v:0$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -612,7 +574,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:99:5 + --> $DIR/uninlined_format_args.rs:98:5 | LL | println!("{v:v$.v$}", v = val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -624,7 +586,7 @@ LL + println!("{val:val$.val$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:100:5 + --> $DIR/uninlined_format_args.rs:99:5 | LL | println!("{:0$}", width); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -636,7 +598,7 @@ LL + println!("{width:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:101:5 + --> $DIR/uninlined_format_args.rs:100:5 | LL | println!("{:1$}", local_i32, width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -648,7 +610,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:102:5 + --> $DIR/uninlined_format_args.rs:101:5 | LL | println!("{:w$}", w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -660,7 +622,7 @@ LL + println!("{width:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:103:5 + --> $DIR/uninlined_format_args.rs:102:5 | LL | println!("{:w$}", local_i32, w = width); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -672,7 +634,7 @@ LL + println!("{local_i32:width$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:104:5 + --> $DIR/uninlined_format_args.rs:103:5 | LL | println!("{:.0$}", prec); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -684,7 +646,7 @@ LL + println!("{prec:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:105:5 + --> $DIR/uninlined_format_args.rs:104:5 | LL | println!("{:.1$}", local_i32, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -696,7 +658,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:106:5 + --> $DIR/uninlined_format_args.rs:105:5 | LL | println!("{:.p$}", p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -708,7 +670,7 @@ LL + println!("{prec:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:107:5 + --> $DIR/uninlined_format_args.rs:106:5 | LL | println!("{:.p$}", local_i32, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -720,7 +682,7 @@ LL + println!("{local_i32:.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:108:5 + --> $DIR/uninlined_format_args.rs:107:5 | LL | println!("{:0$.1$}", width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -732,7 +694,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:109:5 + --> $DIR/uninlined_format_args.rs:108:5 | LL | println!("{:0$.w$}", width, w = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -744,7 +706,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:110:5 + --> $DIR/uninlined_format_args.rs:109:5 | LL | println!("{:1$.2$}", local_f64, width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -756,7 +718,7 @@ LL + println!("{local_f64:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:111:5 + --> $DIR/uninlined_format_args.rs:110:5 | LL | println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -768,7 +730,16 @@ LL + println!("{local_f64:width$.prec$} {local_f64} {width} {prec}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:123:5 + --> $DIR/uninlined_format_args.rs:111:5 + | +LL | / println!( +LL | | "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}", +LL | | local_i32, width, prec, +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:122:5 | LL | println!("Width = {}, value with width = {:0$}", local_i32, local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -780,7 +751,7 @@ LL + println!("Width = {local_i32}, value with width = {local_f64:local_i32$ | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:124:5 + --> $DIR/uninlined_format_args.rs:123:5 | LL | println!("{:w$.p$}", local_i32, w = width, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -792,7 +763,7 @@ LL + println!("{local_i32:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:125:5 + --> $DIR/uninlined_format_args.rs:124:5 | LL | println!("{:w$.p$}", w = width, p = prec); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -804,7 +775,7 @@ LL + println!("{width:width$.prec$}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:126:20 + --> $DIR/uninlined_format_args.rs:125:20 | LL | println!("{}", format!("{}", local_i32)); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -816,7 +787,17 @@ LL + println!("{}", format!("{local_i32}")); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:149:5 + --> $DIR/uninlined_format_args.rs:143:5 + | +LL | / println!( +LL | | "{}", +LL | | // comment with a comma , in it +LL | | val, +LL | | ); + | |_____^ + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:148:5 | LL | println!("{}", /* comment with a comma , in it */ val); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -828,7 +809,7 @@ LL + println!("{val}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:155:9 + --> $DIR/uninlined_format_args.rs:154:9 | LL | panic!("p1 {}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -840,7 +821,7 @@ LL + panic!("p1 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:158:9 + --> $DIR/uninlined_format_args.rs:157:9 | LL | panic!("p2 {0}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -852,7 +833,7 @@ LL + panic!("p2 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:161:9 + --> $DIR/uninlined_format_args.rs:160:9 | LL | panic!("p3 {local_i32}", local_i32 = local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -864,7 +845,7 @@ LL + panic!("p3 {local_i32}"); | error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:181:5 + --> $DIR/uninlined_format_args.rs:180:5 | LL | println!("expand='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -875,5 +856,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 73 previous errors +error: aborting due to 72 previous errors diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed index 96cc0877960e..52b5343c351e 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {}", var); + debug_assert!(var == 1, "p6 {}", var); } diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed index faf8ca4d3a79..ee72065e28ab 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {var}"); + debug_assert!(var == 1, "p6 {var}"); } diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr index 0f09c45f4132..fc7b125080e7 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -47,5 +47,29 @@ LL - panic!("p3 {var}", var = var); LL + panic!("p3 {var}"); | -error: aborting due to 4 previous errors +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:30:5 + | +LL | assert!(var == 1, "p5 {}", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - assert!(var == 1, "p5 {}", var); +LL + assert!(var == 1, "p5 {var}"); + | + +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args_panic.rs:31:5 + | +LL | debug_assert!(var == 1, "p6 {}", var); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - debug_assert!(var == 1, "p6 {}", var); +LL + debug_assert!(var == 1, "p6 {var}"); + | + +error: aborting due to 6 previous errors diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs index 6421c5bbed2f..b4a0a0f496e4 100644 --- a/tests/ui/uninlined_format_args_panic.rs +++ b/tests/ui/uninlined_format_args_panic.rs @@ -26,4 +26,7 @@ fn main() { panic!("p4 {var}"); } } + + assert!(var == 1, "p5 {}", var); + debug_assert!(var == 1, "p6 {}", var); } diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 94dc96427263..2f7e2997e739 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -41,6 +41,17 @@ fn main() { // do not lint cast to alias type 1 as I32Alias; &1 as &I32Alias; + + // issue #9960 + macro_rules! bind_var { + ($id:ident, $e:expr) => {{ + let $id = 0usize; + let _ = $e != 0usize; + let $id = 0isize; + let _ = $e != 0usize; + }} + } + bind_var!(x, (x as usize) + 1); } type I32Alias = i32; @@ -85,6 +96,9 @@ mod fixable { let _ = 1 as I32Alias; let _ = &1 as &I32Alias; + + let x = 1i32; + let _ = &{ x }; } type I32Alias = i32; @@ -111,4 +125,8 @@ mod fixable { let _num = foo(); } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index e5150256f69a..54dd46ba59f1 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -41,6 +41,17 @@ fn main() { // do not lint cast to alias type 1 as I32Alias; &1 as &I32Alias; + + // issue #9960 + macro_rules! bind_var { + ($id:ident, $e:expr) => {{ + let $id = 0usize; + let _ = $e != 0usize; + let $id = 0isize; + let _ = $e != 0usize; + }} + } + bind_var!(x, (x as usize) + 1); } type I32Alias = i32; @@ -85,6 +96,9 @@ mod fixable { let _ = 1 as I32Alias; let _ = &1 as &I32Alias; + + let x = 1i32; + let _ = &(x as i32); } type I32Alias = i32; @@ -111,4 +125,8 @@ mod fixable { let _num = foo() as f32; } + + fn issue_9603() { + let _: f32 = -0x400 as f32; + } } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index e5c3dd5e53f8..fcee4ee2a65c 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -49,136 +49,142 @@ LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:53:9 + --> $DIR/unnecessary_cast.rs:64:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:54:9 + --> $DIR/unnecessary_cast.rs:65:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:55:9 + --> $DIR/unnecessary_cast.rs:66:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:56:17 + --> $DIR/unnecessary_cast.rs:67:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:57:17 + --> $DIR/unnecessary_cast.rs:68:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:58:17 + --> $DIR/unnecessary_cast.rs:69:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:59:9 + --> $DIR/unnecessary_cast.rs:70:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:60:9 + --> $DIR/unnecessary_cast.rs:71:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:72:9 + --> $DIR/unnecessary_cast.rs:83:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:73:9 + --> $DIR/unnecessary_cast.rs:84:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast.rs:74:9 + --> $DIR/unnecessary_cast.rs:85:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast.rs:75:9 + --> $DIR/unnecessary_cast.rs:86:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:76:9 + --> $DIR/unnecessary_cast.rs:87:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:78:9 + --> $DIR/unnecessary_cast.rs:89:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:79:9 + --> $DIR/unnecessary_cast.rs:90:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:83:17 + --> $DIR/unnecessary_cast.rs:94:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:84:17 + --> $DIR/unnecessary_cast.rs:95:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` +error: casting to the same type is unnecessary (`i32` -> `i32`) + --> $DIR/unnecessary_cast.rs:101:18 + | +LL | let _ = &(x as i32); + | ^^^^^^^^^^ help: try: `{ x }` + error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:93:22 + --> $DIR/unnecessary_cast.rs:107:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> $DIR/unnecessary_cast.rs:95:22 + --> $DIR/unnecessary_cast.rs:109:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:102:22 + --> $DIR/unnecessary_cast.rs:116:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:104:23 + --> $DIR/unnecessary_cast.rs:118:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> $DIR/unnecessary_cast.rs:112:20 + --> $DIR/unnecessary_cast.rs:126:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` -error: aborting due to 30 previous errors +error: aborting due to 31 previous errors diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 94cc7777acac..6022d9fa4c5c 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -38,13 +38,13 @@ LL | t.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on type `std::option::Option` which implements the `Copy` trait +error: using `clone` on type `Option` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` -error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec` instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&Vec` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); @@ -57,10 +57,10 @@ LL | let z: &Vec<_> = &(*y).clone(); | ~~~~~~~~~~~~~ help: or try being explicit if you are sure, that you want to clone a reference | -LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let z: &Vec<_> = <&Vec>::clone(y); + | ~~~~~~~~~~~~~~~~~~~~~ -error: using `clone` on type `many_derefs::E` which implements the `Copy` trait +error: using `clone` on type `E` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); diff --git a/tests/ui/unnecessary_join.stderr b/tests/ui/unnecessary_join.stderr index 0b14b143affd..e919a6d1d8aa 100644 --- a/tests/ui/unnecessary_join.stderr +++ b/tests/ui/unnecessary_join.stderr @@ -1,4 +1,4 @@ -error: called `.collect>().join("")` on an iterator +error: called `.collect::>().join("")` on an iterator --> $DIR/unnecessary_join.rs:11:10 | LL | .collect::>() @@ -8,7 +8,7 @@ LL | | .join(""); | = note: `-D clippy::unnecessary-join` implied by `-D warnings` -error: called `.collect>().join("")` on an iterator +error: called `.collect::>().join("")` on an iterator --> $DIR/unnecessary_join.rs:20:10 | LL | .collect::>() diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index ce4a82e02174..22e9bd8bdc51 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -33,6 +33,14 @@ impl Drop for Issue9427 { } } +struct Issue9427FollowUp; + +impl Drop for Issue9427FollowUp { + fn drop(&mut self) { + panic!("side effect drop"); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -87,6 +95,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); @@ -133,13 +142,13 @@ fn main() { let _: Result = res.or(Ok(astronomers_pi)); let _: Result = res.or(Ok(ext_str.some_field)); let _: Result = res. - // some lines - // some lines - // some lines - // some lines - // some lines - // some lines - or(Ok(ext_str.some_field)); + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or(Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 59cdf6628546..8726d84a23fc 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -33,6 +33,14 @@ impl Drop for Issue9427 { } } +struct Issue9427FollowUp; + +impl Drop for Issue9427FollowUp { + fn drop(&mut self) { + panic!("side effect drop"); + } +} + fn main() { let astronomers_pi = 10; let ext_arr: [usize; 1] = [2]; @@ -87,6 +95,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop + let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); @@ -133,13 +142,13 @@ fn main() { let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); let _: Result = res. - // some lines - // some lines - // some lines - // some lines - // some lines - // some lines - or_else(|_| Ok(ext_str.some_field)); + // some lines + // some lines + // some lines + // some lines + // some lines + // some lines + or_else(|_| Ok(ext_str.some_field)); // neither bind_instead_of_map nor unnecessary_lazy_eval applies here let _: Result = res.and_then(|x| Err(x)); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 8a9ece4aa7e5..0339755442c5 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -9,7 +9,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -17,7 +17,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -25,7 +25,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:60:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -33,7 +33,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:13 + --> $DIR/unnecessary_lazy_eval.rs:61:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -41,7 +41,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:62:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -49,7 +49,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:63:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -57,7 +57,7 @@ LL | let _ = opt.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:56:13 + --> $DIR/unnecessary_lazy_eval.rs:64:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -65,7 +65,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:65:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -73,7 +73,7 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:66:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- @@ -81,7 +81,7 @@ LL | let _ = cond.then(|| astronomers_pi); | help: use `then_some(..)` instead: `then_some(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:61:13 + --> $DIR/unnecessary_lazy_eval.rs:69:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -89,7 +89,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:62:13 + --> $DIR/unnecessary_lazy_eval.rs:70:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -97,7 +97,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:63:28 + --> $DIR/unnecessary_lazy_eval.rs:71:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -105,7 +105,7 @@ LL | let _: Option = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:64:13 + --> $DIR/unnecessary_lazy_eval.rs:72:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -113,7 +113,7 @@ LL | let _ = None.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:65:35 + --> $DIR/unnecessary_lazy_eval.rs:73:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -121,7 +121,7 @@ LL | let _: Result = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:66:28 + --> $DIR/unnecessary_lazy_eval.rs:74:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^---------------- @@ -129,7 +129,7 @@ LL | let _: Option = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:69:13 + --> $DIR/unnecessary_lazy_eval.rs:77:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -137,7 +137,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:70:13 + --> $DIR/unnecessary_lazy_eval.rs:78:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -145,7 +145,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:71:13 + --> $DIR/unnecessary_lazy_eval.rs:79:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -153,7 +153,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:72:13 + --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -161,7 +161,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:81:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -169,7 +169,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:96:28 + --> $DIR/unnecessary_lazy_eval.rs:105:28 | LL | let _: Option = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -177,7 +177,7 @@ LL | let _: Option = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:97:13 + --> $DIR/unnecessary_lazy_eval.rs:106:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -185,7 +185,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:98:13 + --> $DIR/unnecessary_lazy_eval.rs:107:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -193,7 +193,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:104:13 + --> $DIR/unnecessary_lazy_eval.rs:113:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -201,7 +201,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:105:13 + --> $DIR/unnecessary_lazy_eval.rs:114:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -209,7 +209,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:106:13 + --> $DIR/unnecessary_lazy_eval.rs:115:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -217,7 +217,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:128:35 + --> $DIR/unnecessary_lazy_eval.rs:137:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -225,7 +225,7 @@ LL | let _: Result = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:129:35 + --> $DIR/unnecessary_lazy_eval.rs:138:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -233,7 +233,7 @@ LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:130:35 + --> $DIR/unnecessary_lazy_eval.rs:139:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -241,7 +241,7 @@ LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)) | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:132:35 + --> $DIR/unnecessary_lazy_eval.rs:141:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -249,7 +249,7 @@ LL | let _: Result = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:133:35 + --> $DIR/unnecessary_lazy_eval.rs:142:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -257,7 +257,7 @@ LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:134:35 + --> $DIR/unnecessary_lazy_eval.rs:143:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -265,19 +265,19 @@ LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:135:35 + --> $DIR/unnecessary_lazy_eval.rs:144:35 | LL | let _: Result = res. | ___________________________________^ -LL | | // some lines -LL | | // some lines -LL | | // some lines +LL | | // some lines +LL | | // some lines +LL | | // some lines ... | -LL | | // some lines -LL | | or_else(|_| Ok(ext_str.some_field)); - | |_________----------------------------------^ - | | - | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` +LL | | // some lines +LL | | or_else(|_| Ok(ext_str.some_field)); + | |_____----------------------------------^ + | | + | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: aborting due to 34 previous errors diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index bf0ec8deb345..d37163570abe 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -76,4 +76,13 @@ fn main() { DropStruct { ..get_drop_struct() }; DropEnum::Tuple(get_number()); DropEnum::Struct { field: get_number() }; + + // Issue #9954 + fn one() -> i8 { + 1 + } + macro_rules! use_expr { + ($($e:expr),*) => {{ $($e;)* }} + } + use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); } diff --git a/tests/ui/unnecessary_operation.rs b/tests/ui/unnecessary_operation.rs index 08cb9ab522ee..a14fd4bca0ef 100644 --- a/tests/ui/unnecessary_operation.rs +++ b/tests/ui/unnecessary_operation.rs @@ -80,4 +80,13 @@ fn main() { DropStruct { ..get_drop_struct() }; DropEnum::Tuple(get_number()); DropEnum::Struct { field: get_number() }; + + // Issue #9954 + fn one() -> i8 { + 1 + } + macro_rules! use_expr { + ($($e:expr),*) => {{ $($e;)* }} + } + use_expr!(isize::MIN / -(one() as isize), i8::MIN / -one()); } diff --git a/tests/ui/unnecessary_safety_comment.rs b/tests/ui/unnecessary_safety_comment.rs new file mode 100644 index 000000000000..89fedb145f88 --- /dev/null +++ b/tests/ui/unnecessary_safety_comment.rs @@ -0,0 +1,68 @@ +#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] + +mod unsafe_items_invalid_comment { + // SAFETY: + const CONST: u32 = 0; + // SAFETY: + static STATIC: u32 = 0; + // SAFETY: + struct Struct; + // SAFETY: + enum Enum {} + // SAFETY: + mod module {} +} + +mod unnecessary_from_macro { + trait T {} + + macro_rules! no_safety_comment { + ($t:ty) => { + impl T for $t {} + }; + } + + // FIXME: This is not caught + // Safety: unnecessary + no_safety_comment!(()); + + macro_rules! with_safety_comment { + ($t:ty) => { + // Safety: unnecessary + impl T for $t {} + }; + } + + with_safety_comment!(i32); +} + +fn unnecessary_on_stmt_and_expr() -> u32 { + // SAFETY: unnecessary + let num = 42; + + // SAFETY: unnecessary + if num > 24 {} + + // SAFETY: unnecessary + 24 +} + +mod issue_10084 { + unsafe fn bar() -> i32 { + 42 + } + + macro_rules! foo { + () => { + // SAFETY: This is necessary + unsafe { bar() } + }; + } + + fn main() { + foo!(); + } +} + +fn main() {} diff --git a/tests/ui/unnecessary_safety_comment.stderr b/tests/ui/unnecessary_safety_comment.stderr new file mode 100644 index 000000000000..7b2af67d64c7 --- /dev/null +++ b/tests/ui/unnecessary_safety_comment.stderr @@ -0,0 +1,115 @@ +error: constant item has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:6:5 + | +LL | const CONST: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:5:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + = note: `-D clippy::unnecessary-safety-comment` implied by `-D warnings` + +error: static item has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:8:5 + | +LL | static STATIC: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:7:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: struct has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:10:5 + | +LL | struct Struct; + | ^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:9:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: enum has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:12:5 + | +LL | enum Enum {} + | ^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:11:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: module has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:14:5 + | +LL | mod module {} + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:13:5 + | +LL | // SAFETY: + | ^^^^^^^^^^ + +error: impl has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:33:13 + | +LL | impl T for $t {} + | ^^^^^^^^^^^^^^^^ +... +LL | with_safety_comment!(i32); + | ------------------------- in this macro invocation + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:32:13 + | +LL | // Safety: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `with_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expression has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:48:5 + | +LL | 24 + | ^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:47:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: statement has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:42:5 + | +LL | let num = 42; + | ^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:41:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: statement has unnecessary safety comment + --> $DIR/unnecessary_safety_comment.rs:45:5 + | +LL | if num > 24 {} + | ^^^^^^^^^^^^^^ + | +help: consider removing the safety comment + --> $DIR/unnecessary_safety_comment.rs:44:5 + | +LL | // SAFETY: unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index fe09aad06bc8..345f6d604c4f 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -2,7 +2,6 @@ #![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] -#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result, V: ?Sized>(K, PhantomData); + + impl, V: ?Sized> Key { + pub fn new(key: K) -> Key { + Key(key, PhantomData) + } + } + + pub fn pkh(pkh: &[u8]) -> Key, String> { + Key::new([b"pkh-", pkh].concat().to_vec()) + } +} + +mod issue_9771b { + #![allow(dead_code)] + + pub struct Key>(K); + + pub fn from(c: &[u8]) -> Key> { + let v = [c].concat(); + Key(v.to_vec()) + } +} + +// This is a watered down version of the code in: https://github.com/oxigraph/rio +// The ICE is triggered by the call to `to_owned` on this line: +// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 +mod issue_10021 { + #![allow(unused)] + + pub struct Iri(T); + + impl> Iri { + pub fn parse(iri: T) -> Result { + unimplemented!() + } + } + + pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { + let base_iri = Iri::parse(url.to_owned())?; + Ok(()) + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index 3de6d0903c0f..7eb53df39e5b 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -2,7 +2,6 @@ #![allow(clippy::needless_borrow, clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] -#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -215,14 +214,14 @@ fn get_file_path(_file_type: &FileType) -> Result, V: ?Sized>(K, PhantomData); + + impl, V: ?Sized> Key { + pub fn new(key: K) -> Key { + Key(key, PhantomData) + } + } + + pub fn pkh(pkh: &[u8]) -> Key, String> { + Key::new([b"pkh-", pkh].concat().to_vec()) + } +} + +mod issue_9771b { + #![allow(dead_code)] + + pub struct Key>(K); + + pub fn from(c: &[u8]) -> Key> { + let v = [c].concat(); + Key(v.to_vec()) + } +} + +// This is a watered down version of the code in: https://github.com/oxigraph/rio +// The ICE is triggered by the call to `to_owned` on this line: +// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 +mod issue_10021 { + #![allow(unused)] + + pub struct Iri(T); + + impl> Iri { + pub fn parse(iri: T) -> Result { + unimplemented!() + } + } + + pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { + let base_iri = Iri::parse(url.to_owned())?; + Ok(()) + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index 02bf45a33fbe..4918fe355986 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -1,66 +1,66 @@ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:151:64 + --> $DIR/unnecessary_to_owned.rs:150:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:20 + --> $DIR/unnecessary_to_owned.rs:150:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: `-D clippy::redundant-clone` implied by `-D warnings` error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:40 + --> $DIR/unnecessary_to_owned.rs:151:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:21 + --> $DIR/unnecessary_to_owned.rs:151:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:153:48 + --> $DIR/unnecessary_to_owned.rs:152:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:153:19 + --> $DIR/unnecessary_to_owned.rs:152:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:154:35 + --> $DIR/unnecessary_to_owned.rs:153:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:154:18 + --> $DIR/unnecessary_to_owned.rs:153:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:155:39 + --> $DIR/unnecessary_to_owned.rs:154:39 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:155:20 + --> $DIR/unnecessary_to_owned.rs:154:20 | LL | require_slice(&[String::from("x")].to_owned()); | ^^^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:60:36 + --> $DIR/unnecessary_to_owned.rs:59:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -68,415 +68,415 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:61:19 + --> $DIR/unnecessary_to_owned.rs:60:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:63:20 + --> $DIR/unnecessary_to_owned.rs:62:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:64:38 + --> $DIR/unnecessary_to_owned.rs:63:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:65:20 + --> $DIR/unnecessary_to_owned.rs:64:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:67:18 + --> $DIR/unnecessary_to_owned.rs:66:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:68:34 + --> $DIR/unnecessary_to_owned.rs:67:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:69:18 + --> $DIR/unnecessary_to_owned.rs:68:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:71:17 + --> $DIR/unnecessary_to_owned.rs:70:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:72:30 + --> $DIR/unnecessary_to_owned.rs:71:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:73:17 + --> $DIR/unnecessary_to_owned.rs:72:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:74:17 + --> $DIR/unnecessary_to_owned.rs:73:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:76:19 + --> $DIR/unnecessary_to_owned.rs:75:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:77:36 + --> $DIR/unnecessary_to_owned.rs:76:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:78:19 + --> $DIR/unnecessary_to_owned.rs:77:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:78:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:80:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:83:42 + --> $DIR/unnecessary_to_owned.rs:82:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:25 + --> $DIR/unnecessary_to_owned.rs:85:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:26 + --> $DIR/unnecessary_to_owned.rs:86:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:88:24 + --> $DIR/unnecessary_to_owned.rs:87:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:23 + --> $DIR/unnecessary_to_owned.rs:88:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:90:25 + --> $DIR/unnecessary_to_owned.rs:89:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:30 + --> $DIR/unnecessary_to_owned.rs:91:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:31 + --> $DIR/unnecessary_to_owned.rs:92:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:94:29 + --> $DIR/unnecessary_to_owned.rs:93:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:28 + --> $DIR/unnecessary_to_owned.rs:94:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:96:30 + --> $DIR/unnecessary_to_owned.rs:95:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:29 + --> $DIR/unnecessary_to_owned.rs:97:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:43 + --> $DIR/unnecessary_to_owned.rs:97:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:99:47 + --> $DIR/unnecessary_to_owned.rs:98:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:26 + --> $DIR/unnecessary_to_owned.rs:100:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:27 + --> $DIR/unnecessary_to_owned.rs:101:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:25 + --> $DIR/unnecessary_to_owned.rs:102:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:24 + --> $DIR/unnecessary_to_owned.rs:103:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:24 + --> $DIR/unnecessary_to_owned.rs:104:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:26 + --> $DIR/unnecessary_to_owned.rs:105:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:26 + --> $DIR/unnecessary_to_owned.rs:106:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:108:26 + --> $DIR/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:31 + --> $DIR/unnecessary_to_owned.rs:109:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:32 + --> $DIR/unnecessary_to_owned.rs:110:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:30 + --> $DIR/unnecessary_to_owned.rs:111:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:29 + --> $DIR/unnecessary_to_owned.rs:112:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:29 + --> $DIR/unnecessary_to_owned.rs:113:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:31 + --> $DIR/unnecessary_to_owned.rs:114:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:31 + --> $DIR/unnecessary_to_owned.rs:115:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:117:31 + --> $DIR/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:30 + --> $DIR/unnecessary_to_owned.rs:118:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:44 + --> $DIR/unnecessary_to_owned.rs:118:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:30 + --> $DIR/unnecessary_to_owned.rs:119:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:44 + --> $DIR/unnecessary_to_owned.rs:119:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:120:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:44 + --> $DIR/unnecessary_to_owned.rs:120:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:121:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:48 + --> $DIR/unnecessary_to_owned.rs:121:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:52 + --> $DIR/unnecessary_to_owned.rs:122:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:124:48 + --> $DIR/unnecessary_to_owned.rs:123:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:126:20 + --> $DIR/unnecessary_to_owned.rs:125:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:127:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:129:13 + --> $DIR/unnecessary_to_owned.rs:128:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:129:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:131:13 + --> $DIR/unnecessary_to_owned.rs:130:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:132:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:135:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:136:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:198:14 + --> $DIR/unnecessary_to_owned.rs:197:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -492,25 +492,25 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:221:14 + --> $DIR/unnecessary_to_owned.rs:220:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:226:14 + --> $DIR/unnecessary_to_owned.rs:225:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:273:24 + --> $DIR/unnecessary_to_owned.rs:272:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:381:12 + --> $DIR/unnecessary_to_owned.rs:380:12 | LL | id("abc".to_string()) | ^^^^^^^^^^^^^^^^^ help: use: `"abc"` diff --git a/tests/ui/unnecessary_unsafety_doc.rs b/tests/ui/unnecessary_unsafety_doc.rs new file mode 100644 index 000000000000..c160e31afd33 --- /dev/null +++ b/tests/ui/unnecessary_unsafety_doc.rs @@ -0,0 +1,149 @@ +// aux-build:doc_unsafe_macros.rs + +#![allow(clippy::let_unit_value)] +#![warn(clippy::unnecessary_safety_doc)] + +#[macro_use] +extern crate doc_unsafe_macros; + +/// This is has no safety section, and does not need one either +pub fn destroy_the_planet() { + unimplemented!(); +} + +/// This one does not need a `Safety` section +/// +/// # Safety +/// +/// This function shouldn't be called unless the horsemen are ready +pub fn apocalypse(universe: &mut ()) { + unimplemented!(); +} + +/// This is a private function, skip to match behavior with `missing_safety_doc`. +/// +/// # Safety +/// +/// Boo! +fn you_dont_see_me() { + unimplemented!(); +} + +mod private_mod { + /// This is public but unexported function, skip to match behavior with `missing_safety_doc`. + /// + /// # Safety + /// + /// Very safe! + pub fn only_crate_wide_accessible() { + unimplemented!(); + } + + /// # Safety + /// + /// Unnecessary safety! + pub fn republished() { + unimplemented!(); + } +} + +pub use private_mod::republished; + +pub trait SafeTraitSafeMethods { + fn woefully_underdocumented(self); + + /// # Safety + /// + /// Unnecessary! + fn documented(self); +} + +pub trait SafeTrait { + fn method(); +} + +/// # Safety +/// +/// Unnecessary! +pub trait DocumentedSafeTrait { + fn method2(); +} + +pub struct Struct; + +impl SafeTraitSafeMethods for Struct { + fn woefully_underdocumented(self) { + // all is well + } + + fn documented(self) { + // all is still well + } +} + +impl SafeTrait for Struct { + fn method() {} +} + +impl DocumentedSafeTrait for Struct { + fn method2() {} +} + +impl Struct { + /// # Safety + /// + /// Unnecessary! + pub fn documented() -> Self { + unimplemented!(); + } + + pub fn undocumented(&self) { + unimplemented!(); + } + + /// Private, fine again to stay consistent with `missing_safety_doc`. + /// + /// # Safety + /// + /// Unnecessary! + fn private(&self) { + unimplemented!(); + } +} + +macro_rules! very_safe { + () => { + pub fn whee() { + unimplemented!() + } + + /// # Safety + /// + /// Driving is very safe already! + pub fn drive() { + whee() + } + }; +} + +very_safe!(); + +// we don't lint code from external macros +undocd_safe!(); + +fn main() {} + +// do not lint if any parent has `#[doc(hidden)]` attribute +// see #7347 +#[doc(hidden)] +pub mod __macro { + pub struct T; + impl T { + pub unsafe fn f() {} + } +} + +/// # Implementation safety +pub trait DocumentedSafeTraitWithImplementationHeader { + fn method(); +} diff --git a/tests/ui/unnecessary_unsafety_doc.stderr b/tests/ui/unnecessary_unsafety_doc.stderr new file mode 100644 index 000000000000..72898c93fa11 --- /dev/null +++ b/tests/ui/unnecessary_unsafety_doc.stderr @@ -0,0 +1,51 @@ +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/unnecessary_unsafety_doc.rs:19:1 + | +LL | pub fn apocalypse(universe: &mut ()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-safety-doc` implied by `-D warnings` + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/unnecessary_unsafety_doc.rs:45:5 + | +LL | pub fn republished() { + | ^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/unnecessary_unsafety_doc.rs:58:5 + | +LL | fn documented(self); + | ^^^^^^^^^^^^^^^^^^^^ + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/unnecessary_unsafety_doc.rs:68:1 + | +LL | pub trait DocumentedSafeTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/unnecessary_unsafety_doc.rs:96:5 + | +LL | pub fn documented() -> Self { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: safe function's docs have unnecessary `# Safety` section + --> $DIR/unnecessary_unsafety_doc.rs:123:9 + | +LL | pub fn drive() { + | ^^^^^^^^^^^^^^ +... +LL | very_safe!(); + | ------------ in this macro invocation + | + = note: this error originates in the macro `very_safe` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for safe trait have unnecessary `# Safety` section + --> $DIR/unnecessary_unsafety_doc.rs:147:1 + | +LL | pub trait DocumentedSafeTraitWithImplementationHeader { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index c223b5bc711b..0a8e7b34dfa4 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -3,7 +3,7 @@ #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,13 @@ fn main() { if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +#[clippy::msrv = "1.52"] +fn msrv_1_52() { + if let [1] | [52] = [0] {} +} + +#[clippy::msrv = "1.53"] +fn msrv_1_53() { + if let [1 | 53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 04cd11036e4e..2c454adfe89d 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -3,7 +3,7 @@ #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] #![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] -#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] +#![allow(unreachable_patterns, irrefutable_let_patterns, unused)] fn main() { // Should be ignored by this lint, as nesting requires more characters. @@ -33,3 +33,13 @@ fn main() { if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} } + +#[clippy::msrv = "1.52"] +fn msrv_1_52() { + if let [1] | [52] = [0] {} +} + +#[clippy::msrv = "1.53"] +fn msrv_1_53() { + if let [1] | [53] = [0] {} +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index 453c66cbba8f..a1f193db555a 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -175,5 +175,16 @@ help: nest the patterns LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | ~~~~~~~~~~~~~~~~~ -error: aborting due to 16 previous errors +error: unnested or-patterns + --> $DIR/unnested_or_patterns.rs:44:12 + | +LL | if let [1] | [53] = [0] {} + | ^^^^^^^^^^ + | +help: nest the patterns + | +LL | if let [1 | 53] = [0] {} + | ~~~~~~~~ + +error: aborting due to 17 previous errors diff --git a/tests/ui/unused_format_specs.fixed b/tests/ui/unused_format_specs.fixed new file mode 100644 index 000000000000..2930722b42d9 --- /dev/null +++ b/tests/ui/unused_format_specs.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{}", 1.0); + println!("{f} {f:?}"); + + println!("{}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.rs b/tests/ui/unused_format_specs.rs new file mode 100644 index 000000000000..ee192a000d4b --- /dev/null +++ b/tests/ui/unused_format_specs.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +fn main() { + let f = 1.0f64; + println!("{:.}", 1.0); + println!("{f:.} {f:.?}"); + + println!("{:.}", 1); +} + +fn should_not_lint() { + let f = 1.0f64; + println!("{:.1}", 1.0); + println!("{f:.w$} {f:.*?}", 3, w = 2); +} diff --git a/tests/ui/unused_format_specs.stderr b/tests/ui/unused_format_specs.stderr new file mode 100644 index 000000000000..7231c17e74c1 --- /dev/null +++ b/tests/ui/unused_format_specs.stderr @@ -0,0 +1,54 @@ +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:8:17 + | +LL | println!("{:.}", 1.0); + | ^ + | + = note: a precision specifier is not required to format floats + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: remove the `.` + | +LL - println!("{:.}", 1.0); +LL + println!("{}", 1.0); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:18 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f} {f:.?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:9:24 + | +LL | println!("{f:.} {f:.?}"); + | ^ + | + = note: a precision specifier is not required to format floats +help: remove the `.` + | +LL - println!("{f:.} {f:.?}"); +LL + println!("{f:.} {f:?}"); + | + +error: empty precision specifier has no effect + --> $DIR/unused_format_specs.rs:11:17 + | +LL | println!("{:.}", 1); + | ^ + | +help: remove the `.` + | +LL - println!("{:.}", 1); +LL + println!("{}", 1); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unused_format_specs_unfixable.rs b/tests/ui/unused_format_specs_unfixable.rs new file mode 100644 index 000000000000..78601a3483d3 --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.rs @@ -0,0 +1,30 @@ +#![warn(clippy::unused_format_specs)] +#![allow(unused)] + +macro_rules! format_args_from_macro { + () => { + format_args!("from macro") + }; +} + +fn main() { + // prints `.`, not ` .` + println!("{:5}.", format_args!("")); + //prints `abcde`, not `abc` + println!("{:.3}", format_args!("abcde")); + + println!("{:5}.", format_args_from_macro!()); + + let args = format_args!(""); + println!("{args:5}"); +} + +fn should_not_lint() { + println!("{}", format_args!("")); + // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use + // debug formatting so allow it + println!("{:?}", format_args!("")); + + let args = format_args!(""); + println!("{args}"); +} diff --git a/tests/ui/unused_format_specs_unfixable.stderr b/tests/ui/unused_format_specs_unfixable.stderr new file mode 100644 index 000000000000..9f1890282e6a --- /dev/null +++ b/tests/ui/unused_format_specs_unfixable.stderr @@ -0,0 +1,69 @@ +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:12:15 + | +LL | println!("{:5}.", format_args!("")); + | ^^^^ + | + = note: `-D clippy::unused-format-specs` implied by `-D warnings` +help: for the width to apply consider using `format!()` + | +LL | println!("{:5}.", format!("")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args!("")); +LL + println!("{}.", format_args!("")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:14:15 + | +LL | println!("{:.3}", format_args!("abcde")); + | ^^^^^ + | +help: for the precision to apply consider using `format!()` + | +LL | println!("{:.3}", format!("abcde")); + | ~~~~~~ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:.3}", format_args!("abcde")); +LL + println!("{}", format_args!("abcde")); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:16:15 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:16:17 + | +LL | println!("{:5}.", format_args_from_macro!()); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{:5}.", format_args_from_macro!()); +LL + println!("{}.", format_args_from_macro!()); + | + +error: format specifiers have no effect on `format_args!()` + --> $DIR/unused_format_specs_unfixable.rs:19:15 + | +LL | println!("{args:5}"); + | ^^^^^^^^ + | +help: for the width to apply consider using `format!()` + --> $DIR/unused_format_specs_unfixable.rs:19:21 + | +LL | println!("{args:5}"); + | ^ +help: if the current behavior is intentional, remove the format specifiers + | +LL - println!("{args:5}"); +LL + println!("{args}"); + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index 4b059558173f..8d3e094b7596 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -63,6 +63,14 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +fn is_ok_err(s: &mut T) { + s.write(b"ok").is_ok(); + s.write(b"err").is_err(); + let mut buf = [0u8; 0]; + s.read(&mut buf).is_ok(); + s.read(&mut buf).is_err(); +} + async fn bad_async_write(w: &mut W) { w.write(b"hello world").await.unwrap(); } diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 7ba7e09c0f0d..0865c5213f68 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -82,13 +82,45 @@ LL | | .expect("error"); error: written amount is not handled --> $DIR/unused_io_amount.rs:67:5 | +LL | s.write(b"ok").is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:68:5 + | +LL | s.write(b"err").is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:70:5 + | +LL | s.read(&mut buf).is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:71:5 + | +LL | s.read(&mut buf).is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:75:5 + | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:72:5 + --> $DIR/unused_io_amount.rs:80:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | r.read(&mut buf[..]).await.unwrap(); = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:85:9 + --> $DIR/unused_io_amount.rs:93:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | w.write(b"hello world").await?; = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:93:9 + --> $DIR/unused_io_amount.rs:101:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:101:5 + --> $DIR/unused_io_amount.rs:109:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:106:5 + --> $DIR/unused_io_amount.rs:114:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed index 54f85806ac3b..f6f734c05ed5 100644 --- a/tests/ui/unused_rounding.fixed +++ b/tests/ui/unused_rounding.fixed @@ -6,4 +6,12 @@ fn main() { let _ = 1.0f64; let _ = 1.00f32; let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32; + + let _ = 3_3.0_0_f32; + let _ = 3_3.0_1_f64.round(); } diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs index 8d007bc4a1dc..a0267d8144aa 100644 --- a/tests/ui/unused_rounding.rs +++ b/tests/ui/unused_rounding.rs @@ -6,4 +6,12 @@ fn main() { let _ = 1.0f64.floor(); let _ = 1.00f32.round(); let _ = 2e-54f64.floor(); + + // issue9866 + let _ = 3.3_f32.round(); + let _ = 3.3_f64.round(); + let _ = 3.0_f32.round(); + + let _ = 3_3.0_0_f32.round(); + let _ = 3_3.0_1_f64.round(); } diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr index 6cfb02e04028..b867996fe576 100644 --- a/tests/ui/unused_rounding.stderr +++ b/tests/ui/unused_rounding.stderr @@ -18,5 +18,17 @@ error: used the `round` method with a whole number float LL | let _ = 1.00f32.round(); | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` -error: aborting due to 3 previous errors +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:13:13 + | +LL | let _ = 3.0_f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32` + +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:15:13 + | +LL | let _ = 3_3.0_0_f32.round(); + | ^^^^^^^^^^^^^^^^^^^ help: remove the `round` method call: `3_3.0_0_f32` + +error: aborting due to 5 previous errors diff --git a/tests/ui/unused_self.rs b/tests/ui/unused_self.rs index 92e8e1dba69d..55bd5607185c 100644 --- a/tests/ui/unused_self.rs +++ b/tests/ui/unused_self.rs @@ -60,6 +60,16 @@ mod unused_self_allow { // shouldn't trigger for public methods pub fn unused_self_move(self) {} } + + pub struct E; + + impl E { + // shouldn't trigger if body contains todo!() + pub fn unused_self_todo(self) { + let x = 42; + todo!() + } + } } pub use unused_self_allow::D; diff --git a/tests/ui/unused_self.stderr b/tests/ui/unused_self.stderr index 23186122a9af..919f9b6efdab 100644 --- a/tests/ui/unused_self.stderr +++ b/tests/ui/unused_self.stderr @@ -4,7 +4,7 @@ error: unused `self` argument LL | fn unused_self_move(self) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function = note: `-D clippy::unused-self` implied by `-D warnings` error: unused `self` argument @@ -13,7 +13,7 @@ error: unused `self` argument LL | fn unused_self_ref(&self) {} | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:13:32 @@ -21,7 +21,7 @@ error: unused `self` argument LL | fn unused_self_mut_ref(&mut self) {} | ^^^^^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:14:32 @@ -29,7 +29,7 @@ error: unused `self` argument LL | fn unused_self_pin_ref(self: Pin<&Self>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:15:36 @@ -37,7 +37,7 @@ error: unused `self` argument LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:16:35 @@ -45,7 +45,7 @@ error: unused `self` argument LL | fn unused_self_pin_nested(self: Pin>) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:17:28 @@ -53,7 +53,7 @@ error: unused `self` argument LL | fn unused_self_box(self: Box) {} | ^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:18:40 @@ -61,7 +61,7 @@ error: unused `self` argument LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 { | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: unused `self` argument --> $DIR/unused_self.rs:21:37 @@ -69,7 +69,7 @@ error: unused `self` argument LL | fn unused_self_class_method(&self) { | ^^^^^ | - = help: consider refactoring to a associated function + = help: consider refactoring to an associated function error: aborting due to 9 previous errors diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 7bb43cf7ae82..3dd640b86f0b 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 21073fb802ad..bddecf06fb76 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -7,6 +7,7 @@ // test of the JSON error format. #![feature(custom_inner_attributes)] +#![feature(closure_lifetime_binder)] #![rustfmt::skip] #![deny(clippy::unused_unit)] @@ -87,3 +88,9 @@ fn macro_expr() { } e!() } + +mod issue9748 { + fn main() { + let _ = for<'a> |_: &'a u32| -> () {}; + } +} diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 0d2cb77855be..ce06738cfe47 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,119 +1,119 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:58 + --> $DIR/unused_unit.rs:20:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here - --> $DIR/unused_unit.rs:12:9 + --> $DIR/unused_unit.rs:13:9 | LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:28 + --> $DIR/unused_unit.rs:20:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:18 + --> $DIR/unused_unit.rs:21:18 | LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:21:26 + --> $DIR/unused_unit.rs:22:26 | LL | let _y: &dyn Fn() -> () = &f; | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:28:18 + --> $DIR/unused_unit.rs:29:18 | LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:29:9 + --> $DIR/unused_unit.rs:30:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:34:29 + --> $DIR/unused_unit.rs:35:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:19 + --> $DIR/unused_unit.rs:37:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:37:16 + --> $DIR/unused_unit.rs:38:16 | LL | H: Fn() -> (); | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:41:29 + --> $DIR/unused_unit.rs:42:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:19 + --> $DIR/unused_unit.rs:44:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:44:16 + --> $DIR/unused_unit.rs:45:16 | LL | H: Fn() -> () {} | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:47:17 + --> $DIR/unused_unit.rs:48:17 | LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:47:26 + --> $DIR/unused_unit.rs:48:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:57:14 + --> $DIR/unused_unit.rs:58:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:59:11 + --> $DIR/unused_unit.rs:60:11 | LL | return(); | ^^ help: remove the `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:76:10 + --> $DIR/unused_unit.rs:77:10 | LL | fn test()->(){} | ^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:79:11 + --> $DIR/unused_unit.rs:80:11 | LL | fn test2() ->(){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:82:11 + --> $DIR/unused_unit.rs:83:11 | LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr index e88d580f7bd2..d49bf2b32283 100644 --- a/tests/ui/unwrap.stderr +++ b/tests/ui/unwrap.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap.rs:5:13 | LL | let _ = opt.unwrap(); @@ -7,7 +7,7 @@ LL | let _ = opt.unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap.rs:10:13 | LL | let _ = res.unwrap(); @@ -15,7 +15,7 @@ LL | let _ = res.unwrap(); | = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap.rs:11:13 | LL | let _ = res.unwrap_err(); diff --git a/tests/ui/unwrap_expect_used.stderr b/tests/ui/unwrap_expect_used.stderr index 211d2be18342..fe4ecef11453 100644 --- a/tests/ui/unwrap_expect_used.stderr +++ b/tests/ui/unwrap_expect_used.stderr @@ -1,4 +1,4 @@ -error: used `unwrap()` on `an Option` value +error: used `unwrap()` on an `Option` value --> $DIR/unwrap_expect_used.rs:23:5 | LL | Some(3).unwrap(); @@ -7,7 +7,7 @@ LL | Some(3).unwrap(); = help: if this value is `None`, it will panic = note: `-D clippy::unwrap-used` implied by `-D warnings` -error: used `expect()` on `an Option` value +error: used `expect()` on an `Option` value --> $DIR/unwrap_expect_used.rs:24:5 | LL | Some(3).expect("Hello world!"); @@ -16,7 +16,7 @@ LL | Some(3).expect("Hello world!"); = help: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` -error: used `unwrap()` on `a Result` value +error: used `unwrap()` on a `Result` value --> $DIR/unwrap_expect_used.rs:31:5 | LL | a.unwrap(); @@ -24,7 +24,7 @@ LL | a.unwrap(); | = help: if this value is an `Err`, it will panic -error: used `expect()` on `a Result` value +error: used `expect()` on a `Result` value --> $DIR/unwrap_expect_used.rs:32:5 | LL | a.expect("Hello world!"); @@ -32,7 +32,7 @@ LL | a.expect("Hello world!"); | = help: if this value is an `Err`, it will panic -error: used `unwrap_err()` on `a Result` value +error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:33:5 | LL | a.unwrap_err(); @@ -40,7 +40,7 @@ LL | a.unwrap_err(); | = help: if this value is an `Ok`, it will panic -error: used `expect_err()` on `a Result` value +error: used `expect_err()` on a `Result` value --> $DIR/unwrap_expect_used.rs:34:5 | LL | a.expect_err("Hello error!"); diff --git a/tests/ui/unwrap_or.rs b/tests/ui/unwrap_or.rs index bfb41e439473..a0c003f5b1ea 100644 --- a/tests/ui/unwrap_or.rs +++ b/tests/ui/unwrap_or.rs @@ -1,4 +1,4 @@ -#![warn(clippy::all)] +#![warn(clippy::all, clippy::or_fun_call)] fn main() { let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 37986187da17..0a6166571ebe 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -617,3 +617,33 @@ mod issue6902 { Bar = 1, } } + +#[clippy::msrv = "1.36"] +fn msrv_1_36() { + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +#[clippy::msrv = "1.37"] +fn msrv_1_37() { + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + Self::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 1b2b3337c92e..39c2b431f7fb 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -617,3 +617,33 @@ mod issue6902 { Bar = 1, } } + +#[clippy::msrv = "1.36"] +fn msrv_1_36() { + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} + +#[clippy::msrv = "1.37"] +fn msrv_1_37() { + enum E { + A, + } + + impl E { + fn foo(self) { + match self { + E::A => {}, + } + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index f06bb959b3bd..48364c40c3b2 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -246,5 +246,11 @@ error: unnecessary structure name repetition LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 41 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:645:17 + | +LL | E::A => {}, + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 42 previous errors diff --git a/tests/ui/use_self_trait.fixed b/tests/ui/use_self_trait.fixed index 9bcd692fb351..4e779308d024 100644 --- a/tests/ui/use_self_trait.fixed +++ b/tests/ui/use_self_trait.fixed @@ -47,8 +47,7 @@ impl Mul for Bad { impl Clone for Bad { fn clone(&self) -> Self { - // FIXME: applicable here - Bad + Self } } @@ -112,4 +111,42 @@ impl NameTrait for u8 { } } +mod impl_in_macro { + macro_rules! parse_ip_impl { + // minimized from serde=1.0.118 + ($ty:ty) => { + impl FooTrait for $ty { + fn new() -> Self { + <$ty>::bar() + } + } + }; + } + + struct Foo; + + trait FooTrait { + fn new() -> Self; + } + + impl Foo { + fn bar() -> Self { + Self + } + } + parse_ip_impl!(Foo); // Should not lint +} + +mod full_path_replacement { + trait Error { + fn custom(_msg: T) -> Self; + } + + impl Error for std::fmt::Error { + fn custom(_msg: T) -> Self { + Self // Should lint + } + } +} + fn main() {} diff --git a/tests/ui/use_self_trait.rs b/tests/ui/use_self_trait.rs index de305d40f330..325dc73b21ea 100644 --- a/tests/ui/use_self_trait.rs +++ b/tests/ui/use_self_trait.rs @@ -47,7 +47,6 @@ impl Mul for Bad { impl Clone for Bad { fn clone(&self) -> Self { - // FIXME: applicable here Bad } } @@ -112,4 +111,42 @@ impl NameTrait for u8 { } } +mod impl_in_macro { + macro_rules! parse_ip_impl { + // minimized from serde=1.0.118 + ($ty:ty) => { + impl FooTrait for $ty { + fn new() -> Self { + <$ty>::bar() + } + } + }; + } + + struct Foo; + + trait FooTrait { + fn new() -> Self; + } + + impl Foo { + fn bar() -> Self { + Self + } + } + parse_ip_impl!(Foo); // Should not lint +} + +mod full_path_replacement { + trait Error { + fn custom(_msg: T) -> Self; + } + + impl Error for std::fmt::Error { + fn custom(_msg: T) -> Self { + std::fmt::Error // Should lint + } + } +} + fn main() {} diff --git a/tests/ui/use_self_trait.stderr b/tests/ui/use_self_trait.stderr index 55af3ff2a93d..090729b9c3d5 100644 --- a/tests/ui/use_self_trait.stderr +++ b/tests/ui/use_self_trait.stderr @@ -84,5 +84,17 @@ error: unnecessary structure name repetition LL | fn mul(self, rhs: Bad) -> Bad { | ^^^ help: use the applicable keyword: `Self` -error: aborting due to 14 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:50:9 + | +LL | Bad + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:147:13 + | +LL | std::fmt::Error // Should lint + | ^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 16 previous errors diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index c23231a99e9f..871e4fb5c3a9 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #![allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #![allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 7a7b198ea607..cb50736ba395 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -1,6 +1,7 @@ // run-rustfix // aux-build:proc_macro_derive.rs +#![allow(unused)] #![warn(clippy::useless_attribute)] #![warn(unreachable_pub)] #![feature(rustc_private)] @@ -16,6 +17,13 @@ extern crate rustc_middle; #[macro_use] extern crate proc_macro_derive; +fn test_indented_attr() { + #[allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::::default(); +} + // don't lint on unused_import for `use` items #[allow(unused_imports)] use std::collections; @@ -63,13 +71,16 @@ mod c { pub(crate) struct S; } -fn test_indented_attr() { - #[allow(clippy::almost_swapped)] - use std::collections::HashSet; - - let _ = HashSet::::default(); +// https://github.com/rust-lang/rust-clippy/issues/7511 +pub mod split { + #[allow(clippy::module_name_repetitions)] + pub use regex::SplitN; } +// https://github.com/rust-lang/rust-clippy/issues/8768 +#[allow(clippy::single_component_path_imports)] +use regex; + fn main() { test_indented_attr(); } diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 255d28763553..a7ea0df22945 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -1,5 +1,5 @@ error: useless lint attribute - --> $DIR/useless_attribute.rs:8:1 + --> $DIR/useless_attribute.rs:9:1 | LL | #[allow(dead_code)] | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]` @@ -7,13 +7,13 @@ LL | #[allow(dead_code)] = note: `-D clippy::useless-attribute` implied by `-D warnings` error: useless lint attribute - --> $DIR/useless_attribute.rs:9:1 + --> $DIR/useless_attribute.rs:10:1 | LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:67:5 + --> $DIR/useless_attribute.rs:21:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 70ff08f36551..94b206d8e58f 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS; + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index f2444a8f436b..c7ae927941bf 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -33,12 +33,71 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } -fn test_issue_5833() -> Result<(), ()> { +fn dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr() { let text = "foo\r\nbar\n\nbaz\n"; let lines = text.lines(); if Some("ok") == lines.into_iter().next() {} +} - Ok(()) +fn lint_into_iter_on_mutable_local_implementing_iterator_in_expr() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator() { + let text = "foo\r\nbar\n\nbaz\n"; + let mut lines = text.lines().into_iter(); + if Some("ok") == lines.next() {} +} + +fn lint_into_iter_on_expr_implementing_iterator_2() { + let text = "foo\r\nbar\n\nbaz\n"; + if Some("ok") == text.lines().into_iter().next() {} +} + +#[allow(const_item_mutation)] +fn lint_into_iter_on_const_implementing_iterator() { + const NUMBERS: std::ops::Range = 0..10; + let _ = NUMBERS.into_iter().next(); +} + +fn lint_into_iter_on_const_implementing_iterator_2() { + const NUMBERS: std::ops::Range = 0..10; + let mut n = NUMBERS.into_iter(); + n.next(); +} + +#[derive(Clone, Copy)] +struct CopiableCounter { + counter: u32, +} + +impl Iterator for CopiableCounter { + type Item = u32; + + fn next(&mut self) -> Option { + self.counter = self.counter.wrapping_add(1); + Some(self.counter) + } +} + +fn dont_lint_into_iter_on_copy_iter() { + let mut c = CopiableCounter { counter: 0 }; + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.into_iter().next(), Some(1)); + assert_eq!(c.next(), Some(1)); + assert_eq!(c.next(), Some(2)); +} + +fn dont_lint_into_iter_on_static_copy_iter() { + static mut C: CopiableCounter = CopiableCounter { counter: 0 }; + unsafe { + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.into_iter().next(), Some(1)); + assert_eq!(C.next(), Some(1)); + assert_eq!(C.next(), Some(2)); + } } fn main() { @@ -46,7 +105,15 @@ fn main() { test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); - test_issue_5833().unwrap(); + + dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); + lint_into_iter_on_expr_implementing_iterator(); + lint_into_iter_on_expr_implementing_iterator_2(); + lint_into_iter_on_const_implementing_iterator(); + lint_into_iter_on_const_implementing_iterator_2(); + dont_lint_into_iter_on_copy_iter(); + dont_lint_into_iter_on_static_copy_iter(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 65ee3807fa9d..be067c6843ac 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -22,71 +22,101 @@ error: useless conversion to the same type: `i32` LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:45:22 + | +LL | if Some("ok") == lines.into_iter().next() {} + | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:50:21 + | +LL | let mut lines = text.lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::str::Lines<'_>` + --> $DIR/useless_conversion.rs:56:22 + | +LL | if Some("ok") == text.lines().into_iter().next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:62:13 + | +LL | let _ = NUMBERS.into_iter().next(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + +error: useless conversion to the same type: `std::ops::Range` + --> $DIR/useless_conversion.rs:67:17 + | +LL | let mut n = NUMBERS.into_iter(); + | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` + error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:61:21 + --> $DIR/useless_conversion.rs:128:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:62:21 + --> $DIR/useless_conversion.rs:129:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:63:13 + --> $DIR/useless_conversion.rs:130:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:64:13 + --> $DIR/useless_conversion.rs:131:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> $DIR/useless_conversion.rs:65:13 + --> $DIR/useless_conversion.rs:132:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> $DIR/useless_conversion.rs:66:13 + --> $DIR/useless_conversion.rs:133:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:67:21 + --> $DIR/useless_conversion.rs:134:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:72:13 + --> $DIR/useless_conversion.rs:139:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:78:23 + --> $DIR/useless_conversion.rs:145:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> $DIR/useless_conversion.rs:80:13 + --> $DIR/useless_conversion.rs:147:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> $DIR/useless_conversion.rs:82:13 + --> $DIR/useless_conversion.rs:149:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` -error: aborting due to 14 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 23607497841e..293bf75a7176 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -96,7 +96,7 @@ fn main() { } match Enum::A { Enum::A => (), - Enum::B | _ => (), + Enum::B | Enum::__Private => (), } } } diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index efecc9576cc7..30d29aa4e77a 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -34,11 +34,11 @@ error: wildcard matches known variants and will also match future added variants LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` -error: wildcard matches known variants and will also match future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), - | ^ help: try this: `Enum::B | _` + | ^ help: try this: `Enum::B | Enum::__Private` error: aborting due to 6 previous errors diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ef55f1c31a88..0baec6f0b641 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index b81285142069..db591d56ab4d 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -5,7 +5,6 @@ // the 2015 edition here is needed because edition 2018 changed the module system // (see https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html) which means the lint // no longer detects some of the cases starting with Rust 2018. -// FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] #![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 626c1754fc82..6b469cdfc444 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:16:5 + --> $DIR/wildcard_imports.rs:15:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:20:5 + --> $DIR/wildcard_imports.rs:19:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:24:5 + --> $DIR/wildcard_imports.rs:23:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:95:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:102:75 + --> $DIR/wildcard_imports.rs:101:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:13 + --> $DIR/wildcard_imports.rs:102:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:20 + --> $DIR/wildcard_imports.rs:113:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:30 + --> $DIR/wildcard_imports.rs:113:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:121:13 + --> $DIR/wildcard_imports.rs:120:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:150:9 + --> $DIR/wildcard_imports.rs:149:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:159:9 + --> $DIR/wildcard_imports.rs:158:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:171:13 + --> $DIR/wildcard_imports.rs:170:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:206:17 + --> $DIR/wildcard_imports.rs:205:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:214:13 + --> $DIR/wildcard_imports.rs:213:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:223:17 + --> $DIR/wildcard_imports.rs:222:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:232:13 + --> $DIR/wildcard_imports.rs:231:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:240:13 + --> $DIR/wildcard_imports.rs:239:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wildcard_imports_2021.edition2018.fixed b/tests/ui/wildcard_imports_2021.edition2018.fixed new file mode 100644 index 000000000000..6d534a10edcd --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2018.stderr b/tests/ui/wildcard_imports_2021.edition2018.stderr new file mode 100644 index 000000000000..acca9f651b47 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2018.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.edition2021.fixed b/tests/ui/wildcard_imports_2021.edition2021.fixed new file mode 100644 index 000000000000..6d534a10edcd --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.fixed @@ -0,0 +1,240 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.edition2021.stderr b/tests/ui/wildcard_imports_2021.edition2021.stderr new file mode 100644 index 000000000000..acca9f651b47 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.edition2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:13:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:14:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:16:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:21:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:91:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:97:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:98:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:109:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:116:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:145:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:154:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:155:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:166:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:201:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:209:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:218:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:227:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:235:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/wildcard_imports_2021.rs b/tests/ui/wildcard_imports_2021.rs new file mode 100644 index 000000000000..b5ed58e68136 --- /dev/null +++ b/tests/ui/wildcard_imports_2021.rs @@ -0,0 +1,241 @@ +// revisions: edition2018 edition2021 +//[edition2018] edition:2018 +//[edition2021] edition:2021 +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::*; +use crate::mod_mod::*; +use crate::multi_fn_mod::*; +use crate::struct_mod::*; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import::*; +use wildcard_imports_helper::prelude::v1::*; +use wildcard_imports_helper::*; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + let _ = PreludeModAnywhere; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::*; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + use wildcard_imports_helper::*; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::*, inner2::*}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::*; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::*; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test:: * ; + use crate:: fn_mod:: + *; + + exported(); + foo(); +} + +mod super_imports { + fn foofoo() {} + + mod should_be_replaced { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod test_should_pass_inside_function { + fn with_super_inside_function() { + use super::*; + let _ = foofoo(); + } + } + + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { + use crate::super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super_should_be_replaced { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit_should_be_replaced { + use super::super::super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } + + mod attestation_should_be_replaced { + use super::*; + + fn with_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports_2021.stderr b/tests/ui/wildcard_imports_2021.stderr new file mode 100644 index 000000000000..92f6d31530fa --- /dev/null +++ b/tests/ui/wildcard_imports_2021.stderr @@ -0,0 +1,132 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:9:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:10:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:11:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:12:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:15:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:17:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:87:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:93:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:94:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:105:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:112:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:141:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:150:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:151:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:162:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:197:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:205:13 + | +LL | use crate::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:214:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:223:13 + | +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports_2021.rs:231:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: aborting due to 21 previous errors + diff --git a/tests/ui/zero_ptr_no_std.fixed b/tests/ui/zero_ptr_no_std.fixed new file mode 100644 index 000000000000..8906c776977a --- /dev/null +++ b/tests/ui/zero_ptr_no_std.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#![feature(lang_items, start, libc)] +#![no_std] +#![deny(clippy::zero_ptr)] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _ = core::ptr::null::(); + let _ = core::ptr::null_mut::(); + let _: *const u8 = core::ptr::null(); + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.rs b/tests/ui/zero_ptr_no_std.rs new file mode 100644 index 000000000000..379c1b18d299 --- /dev/null +++ b/tests/ui/zero_ptr_no_std.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#![feature(lang_items, start, libc)] +#![no_std] +#![deny(clippy::zero_ptr)] + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let _ = 0 as *const usize; + let _ = 0 as *mut f64; + let _: *const u8 = 0 as *const _; + 0 +} + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/zero_ptr_no_std.stderr b/tests/ui/zero_ptr_no_std.stderr new file mode 100644 index 000000000000..d92bb4a6528d --- /dev/null +++ b/tests/ui/zero_ptr_no_std.stderr @@ -0,0 +1,26 @@ +error: `0 as *const _` detected + --> $DIR/zero_ptr_no_std.rs:9:13 + | +LL | let _ = 0 as *const usize; + | ^^^^^^^^^^^^^^^^^ help: try: `core::ptr::null::()` + | +note: the lint level is defined here + --> $DIR/zero_ptr_no_std.rs:5:9 + | +LL | #![deny(clippy::zero_ptr)] + | ^^^^^^^^^^^^^^^^ + +error: `0 as *mut _` detected + --> $DIR/zero_ptr_no_std.rs:10:13 + | +LL | let _ = 0 as *mut f64; + | ^^^^^^^^^^^^^ help: try: `core::ptr::null_mut::()` + +error: `0 as *const _` detected + --> $DIR/zero_ptr_no_std.rs:11:24 + | +LL | let _: *const u8 = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `core::ptr::null()` + +error: aborting due to 3 previous errors + diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index a6d8d0307ce5..c721e9969c9a 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -2,11 +2,10 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::single_match_else)] -use rustc_tools_util::VersionInfo; use std::fs; #[test] -fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { +fn consistent_clippy_crate_versions() { fn read_version(path: &str) -> String { let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{path}`: {e:?}")); contents @@ -24,11 +23,16 @@ fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { } let clippy_version = read_version("Cargo.toml"); - let clippy_lints_version = read_version("clippy_lints/Cargo.toml"); - let clippy_utils_version = read_version("clippy_utils/Cargo.toml"); - assert_eq!(clippy_version, clippy_lints_version); - assert_eq!(clippy_version, clippy_utils_version); + let paths = [ + "declare_clippy_lint/Cargo.toml", + "clippy_lints/Cargo.toml", + "clippy_utils/Cargo.toml", + ]; + + for path in paths { + assert_eq!(clippy_version, read_version(path), "{path} version differs"); + } } #[test] diff --git a/triagebot.toml b/triagebot.toml index 80c30393832c..6f50ef932e11 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,12 +1,29 @@ [relabel] allow-unauthenticated = [ "A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*", - "good-first-issue" + "good-first-issue", "beta-nominated" ] -[assign] - # Allows shortcuts like `@rustbot ready` # # See https://github.com/rust-lang/triagebot/wiki/Shortcuts [shortcut] + +[autolabel."S-waiting-on-review"] +new_pr = true + +[assign] +contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" + +[assign.owners] +"/.github" = ["@flip1995"] +"*" = [ + "@flip1995", + "@Manishearth", + "@llogiq", + "@giraffate", + "@xFrednet", + "@Alexendoo", + "@dswij", + "@Jarcho", +]