--- /dev/null
+name: Clippy Test (bors)
+
+on:
+ push:
+ branches:
+ - auto
+ - try
+
+env:
+ RUST_BACKTRACE: 1
+ CARGO_TARGET_DIR: '${{ github.workspace }}/target'
+ NO_FMT_TEST: 1
+ CARGO_INCREMENTAL: 0
+ CARGO_UNSTABLE_SPARSE_REGISTRY: true
+
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ changelog:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+ with:
+ github_token: "${{ secrets.github_token }}"
+
+ - name: Checkout
+ uses: actions/checkout@v3.0.2
+ with:
+ ref: ${{ github.ref }}
+
+ # Run
+ - name: Check Changelog
+ run: |
+ MESSAGE=$(git log --format=%B -n 1)
+ PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
+ body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
+ python -c "import sys, json; print(json.load(sys.stdin)['body'])")
+ output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
+ echo "ERROR: PR body must contain 'changelog: ...'"
+ exit 1
+ }
+ if [[ "$output" = "none" ]]; then
+ echo "WARNING: changelog is 'none'"
+ else
+ echo "changelog: $output"
+ fi
+ env:
+ PYTHONIOENCODING: 'utf-8'
+ base:
+ needs: changelog
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+ host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc]
+ exclude:
+ - os: ubuntu-latest
+ host: x86_64-apple-darwin
+ - os: ubuntu-latest
+ host: x86_64-pc-windows-msvc
+ - os: macos-latest
+ host: x86_64-unknown-linux-gnu
+ - os: macos-latest
+ host: i686-unknown-linux-gnu
+ - os: macos-latest
+ host: x86_64-pc-windows-msvc
+ - os: windows-latest
+ host: x86_64-unknown-linux-gnu
+ - os: windows-latest
+ host: i686-unknown-linux-gnu
+ - os: windows-latest
+ host: x86_64-apple-darwin
+
+ runs-on: ${{ matrix.os }}
+
+ # NOTE: If you modify this job, make sure you copy the changes to clippy.yml
+ steps:
+ # Setup
+ - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+ with:
+ github_token: "${{ secrets.github_token }}"
+
+ - name: Checkout
+ uses: actions/checkout@v3.0.2
+
+ - name: Install toolchain
+ run: rustup show active-toolchain
+
+ # Run
+ - name: Set LD_LIBRARY_PATH (Linux)
+ if: runner.os == 'Linux'
+ run: |
+ SYSROOT=$(rustc --print sysroot)
+ echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
+ - name: Link rustc dylib (MacOS)
+ if: runner.os == 'macOS'
+ run: |
+ SYSROOT=$(rustc --print sysroot)
+ sudo mkdir -p /usr/local/lib
+ sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \;
+ - name: Set PATH (Windows)
+ if: runner.os == 'Windows'
+ run: |
+ SYSROOT=$(rustc --print sysroot)
+ echo "$SYSROOT/bin" >> $GITHUB_PATH
+
+ - name: Build
+ run: cargo build --features deny-warnings,internal
+
+ - name: Test
+ if: runner.os == 'Linux'
+ run: cargo test --features deny-warnings,internal
+
+ - name: Test
+ if: runner.os != 'Linux'
+ run: cargo test --features deny-warnings,internal -- --skip dogfood
+
+ - name: Test clippy_lints
+ run: cargo test --features deny-warnings,internal
+ working-directory: clippy_lints
+
+ - name: Test clippy_utils
+ run: cargo test --features deny-warnings,internal
+ working-directory: clippy_utils
+
+ - name: Test rustc_tools_util
+ run: cargo test --features deny-warnings
+ working-directory: rustc_tools_util
+
+ - name: Test clippy_dev
+ run: cargo test --features deny-warnings
+ working-directory: clippy_dev
+
+ - name: Test clippy-driver
+ run: bash .github/driver.sh
+ env:
+ OS: ${{ runner.os }}
+
+ metadata_collection:
+ needs: changelog
+ runs-on: ubuntu-latest
+
+ steps:
+ # Setup
+ - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+ with:
+ github_token: "${{ secrets.github_token }}"
+
+ - name: Checkout
+ uses: actions/checkout@v3.0.2
+
+ - name: Install toolchain
+ run: rustup show active-toolchain
+
+ - 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
+
+ steps:
+ # Setup
+ - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+ with:
+ github_token: "${{ secrets.github_token }}"
+
+ - name: Checkout
+ uses: actions/checkout@v3.0.2
+
+ - name: Install toolchain
+ run: rustup show active-toolchain
+
+ # Run
+ - name: Build Integration Test
+ run: cargo test --test integration --features integration --no-run
+
+ # Upload
+ - name: Extract Binaries
+ run: |
+ DIR=$CARGO_TARGET_DIR/debug
+ rm $DIR/deps/integration-*.d
+ mv $DIR/deps/integration-* $DIR/integration
+ find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
+ rm -rf $CARGO_TARGET_DIR/release
+
+ - name: Upload Binaries
+ uses: actions/upload-artifact@v1
+ with:
+ name: target
+ path: target
+
+ integration:
+ needs: integration_build
+ strategy:
+ fail-fast: false
+ max-parallel: 6
+ matrix:
+ integration:
+ - 'rust-lang/cargo'
+ # FIXME: re-enable once fmt_macros is renamed in RLS
+ # - 'rust-lang/rls'
+ - 'rust-lang/chalk'
+ - 'rust-lang/rustfmt'
+ - 'Marwes/combine'
+ - 'Geal/nom'
+ - 'rust-lang/stdarch'
+ - 'serde-rs/serde'
+ # FIXME: chrono currently cannot be compiled with `--all-targets`
+ # - 'chronotope/chrono'
+ - 'hyperium/hyper'
+ - 'rust-random/rand'
+ - 'rust-lang/futures-rs'
+ - 'rust-itertools/itertools'
+ - 'rust-lang-nursery/failure'
+ - 'rust-lang/log'
+
+ runs-on: ubuntu-latest
+
+ steps:
+ # Setup
+ - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
+ with:
+ github_token: "${{ secrets.github_token }}"
+
+ - name: Checkout
+ uses: actions/checkout@v3.0.2
+
+ - name: Install toolchain
+ run: rustup show active-toolchain
+
+ # Download
+ - name: Download target dir
+ uses: actions/download-artifact@v1
+ with:
+ name: target
+ path: target
+
+ - name: Make Binaries Executable
+ run: chmod +x $CARGO_TARGET_DIR/debug/*
+
+ # Run
+ - name: Test ${{ matrix.integration }}
+ run: |
+ RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
+ $CARGO_TARGET_DIR/debug/integration
+ env:
+ INTEGRATION: ${{ matrix.integration }}
+
+ # These jobs doesn't actually test anything, but they're only used to tell
+ # bors the build completed, as there is no practical way to detect when a
+ # workflow is successful listening to webhooks only.
+ #
+ # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
+
+ end-success:
+ name: bors test finished
+ if: github.event.pusher.name == 'bors' && success()
+ runs-on: ubuntu-latest
+ needs: [changelog, base, metadata_collection, integration_build, integration]
+
+ steps:
+ - name: Mark the job as successful
+ run: exit 0
+
+ end-failure:
+ name: bors test finished
+ if: github.event.pusher.name == 'bors' && (failure() || cancelled())
+ runs-on: ubuntu-latest
+ needs: [changelog, base, metadata_collection, integration_build, integration]
+
+ steps:
+ - name: Mark the job as a failure
+ run: exit 1
--- /dev/null
- [4f142aa1...master](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...master)
+# Changelog
+
+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 / Beta / In Rust Nightly
+
- Current stable, released 2022-12-15
++[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<Self>` 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 <LINT>` 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
+
+Released 2022-09-22
+
+[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc)
+
+### New Lints
+
+* [`arithmetic_side_effects`]
+ [#9130](https://github.com/rust-lang/rust-clippy/pull/9130)
+* [`invalid_utf8_in_unchecked`]
+ [#9105](https://github.com/rust-lang/rust-clippy/pull/9105)
+* [`assertions_on_result_states`]
+ [#9225](https://github.com/rust-lang/rust-clippy/pull/9225)
+* [`manual_find`]
+ [#8649](https://github.com/rust-lang/rust-clippy/pull/8649)
+* [`manual_retain`]
+ [#8972](https://github.com/rust-lang/rust-clippy/pull/8972)
+* [`default_instead_of_iter_empty`]
+ [#8989](https://github.com/rust-lang/rust-clippy/pull/8989)
+* [`manual_rem_euclid`]
+ [#9031](https://github.com/rust-lang/rust-clippy/pull/9031)
+* [`obfuscated_if_else`]
+ [#9148](https://github.com/rust-lang/rust-clippy/pull/9148)
+* [`std_instead_of_core`]
+ [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
+* [`std_instead_of_alloc`]
+ [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
+* [`alloc_instead_of_core`]
+ [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
+* [`explicit_auto_deref`]
+ [#8355](https://github.com/rust-lang/rust-clippy/pull/8355)
+
+
+### Moves and Deprecations
+
+* Moved [`format_push_string`] to `restriction` (now allow-by-default)
+ [#9161](https://github.com/rust-lang/rust-clippy/pull/9161)
+
+### Enhancements
+
+* [`significant_drop_in_scrutinee`]: Now gives more context in the lint message
+ [#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
+* [`single_match`], [`single_match_else`]: Now catches more `Option` cases
+ [#8985](https://github.com/rust-lang/rust-clippy/pull/8985)
+* [`unused_async`]: Now works for async methods
+ [#9025](https://github.com/rust-lang/rust-clippy/pull/9025)
+* [`manual_filter_map`], [`manual_find_map`]: Now lint more expressions
+ [#8958](https://github.com/rust-lang/rust-clippy/pull/8958)
+* [`question_mark`]: Now works for simple `if let` expressions
+ [#8356](https://github.com/rust-lang/rust-clippy/pull/8356)
+* [`undocumented_unsafe_blocks`]: Now finds comments before the start of closures
+ [#9117](https://github.com/rust-lang/rust-clippy/pull/9117)
+* [`trait_duplication_in_bounds`]: Now catches duplicate bounds in where clauses
+ [#8703](https://github.com/rust-lang/rust-clippy/pull/8703)
+* [`shadow_reuse`], [`shadow_same`], [`shadow_unrelated`]: Now lint in const blocks
+ [#9124](https://github.com/rust-lang/rust-clippy/pull/9124)
+* [`slow_vector_initialization`]: Now detects cases with `vec.capacity()`
+ [#8953](https://github.com/rust-lang/rust-clippy/pull/8953)
+* [`unused_self`]: Now respects the `avoid-breaking-exported-api` config option
+ [#9199](https://github.com/rust-lang/rust-clippy/pull/9199)
+* [`box_collection`]: Now supports all std collections
+ [#9170](https://github.com/rust-lang/rust-clippy/pull/9170)
+
+### False Positive Fixes
+
+* [`significant_drop_in_scrutinee`]: Now ignores calls to `IntoIterator::into_iter`
+ [#9140](https://github.com/rust-lang/rust-clippy/pull/9140)
+* [`while_let_loop`]: Now ignores cases when the significant drop order would change
+ [#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
+* [`branches_sharing_code`]: Now ignores cases where moved variables have a significant
+ drop or variable modifications can affect the conditions
+ [#9138](https://github.com/rust-lang/rust-clippy/pull/9138)
+* [`let_underscore_lock`]: Now ignores bindings that aren't locked
+ [#8990](https://github.com/rust-lang/rust-clippy/pull/8990)
+* [`trivially_copy_pass_by_ref`]: Now tracks lifetimes and ignores cases where unsafe
+ pointers are used
+ [#8639](https://github.com/rust-lang/rust-clippy/pull/8639)
+* [`let_unit_value`]: No longer ignores `#[allow]` attributes on the value
+ [#9082](https://github.com/rust-lang/rust-clippy/pull/9082)
+* [`declare_interior_mutable_const`]: Now ignores the `thread_local!` macro
+ [#9015](https://github.com/rust-lang/rust-clippy/pull/9015)
+* [`if_same_then_else`]: Now ignores branches with `todo!` and `unimplemented!`
+ [#9006](https://github.com/rust-lang/rust-clippy/pull/9006)
+* [`enum_variant_names`]: Now ignores names with `_` prefixes
+ [#9032](https://github.com/rust-lang/rust-clippy/pull/9032)
+* [`let_unit_value`]: Now ignores cases, where the unit type is manually specified
+ [#9056](https://github.com/rust-lang/rust-clippy/pull/9056)
+* [`match_same_arms`]: Now ignores branches with `todo!`
+ [#9207](https://github.com/rust-lang/rust-clippy/pull/9207)
+* [`assign_op_pattern`]: Ignores cases that break borrowing rules
+ [#9214](https://github.com/rust-lang/rust-clippy/pull/9214)
+* [`extra_unused_lifetimes`]: No longer triggers in derive macros
+ [#9037](https://github.com/rust-lang/rust-clippy/pull/9037)
+* [`mismatching_type_param_order`]: Now ignores complicated generic parameters
+ [#9146](https://github.com/rust-lang/rust-clippy/pull/9146)
+* [`equatable_if_let`]: No longer lints in macros
+ [#9074](https://github.com/rust-lang/rust-clippy/pull/9074)
+* [`new_without_default`]: Now ignores generics and lifetime parameters on `fn new`
+ [#9115](https://github.com/rust-lang/rust-clippy/pull/9115)
+* [`needless_borrow`]: Now ignores cases that result in the execution of different traits
+ [#9096](https://github.com/rust-lang/rust-clippy/pull/9096)
+* [`declare_interior_mutable_const`]: No longer triggers in thread-local initializers
+ [#9246](https://github.com/rust-lang/rust-clippy/pull/9246)
+
+### Suggestion Fixes/Improvements
+
+* [`type_repetition_in_bounds`]: The suggestion now works with maybe bounds
+ [#9132](https://github.com/rust-lang/rust-clippy/pull/9132)
+* [`transmute_ptr_to_ref`]: Now suggests `pointer::cast` when possible
+ [#8939](https://github.com/rust-lang/rust-clippy/pull/8939)
+* [`useless_format`]: Now suggests the correct variable name
+ [#9237](https://github.com/rust-lang/rust-clippy/pull/9237)
+* [`or_fun_call`]: The lint emission will now only span over the `unwrap_or` call
+ [#9144](https://github.com/rust-lang/rust-clippy/pull/9144)
+* [`neg_multiply`]: Now suggests adding parentheses around suggestion if needed
+ [#9026](https://github.com/rust-lang/rust-clippy/pull/9026)
+* [`unnecessary_lazy_evaluations`]: Now suggest for `bool::then_some` for lazy evaluation
+ [#9099](https://github.com/rust-lang/rust-clippy/pull/9099)
+* [`manual_flatten`]: Improved message for long code snippets
+ [#9156](https://github.com/rust-lang/rust-clippy/pull/9156)
+* [`explicit_counter_loop`]: The suggestion is now machine applicable
+ [#9149](https://github.com/rust-lang/rust-clippy/pull/9149)
+* [`needless_borrow`]: Now keeps parentheses around fields, when needed
+ [#9210](https://github.com/rust-lang/rust-clippy/pull/9210)
+* [`while_let_on_iterator`]: The suggestion now works in `FnOnce` closures
+ [#9134](https://github.com/rust-lang/rust-clippy/pull/9134)
+
+### ICE Fixes
+
+* Fix ICEs related to `#![feature(generic_const_exprs)]` usage
+ [#9241](https://github.com/rust-lang/rust-clippy/pull/9241)
+* Fix ICEs related to reference lints
+ [#9093](https://github.com/rust-lang/rust-clippy/pull/9093)
+* [`question_mark`]: Fix ICE on zero field tuple structs
+ [#9244](https://github.com/rust-lang/rust-clippy/pull/9244)
+
+### Documentation Improvements
+
+* [`needless_option_take`]: Now includes a "What it does" and "Why is this bad?" section.
+ [#9022](https://github.com/rust-lang/rust-clippy/pull/9022)
+
+### Others
+
+* Using `--cap-lints=allow` and only `--force-warn`ing some will now work with Clippy's driver
+ [#9036](https://github.com/rust-lang/rust-clippy/pull/9036)
+* Clippy now tries to read the `rust-version` from `Cargo.toml` to identify the
+ minimum supported rust version
+ [#8774](https://github.com/rust-lang/rust-clippy/pull/8774)
+
+## Rust 1.63
+
+Released 2022-08-11
+
+[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
+
+### New Lints
+
+* [`borrow_deref_ref`]
+ [#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
+* [`doc_link_with_quotes`]
+ [#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
+* [`no_effect_replace`]
+ [#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
+* [`rc_clone_in_vec_init`]
+ [#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
+* [`derive_partial_eq_without_eq`]
+ [#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
+* [`mismatching_type_param_order`]
+ [#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
+* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
+* [`unused_rounding`]
+ [#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
+* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
+* [`swap_ptr_to_ref`]
+ [#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
+* [`almost_complete_letter_range`]
+ [#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
+* [`needless_parens_on_range_literals`]
+ [#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
+* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
+
+### Moves and Deprecations
+
+* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
+ `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
+
+### Enhancements
+
+* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
+ [#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
+* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
+ [#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
+* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
+ option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
+* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
+ option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
+* [`disallowed_methods`]: Now also lints indirect usages
+ [#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
+* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
+ [#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
+* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
+ [#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
+* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
+ [#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
+* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
+ [#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
+* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
+ [#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
+* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
+ method chains inside `map`
+ [#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
+* [`needless_return`]: Now also lints on macro expressions in return statements
+ [#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
+* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
+ should extend the default and not replace it
+ [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
+* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
+ config should extend the default and not replace it
+ [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
+* [`never_loop`]: Now checks for `continue` in struct expression
+ [#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
+
+### False Positive Fixes
+
+* [`useless_transmute`]: No longer lints on types with erased regions
+ [#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
+* [`vec_init_then_push`]: No longer lints when further extended
+ [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
+* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
+ [#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
+* [`redundant_allocation`]: No longer lints on fat pointers that would become
+ thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
+* [`derive_partial_eq_without_eq`]:
+ * Handle differing predicates applied by `#[derive(PartialEq)]` and
+ `#[derive(Eq)]`
+ [#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
+ * No longer lints on non-public types and better handles generics
+ [#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
+* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
+ string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
+* [`branches_sharing_code`]: No longer lints when using different binding names
+ [#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
+* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
+ desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
+* [`checked_conversions`]: No longer lints in `const` contexts
+ [#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
+* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
+ `T::Item` doesn't implement `IntoIterator`
+ [#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
+
+### Suggestion Fixes/Improvements
+
+* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
+ [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
+* [`manual_range_contains`]: Fix suggestion for integers with different signs
+ [#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
+* [`identity_op`]: Add parenthesis to suggestions where required
+ [#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
+* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
+ [#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
+* [`rc_clone_in_vec_init`]: Add suggestion
+ [#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
+* The "unknown field" error messages for config files now wraps the field names
+ [#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
+* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
+ [#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
+* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
+ references and not trivially clone-able
+ [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
+* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
+ `iter_mut()` or `into_iter()`
+ [#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
+
+### ICE Fixes
+
+* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
+ [#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
+* Fix ICEs on callable `static`/`const`s
+ [#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
+* [`needless_late_init`]
+ [#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
+* Fix ICE in shadow lints
+ [#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
+
+### Documentation Improvements
+
+* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
+ [#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
+* Add a *copy lint name*-button to Clippy's lint list
+ [#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
+* Display past names of renamed lints on Clippy's lint list
+ [#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
+* Add the ability to show the lint output in the lint list
+ [#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
+
+## Rust 1.62
+
+Released 2022-06-30
+
+[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
+
+### New Lints
+
+* [`large_include_file`]
+ [#8727](https://github.com/rust-lang/rust-clippy/pull/8727)
+* [`cast_abs_to_unsigned`]
+ [#8635](https://github.com/rust-lang/rust-clippy/pull/8635)
+* [`err_expect`]
+ [#8606](https://github.com/rust-lang/rust-clippy/pull/8606)
+* [`unnecessary_owned_empty_strings`]
+ [#8660](https://github.com/rust-lang/rust-clippy/pull/8660)
+* [`empty_structs_with_brackets`]
+ [#8594](https://github.com/rust-lang/rust-clippy/pull/8594)
+* [`crate_in_macro_def`]
+ [#8576](https://github.com/rust-lang/rust-clippy/pull/8576)
+* [`needless_option_take`]
+ [#8665](https://github.com/rust-lang/rust-clippy/pull/8665)
+* [`bytes_count_to_len`]
+ [#8711](https://github.com/rust-lang/rust-clippy/pull/8711)
+* [`is_digit_ascii_radix`]
+ [#8624](https://github.com/rust-lang/rust-clippy/pull/8624)
+* [`await_holding_invalid_type`]
+ [#8707](https://github.com/rust-lang/rust-clippy/pull/8707)
+* [`trim_split_whitespace`]
+ [#8575](https://github.com/rust-lang/rust-clippy/pull/8575)
+* [`pub_use`]
+ [#8670](https://github.com/rust-lang/rust-clippy/pull/8670)
+* [`format_push_string`]
+ [#8626](https://github.com/rust-lang/rust-clippy/pull/8626)
+* [`empty_drop`]
+ [#8571](https://github.com/rust-lang/rust-clippy/pull/8571)
+* [`drop_non_drop`]
+ [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
+* [`forget_non_drop`]
+ [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
+
+### Moves and Deprecations
+
+* Move [`only_used_in_recursion`] to `nursery` (now allow-by-default)
+ [#8783](https://github.com/rust-lang/rust-clippy/pull/8783)
+* Move [`stable_sort_primitive`] to `pedantic` (now allow-by-default)
+ [#8716](https://github.com/rust-lang/rust-clippy/pull/8716)
+
+### Enhancements
+
+* Remove overlap between [`manual_split_once`] and [`needless_splitn`]
+ [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
+* [`map_identity`]: Now checks for needless `map_err`
+ [#8487](https://github.com/rust-lang/rust-clippy/pull/8487)
+* [`extra_unused_lifetimes`]: Now checks for impl lifetimes
+ [#8737](https://github.com/rust-lang/rust-clippy/pull/8737)
+* [`cast_possible_truncation`]: Now catches more cases with larger shift or divide operations
+ [#8687](https://github.com/rust-lang/rust-clippy/pull/8687)
+* [`identity_op`]: Now checks for modulo expressions
+ [#8519](https://github.com/rust-lang/rust-clippy/pull/8519)
+* [`panic`]: No longer lint in constant context
+ [#8592](https://github.com/rust-lang/rust-clippy/pull/8592)
+* [`manual_split_once`]: Now lints manual iteration of `splitn`
+ [#8717](https://github.com/rust-lang/rust-clippy/pull/8717)
+* [`self_named_module_files`], [`mod_module_files`]: Now handle relative module paths
+ [#8611](https://github.com/rust-lang/rust-clippy/pull/8611)
+* [`unsound_collection_transmute`]: Now has better size and alignment checks
+ [#8648](https://github.com/rust-lang/rust-clippy/pull/8648)
+* [`unnested_or_patterns`]: Ignore cases, where the suggestion would be longer
+ [#8619](https://github.com/rust-lang/rust-clippy/pull/8619)
+
+### False Positive Fixes
+
+* [`rest_pat_in_fully_bound_structs`]: Now ignores structs marked with `#[non_exhaustive]`
+ [#8690](https://github.com/rust-lang/rust-clippy/pull/8690)
+* [`needless_late_init`]: No longer lints `if let` statements, `let mut` bindings or instances that
+ changes the drop order significantly
+ [#8617](https://github.com/rust-lang/rust-clippy/pull/8617)
+* [`unnecessary_cast`]: No longer lints to casts to aliased or non-primitive types
+ [#8596](https://github.com/rust-lang/rust-clippy/pull/8596)
+* [`init_numbered_fields`]: No longer lints type aliases
+ [#8780](https://github.com/rust-lang/rust-clippy/pull/8780)
+* [`needless_option_as_deref`]: No longer lints for `as_deref_mut` on `Option` values that can't be moved
+ [#8646](https://github.com/rust-lang/rust-clippy/pull/8646)
+* [`mistyped_literal_suffixes`]: Now ignores float literals without an exponent
+ [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
+* [`undocumented_unsafe_blocks`]: Now ignores unsafe blocks from proc-macros and works better for sub-expressions
+ [#8450](https://github.com/rust-lang/rust-clippy/pull/8450)
+* [`same_functions_in_if_condition`]: Now allows different constants, even if they have the same value
+ [#8673](https://github.com/rust-lang/rust-clippy/pull/8673)
+* [`needless_match`]: Now checks for more complex types and ignores type coercion
+ [#8549](https://github.com/rust-lang/rust-clippy/pull/8549)
+* [`assertions_on_constants`]: Now ignores constants from `cfg!` macros
+ [#8614](https://github.com/rust-lang/rust-clippy/pull/8614)
+* [`indexing_slicing`]: Fix false positives with constant indices in
+ [#8588](https://github.com/rust-lang/rust-clippy/pull/8588)
+* [`iter_with_drain`]: Now ignores iterator references
+ [#8668](https://github.com/rust-lang/rust-clippy/pull/8668)
+* [`useless_attribute`]: Now allows [`redundant_pub_crate`] on `use` items
+ [#8743](https://github.com/rust-lang/rust-clippy/pull/8743)
+* [`cast_ptr_alignment`]: Now ignores expressions, when used for unaligned reads and writes
+ [#8632](https://github.com/rust-lang/rust-clippy/pull/8632)
+* [`wrong_self_convention`]: Now allows `&mut self` and no self as arguments for `is_*` methods
+ [#8738](https://github.com/rust-lang/rust-clippy/pull/8738)
+* [`mut_from_ref`]: Only lint in unsafe code
+ [#8647](https://github.com/rust-lang/rust-clippy/pull/8647)
+* [`redundant_pub_crate`]: Now allows macro exports
+ [#8736](https://github.com/rust-lang/rust-clippy/pull/8736)
+* [`needless_match`]: Ignores cases where the else block expression is different
+ [#8700](https://github.com/rust-lang/rust-clippy/pull/8700)
+* [`transmute_int_to_char`]: Now allows transmutations in `const` code
+ [#8610](https://github.com/rust-lang/rust-clippy/pull/8610)
+* [`manual_non_exhaustive`]: Ignores cases, where the enum value is used
+ [#8645](https://github.com/rust-lang/rust-clippy/pull/8645)
+* [`redundant_closure`]: Now ignores coerced closure
+ [#8431](https://github.com/rust-lang/rust-clippy/pull/8431)
+* [`identity_op`]: Is now ignored in cases where extra brackets would be needed
+ [#8730](https://github.com/rust-lang/rust-clippy/pull/8730)
+* [`let_unit_value`]: Now ignores cases which are used for type inference
+ [#8563](https://github.com/rust-lang/rust-clippy/pull/8563)
+
+### Suggestion Fixes/Improvements
+
+* [`manual_split_once`]: Fixed incorrect suggestions for single result accesses
+ [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
+* [`bytes_nth`]: Fix typos in the diagnostic message
+ [#8403](https://github.com/rust-lang/rust-clippy/pull/8403)
+* [`mistyped_literal_suffixes`]: Now suggests the correct integer types
+ [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
+* [`unnecessary_to_owned`]: Fixed suggestion based on the configured msrv
+ [#8692](https://github.com/rust-lang/rust-clippy/pull/8692)
+* [`single_element_loop`]: Improve lint for Edition 2021 arrays
+ [#8616](https://github.com/rust-lang/rust-clippy/pull/8616)
+* [`manual_bits`]: Now includes a cast for proper type conversion, when needed
+ [#8677](https://github.com/rust-lang/rust-clippy/pull/8677)
+* [`option_map_unit_fn`], [`result_map_unit_fn`]: Fix some incorrect suggestions
+ [#8584](https://github.com/rust-lang/rust-clippy/pull/8584)
+* [`collapsible_else_if`]: Add whitespace in suggestion
+ [#8729](https://github.com/rust-lang/rust-clippy/pull/8729)
+* [`transmute_bytes_to_str`]: Now suggest `from_utf8_unchecked` in `const` context
+ [#8612](https://github.com/rust-lang/rust-clippy/pull/8612)
+* [`map_clone`]: Improve message and suggestion based on the msrv
+ [#8688](https://github.com/rust-lang/rust-clippy/pull/8688)
+* [`needless_late_init`]: Now shows the `let` statement where it was first initialized
+ [#8779](https://github.com/rust-lang/rust-clippy/pull/8779)
+
+### ICE Fixes
+
+* [`only_used_in_recursion`]
+ [#8691](https://github.com/rust-lang/rust-clippy/pull/8691)
+* [`cast_slice_different_sizes`]
+ [#8720](https://github.com/rust-lang/rust-clippy/pull/8720)
+* [`iter_overeager_cloned`]
+ [#8602](https://github.com/rust-lang/rust-clippy/pull/8602)
+* [`undocumented_unsafe_blocks`]
+ [#8686](https://github.com/rust-lang/rust-clippy/pull/8686)
+
+## Rust 1.61
+
+Released 2022-05-19
+
+[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
+
+### New Lints
+
+* [`only_used_in_recursion`]
+ [#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
+* [`cast_enum_truncation`]
+ [#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
+* [`missing_spin_loop`]
+ [#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
+* [`deref_by_slicing`]
+ [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
+* [`needless_match`]
+ [#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
+* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
+ [#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
+* [`print_in_format_impl`]
+ [#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
+* [`unnecessary_find_map`]
+ [#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
+* [`or_then_unwrap`]
+ [#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
+* [`unnecessary_join`]
+ [#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
+* [`iter_with_drain`]
+ [#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
+* [`cast_enum_constructor`]
+ [#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
+* [`cast_slice_different_sizes`]
+ [#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
+
+### Moves and Deprecations
+
+* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
+ [#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
+* Moved [`try_err`] to `restriction`
+ [#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
+* Move [`iter_with_drain`] to `nursery`
+ [#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
+* Renamed `to_string_in_display` to [`recursive_format_impl`]
+ [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
+
+### Enhancements
+
+* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
+ [#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
+* [`ptr_as_ptr`]: Now works inside macros
+ [#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
+* [`use_self`]: Now works for variants in match expressions
+ [#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
+* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
+ [#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
+* [`recursive_format_impl`]: Now checks for format calls on `self`
+ [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
+
+### False Positive Fixes
+
+* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
+ [#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
+* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
+ generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
+ [#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
+ [#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
+ [#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
+ [#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
+* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
+ lint `match` expressions with `cfg`ed arms
+ [#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
+* [`single_component_path_imports`]: No longer lint on macros
+ [#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
+* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
+ [#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
+* [`needless_borrow`]: No longer lints for method calls
+ [#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
+* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
+ [#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
+* [`default_trait_access`]: Now allows `Default::default` in update expressions
+ [#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
+
+### Suggestion Fixes/Improvements
+
+* [`redundant_slicing`]: Fixed suggestion for a method calls
+ [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
+* [`map_flatten`]: Long suggestions will now be split up into two help messages
+ [#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
+* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
+ [#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
+* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
+ [#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
+* [`search_is_some`]: More suggestions are now `MachineApplicable`
+ [#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
+
+### Documentation Improvements
+
+* [`new_without_default`]: Document `pub` requirement for the struct and fields
+ [#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
+
+## Rust 1.60
+
+Released 2022-04-07
+
+[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
+
+### New Lints
+
+* [`single_char_lifetime_names`]
+ [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
+* [`iter_overeager_cloned`]
+ [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
+* [`transmute_undefined_repr`]
+ [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
+* [`default_union_representation`]
+ [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
+* [`manual_bits`]
+ [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
+* [`borrow_as_ptr`]
+ [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
+
+### Moves and Deprecations
+
+* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
+ [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
+* Rename `ref_in_deref` to [`needless_borrow`]
+ [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
+* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
+ [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
+
+### Enhancements
+
+* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
+ [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
+* [`unused_io_amount`]: Now supports async read and write traits
+ [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
+* [`while_let_on_iterator`]: Improved detection to catch more cases
+ [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
+* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
+ [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
+* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
+ [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
+* [`map_clone`]: The suggestion takes `msrv` into account
+ [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
+* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
+ [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
+* [`disallowed_methods`]: Now works for methods on primitive types
+ [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
+* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
+ [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
+* [`needless_question_mark`]: Now works for async functions
+ [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
+* [`iter_not_returning_iterator`]: Now handles type projections
+ [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
+* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
+ [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
+* [`single_match`]: Now works for `match` statements with tuples
+ [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
+
+### False Positive Fixes
+
+* [`erasing_op`]: No longer triggers if the output type changes
+ [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
+* [`if_same_then_else`]: No longer triggers for `if let` statements
+ [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
+* [`manual_memcpy`]: No longer lints on `VecDeque`
+ [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
+* [`trait_duplication_in_bounds`]: Now takes path segments into account
+ [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
+* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
+ [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
+* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
+ [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
+* [`ptr_arg`]: No longer lint for mutable references in traits
+ [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
+* [`implicit_clone`]: No longer lints for double references
+ [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
+* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
+ [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
+* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
+ [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
+* [`enum_variant_names`]: No longer triggers for empty variant names
+ [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
+* [`redundant_closure`]: No longer lints for `Arc<T>` or `Rc<T>`
+ [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
+* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
+ [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
+* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
+ [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
+* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
+ [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
+* [`useless_format`]: Now works for implicit named arguments
+ [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
+
+### Suggestion Fixes/Improvements
+
+* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
+ [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
+* [`chars_next_cmp`]: Correctly escapes the suggestion
+ [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
+* [`explicit_write`]: Add suggestions for `write!`s with format arguments
+ [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
+* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
+ [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
+* [`or_fun_call`]: Improved suggestion display for long arguments
+ [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
+* [`unnecessary_cast`]: Now correctly includes the sign
+ [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
+* [`cmp_owned`]: No longer flips the comparison order
+ [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
+* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
+ [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
+
+### ICE Fixes
+
+* [`manual_split_once`]
+ [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
+
+### Documentation Improvements
+
+* [`map_flatten`]: Add documentation for the `Option` type
+ [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
+* Document that Clippy's driver might use a different code generation than rustc
+ [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
+* Clippy's lint list will now automatically focus the search box
+ [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
+
+### Others
+
+* Clippy now warns if we find multiple Clippy config files exist
+ [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
+
+## Rust 1.59
+
+Released 2022-02-24
+
+[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
+
+### New Lints
+
+* [`index_refutable_slice`]
+ [#7643](https://github.com/rust-lang/rust-clippy/pull/7643)
+* [`needless_splitn`]
+ [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
+* [`unnecessary_to_owned`]
+ [#7978](https://github.com/rust-lang/rust-clippy/pull/7978)
+* [`needless_late_init`]
+ [#7995](https://github.com/rust-lang/rust-clippy/pull/7995)
+* [`octal_escapes`] [#8007](https://github.com/rust-lang/rust-clippy/pull/8007)
+* [`return_self_not_must_use`]
+ [#8071](https://github.com/rust-lang/rust-clippy/pull/8071)
+* [`init_numbered_fields`]
+ [#8170](https://github.com/rust-lang/rust-clippy/pull/8170)
+
+### Moves and Deprecations
+
+* Move `if_then_panic` to `pedantic` and rename to [`manual_assert`] (now
+ allow-by-default) [#7810](https://github.com/rust-lang/rust-clippy/pull/7810)
+* Rename `disallow_type` to [`disallowed_types`] and `disallowed_method` to
+ [`disallowed_methods`]
+ [#7984](https://github.com/rust-lang/rust-clippy/pull/7984)
+* Move [`map_flatten`] to `complexity` (now warn-by-default)
+ [#8054](https://github.com/rust-lang/rust-clippy/pull/8054)
+
+### Enhancements
+
+* [`match_overlapping_arm`]: Fix false negative where after included ranges,
+ overlapping ranges weren't linted anymore
+ [#7909](https://github.com/rust-lang/rust-clippy/pull/7909)
+* [`deprecated_cfg_attr`]: Now takes the specified MSRV into account
+ [#7944](https://github.com/rust-lang/rust-clippy/pull/7944)
+* [`cast_lossless`]: Now also lints for `bool` to integer casts
+ [#7948](https://github.com/rust-lang/rust-clippy/pull/7948)
+* [`let_underscore_lock`]: Also emit lints for the `parking_lot` crate
+ [#7957](https://github.com/rust-lang/rust-clippy/pull/7957)
+* [`needless_borrow`]
+ [#7977](https://github.com/rust-lang/rust-clippy/pull/7977)
+ * Lint when a borrow is auto-dereffed more than once
+ * Lint in the trailing expression of a block for a match arm
+* [`strlen_on_c_strings`]
+ [8001](https://github.com/rust-lang/rust-clippy/pull/8001)
+ * Lint when used without a fully-qualified path
+ * Suggest removing the surrounding unsafe block when possible
+* [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s
+ [#8034](https://github.com/rust-lang/rust-clippy/pull/8034)
+* [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`,
+ `rsplit_once`, `replace`, and `replacen`
+ [#8077](https://github.com/rust-lang/rust-clippy/pull/8077)
+* [`unwrap_or_else_default`]: Now also lints on `std` constructors like
+ `Vec::new`, `HashSet::new`, and `HashMap::new`
+ [#8163](https://github.com/rust-lang/rust-clippy/pull/8163)
+* [`shadow_reuse`]: Now also lints on shadowed `if let` bindings, instead of
+ [`shadow_unrelated`]
+ [#8165](https://github.com/rust-lang/rust-clippy/pull/8165)
+
+### False Positive Fixes
+
+* [`or_fun_call`], [`unnecessary_lazy_evaluations`]: Improve heuristics, so that
+ cheap functions (e.g. calling `.len()` on a `Vec`) won't get linted anymore
+ [#7639](https://github.com/rust-lang/rust-clippy/pull/7639)
+* [`manual_split_once`]: No longer suggests code changing the original behavior
+ [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
+* Don't show [`no_effect`] or [`unnecessary_operation`] warning for unit struct
+ implementing `FnOnce`
+ [#7898](https://github.com/rust-lang/rust-clippy/pull/7898)
+* [`semicolon_if_nothing_returned`]: Fixed a bug, where the lint wrongly
+ triggered on `let-else` statements
+ [#7955](https://github.com/rust-lang/rust-clippy/pull/7955)
+* [`if_then_some_else_none`]: No longer lints if there is an early return
+ [#7980](https://github.com/rust-lang/rust-clippy/pull/7980)
+* [`needless_collect`]: No longer suggests removal of `collect` when removal
+ would create code requiring mutably borrowing a value multiple times
+ [#7982](https://github.com/rust-lang/rust-clippy/pull/7982)
+* [`shadow_same`]: Fix false positive for `async` function's params
+ [#7997](https://github.com/rust-lang/rust-clippy/pull/7997)
+* [`suboptimal_flops`]: No longer triggers in constant functions
+ [#8009](https://github.com/rust-lang/rust-clippy/pull/8009)
+* [`type_complexity`]: No longer lints on associated types in traits
+ [#8030](https://github.com/rust-lang/rust-clippy/pull/8030)
+* [`question_mark`]: No longer lints if returned object is not local
+ [#8080](https://github.com/rust-lang/rust-clippy/pull/8080)
+* [`option_if_let_else`]: No longer lint on complex sub-patterns
+ [#8086](https://github.com/rust-lang/rust-clippy/pull/8086)
+* [`blocks_in_if_conditions`]: No longer lints on empty closures
+ [#8100](https://github.com/rust-lang/rust-clippy/pull/8100)
+* [`enum_variant_names`]: No longer lint when first prefix is only a substring
+ of a camel-case word
+ [#8127](https://github.com/rust-lang/rust-clippy/pull/8127)
+* [`identity_op`]: Only lint on integral operands
+ [#8183](https://github.com/rust-lang/rust-clippy/pull/8183)
+
+### Suggestion Fixes/Improvements
+
+* [`search_is_some`]: Fix suggestion for `any()` not taking item by reference
+ [#7463](https://github.com/rust-lang/rust-clippy/pull/7463)
+* [`almost_swapped`]: Now detects if there is a `no_std` or `no_core` attribute
+ and adapts the suggestion accordingly
+ [#7877](https://github.com/rust-lang/rust-clippy/pull/7877)
+* [`redundant_pattern_matching`]: Fix suggestion for deref expressions
+ [#7949](https://github.com/rust-lang/rust-clippy/pull/7949)
+* [`explicit_counter_loop`]: Now also produces a suggestion for non-`usize`
+ types [#7950](https://github.com/rust-lang/rust-clippy/pull/7950)
+* [`manual_map`]: Fix suggestion when used with unsafe functions and blocks
+ [#7968](https://github.com/rust-lang/rust-clippy/pull/7968)
+* [`option_map_or_none`]: Suggest `map` over `and_then` when possible
+ [#7971](https://github.com/rust-lang/rust-clippy/pull/7971)
+* [`option_if_let_else`]: No longer expands macros in the suggestion
+ [#7974](https://github.com/rust-lang/rust-clippy/pull/7974)
+* [`iter_cloned_collect`]: Suggest `copied` over `cloned` when possible
+ [#8006](https://github.com/rust-lang/rust-clippy/pull/8006)
+* [`doc_markdown`]: No longer uses inline hints to improve readability of
+ suggestion [#8011](https://github.com/rust-lang/rust-clippy/pull/8011)
+* [`needless_question_mark`]: Now better explains the suggestion
+ [#8028](https://github.com/rust-lang/rust-clippy/pull/8028)
+* [`single_char_pattern`]: Escape backslash `\` in suggestion
+ [#8067](https://github.com/rust-lang/rust-clippy/pull/8067)
+* [`needless_bool`]: Suggest `a != b` over `!(a == b)`
+ [#8117](https://github.com/rust-lang/rust-clippy/pull/8117)
+* [`iter_skip_next`]: Suggest to add a `mut` if it is necessary in order to
+ apply this lints suggestion
+ [#8133](https://github.com/rust-lang/rust-clippy/pull/8133)
+* [`neg_multiply`]: Now produces a suggestion
+ [#8144](https://github.com/rust-lang/rust-clippy/pull/8144)
+* [`needless_return`]: Now suggests the unit type `()` over an empty block `{}`
+ in match arms [#8185](https://github.com/rust-lang/rust-clippy/pull/8185)
+* [`suboptimal_flops`]: Now gives a syntactically correct suggestion for
+ `to_radians` and `to_degrees`
+ [#8187](https://github.com/rust-lang/rust-clippy/pull/8187)
+
+### ICE Fixes
+
+* [`undocumented_unsafe_blocks`]
+ [#7945](https://github.com/rust-lang/rust-clippy/pull/7945)
+ [#7988](https://github.com/rust-lang/rust-clippy/pull/7988)
+* [`unnecessary_cast`]
+ [#8167](https://github.com/rust-lang/rust-clippy/pull/8167)
+
+### Documentation Improvements
+
+* [`print_stdout`], [`print_stderr`], [`dbg_macro`]: Document how the lint level
+ can be changed crate-wide
+ [#8040](https://github.com/rust-lang/rust-clippy/pull/8040)
+* Added a note to the `README` that config changes don't apply to already
+ compiled code [#8175](https://github.com/rust-lang/rust-clippy/pull/8175)
+
+### Others
+
+* [Clippy's lint
+ list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays
+ the version a lint was added. :tada:
+ [#7813](https://github.com/rust-lang/rust-clippy/pull/7813)
+* New and improved issue templates
+ [#8032](https://github.com/rust-lang/rust-clippy/pull/8032)
+* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a
+ file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917)
+
+## Rust 1.58
+
+Released 2022-01-13
+
+[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
+
+### Rust 1.58.1
+
+* Move [`non_send_fields_in_send_ty`] to `nursery` (now allow-by-default)
+ [#8075](https://github.com/rust-lang/rust-clippy/pull/8075)
+* [`useless_format`]: Handle implicit named arguments
+ [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
+
+### New lints
+
+* [`transmute_num_to_bytes`]
+ [#7805](https://github.com/rust-lang/rust-clippy/pull/7805)
+* [`match_str_case_mismatch`]
+ [#7806](https://github.com/rust-lang/rust-clippy/pull/7806)
+* [`format_in_format_args`], [`to_string_in_format_args`]
+ [#7743](https://github.com/rust-lang/rust-clippy/pull/7743)
+* [`uninit_vec`]
+ [#7682](https://github.com/rust-lang/rust-clippy/pull/7682)
+* [`fn_to_numeric_cast_any`]
+ [#7705](https://github.com/rust-lang/rust-clippy/pull/7705)
+* [`undocumented_unsafe_blocks`]
+ [#7748](https://github.com/rust-lang/rust-clippy/pull/7748)
+* [`trailing_empty_array`]
+ [#7838](https://github.com/rust-lang/rust-clippy/pull/7838)
+* [`string_slice`]
+ [#7878](https://github.com/rust-lang/rust-clippy/pull/7878)
+
+### Moves or deprecations of lints
+
+* Move [`non_send_fields_in_send_ty`] to `suspicious`
+ [#7874](https://github.com/rust-lang/rust-clippy/pull/7874)
+* Move [`non_ascii_literal`] to `restriction`
+ [#7907](https://github.com/rust-lang/rust-clippy/pull/7907)
+
+### Changes that expand what code existing lints cover
+
+* [`question_mark`] now covers `Result`
+ [#7840](https://github.com/rust-lang/rust-clippy/pull/7840)
+* Make [`useless_format`] recognize bare `format!("")`
+ [#7801](https://github.com/rust-lang/rust-clippy/pull/7801)
+* Lint on underscored variables with no side effects in [`no_effect`]
+ [#7775](https://github.com/rust-lang/rust-clippy/pull/7775)
+* Expand [`match_ref_pats`] to check for multiple reference patterns
+ [#7800](https://github.com/rust-lang/rust-clippy/pull/7800)
+
+### False positive fixes
+
+* Fix false positive of [`implicit_saturating_sub`] with `else` clause
+ [#7832](https://github.com/rust-lang/rust-clippy/pull/7832)
+* Fix [`question_mark`] when there is call in conditional predicate
+ [#7860](https://github.com/rust-lang/rust-clippy/pull/7860)
+* [`mut_mut`] no longer lints when type is defined in external macros
+ [#7795](https://github.com/rust-lang/rust-clippy/pull/7795)
+* Avoid [`eq_op`] in test functions
+ [#7811](https://github.com/rust-lang/rust-clippy/pull/7811)
+* [`cast_possible_truncation`] no longer lints when cast is coming from `signum`
+ method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850)
+* [`match_str_case_mismatch`] no longer lints on uncased characters
+ [#7865](https://github.com/rust-lang/rust-clippy/pull/7865)
+* [`ptr_arg`] no longer lints references to type aliases
+ [#7890](https://github.com/rust-lang/rust-clippy/pull/7890)
+* [`missing_safety_doc`] now also accepts "implementation safety" headers
+ [#7856](https://github.com/rust-lang/rust-clippy/pull/7856)
+* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]`
+ attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849)
+* [`if_not_else`] now ignores else-if statements
+ [#7895](https://github.com/rust-lang/rust-clippy/pull/7895)
+* Avoid linting [`cast_possible_truncation`] on bit-reducing operations
+ [#7819](https://github.com/rust-lang/rust-clippy/pull/7819)
+* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are
+ involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794)
+* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait
+ [#7824](https://github.com/rust-lang/rust-clippy/pull/7824)
+* Fix false positive in [`match_overlapping_arm`]
+ [#7847](https://github.com/rust-lang/rust-clippy/pull/7847)
+* Prevent [`needless_lifetimes`] false positive in `async` function definition
+ [#7901](https://github.com/rust-lang/rust-clippy/pull/7901)
+
+### Suggestion fixes/improvements
+
+* Keep an initial `::` when [`doc_markdown`] suggests to use ticks
+ [#7916](https://github.com/rust-lang/rust-clippy/pull/7916)
+* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks
+ lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904)
+* [`equatable_if_let`] no longer expands macros in the suggestion
+ [#7788](https://github.com/rust-lang/rust-clippy/pull/7788)
+* Make [`shadow_reuse`] suggestion less verbose
+ [#7782](https://github.com/rust-lang/rust-clippy/pull/7782)
+
+### ICE fixes
+
+* Fix ICE in [`enum_variant_names`]
+ [#7873](https://github.com/rust-lang/rust-clippy/pull/7873)
+* Fix ICE in [`undocumented_unsafe_blocks`]
+ [#7891](https://github.com/rust-lang/rust-clippy/pull/7891)
+
+### Documentation improvements
+
+* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`],
+ [`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`])
+ [#7827](https://github.com/rust-lang/rust-clippy/pull/7827)
+* Fix typo in example for [`match_result_ok`]
+ [#7815](https://github.com/rust-lang/rust-clippy/pull/7815)
+
+### Others
+
+* Allow giving reasons for [`disallowed_types`]
+ [#7791](https://github.com/rust-lang/rust-clippy/pull/7791)
+* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust
+ 2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851)
+* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while
+ loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789)
+* Added a new configuration `literal-suffix-style` to enforce a certain style
+ writing [`unseparated_literal_suffix`]
+ [#7726](https://github.com/rust-lang/rust-clippy/pull/7726)
+
+## Rust 1.57
+
+Released 2021-12-02
+
+[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
+
+### New Lints
+
+* [`negative_feature_names`]
+ [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
+* [`redundant_feature_names`]
+ [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
+* [`mod_module_files`]
+ [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
+* [`self_named_module_files`]
+ [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
+* [`manual_split_once`]
+ [#7565](https://github.com/rust-lang/rust-clippy/pull/7565)
+* [`derivable_impls`]
+ [#7570](https://github.com/rust-lang/rust-clippy/pull/7570)
+* [`needless_option_as_deref`]
+ [#7596](https://github.com/rust-lang/rust-clippy/pull/7596)
+* [`iter_not_returning_iterator`]
+ [#7610](https://github.com/rust-lang/rust-clippy/pull/7610)
+* [`same_name_method`]
+ [#7653](https://github.com/rust-lang/rust-clippy/pull/7653)
+* [`manual_assert`] [#7669](https://github.com/rust-lang/rust-clippy/pull/7669)
+* [`non_send_fields_in_send_ty`]
+ [#7709](https://github.com/rust-lang/rust-clippy/pull/7709)
+* [`equatable_if_let`]
+ [#7762](https://github.com/rust-lang/rust-clippy/pull/7762)
+
+### Moves and Deprecations
+
+* Move [`shadow_unrelated`] to `restriction`
+ [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
+* Move [`option_if_let_else`] to `nursery`
+ [#7568](https://github.com/rust-lang/rust-clippy/pull/7568)
+* Move [`branches_sharing_code`] to `nursery`
+ [#7595](https://github.com/rust-lang/rust-clippy/pull/7595)
+* Rename `if_let_some_result` to [`match_result_ok`] which now also handles
+ `while let` cases [#7608](https://github.com/rust-lang/rust-clippy/pull/7608)
+* Move [`many_single_char_names`] to `pedantic`
+ [#7671](https://github.com/rust-lang/rust-clippy/pull/7671)
+* Move [`float_cmp`] to `pedantic`
+ [#7692](https://github.com/rust-lang/rust-clippy/pull/7692)
+* Rename `box_vec` to [`box_collection`] and lint on more general cases
+ [#7693](https://github.com/rust-lang/rust-clippy/pull/7693)
+* Uplift `invalid_atomic_ordering` to rustc
+ [rust-lang/rust#84039](https://github.com/rust-lang/rust/pull/84039)
+
+### Enhancements
+
+* Rewrite the `shadow*` lints, so that they find a lot more shadows and are not
+ limited to certain patterns
+ [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
+* The `avoid-breaking-exported-api` configuration now also works for
+ [`box_collection`], [`redundant_allocation`], [`rc_buffer`], [`vec_box`],
+ [`option_option`], [`linkedlist`], [`rc_mutex`]
+ [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
+* [`unnecessary_unwrap`]: Now also checks for `expect`s
+ [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
+* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
+ lint message
+ [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
+* [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
+ [#7629](https://github.com/rust-lang/rust-clippy/pull/7629)
+* [`approx_constant`]: Add `TAU`
+ [#7642](https://github.com/rust-lang/rust-clippy/pull/7642)
+* [`needless_borrow`]: Now also lints on needless mutable borrows
+ [#7657](https://github.com/rust-lang/rust-clippy/pull/7657)
+* [`missing_safety_doc`]: Now also lints on unsafe traits
+ [#7734](https://github.com/rust-lang/rust-clippy/pull/7734)
+
+### False Positive Fixes
+
+* [`manual_map`]: No longer lints when the option is borrowed in the match and
+ also consumed in the arm
+ [#7531](https://github.com/rust-lang/rust-clippy/pull/7531)
+* [`filter_next`]: No longer lints if `filter` method is not the
+ `Iterator::filter` method
+ [#7562](https://github.com/rust-lang/rust-clippy/pull/7562)
+* [`manual_flatten`]: No longer lints if expression is used after `if let`
+ [#7566](https://github.com/rust-lang/rust-clippy/pull/7566)
+* [`option_if_let_else`]: Multiple fixes
+ [#7573](https://github.com/rust-lang/rust-clippy/pull/7573)
+ * `break` and `continue` statements local to the would-be closure are
+ allowed
+ * Don't lint in const contexts
+ * Don't lint when yield expressions are used
+ * Don't lint when the captures made by the would-be closure conflict with
+ the other branch
+ * Don't lint when a field of a local is used when the type could be
+ potentially moved from
+ * In some cases, don't lint when scrutinee expression conflicts with the
+ captures of the would-be closure
+* [`redundant_allocation`]: No longer lints on `Box<Box<dyn T>>` which replaces
+ wide pointers with thin pointers
+ [#7592](https://github.com/rust-lang/rust-clippy/pull/7592)
+* [`bool_assert_comparison`]: No longer lints on types that do not implement the
+ `Not` trait with `Output = bool`
+ [#7605](https://github.com/rust-lang/rust-clippy/pull/7605)
+* [`mut_range_bound`]: No longer lints on range bound mutations, that are
+ immediately followed by a `break;`
+ [#7607](https://github.com/rust-lang/rust-clippy/pull/7607)
+* [`mutable_key_type`]: Improve accuracy and document remaining false positives
+ and false negatives
+ [#7640](https://github.com/rust-lang/rust-clippy/pull/7640)
+* [`redundant_closure`]: Rewrite the lint to fix various false positives and
+ false negatives [#7661](https://github.com/rust-lang/rust-clippy/pull/7661)
+* [`large_enum_variant`]: No longer wrongly identifies the second largest
+ variant [#7677](https://github.com/rust-lang/rust-clippy/pull/7677)
+* [`needless_return`]: No longer lints on let-else expressions
+ [#7685](https://github.com/rust-lang/rust-clippy/pull/7685)
+* [`suspicious_else_formatting`]: No longer lints in proc-macros
+ [#7707](https://github.com/rust-lang/rust-clippy/pull/7707)
+* [`excessive_precision`]: No longer lints when in some cases the float was
+ already written in the shortest form
+ [#7722](https://github.com/rust-lang/rust-clippy/pull/7722)
+* [`doc_markdown`]: No longer lints on intra-doc links
+ [#7772](https://github.com/rust-lang/rust-clippy/pull/7772)
+
+### Suggestion Fixes/Improvements
+
+* [`unnecessary_operation`]: Recommend using an `assert!` instead of using a
+ function call in an indexing operation
+ [#7453](https://github.com/rust-lang/rust-clippy/pull/7453)
+* [`manual_split_once`]: Produce semantically equivalent suggestion when
+ `rsplitn` is used [#7663](https://github.com/rust-lang/rust-clippy/pull/7663)
+* [`while_let_on_iterator`]: Produce correct suggestion when using `&mut`
+ [#7690](https://github.com/rust-lang/rust-clippy/pull/7690)
+* [`manual_assert`]: No better handles complex conditions
+ [#7741](https://github.com/rust-lang/rust-clippy/pull/7741)
+* Correctly handle signs in exponents in numeric literals lints
+ [#7747](https://github.com/rust-lang/rust-clippy/pull/7747)
+* [`suspicious_map`]: Now also suggests to use `inspect` as an alternative
+ [#7770](https://github.com/rust-lang/rust-clippy/pull/7770)
+* Drop exponent from suggestion if it is 0 in numeric literals lints
+ [#7774](https://github.com/rust-lang/rust-clippy/pull/7774)
+
+### ICE Fixes
+
+* [`implicit_hasher`]
+ [#7761](https://github.com/rust-lang/rust-clippy/pull/7761)
+
+### Others
+
+* Clippy now uses the 2021
+ [Edition!](https://www.youtube.com/watch?v=q0aNduqb2Ro)
+ [#7664](https://github.com/rust-lang/rust-clippy/pull/7664)
+
+## Rust 1.56
+
+Released 2021-10-21
+
+[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
+
+### New Lints
+
+* [`unwrap_or_else_default`]
+ [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
+
+### Enhancements
+
+* [`needless_continue`]: Now also lints in `loop { continue; }` case
+ [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
+* [`disallowed_types`]: Now also primitive types can be disallowed
+ [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
+* [`manual_swap`]: Now also lints on xor swaps
+ [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
+* [`map_flatten`]: Now also lints on the `Result` type
+ [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
+* [`no_effect`]: Now also lints on inclusive ranges
+ [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
+
+### False Positive Fixes
+
+* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
+ [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
+* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
+ [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
+* [`similar_names`]: No longer complains about `iter` and `item` being too
+ similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
+
+### Suggestion Fixes/Improvements
+
+* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
+ [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
+* [`new_without_default`]: No longer shows the full qualified type path when
+ suggesting adding a `Default` implementation
+ [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
+* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
+ [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
+* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
+ references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
+* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
+ [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
+* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
+ applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
+
+### Documentation Improvements
+
+* Clippy now uses a lint to generate its lint documentation. [Lints all the way
+ down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
+ [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
+* Reworked Clippy's website:
+ [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
+ [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
+ * Added applicability information about lints
+ * Added a link to jump into the implementation
+ * Improved loading times
+ * Adapted some styling
+* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
+ [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
+* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
+ example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
+
+## Rust 1.55
+
+Released 2021-09-09
+
+[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
+
+### Important Changes
+
+* Stabilized `cargo clippy --fix` :tada:
+ [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
+
+### New Lints
+
+* [`rc_mutex`]
+ [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
+* [`nonstandard_macro_braces`]
+ [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
+* [`strlen_on_c_strings`]
+ [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
+* [`self_named_constructors`]
+ [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
+* [`disallowed_script_idents`]
+ [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
+* [`disallowed_types`]
+ [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
+* [`missing_enforced_import_renames`]
+ [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
+* [`extend_with_drain`]
+ [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
+
+### Moves and Deprecations
+
+* Moved [`from_iter_instead_of_collect`] to `pedantic`
+ [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
+* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
+ [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
+ * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
+ * Moved [`empty_loop`] to `suspicious`
+ * Moved [`eval_order_dependence`] to `suspicious`
+ * Moved [`float_equality_without_abs`] to `suspicious`
+ * Moved [`for_loops_over_fallibles`] to `suspicious`
+ * Moved [`misrefactored_assign_op`] to `suspicious`
+ * Moved [`mut_range_bound`] to `suspicious`
+ * Moved [`mutable_key_type`] to `suspicious`
+ * Moved [`suspicious_arithmetic_impl`] to `suspicious`
+ * Moved [`suspicious_assignment_formatting`] to `suspicious`
+ * Moved [`suspicious_else_formatting`] to `suspicious`
+ * Moved [`suspicious_map`] to `suspicious`
+ * Moved [`suspicious_op_assign_impl`] to `suspicious`
+ * Moved [`suspicious_unary_op_formatting`] to `suspicious`
+
+### Enhancements
+
+* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
+ [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
+* [`doc_markdown`]:
+ * Now detects unbalanced ticks
+ [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
+ * Add `FreeBSD` to the default configuration as an allowed identifier
+ [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
+* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
+ or hidden variants
+ [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
+* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
+ [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
+* [`disallowed_names`]: Now allows disallowed names in test code
+ [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
+* [`redundant_closure`]: Suggests `&mut` for `FnMut`
+ [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
+* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
+ no longer require fully qualified paths
+ [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
+* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
+ [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
+
+### False Positive Fixes
+
+* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
+ [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
+* [`use_self`]: No longer lints on type parameters
+ [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
+* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
+ [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
+* [`branches_sharing_code`]: Now always checks for block expressions
+ [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
+* [`field_reassign_with_default`]: No longer triggers in macros
+ [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
+* [`redundant_clone`]: No longer lints on required clones for borrowed data
+ [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
+* [`default_numeric_fallback`]: No longer triggers in external macros
+ [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
+* [`needless_bool`]: No longer lints in macros
+ [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
+* [`useless_format`]: No longer triggers when additional text is being appended
+ [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
+* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
+ [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
+
+### Suggestion Fixes/Improvements
+
+* [`needless_collect`]: Now show correct lint messages for shadowed values
+ [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
+* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
+ [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
+* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
+ [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
+* [`redundant_pattern_matching`]
+ [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
+* [`modulo_one`]
+ [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
+* [`use_self`]
+ [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
+
+## Rust 1.54
+
+Released 2021-07-29
+
+[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
+
+### New Lints
+
+- [`ref_binding_to_reference`]
+ [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
+- [`needless_bitwise_bool`]
+ [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
+- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
+- [`manual_str_repeat`]
+ [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
+- [`suspicious_splitn`]
+ [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
+
+### Moves and Deprecations
+
+- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
+ the new `avoid-breaking-exported-api` config option (see
+ [Enhancements](#1-54-enhancements))
+ [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
+- Move [`inconsistent_struct_constructor`] to `pedantic`
+ [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
+- Move [`needless_borrow`] to `style` (now warn-by-default)
+ [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
+- Move [`suspicious_operation_groupings`] to `nursery`
+ [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
+- Move [`semicolon_if_nothing_returned`] to `pedantic`
+ [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
+
+### Enhancements <a name="1-54-enhancements"></a>
+
+- [`while_let_on_iterator`]: Now also lints in nested loops
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
+ [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
+- [`needless_collect`]: Now also lints on assignments with type annotations
+ [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
+- [`if_then_some_else_none`]: Now works with the MSRV config
+ [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
+- Add `avoid-breaking-exported-api` config option for the lints
+ [`enum_variant_names`], [`large_types_passed_by_value`],
+ [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
+ [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
+ this configuration option to `false` before a major release (1.0/2.0/...) to
+ clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
+- [`needless_collect`]: Now lints on even more data structures
+ [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
+- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
+ attributes as sufficient documentation
+ [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
+- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
+ Now work as expected when used with `allow`
+ [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
+
+### False Positive Fixes
+
+- [`implicit_return`]: Now takes all diverging functions in account to avoid
+ false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
+- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
+ and the struct is used in the loop
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`multiple_inherent_impl`]: No longer lints with generic arguments
+ [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
+- [`comparison_chain`]: No longer lints in a `const` context
+ [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
+- [`while_immutable_condition`]: Fix false positive where mutation in the loop
+ variable wasn't picked up
+ [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
+- [`default_trait_access`]: No longer lints in macros
+ [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
+- [`needless_question_mark`]: No longer lints when the inner value is implicitly
+ dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
+- [`unused_unit`]: No longer lints when multiple macro contexts are involved
+ [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
+- [`eval_order_dependence`]: Fix false positive in async context
+ [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
+- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
+ type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
+- [`wrong_self_convention`]: No longer lints in trait implementations of
+ non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
+- [`suboptimal_flops`]: No longer lints on `powi(2)`
+ [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
+- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
+ [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
+- [`option_if_let_else`]: No longer lints on `else if let` pattern
+ [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
+- [`use_self`], [`useless_conversion`]: Fix false positives when generic
+ arguments are involved
+ [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
+- [`manual_unwrap_or`]: Fix false positive with deref coercion
+ [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
+- [`similar_names`]: No longer lints on `wparam`/`lparam`
+ [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
+- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
+ closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
+
+### Suggestion Fixes/Improvements
+
+- [`implicit_return`]
+ [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
+ - Fix suggestion for async functions
+ - Improve suggestion with macros
+ - Suggest to change `break` to `return` when appropriate
+- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
+ [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
+- [`match_single_binding`]: Improve suggestion when match scrutinee has side
+ effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
+- [`needless_borrow`]: Now suggests to also change usage sites as needed
+ [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
+- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
+ buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
+- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
+ when a `<_ as Trait>::_` is involved
+ [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
+- [`not_unsafe_ptr_arg_deref`]: Improved error message
+ [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
+
+### ICE Fixes
+
+- Fix ICE when running Clippy on `libstd`
+ [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
+- [`implicit_return`]
+ [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
+
+## Rust 1.53
+
+Released 2021-06-17
+
+[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
+
+### New Lints
+
+* [`option_filter_map`]
+ [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
+* [`branches_sharing_code`]
+ [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
+* [`needless_for_each`]
+ [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
+* [`if_then_some_else_none`]
+ [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
+* [`non_octal_unix_permissions`]
+ [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
+* [`unnecessary_self_imports`]
+ [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
+* [`bool_assert_comparison`]
+ [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
+* [`cloned_instead_of_copied`]
+ [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
+* [`flat_map_option`]
+ [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
+
+### Moves and Deprecations
+
+* Deprecate [`filter_map`] lint
+ [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
+* Move [`transmute_ptr_to_ptr`] to `pedantic`
+ [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
+
+### Enhancements
+
+* [`mem_replace_with_default`]: Also lint on common std constructors
+ [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
+* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
+ [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
+* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
+ [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
+ * Attempt to find a common path prefix in suggestion
+ * Don't lint on `Option` and `Result`
+ * Consider `Self` prefix
+* [`explicit_deref_methods`]: Also lint on chained `deref` calls
+ [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
+* [`or_fun_call`]: Also lint on `unsafe` blocks
+ [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
+* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
+ `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
+* [`search_is_some`]: Also check for `is_none`
+ [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
+* [`string_lit_as_bytes`]: Also lint on `into_bytes`
+ [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
+* [`len_without_is_empty`]: Also lint if function signatures of `len` and
+ `is_empty` don't match
+ [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
+* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
+ [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
+* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
+ [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
+* [`needless_return`]: Also lint in `async` functions
+ [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
+* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
+ [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
+* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
+ now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
+
+### False Positive Fixes
+
+* [`upper_case_acronyms`]: No longer lints on public items
+ [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
+* [`suspicious_map`]: No longer lints when side effects may occur inside the
+ `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
+* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
+ [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
+* [`wrong_self_convention`]: Now respects `Copy` types
+ [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
+* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
+ from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
+* [`map_entry`]: Better detect if the entry API can be used
+ [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
+* [`or_fun_call`]: No longer lints on some `len` function calls
+ [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
+* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
+ generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
+* [`upper_case_acronyms`]: No longer lints on public items
+ [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
+* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
+ of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
+* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
+ suggesting to use `derive` instead
+ [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
+* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
+ [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
+* [`clone_on_copy`]: Only lint when using the `Clone` trait
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`wrong_self_convention`]: No longer lints inside a trait implementation
+ [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
+* [`redundant_clone`]: No longer lints when the cloned value is modified while
+ the clone is in use
+ [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
+* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
+ [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
+* [`cargo_common_metadata`]: Remove author requirement
+ [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
+* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
+ [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
+* [`panic`]: No longer wrongfully lints on `debug_assert` with message
+ [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
+* [`wrong_self_convention`]: No longer lints in trait implementations where no
+ `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
+* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
+ involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
+* [`suspicious_else_formatting`]: Allow Allman style braces
+ [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
+* [`inconsistent_struct_constructor`]: No longer lints in macros
+ [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
+* [`single_component_path_imports`]: No longer lints on macro re-exports
+ [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
+
+### Suggestion Fixes/Improvements
+
+* [`redundant_pattern_matching`]: Add a note when applying this lint would
+ change the drop order
+ [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
+* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
+ [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
+* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
+ [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
+* [`inconsistent_struct_constructor`]: Make lint description and message clearer
+ [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
+* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
+ as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
+* [`manual_flatten`]: Suggest to insert `copied` if necessary
+ [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
+* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
+ when the value is from a macro call
+ [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
+* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
+ [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
+* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
+ [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
+* [`manual_map`]: Fix suggestion at the end of an if chain
+ [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
+* Fix needless parenthesis output in multiple lint suggestions
+ [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
+* [`needless_collect`]: Better explanation in the lint message
+ [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
+* [`useless_vec`]: Now considers mutability
+ [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
+* [`useless_format`]: Wrap the content in braces if necessary
+ [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
+* [`single_match`]: Don't suggest an equality check for types which don't
+ implement `PartialEq`
+ [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
+* [`from_over_into`]: Mention type in help message
+ [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
+* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
+ [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
+
+### ICE Fixes
+
+* [`macro_use_imports`]
+ [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
+* [`missing_panics_doc`]
+ [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
+* [`tabs_in_doc_comments`]
+ [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
+* [`missing_const_for_fn`]
+ [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
+
+### Others
+
+* [Clippy's lint
+ list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
+ themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
+* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
+ deprecation warning
+ [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
+
+## Rust 1.52
+
+Released 2021-05-06
+
+[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
+
+### New Lints
+
+* [`from_str_radix_10`]
+ [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
+* [`implicit_clone`]
+ [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
+* [`semicolon_if_nothing_returned`]
+ [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
+* [`manual_flatten`]
+ [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
+* [`inconsistent_struct_constructor`]
+ [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
+* [`iter_count`]
+ [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
+* [`default_numeric_fallback`]
+ [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
+* [`bytes_nth`]
+ [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
+* [`filter_map_identity`]
+ [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
+* [`manual_map`]
+ [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
+
+### Moves and Deprecations
+
+* Moved [`upper_case_acronyms`] to `pedantic`
+ [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
+* Moved [`manual_map`] to `nursery`
+ [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
+* Moved [`unnecessary_wraps`] to `pedantic`
+ [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
+* Moved [`trivial_regex`] to `nursery`
+ [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
+* Moved [`naive_bytecount`] to `pedantic`
+ [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
+* Moved [`upper_case_acronyms`] to `style`
+ [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
+* Moved [`manual_map`] to `style`
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+
+### Enhancements
+
+* [`disallowed_methods`]: Now supports functions in addition to methods
+ [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
+* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
+ trigger the lint if there is more than one uppercase character next to each other
+ [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
+* [`collapsible_match`]: Now supports block comparison with different value names
+ [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
+* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
+ [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
+* Improved value usage detection in closures
+ [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
+
+### False Positive Fixes
+
+* [`use_self`]: No longer lints in macros
+ [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
+* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
+ [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
+* [`missing_inline_in_public_items`]: No longer lints for procedural macros
+ [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
+* [`inherent_to_string`]: No longer lints on functions with function generics
+ [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
+* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
+ [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
+* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
+ [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
+* [`collapsible_if`]: No longer lints on if statements with attributes
+ [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
+* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
+ [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
+* [`redundant_closure`]: Now ignores macros
+ [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
+* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+* [`vec_init_then_push`]: Fixed false positives for loops and if statements
+ [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
+* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
+ the `len` method as well as the type definition.
+ [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
+* [`let_underscore_drop`]: Only lints on types which implement `Drop`
+ [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
+* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
+ [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
+* [`cargo_common_metadata`]: No longer lints if
+ [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
+ is defined in the manifest
+ [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
+
+### Suggestion Fixes/Improvements
+
+* [`collapsible_match`]: Fixed lint message capitalization
+ [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
+* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
+ [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
+* [`manual_map`]: No longer expands macros in the suggestions
+ [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
+* Aligned Clippy's lint messages with the rustc dev guide
+ [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
+
+### Documentation Improvements
+
+* [`useless_format`]: Improved the documentation example
+ [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
+* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
+ [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
+
+### Others
+* Running `cargo clippy` after `cargo check` now works as expected
+ (`cargo clippy` and `cargo check` no longer shares the same build cache)
+ [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
+* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
+ [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
+* Extracted Clippy's `utils` module into the new `clippy_utils` crate
+ [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
+* Clippy lintcheck tool improvements
+ [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
+ [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
+ [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
+ [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
+ [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
+ [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
+
+## Rust 1.51
+
+Released 2021-03-25
+
+[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
+
+### New Lints
+
+* [`upper_case_acronyms`]
+ [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
+* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
+* [`case_sensitive_file_extension_comparisons`]
+ [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
+* [`needless_question_mark`]
+ [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
+* [`missing_panics_doc`]
+ [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
+* [`redundant_slicing`]
+ [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
+* [`vec_init_then_push`]
+ [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
+* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
+* [`collapsible_else_if`] (split out from `collapsible_if`)
+ [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
+* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
+* [`manual_filter_map`]
+ [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* [`exhaustive_enums`]
+ [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+* [`exhaustive_structs`]
+ [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
+
+### Moves and Deprecations
+
+* Replace [`find_map`] with [`manual_find_map`]
+ [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
+* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
+ [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
+
+### Enhancements
+
+* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
+ [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
+* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
+ [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
+* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
+ scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
+
+### False Positive Fixes
+
+* [`similar_names`] Ignore underscore prefixed names
+ [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
+* [`print_literal`] and [`write_literal`] No longer lint numeric literals
+ [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
+* [`large_enum_variant`] No longer lints in external macros
+ [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
+* [`empty_enum`] Only lint if `never_type` feature is enabled
+ [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
+* [`field_reassign_with_default`] No longer lints in macros
+ [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
+* [`size_of_in_element_count`] No longer lints when dividing by element size
+ [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
+* [`needless_return`] No longer lints in macros
+ [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
+* [`match_overlapping_arm`] No longer lint when first arm is completely included
+ in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
+* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
+ identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
+
+### Suggestion Fixes/Improvements
+
+* [`field_reassign_with_default`] Don't expand macro in lint suggestion
+ [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
+* [`match_like_matches_macro`] Strip references in suggestion
+ [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
+* [`single_match`] Suggest `if` over `if let` when possible
+ [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
+* `ref_in_deref` Use parentheses correctly in suggestion
+ [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
+* [`stable_sort_primitive`] Clarify error message
+ [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
+
+### ICE Fixes
+
+* [`zero_sized_map_values`]
+ [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
+
+### Documentation Improvements
+
+* Improve search performance on the Clippy website and make it possible to
+ directly search for lints on the GitHub issue tracker
+ [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
+* Clean up `README.md` by removing outdated paragraph
+ [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
+* [`await_holding_refcell_ref`] and [`await_holding_lock`]
+ [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
+* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
+
+### Others
+
+* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
+ project, take a look at the [Roadmap project page]. All issues listed there
+ are actively mentored
+ [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
+* The Clippy version number now corresponds to the Rust version number
+ [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
+* Fix oversight which caused Clippy to lint deps in some environments, where
+ `CLIPPY_TESTS=true` was set somewhere
+ [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
+* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
+ [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
+
+[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/proposals/roadmap-2021.md
+[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
+
+## Rust 1.50
+
+Released 2021-02-11
+
+[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
+
+### New Lints
+
+* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
+* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
+* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
+* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
+* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
+* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
+* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
+* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
+* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
+
+### Moves and Deprecations
+
+* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
+ as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
+* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
+ [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
+* Move [`map_err_ignore`] to `restriction`
+ [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
+* Move [`await_holding_refcell_ref`] to `pedantic`
+ [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
+* Move [`await_holding_lock`] to `pedantic`
+ [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
+
+### Enhancements
+
+* Add the `unreadable-literal-lint-fractions` configuration to disable
+ the `unreadable_literal` lint for fractions
+ [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
+* [`clone_on_copy`]: Now shows the type in the lint message
+ [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
+* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
+ [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
+* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
+ [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
+* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
+ [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
+* [`clone_double_ref`]: Now prints the reference type in the lint message
+ [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
+* [`modulo_one`]: Now also lints on -1.
+ [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
+* [`empty_loop`]: Now lints no_std crates, too
+ [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
+* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
+ [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
+* [`wrong_self_convention`]: Now also lints in trait definitions
+ [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
+* [`needless_borrow`]: Print the type in the lint message
+ [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
+
+[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
+
+### False Positive Fixes
+
+* [`manual_range_contains`]: No longer lints in `const fn`
+ [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
+* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
+ [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
+* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
+ [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
+* [`match_like_matches_macro`]: No longer lints on arms with attributes
+ [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
+* [`map_clone`]: No longer lints with deref and clone
+ [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
+* [`map_clone`]: No longer lints in the case of &mut
+ [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
+* [`needless_update`]: Now ignores `non_exhaustive` structs
+ [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
+* [`needless_collect`]: No longer lints when a collect is needed multiple times
+ [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
+* [`unnecessary_cast`] No longer lints cfg-dependent types
+ [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
+* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
+ Both now ignore enums with frozen variants
+ [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
+* [`field_reassign_with_default`] No longer lint for private fields
+ [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
+
+
+### Suggestion Fixes/Improvements
+
+* [`vec_box`]: Provide correct type scope suggestion
+ [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
+* [`manual_range_contains`]: Give correct suggestion when using floats
+ [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
+* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
+ [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
+* [`manual_async_fn`]: Improve suggestion formatting
+ [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
+* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
+ [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
+
+### ICE Fixes
+
+* Fix a crash in [`from_iter_instead_of_collect`]
+ [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
+* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
+ [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
+
+### Documentation Improvements
+
+* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
+ * Searching for lints with dashes and spaces is possible now. For example
+ `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
+ * Improved fuzzy search in lint descriptions
+* Various README improvements
+ [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
+* Add known problems to [`comparison_chain`] documentation
+ [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
+* Fix example used in [`cargo_common_metadata`]
+ [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
+* Improve [`map_clone`] documentation
+ [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
+
+### Others
+
+* You can now tell Clippy about the MSRV your project supports. Please refer to
+ the specific README section to learn more about MSRV support [here][msrv_readme]
+ [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
+* Add `--no-deps` option to avoid running on path dependencies in workspaces
+ [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
+
+## Rust 1.49
+
+Released 2020-12-31
+
+[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
+
+### New Lints
+
+* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
+* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
+* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
+* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
+* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
+* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
+* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
+* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
+* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
+* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
+* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
+* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
+* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
+* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
+* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
+* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
+* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
+
+### Moves and Deprecations
+
+* Rename `single_char_push_str` to [`single_char_add_str`]
+ [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
+* Rename `zero_width_space` to [`invisible_characters`]
+ [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
+* Deprecate `drop_bounds` (uplifted)
+ [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
+* Move [`string_lit_as_bytes`] to `nursery`
+ [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
+* Move [`rc_buffer`] to `restriction`
+ [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
+
+### Enhancements
+
+* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
+ reliable suggestion)
+ [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
+* [`single_char_add_str`]: Also lint on `String::insert_str`
+ [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
+* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
+ [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
+* [`eq_op`]: Also lint on the `assert_*!` macro family
+ [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
+* [`items_after_statements`]: Also lint in local macro expansions
+ [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
+* [`unnecessary_cast`]: Also lint casts on integer and float literals
+ [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
+* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
+ [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
+* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
+ [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
+* [`integer_arithmetic`]: Better handle `/` an `%` operators
+ [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
+
+### False Positive Fixes
+
+* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
+ lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
+* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
+ is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
+* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
+ [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
+* [`needless_range_loop`]: No longer lints, when the iterable is used in the
+ range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
+* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
+ [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
+* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
+ float (e.g. `713.32_64`)
+ [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
+* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
+ [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
+* [`boxed_local`]: No longer lints on `extern fn` arguments
+ [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
+* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
+ clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
+
+### Suggestion Fixes/Improvements
+
+* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
+ [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
+* [`needless_arbitrary_self_type`]: Correctly handle expanded code
+ [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
+* [`useless_format`]: Preserve raw strings in suggestion
+ [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
+* [`empty_loop`]: Suggest alternatives
+ [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
+* [`borrowed_box`]: Correctly add parentheses in suggestion
+ [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
+* [`unused_unit`]: Improve suggestion formatting
+ [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
+
+### Documentation Improvements
+
+* Some doc improvements:
+ * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
+ * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
+* [`doc_markdown`]: Document problematic link text style
+ [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
+
+## Rust 1.48
+
+Released 2020-11-19
+
+[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
+
+### New lints
+
+* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
+* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
+* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
+* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
+* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
+* `to_string_in_display` [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
+* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
+
+### Moves and Deprecations
+
+* Downgrade [`verbose_bit_mask`] to pedantic
+ [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
+
+### Enhancements
+
+* Extend [`precedence`] to handle chains of methods combined with unary negation
+ [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
+* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
+ [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
+* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
+ [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
+* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
+ [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
+* Avoid [`redundant_pattern_matching`] triggering in macros
+ [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
+* [`option_if_let_else`]: distinguish pure from impure `else` expressions
+ [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
+* [`needless_doctest_main`]: parse doctests instead of using textual search
+ [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
+* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
+ [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
+* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
+ [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
+* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
+ [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
+
+### False Positive Fixes
+
+* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
+ [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
+* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
+ [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
+* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
+ [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
+* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
+ [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
+* [`doc_markdown`]: allow using "GraphQL" without backticks
+ [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
+* `to_string_in_display`: avoid linting when calling `to_string()` on anything that is not `self`
+ [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
+* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
+ [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
+* [`should_implement_trait`]: ignore methods with lifetime parameters
+ [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
+* [`needless_return`]: avoid linting if a temporary borrows a local variable
+ [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
+* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
+ [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
+* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
+ [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
+* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
+ [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
+
+### Suggestion Fixes/Improvements
+
+* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
+ [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
+* [`useless_conversion`]: show the type in the error message
+ [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
+* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
+ [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
+* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
+ [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
+* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
+ [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
+* [`collapsible_if`]: don't use expanded code in the suggestion
+ [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
+* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
+ [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
+* [`unit_arg`]: improve the readability of the suggestion
+ [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
+* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
+ [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
+* Show line count and max lines in [`too_many_lines`] lint message
+ [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
+* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
+ [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
+* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
+ [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
+* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
+ [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
+* Make lint messages adhere to rustc dev guide conventions
+ [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
+
+### ICE Fixes
+
+* Fix ICE in [`repeat_once`]
+ [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
+
+### Documentation Improvements
+
+* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
+ [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
+* [`unnecessary_mut_passed`]: fix typo
+ [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
+* Add example of false positive to [`ptr_arg`] docs.
+ [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
+* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
+ [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
+
+## Rust 1.47
+
+Released 2020-10-08
+
+[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
+
+### New lints
+
+* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
+* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
+* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
+* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
+* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
+* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
+* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
+* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
+* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
+* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
+* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
+* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
+
+### Moves and Deprecations
+
+* Deprecate [`regex_macro`] lint
+ [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
+* Move [`range_minus_one`] to `pedantic`
+ [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
+
+### Enhancements
+
+* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
+ [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
+* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
+ [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
+* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
+ [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
+* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
+ [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
+* Make it possible to allow [`unsafe_derive_deserialize`]
+ [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
+* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
+ [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
+* Make [`clone_on_copy`] suggestion machine applicable
+ [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
+* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
+ [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
+
+### False Positive Fixes
+
+* Avoid triggering [`or_fun_call`] with const fns that take no arguments
+ [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
+* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
+ [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
+* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
+ [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
+* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
+ [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
+* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
+ [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
+* Avoid linting if key borrows in [`unnecessary_sort_by`]
+ [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
+* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
+ [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
+* Take input lifetimes into account in `manual_async_fn`
+ [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
+* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
+ [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
+* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
+ [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
+
+### Suggestion Fixes/Improvements
+
+* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
+ [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
+* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
+ [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
+* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
+ [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
+* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
+ [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
+* Add suggestion for [`iter_skip_next`]
+ [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
+* Improve [`collapsible_if`] fix suggestion
+ [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
+
+### ICE Fixes
+
+* Fix ICE caused by [`needless_collect`]
+ [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
+* Fix ICE caused by [`unnested_or_patterns`]
+ [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
+
+### Documentation Improvements
+
+* Fix grammar of [`await_holding_lock`] documentation
+ [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
+
+### Others
+
+* Make lints adhere to the rustc dev guide
+ [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
+
+## Rust 1.46
+
+Released 2020-08-27
+
+[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
+
+### New lints
+
+* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
+* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
+* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
+* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
+
+### Moves and Deprecations
+
+* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
+
+### Enhancements
+
+* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
+
+### False Positive Fixes
+
+* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
+ [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
+* [`let_and_return`]: Don't lint if a temporary borrow is involved
+ [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
+* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
+ [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
+* [`if_same_then_else`]: Don't assume multiplication is always commutative
+ [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
+* [`disallowed_names`]: Remove `bar` from the default configuration
+ [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
+* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
+ [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
+
+### Suggestion Fixes/Improvements
+
+* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
+ [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
+* Add auto applicable suggestion to [`macro_use_imports`]
+ [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
+
+### ICE Fixes
+
+* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
+
+### Documentation Improvements
+
+* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
+
+### Others
+
+* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
+ into `rustc` and passes all the given arguments to `rustc`. This is especially
+ useful for tools that need the `rustc` version Clippy was compiled with,
+ instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
+ print the output of `rustc --version`.
+ [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
+* New issue templates now make it easier to complain if Clippy is too annoying
+ or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
+
+## Rust 1.45
+
+Released 2020-07-16
+
+[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
+
+### New lints
+
+* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
+* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
+* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
+* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
+* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
+* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
+* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
+* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
+* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
+
+### Moves and Deprecations
+
+* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
+* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
+* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
+* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
+* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
+* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
+[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
+
+### Enhancements
+
+* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
+* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
+* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
+* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
+
+### False Positive Fixes
+
+* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
+* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
+* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
+* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
+* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
+* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
+* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
+* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
+* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
+* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
+* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
+* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
+and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
+* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
+
+### Suggestion Improvements
+
+* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
+* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
+* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
+* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
+
+### ICE Fixes
+
+* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
+* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
+
+### Documentation
+
+* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
+* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
+
+## Rust 1.44
+
+Released 2020-06-04
+
+[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
+
+### New lints
+
+* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
+* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
+* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
+* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
+* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
+* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
+* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
+* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
+* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
+* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
+
+
+### Moves and Deprecations
+
+* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
+* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
+* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
+* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
+* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
+* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
+* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
+* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
+* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
+
+### Enhancements
+
+* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
+ auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
+* Make [`redundant_clone`] also trigger on cases where the cloned value is not
+ consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
+* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
+* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
+* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
+* [`bool_comparison`] now also checks for inequality comparisons that can be
+ written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
+* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
+* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
+* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
+* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
+* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
+
+### False Positive Fixes
+
+* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
+* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
+* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
+* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
+* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
+* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
+* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
+* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
+* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
+
+
+### Suggestion Improvements
+
+* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
+* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
+* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
+
+### ICE Fixes
+
+* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
+* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
+
+### Documentation
+
+* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
+* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
+* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
+* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
+* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
+ not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
+
+## Rust 1.43
+
+Released 2020-04-23
+
+[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
+
+### New lints
+
+* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
+* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
+* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
+* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
+* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
+* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
+* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
+* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
+* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
+
+### Moves and Deprecations
+
+* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
+
+### Enhancements
+
+* Make [`missing_errors_doc`] lint also trigger on `async` functions
+ [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
+* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
+* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
+
+### False Positive Fixes
+
+* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
+* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
+* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
+* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
+
+### Suggestion Improvements
+
+* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
+
+### ICE Fixes
+
+* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
+* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
+* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
+
+### Documentation
+
+* Improve documentation of [`iter_nth_zero`]
+* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
+
+### Others
+
+* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
+
+
+## Rust 1.42
+
+Released 2020-03-12
+
+[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
+
+### New lints
+
+* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
+* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
+* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
+* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
+* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
+* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
+* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
+* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
+* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
+* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
+
+### Moves and Deprecations
+
+* Move [`transmute_float_to_int`] from nursery to complexity group
+ [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
+* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
+* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
+
+### Enhancements
+
+* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
+* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
+* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
+* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
+* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
+
+### False Positive Fixes
+
+* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
+* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
+* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
+* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
+* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
+* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
+* Don't trigger [`let_underscore_must_use`] in external macros
+ [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
+* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
+
+### Suggestion Improvements
+
+* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
+* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
+* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
+* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
+* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
+* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
+* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
+* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
+
+### ICE fixes
+
+* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
+
+### Documentation
+
+* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
+
+
+## Rust 1.41
+
+Released 2020-01-30
+
+[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
+
+* New Lints:
+ * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
+ * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
+ * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
+ * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
+ * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
+ * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
+ * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
+ * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
+ * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
+* Remove plugin interface, see
+ [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
+ details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
+* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
+* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
+* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
+ [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
+* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
+* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
+* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
+* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
+* Fix false positive in `print_with_newline` and `write_with_newline`
+ [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
+* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
+* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
+* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
+* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
+* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
+* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
+* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
+* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
+* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
+* Display help when running `clippy-driver` without arguments, instead of ICEing
+ [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
+* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
+* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
+* Improve Documentation by adding positive examples to some lints
+ [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
+
+## Rust 1.40
+
+Released 2019-12-19
+
+[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
+
+* New Lints:
+ * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
+ * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
+ * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
+ * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
+ * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
+ * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
+ * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
+ * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
+ * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
+ * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+ * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
+* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
+* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
+* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
+* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
+* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
+* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
+* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
+* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
+* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
+* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
+* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
+* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
+* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
+* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
+* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
+* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
+* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
+
+## Rust 1.39
+
+Released 2019-11-07
+
+[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
+
+* New Lints:
+ * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
+ * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
+ * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
+ * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
+ * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
+ * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
+ * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
+* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
+* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
+* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
+* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
+* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
+* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
+* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
+* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
+* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
+* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
+* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
+* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
+* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
+* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
+* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
+* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
+* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
+* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
+* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
+* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
+* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
+* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
+* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
+* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
+* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
+* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
+* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
+* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
+* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
+
+## Rust 1.38
+
+Released 2019-09-26
+
+[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
+
+* New Lints:
+ * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
+ * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
+ * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
+ * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
+* Move `{unnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
+* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
+* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
+* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
+* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
+* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
+* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
+* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
+* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
+* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
+* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
+* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
+* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
+* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
+* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
+* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
+
+## Rust 1.37
+
+Released 2019-08-15
+
+[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
+
+* New Lints:
+ * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
+ * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
+ * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
+* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
+ The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
+* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
+* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
+* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
+* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
+* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
+* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
+* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
+* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
+* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
+* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
+* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
+
+## Rust 1.36
+
+Released 2019-07-04
+
+[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
+
+* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
+* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
+* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
+* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
+* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
+* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
+* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
+* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
+* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
+* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
+* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
+* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
+* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
+* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
+* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
+* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
+* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
+* Add macro check for [`unnecessary_cast`] [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
+* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
+* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
+* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
+
+
+## Rust 1.35
+
+Released 2019-05-20
+
+[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
+
+* New lint: `drop_bounds` to detect `T: Drop` bounds
+* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
+* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
+* Move [`get_unwrap`] to the restriction category
+* Improve suggestions for [`iter_cloned_collect`]
+* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
+* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
+* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
+* Fix false positive in [`bool_comparison`] pertaining to non-bool types
+* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
+* Fix false positive in `option_map_unwrap_or` on non-copy types
+* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
+* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
+* Fix false positive in [`needless_continue`] pertaining to loop labels
+* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
+* Fix false positive for [`use_self`] in nested functions
+* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
+* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
+* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
+* Avoid triggering [`redundant_closure`] in macros
+* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
+
+## Rust 1.34
+
+Released 2019-04-10
+
+[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
+
+* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
+* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
+* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
+* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
+ configured using the `too-many-lines-threshold` configuration.
+* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
+* Expand `redundant_closure` to also work for methods (not only functions)
+* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
+* Fix false positive in `cast_sign_loss`
+* Fix false positive in `integer_arithmetic`
+* Fix false positive in `unit_arg`
+* Fix false positives in `implicit_return`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `cast_lossless`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `needless_bool`
+* Fix incorrect suggestion for `needless_range_loop`
+* Fix incorrect suggestion for `use_self`
+* Fix incorrect suggestion for `while_let_on_iterator`
+* Clippy is now slightly easier to invoke in non-cargo contexts. See
+ [#3665][pull3665] for more details.
+* We now have [improved documentation][adding_lints] on how to add new lints
+
+## Rust 1.33
+
+Released 2019-02-26
+
+[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
+
+* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
+* The `rust-clippy` repository is now part of the `rust-lang` org.
+* Rename `stutter` to `module_name_repetitions`
+* Merge `new_without_default_derive` into `new_without_default` lint
+* Move `large_digit_groups` from `style` group to `pedantic`
+* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
+ comparisons against booleans
+* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
+* Expand `redundant_clone` to work on struct fields
+* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
+* Expand `use_self` to work on tuple structs and also in local macros
+* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
+* Fix false positives in `implicit_return`
+* Fix false positives in `use_self`
+* Fix false negative in `clone_on_copy`
+* Fix false positive in `doc_markdown`
+* Fix false positive in `empty_loop`
+* Fix false positive in `if_same_then_else`
+* Fix false positive in `infinite_iter`
+* Fix false positive in `question_mark`
+* Fix false positive in `useless_asref`
+* Fix false positive in `wildcard_dependencies`
+* Fix false positive in `write_with_newline`
+* Add suggestion to `explicit_write`
+* Improve suggestions for `question_mark` lint
+* Fix incorrect suggestion for `get_unwrap`
+
+## Rust 1.32
+
+Released 2019-01-17
+
+[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
+
+* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
+ [`redundant_clone`], [`wildcard_dependencies`],
+ [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
+ [`cargo_common_metadata`]
+* Add support for `u128` and `i128` to integer related lints
+* Add float support to `mistyped_literal_suffixes`
+* Fix false positives in `use_self`
+* Fix false positives in `missing_comma`
+* Fix false positives in `new_ret_no_self`
+* Fix false positives in `possible_missing_comma`
+* Fix false positive in `integer_arithmetic` in constant items
+* Fix false positive in `needless_borrow`
+* Fix false positive in `out_of_bounds_indexing`
+* Fix false positive in `new_without_default_derive`
+* Fix false positive in `string_lit_as_bytes`
+* Fix false negative in `out_of_bounds_indexing`
+* Fix false negative in `use_self`. It will now also check existential types
+* Fix incorrect suggestion for `redundant_closure_call`
+* Fix various suggestions that contained expanded macros
+* Fix `bool_comparison` triggering 3 times on on on the same code
+* Expand `trivially_copy_pass_by_ref` to work on trait methods
+* Improve suggestion for `needless_range_loop`
+* Move `needless_pass_by_value` from `pedantic` group to `style`
+
+## Rust 1.31
+
+Released 2018-12-06
+
+[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
+
+* Clippy has been relicensed under a dual MIT / Apache license.
+ See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
+ information.
+* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
+ installation method is via `rustup component add clippy`.
+* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
+ [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
+* Fix ICE in `if_let_redundant_pattern_matching`
+* Fix ICE in `needless_pass_by_value` when encountering a generic function
+ argument with a lifetime parameter
+* Fix ICE in `needless_range_loop`
+* Fix ICE in `single_char_pattern` when encountering a constant value
+* Fix false positive in `assign_op_pattern`
+* Fix false positive in `boxed_local` on trait implementations
+* Fix false positive in `cmp_owned`
+* Fix false positive in `collapsible_if` when conditionals have comments
+* Fix false positive in `double_parens`
+* Fix false positive in `excessive_precision`
+* Fix false positive in `explicit_counter_loop`
+* Fix false positive in `fn_to_numeric_cast_with_truncation`
+* Fix false positive in `map_clone`
+* Fix false positive in `new_ret_no_self`
+* Fix false positive in `new_without_default` when `new` is unsafe
+* Fix false positive in `type_complexity` when using extern types
+* Fix false positive in `useless_format`
+* Fix false positive in `wrong_self_convention`
+* Fix incorrect suggestion for `excessive_precision`
+* Fix incorrect suggestion for `expect_fun_call`
+* Fix incorrect suggestion for `get_unwrap`
+* Fix incorrect suggestion for `useless_format`
+* `fn_to_numeric_cast_with_truncation` lint can be disabled again
+* Improve suggestions for `manual_memcpy`
+* Improve help message for `needless_lifetimes`
+
+## Rust 1.30
+
+Released 2018-10-25
+
+[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
+
+* Deprecate `assign_ops` lint
+* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
+ [`needless_collect`], [`copy_iterator`]
+* `cargo clippy -V` now includes the Clippy commit hash of the Rust
+ Clippy component
+* Fix ICE in `implicit_hasher`
+* Fix ICE when encountering `println!("{}" a);`
+* Fix ICE when encountering a macro call in match statements
+* Fix false positive in `default_trait_access`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `similar_names`
+* Fix false positive in `redundant_field_name`
+* Fix false positive in `expect_fun_call`
+* Fix false negative in `identity_conversion`
+* Fix false negative in `explicit_counter_loop`
+* Fix `range_plus_one` suggestion and false negative
+* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
+* Fix `useless_attribute` to also whitelist `unused_extern_crates`
+* Fix incorrect suggestion for `single_char_pattern`
+* Improve suggestion for `identity_conversion` lint
+* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
+* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
+* Move `shadow_unrelated` from `restriction` group to `pedantic`
+* Move `indexing_slicing` from `pedantic` group to `restriction`
+
+## Rust 1.29
+
+Released 2018-09-13
+
+[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
+
+* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
+ :tada:
+ You can now run `rustup component add clippy-preview` and then `cargo
+ clippy` to run Clippy. This should put an end to the continuous nightly
+ upgrades for Clippy users.
+* Clippy now follows the Rust versioning scheme instead of its own
+* Fix ICE when encountering a `while let (..) = x.iter()` construct
+* Fix false positives in `use_self`
+* Fix false positive in `trivially_copy_pass_by_ref`
+* Fix false positive in `useless_attribute` lint
+* Fix false positive in `print_literal`
+* Fix `use_self` regressions
+* Improve lint message for `neg_cmp_op_on_partial_ord`
+* Improve suggestion highlight for `single_char_pattern`
+* Improve suggestions for various print/write macro lints
+* Improve website header
+
+## 0.0.212 (2018-07-10)
+* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
+
+## 0.0.211
+* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
+
+## 0.0.210
+* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
+
+## 0.0.209
+* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
+
+## 0.0.208
+* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
+
+## 0.0.207
+* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
+
+## 0.0.206
+* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
+
+## 0.0.205
+* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
+* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
+
+## 0.0.204
+* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
+
+## 0.0.203
+* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
+* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
+
+## 0.0.202
+* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
+
+## 0.0.201
+* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
+
+## 0.0.200
+* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
+
+## 0.0.199
+* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
+
+## 0.0.198
+* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
+
+## 0.0.197
+* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
+
+## 0.0.196
+* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
+
+## 0.0.195
+* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
+
+## 0.0.194
+* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
+* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
+
+## 0.0.193
+* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
+
+## 0.0.192
+* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
+* New lint: [`print_literal`]
+
+## 0.0.191
+* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
+* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
+
+## 0.0.190
+* Fix a bunch of intermittent cargo bugs
+
+## 0.0.189
+* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
+
+## 0.0.188
+* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
+* New lint: [`while_immutable_condition`]
+
+## 0.0.187
+* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
+* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
+
+## 0.0.186
+* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
+* Various false positive fixes
+
+## 0.0.185
+* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
+* New lint: [`question_mark`]
+
+## 0.0.184
+* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
+* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
+
+## 0.0.183
+* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
+* New lint: [`misaligned_transmute`]
+
+## 0.0.182
+* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
+* New lint: [`decimal_literal_representation`]
+
+## 0.0.181
+* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
+* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
+* Removed `unit_expr`
+* Various false positive fixes for [`needless_pass_by_value`]
+
+## 0.0.180
+* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
+
+## 0.0.179
+* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
+
+## 0.0.178
+* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
+
+## 0.0.177
+* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
+* New lint: [`match_as_ref`]
+
+## 0.0.176
+* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
+
+## 0.0.175
+* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
+
+## 0.0.174
+* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
+
+## 0.0.173
+* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
+
+## 0.0.172
+* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
+
+## 0.0.171
+* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
+
+## 0.0.170
+* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
+
+## 0.0.169
+* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
+* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
+
+## 0.0.168
+* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
+
+## 0.0.167
+* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
+* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
+
+## 0.0.166
+* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
+* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
+ [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
+ [`transmute_int_to_float`]
+
+## 0.0.165
+* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
+* New lint: [`mut_range_bound`]
+
+## 0.0.164
+* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
+* New lint: [`int_plus_one`]
+
+## 0.0.163
+* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
+
+## 0.0.162
+* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
+* New lint: [`chars_last_cmp`]
+* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
+
+## 0.0.161
+* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
+
+## 0.0.160
+* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
+
+## 0.0.159
+* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
+* New lint: [`clone_on_ref_ptr`]
+
+## 0.0.158
+* New lint: [`manual_memcpy`]
+* [`cast_lossless`] no longer has redundant parentheses in its suggestions
+* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
+
+## 0.0.157 - 2017-09-04
+* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
+* New lint: `unit_expr`
+
+## 0.0.156 - 2017-09-03
+* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
+
+## 0.0.155
+* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
+* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
+
+## 0.0.154
+* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
+* Fix [`use_self`] triggering inside derives
+* Add support for linting an entire workspace with `cargo clippy --all`
+* New lint: [`naive_bytecount`]
+
+## 0.0.153
+* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
+* New lint: [`use_self`]
+
+## 0.0.152
+* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
+
+## 0.0.151
+* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
+
+## 0.0.150
+* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
+
+## 0.0.148
+* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
+* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
+
+## 0.0.147
+* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
+
+## 0.0.146
+* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
+* Fixes false positives in `inline_always`
+* Fixes false negatives in `panic_params`
+
+## 0.0.145
+* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
+
+## 0.0.144
+* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
+
+## 0.0.143
+* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
+* Fix `cargo clippy` crashing on `dylib` projects
+* Fix false positives around `nested_while_let` and `never_loop`
+
+## 0.0.142
+* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
+
+## 0.0.141
+* Rewrite of the `doc_markdown` lint.
+* Deprecated [`range_step_by_zero`]
+* New lint: [`iterator_step_by_zero`]
+* New lint: [`needless_borrowed_reference`]
+* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
+
+## 0.0.140 - 2017-06-16
+* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
+
+## 0.0.139 — 2017-06-10
+* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
+* Fix bugs with for loop desugaring
+* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
+
+## 0.0.138 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
+
+## 0.0.137 — 2017-06-05
+* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
+
+## 0.0.136 — 2017—05—26
+* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
+
+## 0.0.135 — 2017—05—24
+* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
+
+## 0.0.134 — 2017—05—19
+* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
+
+## 0.0.133 — 2017—05—14
+* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
+
+## 0.0.132 — 2017—05—05
+* Fix various bugs and some ices
+
+## 0.0.131 — 2017—05—04
+* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
+
+## 0.0.130 — 2017—05—03
+* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
+
+## 0.0.129 — 2017-05-01
+* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
+
+## 0.0.128 — 2017-04-28
+* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
+
+## 0.0.127 — 2017-04-27
+* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
+* New lint: [`needless_continue`]
+
+## 0.0.126 — 2017-04-24
+* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
+
+## 0.0.125 — 2017-04-19
+* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
+
+## 0.0.124 — 2017-04-16
+* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
+
+## 0.0.123 — 2017-04-07
+* Fix various false positives
+
+## 0.0.122 — 2017-04-07
+* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
+* New lint: [`op_ref`]
+
+## 0.0.121 — 2017-03-21
+* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
+
+## 0.0.120 — 2017-03-17
+* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
+
+## 0.0.119 — 2017-03-13
+* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
+
+## 0.0.118 — 2017-03-05
+* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
+
+## 0.0.117 — 2017-03-01
+* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
+
+## 0.0.116 — 2017-02-28
+* Fix `cargo clippy` on 64 bit windows systems
+
+## 0.0.115 — 2017-02-27
+* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
+* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
+
+## 0.0.114 — 2017-02-08
+* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
+* Tests are now ui tests (testing the exact output of rustc)
+
+## 0.0.113 — 2017-02-04
+* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
+* New lint: [`large_enum_variant`]
+* `explicit_into_iter_loop` provides suggestions
+
+## 0.0.112 — 2017-01-27
+* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
+
+## 0.0.111 — 2017-01-21
+* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
+
+## 0.0.110 — 2017-01-20
+* Add badges and categories to `Cargo.toml`
+
+## 0.0.109 — 2017-01-19
+* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
+
+## 0.0.108 — 2017-01-12
+* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
+
+## 0.0.107 — 2017-01-11
+* Update regex dependency
+* Fix FP when matching `&&mut` by `&ref`
+* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
+* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
+
+## 0.0.106 — 2017-01-04
+* Fix FP introduced by rustup in [`wrong_self_convention`]
+
+## 0.0.105 — 2017-01-04
+* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
+* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
+* Fix suggestion in [`new_without_default`]
+* FP fix in [`absurd_extreme_comparisons`]
+
+## 0.0.104 — 2016-12-15
+* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
+
+## 0.0.103 — 2016-11-25
+* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
+
+## 0.0.102 — 2016-11-24
+* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
+
+## 0.0.101 — 2016-11-23
+* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
+* New lint: [`string_extend_chars`]
+
+## 0.0.100 — 2016-11-20
+* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
+
+## 0.0.99 — 2016-11-18
+* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
+* New lint: [`get_unwrap`]
+
+## 0.0.98 — 2016-11-08
+* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
+
+## 0.0.97 — 2016-11-03
+* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
+ previously added for a short time under the name `clippy` but removed for
+ compatibility.
+* `cargo clippy --help` is more helping (and less helpful :smile:)
+* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
+* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
+
+## 0.0.96 — 2016-10-22
+* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
+* New lint: [`iter_skip_next`]
+
+## 0.0.95 — 2016-10-06
+* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
+
+## 0.0.94 — 2016-10-04
+* Fixes bustage on Windows due to forbidden directory name
+
+## 0.0.93 — 2016-10-03
+* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
+* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
+ allowed by default.
+* New lint: [`explicit_into_iter_loop`]
+
+## 0.0.92 — 2016-09-30
+* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
+
+## 0.0.91 — 2016-09-28
+* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
+
+## 0.0.90 — 2016-09-09
+* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
+
+## 0.0.89 — 2016-09-06
+* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
+
+## 0.0.88 — 2016-09-04
+* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
+* The following lints are not new but were only usable through the `clippy`
+ lint groups: [`filter_next`], `for_loop_over_option`,
+ `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
+ able to `#[allow/deny]` them individually and they are available directly
+ through `cargo clippy`.
+
+## 0.0.87 — 2016-08-31
+* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
+* New lints: [`builtin_type_shadow`]
+* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
+
+## 0.0.86 — 2016-08-28
+* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
+* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
+
+## 0.0.85 — 2016-08-19
+* Fix ICE with [`useless_attribute`]
+* [`useless_attribute`] ignores `unused_imports` on `use` statements
+
+## 0.0.84 — 2016-08-18
+* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
+
+## 0.0.83 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
+* New lints: [`print_with_newline`], [`useless_attribute`]
+
+## 0.0.82 — 2016-08-17
+* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
+* New lint: [`module_inception`]
+
+## 0.0.81 — 2016-08-14
+* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
+* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
+* False positive fix in [`too_many_arguments`]
+* Addition of functionality to [`needless_borrow`]
+* Suggestions for [`clone_on_copy`]
+* Bug fix in [`wrong_self_convention`]
+* Doc improvements
+
+## 0.0.80 — 2016-07-31
+* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
+* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
+
+## 0.0.79 — 2016-07-10
+* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
+* Major suggestions refactoring
+
+## 0.0.78 — 2016-07-02
+* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
+* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
+* For compatibility, `cargo clippy` does not defines the `clippy` feature
+ introduced in 0.0.76 anymore
+* [`collapsible_if`] now considers `if let`
+
+## 0.0.77 — 2016-06-21
+* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
+* New lints: `stutter` and [`iter_nth`]
+
+## 0.0.76 — 2016-06-10
+* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
+* `cargo clippy` now automatically defines the `clippy` feature
+* New lint: [`not_unsafe_ptr_arg_deref`]
+
+## 0.0.75 — 2016-06-08
+* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
+
+## 0.0.74 — 2016-06-07
+* Fix bug with `cargo-clippy` JSON parsing
+* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
+ “for further information visit *lint-link*” message.
+
+## 0.0.73 — 2016-06-05
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.72 — 2016-06-04
+* Fix false positives in [`useless_let_if_seq`]
+
+## 0.0.71 — 2016-05-31
+* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
+* New lint: [`useless_let_if_seq`]
+
+## 0.0.70 — 2016-05-28
+* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
+* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
+ `RegexBuilder::new` and byte regexes
+
+## 0.0.69 — 2016-05-20
+* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
+* [`used_underscore_binding`] has been made `Allow` temporarily
+
+## 0.0.68 — 2016-05-17
+* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
+* New lint: [`unnecessary_operation`]
+
+## 0.0.67 — 2016-05-12
+* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
+
+## 0.0.66 — 2016-05-11
+* New `cargo clippy` subcommand
+* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
+
+## 0.0.65 — 2016-05-08
+* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
+* New lints: [`float_arithmetic`], [`integer_arithmetic`]
+
+## 0.0.64 — 2016-04-26
+* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
+* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
+
+## 0.0.63 — 2016-04-08
+* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
+
+## 0.0.62 — 2016-04-07
+* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
+
+## 0.0.61 — 2016-04-03
+* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
+* New lint: [`invalid_upcast_comparisons`]
+
+## 0.0.60 — 2016-04-01
+* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
+
+## 0.0.59 — 2016-03-31
+* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
+* New lints: [`logic_bug`], [`nonminimal_bool`]
+* Fixed: [`match_same_arms`] now ignores arms with guards
+* Improved: [`useless_vec`] now warns on `for … in vec![…]`
+
+## 0.0.58 — 2016-03-27
+* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
+* New lint: [`doc_markdown`]
+
+## 0.0.57 — 2016-03-27
+* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
+* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
+* New lint: [`crosspointer_transmute`]
+
+## 0.0.56 — 2016-03-23
+* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
+* New lints: [`many_single_char_names`] and [`similar_names`]
+
+## 0.0.55 — 2016-03-21
+* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
+
+## 0.0.54 — 2016-03-16
+* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
+
+## 0.0.53 — 2016-03-15
+* Add a [configuration file]
+
+## ~~0.0.52~~
+
+## 0.0.51 — 2016-03-13
+* Add `str` to types considered by [`len_zero`]
+* New lints: [`indexing_slicing`]
+
+## 0.0.50 — 2016-03-11
+* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
+
+## 0.0.49 — 2016-03-09
+* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
+* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
+
+## 0.0.48 — 2016-03-07
+* Fixed: ICE in [`needless_range_loop`] with globals
+
+## 0.0.47 — 2016-03-07
+* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
+* New lint: [`redundant_closure_call`]
+
+[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
+[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
+[configuration file]: ./rust-clippy#configuration
+[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
+[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
+
+<!-- lint disable no-unused-definitions -->
+<!-- begin autogenerated links to lint list -->
+[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
+[`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
+[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
+[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut
+[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
+[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
+[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
+[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
+[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
+[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
+[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
+[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
+[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
+[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
+[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
+[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
+[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
+[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
+[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
+[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
+[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
+[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
+[`bool_to_int_with_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_to_int_with_if
+[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
+[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
+[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
+[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
+[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
+[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default
+[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
+[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
+[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
+[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
+[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
+[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
+[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
+[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
+[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
+[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
+[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
+[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int
+[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
+[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
+[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
+[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
+[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
+[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
+[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
+[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
+[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
+[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
+[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
+[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
+[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
+[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
+[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
+[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
+[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
+[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
+[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
+[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
+[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
+[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
+[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
+[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
+[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
+[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
+[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
+[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
+[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
+[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
+[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
+[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
+[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
+[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
+[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
+[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
+[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
+[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
+[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
+[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
+[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
+[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
+[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
+[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
+[`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
+[`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
+[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
+[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
+[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
+[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
+[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
+[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
+[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
+[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
+[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
+[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
+[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
+[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
+[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
+[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
+[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
+[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
+[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
+[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
+[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
+[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
+[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
+[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
+[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
+[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
+[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
+[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
+[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
+[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
+[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
+[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
+[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
+[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
+[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
+[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
+[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
+[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
+[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
+[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
+[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
+[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
+[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
+[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
+[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
+[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
+[`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
+[`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
+[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
+[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
+[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
+[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
+[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
+[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
+[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
+[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
+[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
+[`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
+[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
+[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
+[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
+[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
+[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
+[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
+[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
+[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
+[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
+[`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
+[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
+[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
+[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
+[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
+[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
+[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
+[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
+[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
+[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
+[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
+[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
+[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
+[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
+[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
+[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add
+[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
+[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
+[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
+[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
+[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
+[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
+[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
+[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
+[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
+[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
+[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
+[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
+[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
+[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
+[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
+[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
+[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
+[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
+[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
+[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
+[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
+[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
+[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
+[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
+[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
+[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
+[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
+[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
+[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
+[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
+[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
+[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
+[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
+[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
+[`iter_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map
+[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
+[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
+[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
+[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
+[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
+[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
+[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
+[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
+[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
+[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
+[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
+[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
+[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
+[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
+[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
+[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
+[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
+[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
+[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
+[`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
+[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
+[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
+[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
+[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
+[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
+[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
+[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
+[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
+[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
+[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
+[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
+[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
+[`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
+[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
+[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
+[`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
+[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
+[`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
+[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
+[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
+[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
+[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
+[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
+[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
+[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
+[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
+[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
+[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
+[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
+[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
+[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
+[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
+[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
+[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
+[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
+[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
+[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
+[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
+[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
+[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
+[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
+[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
+[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
+[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
+[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
+[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
+[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
+[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
+[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
+[`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
+[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
+[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
+[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
+[`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
+[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
+[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
+[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
+[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
+[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
+[`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
+[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
+[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
+[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
+[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
+[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
+[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
+[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
+[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
+[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
+[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
+[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
+[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
+[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
+[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
+[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
+[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
+[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
+[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
+[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
+[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
+[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
+[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
+[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
+[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
+[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
+[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
+[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
+[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
+[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
+[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
+[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
+[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
+[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
+[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
+[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive
+[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
+[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
+[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
+[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
+[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
+[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
+[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
+[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
+[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
+[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
+[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
+[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
+[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
+[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
+[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
+[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
+[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
+[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
+[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
+[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
+[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
+[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
+[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
+[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
+[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
+[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
+[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
+[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
+[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
+[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
+[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
+[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
+[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
+[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
+[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
+[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
+[`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
+[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
+[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
+[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
+[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
+[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
+[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
+[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
+[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
+[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
+[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
+[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
+[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
+[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
+[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
+[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
+[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
+[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
+[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
+[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init
+[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
+[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
+[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
+[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
+[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
+[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
+[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
+[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
+[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
+[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
+[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
+[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
+[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
+[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
+[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
+[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
+[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
+[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
+[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
+[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
+[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
+[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
+[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
+[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
+[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
+[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
+[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
+[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
+[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
+[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
+[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
+[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
+[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
+[`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
+[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
+[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
+[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
+[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
+[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
+[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
+[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
+[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
+[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
+[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
+[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
+[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
+[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
+[`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
+[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
+[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
+[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
+[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
+[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
+[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
+[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
+[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
+[`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
+[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
+[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
+[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
+[`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter
+[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
+[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
+[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
+[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
+[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
+[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
+[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
+[`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
+[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
+[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
+[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
+[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
+[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
+[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
+[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
+[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
+[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
+[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
+[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
+[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
+[`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
+[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
+[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
+[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
+[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
+[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
+[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
+[`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
+[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
+[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
+[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
+[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
+[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
+[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
+[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
+[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
+[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
+[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
+[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
+[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
+[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
+[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
+[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
+[`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
+[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
+[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
+[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
+[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
+[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
+[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
+[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
+[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
+[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
+[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
+[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
+[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
+[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
+[`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
+[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
+[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
+[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
+[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
+[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
+[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
+[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
+[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
+[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
+[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
+[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
+[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
+[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
+[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
+[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
+[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
+[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
+[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
+[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
+[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
+[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
+[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
+[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
+[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
+[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
+[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
+[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
+[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
+[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
+[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
+[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
+[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
+[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
+[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
+[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
+[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
+[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
+[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
+[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
+[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
+[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
+[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
+[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
+<!-- end autogenerated links to lint list -->
--- /dev/null
- version = "0.1.68"
+[package]
+name = "clippy"
++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"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
+categories = ["development-tools", "development-tools::cargo-plugins"]
+build = "build.rs"
+edition = "2021"
+publish = false
+
+[[bin]]
+name = "cargo-clippy"
+test = false
+path = "src/main.rs"
+
+[[bin]]
+name = "clippy-driver"
+path = "src/driver.rs"
+
+[dependencies]
+clippy_lints = { path = "clippy_lints" }
+semver = "1.0"
+rustc_tools_util = "0.3.0"
+tempfile = { version = "3.2", optional = true }
+termize = "0.1"
+
+[dev-dependencies]
+compiletest_rs = { version = "0.9", features = ["tmp"] }
+tester = "0.9"
+regex = "1.5"
+toml = "0.5"
+walkdir = "2.3"
+# This is used by the `collect-metadata` alias.
+filetime = "0.2"
+
+# A noop dependency that changes in the Rust repository, it's a bit of a hack.
+# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
+# for more information.
+rustc-workspace-hack = "1.0"
+
+# UI test dependencies
+clippy_utils = { path = "clippy_utils" }
+derive-new = "0.5"
+if_chain = "1.0"
+itertools = "0.10.1"
+quote = "1.0"
+serde = { version = "1.0.125", features = ["derive"] }
+syn = { version = "1.0", features = ["full"] }
+futures = "0.3"
+parking_lot = "0.12"
+tokio = { version = "1", features = ["io-util"] }
+rustc-semver = "1.1"
+
+[build-dependencies]
+rustc_tools_util = "0.3.0"
+
+[features]
+deny-warnings = ["clippy_lints/deny-warnings"]
+integration = ["tempfile"]
+internal = ["clippy_lints/internal", "tempfile"]
+
+[package.metadata.rust-analyzer]
+# This package uses #[feature(rustc_private)]
+rustc_private = true
--- /dev/null
- cognitive-complexity-threshold = 30
+# Clippy
+
+[![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)
+
+Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
+You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
+
+| Category | Description | Default level |
+| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
+| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
+| `clippy::correctness` | code that is outright wrong or useless | **deny** |
+| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
+| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
+| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
+| `clippy::perf` | code that can be written to run faster | **warn** |
+| `clippy::pedantic` | lints which are rather strict or have occasional false positives | allow |
+| `clippy::nursery` | new lints that are still under development | allow |
+| `clippy::cargo` | lints for the cargo manifest | allow |
+
+More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
+
+The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
+for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
+very selectively, if at all.
+
+Table of contents:
+
+* [Usage instructions](#usage)
+* [Configuration](#configuration)
+* [Contributing](#contributing)
+* [License](#license)
+
+## Usage
+
+Below are instructions on how to use Clippy as a cargo subcommand,
+in projects that do not use cargo, or in Travis CI.
+
+### As a cargo subcommand (`cargo clippy`)
+
+One way to use Clippy is by installing Clippy through rustup as a cargo
+subcommand.
+
+#### Step 1: Install Rustup
+
+You can install [Rustup](https://rustup.rs/) on supported platforms. This will help
+us install Clippy and its dependencies.
+
+If you already have Rustup installed, update to ensure you have the latest
+Rustup and compiler:
+
+```terminal
+rustup update
+```
+
+#### Step 2: Install Clippy
+
+Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command:
+
+```terminal
+rustup component add clippy
+```
+If it says that it can't find the `clippy` component, please run `rustup self update`.
+
+#### Step 3: Run Clippy
+
+Now you can run Clippy by invoking the following command:
+
+```terminal
+cargo clippy
+```
+
+#### Automatically applying Clippy suggestions
+
+Clippy can automatically apply some lint suggestions, just like the compiler.
+
+```terminal
+cargo clippy --fix
+```
+
+#### Workspaces
+
+All the usual workspace options should work with Clippy. For example the following command
+will run Clippy on the `example` crate:
+
+```terminal
+cargo clippy -p example
+```
+
+As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies.
+If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this:
+
+```terminal
+cargo clippy -p example -- --no-deps
+```
+
+### Using `clippy-driver`
+
+Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver`
+with the same arguments you use for `rustc`. For example:
+
+```terminal
+clippy-driver --edition 2018 -Cpanic=abort foo.rs
+```
+
+Note that `clippy-driver` is designed for running Clippy only and should not be used as a general
+replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected,
+for example.
+
+### Travis CI
+
+You can add Clippy to Travis CI in the same way you use it locally:
+
+```yml
+language: rust
+rust:
+ - stable
+ - beta
+before_script:
+ - rustup component add clippy
+script:
+ - cargo clippy
+ # if you want the build job to fail when encountering warnings, use
+ - cargo clippy -- -D warnings
+ # in order to also check tests and non-default crate features, use
+ - cargo clippy --all-targets --all-features -- -D warnings
+ - cargo test
+ # etc.
+```
+
+Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
+That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
+an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
+line. (You can swap `clippy::all` with the specific lint category you are targeting.)
+
+## Configuration
+
+### Allowing/denying lints
+
+You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
+
+* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`).
+ Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
+
+* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
+ `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
+ lints prone to false positives.
+
+* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
+
+* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
+
+Note: `allow` means to suppress the lint for your code. With `warn` the lint
+will only emit a warning, while with `deny` the lint will emit an error, when
+triggering for your code. An error causes clippy to exit with an error code, so
+is useful in scripts like CI/CD.
+
+If you do not want to include your lint levels in your code, you can globally
+enable/disable lints by passing extra flags to Clippy during the run:
+
+To allow `lint_name`, run
+
+```terminal
+cargo clippy -- -A clippy::lint_name
+```
+
+And to warn on `lint_name`, run
+
+```terminal
+cargo clippy -- -W clippy::lint_name
+```
+
+This also works with lint groups. For example, you
+can run Clippy with warnings for all lints enabled:
+```terminal
+cargo clippy -- -W clippy::pedantic
+```
+
+If you care only about a single lint, you can allow all others and then explicitly warn on
+the lint(s) you are interested in:
+```terminal
+cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
+```
+
+### Configure the behavior of some lints
+
+Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
+value` mapping e.g.
+
+```toml
+avoid-breaking-exported-api = false
+disallowed-names = ["toto", "tata", "titi"]
- See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
- the lint descriptions contain the names and meanings of these configuration 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.
++
++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**
+>
+> `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.
+
+### Specifying the minimum supported Rust version
+
+Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
+specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
+
+```toml
+msrv = "1.30.0"
+```
+
+Alternatively, the [`rust-version` field](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field)
+in the `Cargo.toml` can be used.
+
+```toml
+# Cargo.toml
+rust-version = "1.30"
+```
+
+The MSRV can also be specified as an attribute, like below.
+
+```rust
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.30.0"]
+
+fn main() {
+ ...
+}
+```
+
+You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
+is equivalent to `msrv = 1.30.0`.
+
+Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly.
+
+Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
+
+## Contributing
+
+If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
+
+## License
+
+Copyright 2014-2022 The Rust Project Developers
+
+Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
+<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
+option. Files in the project may not be
+copied, modified, or distributed except according to those terms.
--- /dev/null
+# Summary
+
+[Introduction](README.md)
+
+- [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)
+ - [Travis CI](continuous_integration/travis.md)
+- [Development](development/README.md)
+ - [Basics](development/basics.md)
+ - [Adding Lints](development/adding_lints.md)
+ - [Common Tools](development/common_tools_writing_lints.md)
+ - [Infrastructure](development/infrastructure/README.md)
+ - [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md)
+ - [Backporting Changes](development/infrastructure/backport.md)
+ - [Updating the Changelog](development/infrastructure/changelog_update.md)
+ - [Release a New Version](development/infrastructure/release.md)
+ - [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)
--- /dev/null
- cognitive-complexity-threshold = 30
+# Configuring Clippy
+
+> **Note:** The configuration file is unstable and may be deprecated in the future.
+
+Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a
+basic `variable = value` mapping eg.
+
+```toml
+avoid-breaking-exported-api = false
+disallowed-names = ["toto", "tata", "titi"]
- See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
- the lint descriptions contain the names and meanings of these configuration 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.
+
+### Allowing/denying lints
+
+You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
+
+* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
+
+* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
+ `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive lints prone to false
+ positives.
+
+* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
+
+* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
+
+Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny`
+the lint will emit an error, when triggering for your code. An error causes clippy to exit with an error code, so is
+useful in scripts like CI/CD.
+
+If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra
+flags to Clippy during the run:
+
+To allow `lint_name`, run
+
+```terminal
+cargo clippy -- -A clippy::lint_name
+```
+
+And to warn on `lint_name`, run
+
+```terminal
+cargo clippy -- -W clippy::lint_name
+```
+
+This also works with lint groups. For example you can run Clippy with warnings for all lints enabled:
+
+```terminal
+cargo clippy -- -W clippy::pedantic
+```
+
+If you care only about a single lint, you can allow all others and then explicitly warn on the lint(s) you are
+interested in:
+
+```terminal
+cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
+```
+
+### Specifying the minimum supported Rust version
+
+Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the
+minimum supported Rust version (MSRV) in the clippy configuration file.
+
+```toml
+msrv = "1.30.0"
+```
+
+The MSRV can also be specified as an attribute, like below.
+
+```rust
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.30.0"]
+
+fn main() {
+ ...
+}
+```
+
+You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
+is equivalent to `msrv = 1.30.0`.
+
+Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
+
+Lints that recognize this configuration option can be
+found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
--- /dev/null
- new_lint` we will find by default two new crates, each with its manifest file:
+# Adding a new lint
+
+You are probably here because you want to add a new lint to Clippy. If this is
+the first time you're contributing to Clippy, this document guides you through
+creating an example lint from scratch.
+
+To get started, we will create a lint that detects functions called `foo`,
+because that's clearly a non-descriptive name.
+
+- [Adding a new lint](#adding-a-new-lint)
+ - [Setup](#setup)
+ - [Getting Started](#getting-started)
+ - [Defining Our Lint](#defining-our-lint)
+ - [Standalone](#standalone)
+ - [Specific Type](#specific-type)
+ - [Tests Location](#tests-location)
+ - [Testing](#testing)
+ - [Cargo lints](#cargo-lints)
+ - [Rustfix tests](#rustfix-tests)
+ - [Testing manually](#testing-manually)
+ - [Lint declaration](#lint-declaration)
+ - [Lint registration](#lint-registration)
+ - [Lint passes](#lint-passes)
+ - [Emitting a lint](#emitting-a-lint)
+ - [Adding the lint logic](#adding-the-lint-logic)
+ - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
+ - [Author lint](#author-lint)
+ - [Print HIR lint](#print-hir-lint)
+ - [Documentation](#documentation)
+ - [Running rustfmt](#running-rustfmt)
+ - [Debugging](#debugging)
+ - [PR Checklist](#pr-checklist)
+ - [Adding configuration to a lint](#adding-configuration-to-a-lint)
+ - [Cheat Sheet](#cheat-sheet)
+
+## Setup
+
+See the [Basics](basics.md#get-the-code) documentation.
+
+## Getting Started
+
+There is a bit of boilerplate code that needs to be set up when creating a new
+lint. Fortunately, you can use the Clippy dev tools to handle this for you. We
+are naming our new lint `foo_functions` (lints are generally written in snake
+case), and we don't need type information, so it will have an early pass type
+(more on this later). If you're unsure if the name you chose fits the lint,
+take a look at our [lint naming guidelines][lint_naming].
+
+## Defining Our Lint
+To get started, there are two ways to define our lint.
+
+### Standalone
+Command: `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
+(category will default to nursery if not provided)
+
+This command will create a new file: `clippy_lints/src/foo_functions.rs`, as well
+as [register the lint](#lint-registration).
+
+### Specific Type
+Command: `cargo dev new_lint --name=foo_functions --type=functions --category=pedantic`
+
+This command will create a new file: `clippy_lints/src/{type}/foo_functions.rs`.
+
+Notice how this command has a `--type` flag instead of `--pass`. Unlike a standalone
+definition, this lint won't be registered in the traditional sense. Instead, you will
+call your lint from within the type's lint pass, found in `clippy_lints/src/{type}/mod.rs`.
+
+A "type" is just the name of a directory in `clippy_lints/src`, like `functions` in
+the example command. These are groupings of lints with common behaviors, so if your
+lint falls into one, it would be best to add it to that type.
+
+### Tests Location
+Both commands will create a file: `tests/ui/foo_functions.rs`. For cargo lints,
+two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
+
+Next, we'll open up these files and add our lint!
+
+## Testing
+
+Let's write some tests first that we can execute while we iterate on our lint.
+
+Clippy uses UI tests for testing. UI tests check that the output of Clippy is
+exactly as expected. Each test is just a plain Rust file that contains the code
+we want to check. The output of Clippy is compared against a `.stderr` file.
+Note that you don't have to create this file yourself, we'll get to generating
+the `.stderr` files further down.
+
+We start by opening the test file created at `tests/ui/foo_functions.rs`.
+
+Update the file with some examples to get started:
+
+```rust
+#![allow(unused)]
+#![warn(clippy::foo_functions)]
+
+// Impl methods
+struct A;
+impl A {
+ pub fn fo(&self) {}
+ pub fn foo(&self) {}
+ pub fn food(&self) {}
+}
+
+// Default trait methods
+trait B {
+ fn fo(&self) {}
+ fn foo(&self) {}
+ fn food(&self) {}
+}
+
+// Plain functions
+fn fo() {}
+fn foo() {}
+fn food() {}
+
+fn main() {
+ // We also don't want to lint method calls
+ foo();
+ let a = A;
+ a.foo();
+}
+```
+
+Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently
+this test is meaningless though.
+
+While we are working on implementing our lint, we can keep running the UI test.
+That allows us to check if the output is turning into what we want.
+
+Once we are satisfied with the output, we need to run `cargo dev bless` to
+update the `.stderr` file for our lint. Please note that, we should run
+`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev
+bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we
+commit our lint, we need to commit the generated `.stderr` files, too. In
+general, you should only commit files changed by `cargo dev bless` for the
+specific lint you are creating/editing. Note that if the generated files are
+empty, they should be removed.
+
+> _Note:_ you can run multiple test files by specifying a comma separated list:
+> `TESTNAME=foo_functions,test2,test3`.
+
+### Cargo lints
+
+For cargo lints, the process of testing differs in that we are interested in the
+`Cargo.toml` manifest file. We also need a minimal crate associated with that
+manifest.
+
+If our new lint is named e.g. `foo_categories`, after running `cargo dev
++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.
+* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger
+ the lint.
+
+If you need more cases, you can copy one of those crates (under
+`foo_categories`) and rename it.
+
+The process of generating the `.stderr` file is the same, and prepending the
+`TESTNAME` variable to `cargo uitest` works too.
+
+## Rustfix tests
+
+If the lint you are working on is making use of structured suggestions, the test
+file should include a `// run-rustfix` comment at the top. This will
+additionally run [rustfix] for that test. Rustfix will apply the suggestions
+from the lint to the code of the test file and compare that to the contents of a
+`.fixed` file.
+
+Use `cargo dev bless` to automatically generate the `.fixed` file after running
+the tests.
+
+[rustfix]: https://github.com/rust-lang/rustfix
+
+## Testing manually
+
+Manually testing against an example file can be useful if you have added some
+`println!`s and the test suite output becomes unreadable. To try Clippy with
+your local modifications, run
+
+```
+cargo dev lint input.rs
+```
+
+from the working copy root. With tests in place, let's have a look at
+implementing our lint now.
+
+## Lint declaration
+
+Let's start by opening the new file created in the `clippy_lints` crate at
+`clippy_lints/src/foo_functions.rs`. That's the crate where all the lint code
+is. This file has already imported some initial things we will need:
+
+```rust
+use rustc_lint::{EarlyLintPass, EarlyContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_ast::ast::*;
+```
+
+The next step is to update the lint declaration. Lints are declared using the
+[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
+the auto-generated lint declaration to have a real description, something like
+this:
+
+```rust
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// ### Why is this bad?
+ ///
+ /// ### Example
+ /// ```rust
+ /// // example code
+ /// ```
+ #[clippy::version = "1.29.0"]
+ pub FOO_FUNCTIONS,
+ pedantic,
+ "function named `foo`, which is not a descriptive name"
+}
+```
+
+* The section of lines prefixed with `///` constitutes the lint documentation
+ section. This is the default documentation style and will be displayed [like
+ this][example_lint_page]. To render and open this documentation locally in a
+ browser, run `cargo dev serve`.
+* The `#[clippy::version]` attribute will be rendered as part of the lint
+ documentation. The value should be set to the current Rust version that the
+ lint is developed in, it can be retrieved by running `rustc -vV` in the
+ rust-clippy directory. The version is listed under *release*. (Use the version
+ without the `-nightly`) suffix.
+* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the [lint naming
+ guidelines][lint_naming] here when naming your lint. In short, the name should
+ state the thing that is being checked for and read well when used with
+ `allow`/`warn`/`deny`.
+* `pedantic` sets the lint level to `Allow`. The exact mapping can be found
+ [here][category_level_mapping]
+* The last part should be a text that explains what exactly is wrong with the
+ code
+
+The rest of this file contains an empty implementation for our lint pass, which
+in this case is `EarlyLintPass` and should look like this:
+
+```rust
+// clippy_lints/src/foo_functions.rs
+
+// .. imports and lint declaration ..
+
+declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
+
+impl EarlyLintPass for FooFunctions {}
+```
+
+[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
+[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
+[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
+[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
+
+## Lint registration
+
+When using `cargo dev new_lint`, the lint is automatically registered and
+nothing more has to be done.
+
+When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
+pass may have to be registered manually in the `register_plugins` function in
+`clippy_lints/src/lib.rs`:
+
+```rust
+store.register_early_pass(|| Box::new(foo_functions::FooFunctions));
+```
+
+As one may expect, there is a corresponding `register_late_pass` method
+available as well. Without a call to one of `register_early_pass` or
+`register_late_pass`, the lint pass in question will not be run.
+
+One reason that `cargo dev update_lints` does not automate this step is that
+multiple lints can use the same lint pass, so registering the lint pass may
+already be done when adding a new lint. Another reason that this step is not
+automated is that the order that the passes are registered determines the order
+the passes actually run, which in turn affects the order that any emitted lints
+are output in.
+
+## Lint passes
+
+Writing a lint that only checks for the name of a function means that we only
+have to deal with the AST and don't have to deal with the type system at all.
+This is good, because it makes writing this particular lint less complicated.
+
+We have to make this decision with every new Clippy lint. It boils down to using
+either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
+
+In short, the `LateLintPass` has access to type information while the
+`EarlyLintPass` doesn't. If you don't need access to type information, use the
+`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
+hasn't really been a concern with Clippy so far.
+
+Since we don't need type information for checking the function name, we used
+`--pass=early` when running the new lint automation and all the imports were
+added accordingly.
+
+[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
+[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
+
+## Emitting a lint
+
+With UI tests and the lint declaration in place, we can start working on the
+implementation of the lint logic.
+
+Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
+
+```rust
+impl EarlyLintPass for FooFunctions {
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
+ // TODO: Emit lint here
+ }
+}
+```
+
+We implement the [`check_fn`][check_fn] method from the
+[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
+information about the function that is currently being checked. More on that in
+the next section. Let's worry about the details later and emit our lint for
+*every* function definition first.
+
+Depending on how complex we want our lint message to be, we can choose from a
+variety of lint emission functions. They can all be found in
+[`clippy_utils/src/diagnostics.rs`][diagnostics].
+
+`span_lint_and_help` seems most appropriate in this case. It allows us to
+provide an extra help message and we can't really suggest a better name
+automatically. This is how it looks:
+
+```rust
+impl EarlyLintPass for FooFunctions {
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
+ span_lint_and_help(
+ cx,
+ FOO_FUNCTIONS,
+ span,
+ "function named `foo`",
+ None,
+ "consider using a more meaningful name"
+ );
+ }
+}
+```
+
+Running our UI test should now produce output that contains the lint message.
+
+According to [the rustc-dev-guide], the text should be matter of fact and avoid
+capitalization and periods, unless multiple sentences are needed. When code or
+an identifier must appear in a message or label, it should be surrounded with
+single grave accents \`.
+
+[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
+[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
+[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
+
+## Adding the lint logic
+
+Writing the logic for your lint will most likely be different from our example,
+so this section is kept rather short.
+
+Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
+that has the [`FnKind::Fn`] variant. It provides access to the name of the
+function/method via an [`Ident`][ident].
+
+With that we can expand our `check_fn` method to:
+
+```rust
+impl EarlyLintPass for FooFunctions {
+ fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
+ if is_foo_fn(fn_kind) {
+ span_lint_and_help(
+ cx,
+ FOO_FUNCTIONS,
+ span,
+ "function named `foo`",
+ None,
+ "consider using a more meaningful name"
+ );
+ }
+ }
+}
+```
+
+We separate the lint conditional from the lint emissions because it makes the
+code a bit easier to read. In some cases this separation would also allow to
+write some unit tests (as opposed to only UI tests) for the separate function.
+
+In our example, `is_foo_fn` looks like:
+
+```rust
+// use statements, impl EarlyLintPass, check_fn, ..
+
+fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
+ match fn_kind {
+ FnKind::Fn(_, ident, ..) => {
+ // check if `fn` name is `foo`
+ ident.name.as_str() == "foo"
+ }
+ // ignore closures
+ FnKind::Closure(..) => false
+ }
+}
+```
+
+Now we should also run the full test suite with `cargo test`. At this point
+running `cargo test` should produce the expected output. Remember to run `cargo
+dev bless` to update the `.stderr` file.
+
+`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
+implementation is not violating any Clippy lints itself.
+
+That should be it for the lint implementation. Running `cargo test` should now
+pass.
+
+[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
+[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
+[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
+
+## Specifying the lint's minimum supported Rust version (MSRV)
+
+Sometimes a lint makes suggestions that require a certain version of Rust. For
+example, the `manual_strip` lint suggests using `str::strip_prefix` and
+`str::strip_suffix` which is only available after Rust 1.45. In such cases, you
+need to ensure that the MSRV configured for the project is >= the MSRV of the
+required Rust feature. If multiple features are required, just use the one with
+a lower MSRV.
+
+First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`].
+This can be accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
+
+```rust
+msrv_aliases! {
+ ..
+ 1,45,0 { STR_STRIP_PREFIX }
+}
+```
+
+In order to access the project-configured MSRV, you need to have an `msrv` field
+in the LintPass struct, and a constructor to initialize the field. The `msrv`
+value is passed to the constructor in `clippy_lints/lib.rs`.
+
+```rust
+pub struct ManualStrip {
+ msrv: Msrv,
+}
+
+impl ManualStrip {
+ #[must_use]
+ 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 `Msrv::meets` method.
+
+``` rust
+if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
+ return;
+}
+```
+
+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`.
+
+```rust
+impl<'tcx> LateLintPass<'tcx> for ManualStrip {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ ...
+ }
+ extract_msrv_attr!(LateContext);
+}
+```
+
+Once the `msrv` is added to the lint, a relevant test case should be added to
+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`:
+
+```rust
+define_Conf! {
+ /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
+ (msrv: Option<String> = None),
+ ...
+}
+```
+
+[`clippy_utils::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/msrvs/index.html
+
+## Author lint
+
+If you have trouble implementing your lint, there is also the internal `author`
+lint to generate Clippy code that detects the offending pattern. It does not
+work for all of the Rust syntax, but can give a good starting point.
+
+The quickest way to use it, is the [Rust playground:
+play.rust-lang.org][author_example]. Put the code you want to lint into the
+editor and add the `#[clippy::author]` attribute above the item. Then run Clippy
+via `Tools -> Clippy` and you should see the generated code in the output below.
+
+[Here][author_example] is an example on the playground.
+
+If the command was executed successfully, you can copy the code over to where
+you are implementing your lint.
+
+[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
+
+## Print HIR lint
+
+To implement a lint, it's helpful to first understand the internal
+representation that rustc uses. Clippy has the `#[clippy::dump]` attribute that
+prints the [_High-Level Intermediate Representation (HIR)_] of the item,
+statement, or expression that the attribute is attached to. To attach the
+attribute to expressions you often need to enable
+`#![feature(stmt_expr_attributes)]`.
+
+[Here][print_hir_example] you can find an example, just select _Tools_ and run
+_Clippy_.
+
+[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
+[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
+
+## Documentation
+
+The final thing before submitting our PR is to add some documentation to our
+lint declaration.
+
+Please document your lint with a doc comment akin to the following:
+
+```rust
+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,ignore
+ /// // A short example of code that triggers the lint
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// // A short example of improved code that doesn't trigger the lint
+ /// ```
+ #[clippy::version = "1.29.0"]
+ pub FOO_FUNCTIONS,
+ pedantic,
+ "function named `foo`, which is not a descriptive name"
+}
+```
+
+Once your lint is merged, this documentation will show up in the [lint
+list][lint_list].
+
+[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
+
+## Running rustfmt
+
+[Rustfmt] is a tool for formatting Rust code according to style guidelines. Your
+code has to be formatted by `rustfmt` before a PR can be merged. Clippy uses
+nightly `rustfmt` in the CI.
+
+It can be installed via `rustup`:
+
+```bash
+rustup component add rustfmt --toolchain=nightly
+```
+
+Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
+installed for the nightly toolchain.
+
+[Rustfmt]: https://github.com/rust-lang/rustfmt
+
+## Debugging
+
+If you want to debug parts of your lint implementation, you can use the [`dbg!`]
+macro anywhere in your code. Running the tests should then include the debug
+output in the `stdout` part.
+
+[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
+
+## PR Checklist
+
+Before submitting your PR make sure you followed all of the basic requirements:
+
+<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
+
+- \[ ] Followed [lint naming conventions][lint_naming]
+- \[ ] Added passing UI tests (including committed `.stderr` file)
+- \[ ] `cargo test` passes locally
+- \[ ] Executed `cargo dev update_lints`
+- \[ ] Added lint documentation
+- \[ ] Run `cargo dev fmt`
+
+## Adding configuration to a lint
+
+Clippy supports the configuration of lints values using a `clippy.toml` file in
+the workspace directory. Adding a configuration to a lint can be useful for
+thresholds or to constrain some behavior that can be seen as a false positive
+for some users. Adding a configuration is done in the following steps:
+
+1. Adding a new configuration entry to [`clippy_lints::utils::conf`] like this:
+
+ ```rust
+ /// Lint: LINT_NAME.
+ ///
+ /// <The configuration field doc comment>
+ (configuration_ident: Type = DefaultValue),
+ ```
+
+ The doc comment is automatically added to the documentation of the listed
+ lints. The default value will be formatted using the `Debug` implementation
+ of the type.
+2. Adding the configuration value to the lint impl struct:
+ 1. This first requires the definition of a lint impl struct. Lint impl
+ structs are usually generated with the `declare_lint_pass!` macro. This
+ struct needs to be defined manually to add some kind of metadata to it:
+ ```rust
+ // Generated struct definition
+ declare_lint_pass!(StructName => [
+ LINT_NAME
+ ]);
+
+ // New manual definition struct
+ #[derive(Copy, Clone)]
+ pub struct StructName {}
+
+ impl_lint_pass!(StructName => [
+ LINT_NAME
+ ]);
+ ```
+
+ 2. Next add the configuration value and a corresponding creation method like
+ this:
+ ```rust
+ #[derive(Copy, Clone)]
+ pub struct StructName {
+ configuration_ident: Type,
+ }
+
+ // ...
+
+ impl StructName {
+ pub fn new(configuration_ident: Type) -> Self {
+ Self {
+ configuration_ident,
+ }
+ }
+ }
+ ```
+3. Passing the configuration value to the lint impl struct:
+
+ First find the struct construction in the [`clippy_lints` lib file]. The
+ configuration value is now cloned or copied into a local value that is then
+ passed to the impl struct like this:
+
+ ```rust
+ // Default generated registration:
+ store.register_*_pass(|| box module::StructName);
+
+ // New registration with configuration value
+ let configuration_ident = conf.configuration_ident.clone();
+ store.register_*_pass(move || box module::StructName::new(configuration_ident));
+ ```
+
+ Congratulations the work is almost done. The configuration value can now be
+ accessed in the linting code via `self.configuration_ident`.
+
+4. Adding tests:
+ 1. The default configured value can be tested like any normal lint in
+ [`tests/ui`].
+ 2. The configuration itself will be tested separately in [`tests/ui-toml`].
+ Simply add a new subfolder with a fitting name. This folder contains a
+ `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
+[`tests/ui-toml`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui-toml
+
+## Cheat Sheet
+
+Here are some pointers to things you are likely going to need for every lint:
+
+* [Clippy utils][utils] - Various helper functions. Maybe the function you need
+ is already in here ([`is_type_diagnostic_item`], [`implements_trait`],
+ [`snippet`], etc)
+* [Clippy diagnostics][diagnostics]
+* [Let chains][let-chains]
+* [`from_expansion`][from_expansion] and
+ [`in_external_macro`][in_external_macro]
+* [`Span`][span]
+* [`Applicability`][applicability]
+* [Common tools for writing lints](common_tools_writing_lints.md) helps with
+ common operations
+* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler
+ concepts
+* [The nightly rustc docs][nightly_docs] which has been linked to throughout
+ this guide
+
+For `EarlyLintPass` lints:
+
+* [`EarlyLintPass`][early_lint_pass]
+* [`rustc_ast::ast`][ast]
+
+For `LateLintPass` lints:
+
+* [`LateLintPass`][late_lint_pass]
+* [`Ty::TyKind`][ty]
+
+While most of Clippy's lint utils are documented, most of rustc's internals lack
+documentation currently. This is unfortunate, but in most cases you can probably
+get away with copying things from existing similar lints. If you are stuck,
+don't hesitate to ask on [Zulip] or in the issue/PR.
+
+[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
+[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
+[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
+[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
+[let-chains]: https://github.com/rust-lang/rust/pull/94927
+[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
+[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
+[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
+[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
+[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
+[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
+[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
+[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
+[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
--- /dev/null
- [mdbook](https://github.com/rust-lang/mdBook).
+# The Clippy Book
+
+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
- - [Get mdbook](#get-mdbook)
++[mdBook](https://github.com/rust-lang/mdBook).
+
- ## Get mdbook
++- [Get mdBook](#get-mdbook)
+- [Make changes](#make-changes)
+
- files, having mdbook locally will allow you to build, test and serve the book
++## Get mdBook
+
+While not strictly necessary since the book source is simply Markdown text
- See the mdbook [installation](https://github.com/rust-lang/mdBook#installation)
++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:
+
+```shell
+cargo install mdbook
+```
+
- want to see your changes in real time, you can use the mdbook `serve` command to
++See the mdBook [installation](https://github.com/rust-lang/mdBook#installation)
+instructions for other options.
+
+## Make changes
+
+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
- For more information, see the mdbook
++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:
+
+```shell
+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
+[guide](https://rust-lang.github.io/mdBook/).
--- /dev/null
- 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.
+# Changelog Update
+
+If you want to help with updating the [changelog], you're in the right place.
+
+## When to update
+
+Typos and other small fixes/additions are _always_ welcome.
+
+Special care needs to be taken when it comes to updating the changelog for a new
+Rust release. For that purpose, the changelog is ideally updated during the week
+before an upcoming stable release. You can find the release dates on the [Rust
+Forge][forge].
+
+Most of the time we only need to update the changelog for minor Rust releases.
+It's been very rare that Clippy changes were included in a patch release.
+
+## Changelog update walkthrough
+
+### 1. Finding the relevant Clippy commits
+
+Each Rust release ships with its own version of Clippy. The Clippy subtree can
+be found in the `tools` directory of the Rust repository.
+
+Depending on the current time and what exactly you want to update, the following
+bullet points might be helpful:
+
+* When writing the release notes for the **upcoming stable release** you need to
+ check out the Clippy commit of the current Rust `beta` branch.
+ [Link][rust_beta_tools]
+* When writing the release notes for the **upcoming beta release**, you need to
+ check out the Clippy commit of the current Rust `master`.
+ [Link][rust_master_tools]
+* When writing the (forgotten) release notes for a **past stable release**, you
+ need to check out the Rust release tag of the stable release.
+ [Link][rust_stable_tools]
+
+Usually you want to write the changelog of the **upcoming stable release**. Make
+sure though, that `beta` was already branched in the Rust repository.
+
+To find the commit hash, issue the following command when in a `rust-lang/rust`
+checkout:
+```
+git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g"
+```
+
+### 2. Fetching the PRs between those commits
+
+Once you've got the correct commit range, run
+
+```
+util/fetch_prs_between.sh commit1 commit2 > changes.txt
+```
+
+and open that file in your editor of choice.
+
+When updating the changelog it's also a good idea to make sure that `commit1` is
+already correct in the current changelog.
+
+### 3. Authoring the final changelog
+
+The above script should have dumped all the relevant PRs to the file you
+specified. It should have filtered out most of the irrelevant PRs already, but
+it's a good idea to do a manual cleanup pass where you look for more irrelevant
+PRs. If you're not sure about some PRs, just leave them in for the review and
+ask for feedback.
+
+With the PRs filtered, you can start to take each PR and move the `changelog: `
+content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it
+somewhat coherent.
+
+The order should roughly be:
+
+1. New lints
+2. Moves or deprecations of lints
+3. Changes that expand what code existing lints cover
+4. False positive fixes
+5. Suggestion fixes/improvements
+6. ICE fixes
+7. Documentation improvements
+8. Others
+
+As section headers, we use:
+
+```
+### New Lints
+### Moves and Deprecations
+### Enhancements
+### False Positive Fixes
+### Suggestion Fixes/Improvements
+### ICE Fixes
+### Documentation Improvements
+### Others
+```
+
+Please also be sure to update the Beta/Unreleased sections at the top with the
+relevant commit ranges.
+
++#### 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+
--- /dev/null
--- /dev/null
++<!--
++This file is generated by `cargo collect-metadata`.
++Please use that command to update the file and do not edit it by hand.
++-->
++
++## Lint Configuration Options
++| <div style="width:290px">Option</div> | 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` |
++| [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<String>`)
++
++* [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<String>`)
++
++* [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<String>`)
++
++* [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)
++
++
++### 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<String>`)
++
++* [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<String>`)
++
++* [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<u64>`)
++
++* [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_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move)
++
++
++### 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<crate::utils::conf::DisallowedPath>`)
++
++* [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<crate::utils::conf::DisallowedPath>`)
++
++* [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<crate::utils::conf::DisallowedPath>`)
++
++* [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<crate::nonstandard_macro_braces::MacroMatcher>`)
++
++* [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<crate::utils::conf::Rename>`)
++
++* [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<String>`)
++
++* [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)
++
++
++### 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<String>`)
++
++* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key)
++
++
++### 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
++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)
++
++
++
--- /dev/null
- version = "0.1.68"
+[package]
+name = "clippy_lints"
++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"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "plugin"]
+edition = "2021"
+
+[dependencies]
+cargo_metadata = "0.14"
+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 }
+quine-mc_cluskey = "0.2"
+regex-syntax = "0.6"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = { version = "1.0", optional = true }
+tempfile = { version = "3.2", optional = true }
+toml = "0.5"
+unicode-normalization = "0.1"
+unicode-script = { version = "0.5", default-features = false }
+semver = "1.0"
+rustc-semver = "1.1"
+# NOTE: cargo requires serde feat in its url dep
+# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
+url = { version = "2.2", features = ["serde"] }
+
+[features]
+deny-warnings = ["clippy_utils/deny-warnings"]
+# build clippy with internal lints enabled, off by default
+internal = ["clippy_utils/internal", "serde_json", "tempfile"]
+
+[package.metadata.rust-analyzer]
+# This crate uses #[feature(rustc_private)]
+rustc_private = true
--- /dev/null
- use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
++use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
- use rustc_lint::{LateContext, LateLintPass};
- use rustc_middle::ty;
++use clippy_utils::ty::{implements_trait, is_copy};
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, Lit};
- fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
- let ty = cx.typeck_results().expr_ty(e);
-
++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;
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// assert_eq!("a".is_empty(), false);
+ /// assert_ne!("a".is_empty(), true);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// assert!(!"a".is_empty());
+ /// ```
+ #[clippy::version = "1.53.0"]
+ pub BOOL_ASSERT_COMPARISON,
+ style,
+ "Using a boolean as comparison value in an assert_* macro when there is no need"
+}
+
+declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
+
+fn is_bool_lit(e: &Expr<'_>) -> bool {
+ matches!(
+ e.kind,
+ ExprKind::Lit(Lit {
+ node: LitKind::Bool(_),
+ ..
+ })
+ ) && !e.span.from_expansion()
+}
+
- if !(is_bool_lit(a) ^ is_bool_lit(b)) {
++fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ cx.tcx
+ .lang_items()
+ .not_trait()
+ .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[]))
+ .and_then(|trait_id| {
+ cx.tcx.associated_items(trait_id).find_by_name_and_kind(
+ cx.tcx,
+ Ident::from_str("Output"),
+ ty::AssocKind::Type,
+ trait_id,
+ )
+ })
+ .map_or(false, |assoc_item| {
+ 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()
+ })
+}
+
+impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+ let macro_name = cx.tcx.item_name(macro_call.def_id);
+ if !matches!(
+ macro_name.as_str(),
+ "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne"
+ ) {
+ return;
+ }
+ let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
- return;
- }
++
++ 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!
- if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
++ _ => return,
++ };
+
- span_lint_and_sugg(
++ 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];
- "replace it with",
- format!("{non_eq_mac}!(..)"),
- Applicability::MaybeIncorrect,
++ span_lint_and_then(
+ cx,
+ BOOL_ASSERT_COMPARISON,
+ macro_call.span,
+ &format!("used `{macro_name}!` with a literal bool"),
++ |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,
++ );
++ },
+ );
+ }
+}
--- /dev/null
- use clippy_utils::diagnostics::span_lint;
+use clippy_utils::consts::{constant, Constant};
- pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++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_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};
+
+fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
+ if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
+ Some(c)
+ } else {
+ None
+ }
+}
+
+fn get_constant_bits(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u64> {
+ constant_int(cx, expr).map(|c| u64::from(128 - c.leading_zeros()))
+}
+
+fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: bool) -> u64 {
+ match expr_or_init(cx, expr).kind {
+ ExprKind::Cast(inner, _) => apply_reductions(cx, nbits, inner, signed),
+ ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
+ ExprKind::Binary(op, left, right) => match op.node {
+ BinOpKind::Div => {
+ apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
+ // let's be conservative here
+ 0
+ } else {
+ // by dividing by 1, we remove 0 bits, etc.
+ get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
+ })
+ },
+ BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
+ .unwrap_or(u64::max_value())
+ .min(apply_reductions(cx, nbits, left, signed)),
+ BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
+ .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
+ _ => nbits,
+ },
+ ExprKind::MethodCall(method, left, [right], _) => {
+ if signed {
+ return nbits;
+ }
+ let max_bits = if method.ident.as_str() == "min" {
+ get_constant_bits(cx, right)
+ } else {
+ None
+ };
+ apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
+ },
+ ExprKind::MethodCall(method, _, [lo, hi], _) => {
+ if method.ident.as_str() == "clamp" {
+ //FIXME: make this a diagnostic item
+ if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
+ return lo_bits.max(hi_bits);
+ }
+ }
+ nbits
+ },
+ ExprKind::MethodCall(method, _value, [], _) => {
+ if method.ident.name.as_str() == "signum" {
+ 0 // do not lint if cast comes from a `signum` function
+ } else {
+ nbits
+ }
+ },
+ _ => nbits,
+ }
+}
+
- format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
++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(
+ cx,
+ utils::int_ty_to_nbits(cast_from, cx.tcx),
+ cast_expr,
+ cast_from.is_signed(),
+ );
+ let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
+
+ let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
+ (true, true) | (false, false) => (to_nbits < from_nbits, ""),
+ (true, false) => (
+ to_nbits <= 32,
+ if to_nbits == 32 {
+ " on targets with 64-bit wide pointers"
+ } else {
+ ""
+ },
+ ),
+ (false, true) => (from_nbits == 64, " on targets with 32-bit wide pointers"),
+ };
+
+ if !should_lint {
+ return;
+ }
+
+ format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
+ },
+
+ (ty::Adt(def, _), true) if def.is_enum() => {
+ let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind
+ && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id)
+ {
+ let i = def.variant_index_with_ctor_id(id);
+ let variant = def.variant(i);
+ let nbits = utils::enum_value_nbits(get_discriminant_value(cx.tcx, *def, i));
+ (nbits, Some(variant))
+ } else {
+ (utils::enum_ty_to_nbits(*def, cx.tcx), None)
+ };
+ 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, 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 => "",
+ (false, true) if from_nbits > 64 => "",
+ (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
+ _ => return,
+ };
+
+ if let Some(variant) = variant {
+ span_lint(
+ cx,
+ CAST_ENUM_TRUNCATION,
+ expr.span,
+ &format!(
+ "casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}",
+ variant.name,
+ ),
+ );
+ return;
+ }
- span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
++ format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
+ },
+
+ (ty::Float(_), true) => {
+ format!("casting `{cast_from}` to `{cast_to}` may truncate the value")
+ },
+
+ (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
+ "casting `f64` to `f32` may truncate the value".to_string()
+ },
+
+ _ => return,
+ };
+
++ 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,
++ );
++ });
+}
--- /dev/null
- /// default.
+mod as_ptr_cast_mut;
+mod as_underscore;
+mod borrow_as_ptr;
+mod cast_abs_to_unsigned;
+mod cast_enum_constructor;
+mod cast_lossless;
+mod cast_nan_to_int;
+mod cast_possible_truncation;
+mod cast_possible_wrap;
+mod cast_precision_loss;
+mod cast_ptr_alignment;
+mod cast_ref_to_mut;
+mod cast_sign_loss;
+mod cast_slice_different_sizes;
+mod cast_slice_from_raw_parts;
+mod char_lit_as_u8;
+mod fn_to_numeric_cast;
+mod fn_to_numeric_cast_any;
+mod fn_to_numeric_cast_with_truncation;
+mod ptr_as_ptr;
+mod unnecessary_cast;
+mod utils;
+
+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_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = u64::MAX;
+ /// x as f64;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CAST_PRECISION_LOSS,
+ pedantic,
+ "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let y: i8 = -1;
+ /// y as u128; // will return 18446744073709551615
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CAST_SIGN_LOSS,
+ pedantic,
+ "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
+}
+
+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
- cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
++ /// 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
+ /// truncation. This lint can be activated to help assess where additional
+ /// checks could be beneficial.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn as_u8(x: u64) -> u8 {
+ /// 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,
+ "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// u32::MAX as i32; // will yield a value of `-1`
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CAST_POSSIBLE_WRAP,
+ pedantic,
+ "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn as_u64(x: u8) -> u64 {
+ /// x as u64
+ /// }
+ /// ```
+ ///
+ /// Using `::from` would look like this:
+ ///
+ /// ```rust
+ /// fn as_u64(x: u8) -> u64 {
+ /// u64::from(x)
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CAST_LOSSLESS,
+ pedantic,
+ "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let _ = 2i32 as i32;
+ /// let _ = 0.5 as f32;
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust
+ /// let _ = 2_i32;
+ /// let _ = 0.5_f32;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub UNNECESSARY_CAST,
+ complexity,
+ "cast to the same type, e.g., `x as i32` where `x: i32`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let _ = (&1u8 as *const u8) as *const u16;
+ /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
+ ///
+ /// (&1u8 as *const u8).cast::<u16>();
+ /// (&mut 1u8 as *mut u8).cast::<u16>();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CAST_PTR_ALIGNMENT,
+ pedantic,
+ "cast from a pointer to a more-strictly-aligned pointer"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn fun() -> i32 { 1 }
+ /// let _ = fun as i64;
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # fn fun() -> i32 { 1 }
+ /// let _ = fun as usize;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub FN_TO_NUMERIC_CAST,
+ style,
+ "casting a function pointer to a numeric type other than usize"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn fn1() -> i16 {
+ /// 1
+ /// };
+ /// let _ = fn1 as i32;
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// // 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;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
+ style,
+ "casting a function pointer to a numeric type not wide enough to store the address"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// // fn1 is cast as `usize`
+ /// fn fn1() -> u16 {
+ /// 1
+ /// };
+ /// let _ = fn1 as usize;
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// // 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;
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub FN_TO_NUMERIC_CAST_ANY,
+ restriction,
+ "casting a function pointer to any integer type"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// fn x(r: &i32) {
+ /// unsafe {
+ /// *(r as *const _ as *mut _) += 1;
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Instead consider using interior mutability types.
+ ///
+ /// ```rust
+ /// use std::cell::UnsafeCell;
+ ///
+ /// fn x(r: &UnsafeCell<i32>) {
+ /// unsafe {
+ /// *r.get() += 1;
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.33.0"]
+ pub CAST_REF_TO_MUT,
+ correctness,
+ "a cast of reference to a mutable pointer"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// 'x' as u8
+ /// ```
+ ///
+ /// A better version, using the byte literal:
+ ///
+ /// ```rust,ignore
+ /// b'x'
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CHAR_LIT_AS_U8,
+ complexity,
+ "casting a character literal to `u8` truncates"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// let ptr: *const u32 = &42_u32;
+ /// let mut_ptr: *mut u32 = &mut 42_u32;
+ /// let _ = ptr.cast::<i32>();
+ /// let _ = mut_ptr.cast::<i32>();
+ /// ```
+ #[clippy::version = "1.51.0"]
+ pub PTR_AS_PTR,
+ pedantic,
+ "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// enum E { X = 256 };
+ /// let _ = E::X as u8;
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub CAST_ENUM_TRUNCATION,
+ suspicious,
+ "casts from an enum type to an integral type which will truncate the value"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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)
+ /// ```rust
+ /// 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
+ /// ```rust
+ /// 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);
+ /// }
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub CAST_SLICE_DIFFERENT_SIZES,
+ correctness,
+ "casting using `as` between raw pointers to slices of types with different sizes"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// enum E { X(i32) };
+ /// let _ = E::X as usize;
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub CAST_ENUM_CONSTRUCTOR,
+ suspicious,
+ "casts from an enum tuple constructor to an integer"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x: i32 = -42;
+ /// let y: u32 = x.abs() as u32;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x: i32 = -42;
+ /// let y: u32 = x.unsigned_abs();
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub CAST_ABS_TO_UNSIGNED,
+ suspicious,
+ "casting the result of `abs()` to an unsigned integer can panic"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn foo(n: usize) {}
+ /// let n: u16 = 256;
+ /// foo(n as _);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn foo(n: usize) {}
+ /// let n: u16 = 256;
+ /// foo(n as usize);
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub AS_UNDERSCORE,
+ restriction,
+ "detects `as _` conversion"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// 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);
+ /// ```
+ #[clippy::version = "1.60.0"]
+ pub BORROW_AS_PTR,
+ pedantic,
+ "borrowing just to cast to a raw pointer"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// 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:
+ /// ```rust,ignore
+ /// 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
+ #[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"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let string = String::with_capacity(1);
+ /// let ptr = string.as_ptr() as *mut u8;
+ /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let mut string = String::with_capacity(1);
+ /// let ptr = string.as_mut_ptr();
+ /// unsafe { ptr.write(4) };
+ /// ```
+ #[clippy::version = "1.66.0"]
+ pub AS_PTR_CAST_MUT,
+ nursery,
+ "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// let _: (0.0_f32 / 0.0) as u64;
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// let _: = 0_u64;
+ /// ```
+ #[clippy::version = "1.66.0"]
+ pub CAST_NAN_TO_INT,
+ suspicious,
+ "casting a known floating-point NaN into an integer"
+}
+
+pub struct Casts {
+ msrv: Msrv,
+}
+
+impl Casts {
+ #[must_use]
+ pub fn new(msrv: Msrv) -> Self {
+ Self { msrv }
+ }
+}
+
+impl_lint_pass!(Casts => [
+ CAST_PRECISION_LOSS,
+ CAST_SIGN_LOSS,
+ CAST_POSSIBLE_TRUNCATION,
+ CAST_POSSIBLE_WRAP,
+ CAST_LOSSLESS,
+ CAST_REF_TO_MUT,
+ CAST_PTR_ALIGNMENT,
+ CAST_SLICE_DIFFERENT_SIZES,
+ UNNECESSARY_CAST,
+ FN_TO_NUMERIC_CAST_ANY,
+ FN_TO_NUMERIC_CAST,
+ FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
+ CHAR_LIT_AS_U8,
+ PTR_AS_PTR,
+ CAST_ENUM_TRUNCATION,
+ CAST_ENUM_CONSTRUCTOR,
+ CAST_ABS_TO_UNSIGNED,
+ AS_UNDERSCORE,
+ BORROW_AS_PTR,
+ CAST_SLICE_FROM_RAW_PARTS,
+ AS_PTR_CAST_MUT,
+ CAST_NAN_TO_INT,
+]);
+
+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);
+ }
+
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
+ if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
+ return;
+ }
+ let (cast_from, cast_to) = (
+ cx.typeck_results().expr_ty(cast_expr),
+ cx.typeck_results().expr_ty(expr),
+ );
+
+ 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);
+ 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_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_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_enum_constructor::check(cx, expr, cast_expr, cast_from);
+ }
+
+ as_underscore::check(cx, expr, cast_to_hir);
+
+ if self.msrv.meets(msrvs::BORROW_AS_PTR) {
+ borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
+ }
+ }
+
+ 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);
+ }
+
+ extract_msrv_attr!(LateContext);
+}
--- /dev/null
+// 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::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,
+];
--- /dev/null
- #[clippy::version = "1.66.0"]
+use clippy_utils::attrs::is_doc_hidden;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
+use clippy_utils::macros::{is_panic, root_macro_call_first_node};
+use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
+use if_chain::if_chain;
+use itertools::Itertools;
+use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
+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, SuggestionStyle};
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{AnonConst, Expr};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::nested_filter;
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_parse::maybe_new_parser_from_source_str;
+use rustc_parse::parser::ForceCollect;
+use rustc_session::parse::ParseSess;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::edition::Edition;
+use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span};
+use rustc_span::{sym, FileName, Pos};
+use std::io;
+use std::ops::Range;
+use std::thread;
+use url::Url;
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// /// 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) {}
+ /// ```
+ ///
+ /// ```rust
+ /// // 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() {}
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub DOC_MARKDOWN,
+ pedantic,
+ "presence of `_`, `::` or camel-case outside backticks in documentation"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ ///# type Universe = ();
+ /// /// This function should really be documented
+ /// pub unsafe fn start_apocalypse(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ ///
+ /// At least write a line about safety:
+ ///
+ /// ```rust
+ ///# type Universe = ();
+ /// /// # Safety
+ /// ///
+ /// /// This function should not be called before the horsemen are ready.
+ /// pub unsafe fn start_apocalypse(u: &mut Universe) {
+ /// unimplemented!();
+ /// }
+ /// ```
+ #[clippy::version = "1.39.0"]
+ pub MISSING_SAFETY_DOC,
+ style,
+ "`pub unsafe fn` without `# Safety` docs"
+}
+
+declare_clippy_lint! {
+ /// ### 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:
+ ///
+ /// ```rust
+ ///# use std::io;
+ /// /// # 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<String> {
+ /// unimplemented!();
+ /// }
+ /// ```
+ #[clippy::version = "1.41.0"]
+ pub MISSING_ERRORS_DOC,
+ pedantic,
+ "`pub fn` returns `Result` without `# Errors` in doc comment"
+}
+
+declare_clippy_lint! {
+ /// ### 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:
+ ///
+ /// ```rust
+ /// /// # 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
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "1.51.0"]
+ pub MISSING_PANICS_DOC,
+ pedantic,
+ "`pub fn` may panic without `# Panics` in doc comment"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// /// An example of a doctest with a `main()` function
+ /// ///
+ /// /// # Examples
+ /// ///
+ /// /// ```
+ /// /// fn main() {
+ /// /// // this needs not be in an `fn`
+ /// /// }
+ /// /// ```
+ /// fn needless_main() {
+ /// unimplemented!();
+ /// }
+ /// ```
+ #[clippy::version = "1.40.0"]
+ pub NEEDLESS_DOCTEST_MAIN,
+ style,
+ "presence of `fn main() {` in code examples"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// /// See also: ['foo']
+ /// fn bar() {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// /// See also: [`foo`]
+ /// fn bar() {}
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub DOC_LINK_WITH_QUOTES,
+ pedantic,
+ "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 {
+ valid_idents: FxHashSet<String>,
+ in_trait_impl: bool,
+}
+
+impl DocMarkdown {
+ pub fn new(valid_idents: FxHashSet<String>) -> Self {
+ Self {
+ valid_idents,
+ in_trait_impl: false,
+ }
+ }
+}
+
+impl_lint_pass!(DocMarkdown => [
+ DOC_LINK_WITH_QUOTES,
+ DOC_MARKDOWN,
+ MISSING_SAFETY_DOC,
+ MISSING_ERRORS_DOC,
+ MISSING_PANICS_DOC,
+ NEEDLESS_DOCTEST_MAIN,
+ UNNECESSARY_SAFETY_DOC,
+]);
+
+impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
+ fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+ let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
+ check_attrs(cx, &self.valid_idents, attrs);
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ 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.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.owner_id.def_id),
+ panic_span: None,
+ };
+ fpu.visit_expr(body.value);
+ 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, ..) => 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",
+ ),
+ _ => (),
+ },
+ _ => (),
+ }
+ }
+
+ fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ if let hir::ItemKind::Impl { .. } = item.kind {
+ self.in_trait_impl = false;
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ 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.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 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;
+ }
+ if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
+ let body = cx.tcx.hir().body(body_id);
+ let mut fpu = FindPanicUnwrap {
+ cx,
+ typeck_results: cx.tcx.typeck(item.owner_id.def_id),
+ panic_span: None,
+ };
+ fpu.visit_expr(body.value);
+ lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
+ }
+ }
+}
+
+fn lint_for_missing_headers(
+ cx: &LateContext<'_>,
+ def_id: LocalDefId,
+ sig: &hir::FnSig<'_>,
+ headers: DocHeaders,
+ body_id: Option<hir::BodyId>,
+ panic_span: Option<Span>,
+) {
+ if !cx.effective_visibilities.is_exported(def_id) {
+ return; // Private functions do not require doc comments
+ }
+
+ // do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
+ if cx
+ .tcx
+ .hir()
+ .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id))
+ .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
+ {
+ return;
+ }
+
+ 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(
+ cx,
+ MISSING_PANICS_DOC,
+ span,
+ "docs for function which may panic missing `# Panics` section",
+ panic_span,
+ "first possible panic found here",
+ );
+ }
+ if !headers.errors {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
+ if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
+ span_lint(
+ cx,
+ MISSING_ERRORS_DOC,
+ span,
+ "docs for function returning `Result` missing `# Errors` section",
+ );
+ } else {
+ if_chain! {
+ if let Some(body_id) = body_id;
+ if let Some(future) = cx.tcx.lang_items().future_trait();
+ let typeck = cx.tcx.typeck_body(body_id);
+ 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::Generator(_, subs, _) = ret_ty.kind();
+ if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
+ then {
+ span_lint(
+ cx,
+ MISSING_ERRORS_DOC,
+ span,
+ "docs for function returning `Result` missing `# Errors` section",
+ );
+ }
+ }
+ }
+ }
+}
+
+/// Cleanup documentation decoration.
+///
+/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
+/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
+/// need to keep track of
+/// the spans but this function is inspired from the later.
+#[expect(clippy::cast_possible_truncation)]
+#[must_use]
+pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
+ // one-line comments lose their prefix
+ if comment_kind == CommentKind::Line {
+ let mut doc = doc.to_owned();
+ doc.push('\n');
+ let len = doc.len();
+ // +3 skips the opening delimiter
+ return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
+ }
+
+ let mut sizes = vec![];
+ let mut contains_initial_stars = false;
+ for line in doc.lines() {
+ let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
+ debug_assert_eq!(offset as u32 as usize, offset);
+ contains_initial_stars |= line.trim_start().starts_with('*');
+ // +1 adds the newline, +3 skips the opening delimiter
+ sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
+ }
+ if !contains_initial_stars {
+ return (doc.to_string(), sizes);
+ }
+ // remove the initial '*'s if any
+ let mut no_stars = String::with_capacity(doc.len());
+ for line in doc.lines() {
+ let mut chars = line.chars();
+ for c in &mut chars {
+ if c.is_whitespace() {
+ no_stars.push(c);
+ } else {
+ no_stars.push(if c == '*' { ' ' } else { c });
+ break;
+ }
+ }
+ no_stars.push_str(chars.as_str());
+ no_stars.push('\n');
+ }
+
+ (no_stars, sizes)
+}
+
+#[derive(Copy, Clone, Default)]
+struct DocHeaders {
+ safety: bool,
+ errors: bool,
+ panics: bool,
+}
+
+fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
+ 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
+ /// point to a fake address.
+ #[expect(clippy::unnecessary_wraps)] // we're following a type signature
+ fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
+ Some(("fake".into(), "fake".into()))
+ }
+
+ let mut doc = String::new();
+ let mut spans = vec![];
+
+ for attr in attrs {
+ if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
+ let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span);
+ spans.extend_from_slice(¤t_spans);
+ doc.push_str(&comment);
+ } else if attr.has_name(sym::doc) {
+ // ignore mix of sugared and non-sugared doc
+ // don't trigger the safety or errors check
+ return None;
+ }
+ }
+
+ let mut current = 0;
+ for &mut (ref mut offset, _) in &mut spans {
+ let offset_copy = *offset;
+ *offset = current;
+ current += offset_copy;
+ }
+
+ if doc.is_empty() {
+ return Some(DocHeaders::default());
+ }
+
+ let mut cb = fake_broken_link_callback;
+
+ let parser =
+ pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
+ // Iterate over all `Events` and combine consecutive events into one
+ let events = parser.coalesce(|previous, current| {
+ use pulldown_cmark::Event::Text;
+
+ let previous_range = previous.1;
+ let current_range = current.1;
+
+ match (previous.0, current.0) {
+ (Text(previous), Text(current)) => {
+ let mut previous = previous.to_string();
+ previous.push_str(¤t);
+ Ok((Text(previous.into()), previous_range))
+ },
+ (previous, current) => Err(((previous, previous_range), (current, current_range))),
+ }
+ });
+ Some(check_doc(cx, valid_idents, events, &spans))
+}
+
+const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
+
+fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
+ cx: &LateContext<'_>,
+ valid_idents: &FxHashSet<String>,
+ events: Events,
+ spans: &[(usize, Span)],
+) -> DocHeaders {
+ // true if a safety header was found
+ use pulldown_cmark::Event::{
+ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
+ };
+ use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
+ use pulldown_cmark::{CodeBlockKind, CowStr};
+
+ let mut headers = DocHeaders::default();
+ let mut in_code = false;
+ let mut in_link = None;
+ let mut in_heading = false;
+ let mut is_rust = false;
+ let mut edition = None;
+ let mut ticks_unbalanced = false;
+ let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
+ let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
+ for (event, range) in events {
+ match event {
+ Start(CodeBlock(ref kind)) => {
+ in_code = true;
+ if let CodeBlockKind::Fenced(lang) = kind {
+ for item in lang.split(',') {
+ if item == "ignore" {
+ is_rust = false;
+ break;
+ }
+ if let Some(stripped) = item.strip_prefix("edition") {
+ is_rust = true;
+ edition = stripped.parse::<Edition>().ok();
+ } else if item.is_empty() || RUST_CODE.contains(&item) {
+ is_rust = true;
+ }
+ }
+ }
+ },
+ End(CodeBlock(_)) => {
+ in_code = false;
+ is_rust = false;
+ },
+ Start(Link(_, url, _)) => in_link = Some(url),
+ End(Link(..)) => in_link = None,
+ Start(Heading(_, _, _) | Paragraph | Item) => {
+ if let Start(Heading(_, _, _)) = event {
+ in_heading = true;
+ }
+ ticks_unbalanced = false;
+ let (_, span) = get_current_span(spans, range.start);
+ paragraph_span = first_line_of_span(cx, span);
+ },
+ End(Heading(_, _, _) | Paragraph | Item) => {
+ if let End(Heading(_, _, _)) = event {
+ in_heading = false;
+ }
+ if ticks_unbalanced {
+ span_lint_and_help(
+ cx,
+ DOC_MARKDOWN,
+ paragraph_span,
+ "backticks are unbalanced",
+ None,
+ "a backtick may be missing a pair",
+ );
+ } else {
+ for (text, span) in text_to_check {
+ check_text(cx, valid_idents, &text, span);
+ }
+ }
+ text_to_check = Vec::new();
+ },
+ Start(_tag) | End(_tag) => (), // We don't care about other tags
+ Html(_html) => (), // HTML is weird, just ignore it
+ SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
+ FootnoteReference(text) | Text(text) => {
+ let (begin, span) = get_current_span(spans, range.start);
+ paragraph_span = paragraph_span.with_hi(span.hi());
+ ticks_unbalanced |= text.contains('`') && !in_code;
+ if Some(&text) == in_link.as_ref() || ticks_unbalanced {
+ // Probably a link of the form `<http://example.com>`
+ // Which are represented as a link to "http://example.com" with
+ // text "http://example.com" by pulldown-cmark
+ continue;
+ }
+ let trimmed_text = text.trim();
+ headers.safety |= in_heading && trimmed_text == "Safety";
+ headers.safety |= in_heading && trimmed_text == "Implementation safety";
+ headers.safety |= in_heading && trimmed_text == "Implementation Safety";
+ headers.errors |= in_heading && trimmed_text == "Errors";
+ headers.panics |= in_heading && trimmed_text == "Panics";
+ if in_code {
+ if is_rust {
+ let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
+ check_code(cx, &text, edition, span);
+ }
+ } else {
+ check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
+ // Adjust for the beginning of the current `Event`
+ let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
+ text_to_check.push((text, span));
+ }
+ },
+ }
+ }
+ headers
+}
+
+fn check_link_quotes(
+ cx: &LateContext<'_>,
+ in_link: bool,
+ trimmed_text: &str,
+ span: Span,
+ range: &Range<usize>,
+ begin: usize,
+ text_len: usize,
+) {
+ if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') {
+ // fix the span to only point at the text within the link
+ let lo = span.lo() + BytePos::from_usize(range.start - begin);
+ span_lint(
+ cx,
+ DOC_LINK_WITH_QUOTES,
+ span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)),
+ "possible intra-doc link using quotes instead of backticks",
+ );
+ }
+}
+
+fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
+ let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
+ Ok(o) => o,
+ Err(e) => e - 1,
+ };
+ spans[index]
+}
+
+fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
+ fn has_needless_main(code: String, edition: Edition) -> bool {
+ rustc_driver::catch_fatal_errors(|| {
+ rustc_span::create_session_globals_then(edition, || {
+ let filename = FileName::anon_source_code(&code);
+
+ let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+ let fallback_bundle =
+ rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let emitter = EmitterWriter::new(
+ Box::new(io::sink()),
+ None,
+ None,
+ fallback_bundle,
+ false,
+ false,
+ false,
+ None,
+ false,
+ false,
+ );
+ let handler = Handler::with_emitter(false, None, Box::new(emitter));
+ let sess = ParseSess::with_span_handler(handler, sm);
+
+ let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
+ Ok(p) => p,
+ Err(errs) => {
+ drop(errs);
+ return false;
+ },
+ };
+
+ let mut relevant_main_found = false;
+ loop {
+ match parser.parse_item(ForceCollect::No) {
+ Ok(Some(item)) => match &item.kind {
+ ItemKind::Fn(box Fn {
+ sig, body: Some(block), ..
+ }) if item.ident.name == sym::main => {
+ let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
+ let returns_nothing = match &sig.decl.output {
+ FnRetTy::Default(..) => true,
+ FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
+ FnRetTy::Ty(_) => false,
+ };
+
+ if returns_nothing && !is_async && !block.stmts.is_empty() {
+ // This main function should be linted, but only if there are no other functions
+ relevant_main_found = true;
+ } else {
+ // This main function should not be linted, we're done
+ return false;
+ }
+ },
+ // Tests with one of these items are ignored
+ ItemKind::Static(..)
+ | ItemKind::Const(..)
+ | ItemKind::ExternCrate(..)
+ | ItemKind::ForeignMod(..)
+ // Another function was found; this case is ignored
+ | ItemKind::Fn(..) => return false,
+ _ => {},
+ },
+ Ok(None) => break,
+ Err(e) => {
+ e.cancel();
+ return false;
+ },
+ }
+ }
+
+ relevant_main_found
+ })
+ })
+ .ok()
+ .unwrap_or_default()
+ }
+
+ // Because of the global session, we need to create a new session in a different thread with
+ // the edition we need.
+ let text = text.to_owned();
+ if thread::spawn(move || has_needless_main(text, edition))
+ .join()
+ .expect("thread::spawn failed")
+ {
+ span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
+ }
+}
+
+fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
+ for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
+ // Trim punctuation as in `some comment (see foo::bar).`
+ // ^^
+ // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
+ let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
+
+ // Remove leading or trailing single `:` which may be part of a sentence.
+ if word.starts_with(':') && !word.starts_with("::") {
+ word = word.trim_start_matches(':');
+ }
+ if word.ends_with(':') && !word.ends_with("::") {
+ word = word.trim_end_matches(':');
+ }
+
+ if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
+ continue;
+ }
+
+ // Adjust for the current word
+ let offset = word.as_ptr() as usize - text.as_ptr() as usize;
+ let span = Span::new(
+ span.lo() + BytePos::from_usize(offset),
+ span.lo() + BytePos::from_usize(offset + word.len()),
+ span.ctxt(),
+ span.parent(),
+ );
+
+ check_word(cx, word, span);
+ }
+}
+
+fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
+ /// Checks if a string is camel-case, i.e., contains at least two uppercase
+ /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
+ /// Plurals are also excluded (`IDs` is ok).
+ fn is_camel_case(s: &str) -> bool {
+ if s.starts_with(|c: char| c.is_ascii_digit()) {
+ return false;
+ }
+
+ let s = s.strip_suffix('s').unwrap_or(s);
+
+ s.chars().all(char::is_alphanumeric)
+ && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
+ && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
+ }
+
+ fn has_underscore(s: &str) -> bool {
+ s != "_" && !s.contains("\\_") && s.contains('_')
+ }
+
+ fn has_hyphen(s: &str) -> bool {
+ s != "-" && s.contains('-')
+ }
+
+ if let Ok(url) = Url::parse(word) {
+ // try to get around the fact that `foo::bar` parses as a valid URL
+ if !url.cannot_be_a_base() {
+ span_lint(
+ cx,
+ DOC_MARKDOWN,
+ span,
+ "you should put bare URLs between `<`/`>` or make a proper Markdown link",
+ );
+
+ return;
+ }
+ }
+
+ // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
+ if has_underscore(word) && has_hyphen(word) {
+ return;
+ }
+
+ if has_underscore(word) || word.contains("::") || is_camel_case(word) {
+ let mut applicability = Applicability::MachineApplicable;
+
+ span_lint_and_then(
+ cx,
+ DOC_MARKDOWN,
+ span,
+ "item in documentation is missing backticks",
+ |diag| {
+ let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+ diag.span_suggestion_with_style(
+ span,
+ "try",
+ format!("`{snippet}`"),
+ applicability,
+ // always show the suggestion in a separate line, since the
+ // inline presentation adds another pair of backticks
+ SuggestionStyle::ShowAlways,
+ );
+ },
+ );
+ }
+}
+
+struct FindPanicUnwrap<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ panic_span: Option<Span>,
+ typeck_results: &'tcx ty::TypeckResults<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+
+ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
+ if self.panic_span.is_some() {
+ return;
+ }
+
+ if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
+ if is_panic(self.cx, macro_call.def_id)
+ || matches!(
+ self.cx.tcx.item_name(macro_call.def_id).as_str(),
+ "assert" | "assert_eq" | "assert_ne" | "todo"
+ )
+ {
+ self.panic_span = Some(macro_call.span);
+ }
+ }
+
+ // check for `unwrap`
+ if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
+ let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
+ if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
+ || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
+ {
+ self.panic_span = Some(expr.span);
+ }
+ }
+
+ // and check sub-expressions
+ intravisit::walk_expr(self, expr);
+ }
+
+ // Panics in const blocks will cause compilation to fail.
+ fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+}
--- /dev/null
- item.span,
+//! lint on enum variants that are prefixed or suffixed by the same characters
+
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::source::is_present_in_source;
+use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start};
+use rustc_hir::{EnumDef, Item, ItemKind, Variant};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::source_map::Span;
+use rustc_span::symbol::Symbol;
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// enum Cake {
+ /// BlackForestCake,
+ /// HummingbirdCake,
+ /// BattenbergCake,
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// enum Cake {
+ /// BlackForest,
+ /// Hummingbird,
+ /// Battenberg,
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub ENUM_VARIANT_NAMES,
+ style,
+ "enums where all variants share a prefix/postfix"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// mod cake {
+ /// struct BlackForestCake;
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// mod cake {
+ /// struct BlackForest;
+ /// }
+ /// ```
+ #[clippy::version = "1.33.0"]
+ pub MODULE_NAME_REPETITIONS,
+ pedantic,
+ "type names prefixed/postfixed with their containing module's name"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```ignore
+ /// // lib.rs
+ /// mod foo;
+ /// // foo.rs
+ /// mod foo {
+ /// ...
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MODULE_INCEPTION,
+ style,
+ "modules that have the same name as their parent module"
+}
+
+pub struct EnumVariantNames {
+ modules: Vec<(Symbol, String)>,
+ threshold: u64,
+ avoid_breaking_exported_api: bool,
+}
+
+impl EnumVariantNames {
+ #[must_use]
+ pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self {
+ Self {
+ modules: Vec::new(),
+ threshold,
+ avoid_breaking_exported_api,
+ }
+ }
+}
+
+impl_lint_pass!(EnumVariantNames => [
+ ENUM_VARIANT_NAMES,
+ MODULE_NAME_REPETITIONS,
+ MODULE_INCEPTION
+]);
+
+fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) {
+ let name = variant.ident.name.as_str();
+ let item_name_chars = item_name.chars().count();
+
+ if count_match_start(item_name, name).char_count == item_name_chars
+ && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
+ && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
+ {
+ span_lint(
+ cx,
+ ENUM_VARIANT_NAMES,
+ variant.span,
+ "variant name starts with the enum's name",
+ );
+ }
+}
+
+fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) {
+ let name = variant.ident.name.as_str();
+ let item_name_chars = item_name.chars().count();
+
+ if count_match_end(item_name, name).char_count == item_name_chars {
+ span_lint(
+ cx,
+ ENUM_VARIANT_NAMES,
+ variant.span,
+ "variant name ends with the enum's name",
+ );
+ }
+}
+
+fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_name: &str, span: Span) {
+ if (def.variants.len() as u64) < threshold {
+ return;
+ }
+
+ let first = &def.variants[0].ident.name.as_str();
+ let mut pre = camel_case_split(first);
+ let mut post = pre.clone();
+ post.reverse();
+ for var in def.variants {
+ check_enum_start(cx, item_name, var);
+ check_enum_end(cx, item_name, var);
+ let name = var.ident.name.as_str();
+
+ let variant_split = camel_case_split(name);
+ if variant_split.len() == 1 {
+ return;
+ }
+
+ pre = pre
+ .iter()
+ .zip(variant_split.iter())
+ .take_while(|(a, b)| a == b)
+ .map(|e| *e.0)
+ .collect();
+ post = post
+ .iter()
+ .zip(variant_split.iter().rev())
+ .take_while(|(a, b)| a == b)
+ .map(|e| *e.0)
+ .collect();
+ }
+ let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) {
+ (true, true) => return,
+ (false, _) => ("pre", pre.join("")),
+ (true, false) => {
+ post.reverse();
+ ("post", post.join(""))
+ },
+ };
+ span_lint_and_help(
+ cx,
+ ENUM_VARIANT_NAMES,
+ span,
+ &format!("all variants have the same {what}fix: `{value}`"),
+ None,
+ &format!(
+ "remove the {what}fixes and use full paths to \
+ the variants instead of glob imports"
+ ),
+ );
+}
+
+#[must_use]
+fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
+ prefixes.iter().all(|p| p == &"" || p == &"_")
+}
+
+#[must_use]
+fn to_camel_case(item_name: &str) -> String {
+ let mut s = String::new();
+ let mut up = true;
+ for c in item_name.chars() {
+ if c.is_uppercase() {
+ // we only turn snake case text into CamelCase
+ return item_name.to_string();
+ }
+ if c == '_' {
+ up = true;
+ continue;
+ }
+ if up {
+ up = false;
+ s.extend(c.to_uppercase());
+ } else {
+ s.push(c);
+ }
+ }
+ s
+}
+
+impl LateLintPass<'_> for EnumVariantNames {
+ fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) {
+ let last = self.modules.pop();
+ assert!(last.is_some());
+ }
+
+ #[expect(clippy::similar_names)]
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+ 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((mod_name, mod_camel)) = self.modules.last() {
+ // constants don't have surrounding modules
+ if !mod_camel.is_empty() {
+ if mod_name == &item.ident.name {
+ if let ItemKind::Mod(..) = item.kind {
+ span_lint(
+ cx,
+ MODULE_INCEPTION,
+ item.span,
+ "module has the same name as its containing module",
+ );
+ }
+ }
+ // 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.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();
+
+ let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
+
+ if matching.char_count == nchars {
+ match item_camel.chars().nth(nchars) {
+ 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",
+ ),
+ _ => (),
+ }
+ }
+ if rmatching.char_count == nchars {
+ span_lint(
+ cx,
+ MODULE_NAME_REPETITIONS,
++ item.ident.span,
+ "item name ends with its containing module's name",
+ );
+ }
+ }
+ }
+ }
+ }
+ if let ItemKind::Enum(ref def, _) = item.kind {
+ 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);
+ }
+ }
+ self.modules.push((item.ident.name, item_camel));
+ }
+}
--- /dev/null
- #[clippy::version = "1.66.0"]
+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<T>`, so if you pass a `*mut c_void` you will get a `Box<c_void>` (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
+ }
+}
--- /dev/null
- #[clippy::version = "1.65.0"]
+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,
+ );
+}
--- /dev/null
- #[clippy::version = "1.66"]
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
+use clippy_utils::{is_must_use_func_call, paths};
+use rustc_hir::{Local, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `let _ = <expr>` where expr is `#[must_use]`
+ ///
+ /// ### Why is this bad?
+ /// It's better to explicitly handle the value of a `#[must_use]`
+ /// expr
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn f() -> Result<u32, u32> {
+ /// Ok(0)
+ /// }
+ ///
+ /// let _ = f();
+ /// // is_ok() is marked #[must_use]
+ /// let _ = f().is_ok();
+ /// ```
+ #[clippy::version = "1.42.0"]
+ pub LET_UNDERSCORE_MUST_USE,
+ restriction,
+ "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
+ /// `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
+ /// 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
+ /// ```rust,ignore
+ /// let _ = mutex.lock();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// let _lock = mutex.lock();
+ /// ```
+ #[clippy::version = "1.43.0"]
+ pub LET_UNDERSCORE_LOCK,
+ correctness,
+ "non-binding `let` on a synchronization lock"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
+ ///
+ /// ### Why is this bad?
+ /// 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
+ /// async fn foo() -> Result<(), ()> {
+ /// Ok(())
+ /// }
+ /// let _ = foo();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # async fn context() {
+ /// async fn foo() -> Result<(), ()> {
+ /// Ok(())
+ /// }
+ /// let _ = foo().await;
+ /// # }
+ /// ```
++ #[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_FUTURE]);
+
+const SYNC_GUARD_PATHS: [&[&str]; 3] = [
+ &paths::PARKING_LOT_MUTEX_GUARD,
+ &paths::PARKING_LOT_RWLOCK_READ_GUARD,
+ &paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
+];
+
+impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
+ fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
+ 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",
+ );
+ }
+ }
+ }
+}
--- /dev/null
+#![feature(array_windows)]
+#![feature(binary_heap_into_iter_sorted)]
+#![feature(box_patterns)]
+#![feature(control_flow_enum)]
+#![feature(drain_filter)]
+#![feature(iter_intersperse)]
+#![feature(let_chains)]
+#![feature(lint_reasons)]
+#![feature(never_type)]
+#![feature(once_cell)]
+#![feature(rustc_private)]
+#![feature(stmt_expr_attributes)]
+#![recursion_limit = "512"]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
+#![warn(trivial_casts, trivial_numeric_casts)]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![warn(rustc::internal)]
+// Disable this rustc lint for now, as it was also done in rustc
+#![allow(rustc::potential_query_instability)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+extern crate rustc_arena;
+extern crate rustc_ast;
+extern crate rustc_ast_pretty;
+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;
+extern crate rustc_lint;
+extern crate rustc_middle;
+extern crate rustc_parse;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_target;
+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::msrvs::Msrv;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_lint::{Lint, LintId};
+use rustc_session::Session;
+
+#[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_range;
+mod approx_const;
+mod as_conversions;
+mod asm_syntax;
+mod assertions_on_constants;
+mod assertions_on_result_states;
+mod async_yields_async;
+mod attrs;
+mod await_holding_invalid;
+mod blocks_in_if_conditions;
+mod bool_assert_comparison;
+mod bool_to_int_with_if;
+mod booleans;
+mod borrow_deref_ref;
+mod box_default;
+mod cargo;
+mod casts;
+mod checked_conversions;
+mod cognitive_complexity;
+mod collapsible_if;
+mod comparison_chain;
+mod copies;
+mod copy_iterator;
+mod crate_in_macro_def;
+mod create_dir;
+mod dbg_macro;
+mod default;
+mod default_instead_of_iter_empty;
+mod default_numeric_fallback;
+mod default_union_representation;
+mod dereference;
+mod derivable_impls;
+mod derive;
+mod disallowed_macros;
+mod disallowed_methods;
+mod disallowed_names;
+mod disallowed_script_idents;
+mod disallowed_types;
+mod doc;
+mod double_parens;
+mod drop_forget_ref;
+mod duplicate_mod;
+mod else_if_without_else;
+mod empty_drop;
+mod empty_enum;
+mod empty_structs_with_brackets;
+mod entry;
+mod enum_clike;
+mod enum_variants;
+mod equatable_if_let;
+mod escape;
+mod eta_reduction;
+mod excessive_bools;
+mod exhaustive_items;
+mod exit;
+mod explicit_write;
+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;
+mod if_let_mutex;
+mod if_not_else;
+mod if_then_some_else_none;
+mod implicit_hasher;
+mod implicit_return;
+mod implicit_saturating_add;
+mod implicit_saturating_sub;
+mod inconsistent_struct_constructor;
+mod index_refutable_slice;
+mod indexing_slicing;
+mod infinite_iter;
+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;
+mod items_after_statements;
+mod iter_not_returning_iterator;
+mod large_const_arrays;
+mod large_enum_variant;
+mod large_include_file;
+mod large_stack_arrays;
+mod len_zero;
+mod let_if_seq;
+mod let_underscore;
+mod lifetimes;
+mod literal_representation;
+mod loops;
+mod macro_use;
+mod main_recursion;
+mod manual_assert;
+mod manual_async_fn;
+mod manual_bits;
+mod manual_clamp;
+mod manual_is_ascii_check;
+mod manual_let_else;
+mod manual_non_exhaustive;
+mod manual_rem_euclid;
+mod manual_retain;
+mod manual_string_new;
+mod manual_strip;
+mod map_unit_fn;
+mod match_result_ok;
+mod matches;
+mod mem_forget;
+mod mem_replace;
+mod methods;
+mod minmax;
+mod misc;
+mod misc_early;
+mod mismatching_type_param_order;
+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;
+mod mutable_debug_assertion;
+mod mutex_atomic;
+mod needless_arbitrary_self_type;
+mod needless_bool;
+mod needless_borrowed_ref;
+mod needless_continue;
+mod needless_for_each;
+mod needless_late_init;
+mod needless_parens_on_range_literals;
+mod needless_pass_by_value;
+mod needless_question_mark;
+mod needless_update;
+mod neg_cmp_op_on_partial_ord;
+mod neg_multiply;
+mod new_without_default;
+mod no_effect;
+mod non_copy_const;
+mod non_expressive_names;
+mod non_octal_unix_permissions;
+mod non_send_fields_in_send_ty;
+mod nonstandard_macro_braces;
+mod octal_escapes;
+mod only_used_in_recursion;
+mod operators;
+mod option_env_unwrap;
+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;
+mod pub_use;
+mod question_mark;
+mod ranges;
+mod rc_clone_in_vec_init;
+mod read_zero_byte_vec;
+mod redundant_clone;
+mod redundant_closure_call;
+mod redundant_else;
+mod redundant_field_names;
+mod redundant_pub_crate;
+mod redundant_slicing;
+mod redundant_static_lifetimes;
+mod ref_option_ref;
+mod reference;
+mod regex;
+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;
+mod temporary_assignment;
+mod to_digit_is_some;
+mod trailing_empty_array;
+mod trait_bounds;
+mod transmute;
+mod types;
+mod undocumented_unsafe_blocks;
+mod unicode;
+mod uninit_vec;
+mod unit_return_expecting_ord;
+mod unit_types;
+mod unnamed_address;
+mod unnecessary_owned_empty_strings;
+mod unnecessary_self_imports;
+mod unnecessary_wraps;
+mod unnested_or_patterns;
+mod unsafe_removed_from_name;
+mod unused_async;
+mod unused_io_amount;
+mod unused_peekable;
+mod unused_rounding;
+mod unused_self;
+mod unused_unit;
+mod unwrap;
+mod unwrap_in_result;
+mod upper_case_acronyms;
+mod use_self;
+mod useless_conversion;
+mod vec;
+mod vec_init_then_push;
+mod wildcard_imports;
+mod write;
+mod zero_div_zero;
+mod zero_sized_map_values;
+// end lints modules, do not remove this comment, it’s used in `update_lints`
+
+use crate::utils::conf::{format_error, TryConf};
+pub use crate::utils::conf::{lookup_conf_file, Conf};
+
+/// Register all pre expansion lints
+///
+/// Pre-expansion lints run before any macro expansion has happened.
+///
+/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
+/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
+///
+/// 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();
+
+ store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
+}
+
+#[doc(hidden)]
+pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> 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}"))
+ .emit();
+ return Conf::default();
+ },
+ };
+
+ 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!(
+ "error reading Clippy's configuration file `{}`: {}",
+ file_name.display(),
+ format_error(error)
+ ));
+ }
+
+ for warning in warnings {
+ sess.struct_warn(format!(
+ "error reading Clippy's configuration file `{}`: {}",
+ file_name.display(),
+ format_error(warning)
+ ))
+ .emit();
+ }
+
+ conf
+}
+
+#[derive(Default)]
+struct RegistrationGroups {
+ all: Vec<LintId>,
+ cargo: Vec<LintId>,
+ complexity: Vec<LintId>,
+ correctness: Vec<LintId>,
+ nursery: Vec<LintId>,
+ pedantic: Vec<LintId>,
+ perf: Vec<LintId>,
+ restriction: Vec<LintId>,
+ style: Vec<LintId>,
+ suspicious: Vec<LintId>,
+ #[cfg(feature = "internal")]
+ internal: Vec<LintId>,
+}
+
+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<LintId> {
+ 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");
+
+ #[cfg(feature = "internal")]
+ {
+ if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
+ store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
+ return;
+ }
+ }
+
+ // all the internal lints
+ #[cfg(feature = "internal")]
+ {
+ 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::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default()
+ });
+ store.register_late_pass(|_| {
+ Box::<utils::internal_lints::lint_without_lint_pass::LintWithoutLintPass>::default()
+ });
+ store.register_late_pass(|_| Box::<utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath>::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
+ .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));
+ store.register_late_pass(|_| Box::new(utils::author::Author));
+ let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
+ store.register_late_pass(move |_| {
+ Box::new(await_holding_invalid::AwaitHolding::new(
+ await_holding_invalid_types.clone(),
+ ))
+ });
+ store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
+ let vec_box_size_threshold = conf.vec_box_size_threshold;
+ let type_complexity_threshold = conf.type_complexity_threshold;
+ let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
+ store.register_late_pass(move |_| {
+ Box::new(types::Types::new(
+ vec_box_size_threshold,
+ type_complexity_threshold,
+ avoid_breaking_exported_api,
+ ))
+ });
+ store.register_late_pass(|_| Box::new(booleans::NonminimalBool));
+ store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
+ store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
+ store.register_late_pass(|_| Box::new(ptr::Ptr));
+ 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::<misc::LintPass>::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));
+ store.register_late_pass(|_| Box::new(len_zero::LenZero));
+ store.register_late_pass(|_| Box::new(attrs::Attributes));
+ store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
+ store.register_late_pass(|_| Box::new(unicode::Unicode));
+ store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
+ store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
+ store.register_late_pass(|_| Box::new(strings::StringAdd));
+ store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
+ store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
+ store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
+ store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
+ store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
+ store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
+
+ 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;
+ 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(),
+ allow_expect_in_tests,
+ allow_unwrap_in_tests,
+ ))
+ });
+ 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(|_| 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(),
+ ))
+ });
+ store.register_late_pass(|_| Box::<shadow::Shadow>::default());
+ store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
+ store.register_late_pass(|_| Box::new(loops::Loops));
+ store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
+ store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
+ store.register_late_pass(|_| Box::new(entry::HashMapPass));
+ store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
+ store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
+ store.register_late_pass(|_| Box::new(mutex_atomic::Mutex));
+ store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate));
+ store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
+ 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())));
+ let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
+ store.register_late_pass(move |_| {
+ Box::new(cognitive_complexity::CognitiveComplexity::new(
+ cognitive_complexity_threshold,
+ ))
+ });
+ let too_large_for_stack = conf.too_large_for_stack;
+ store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack }));
+ store.register_late_pass(move |_| Box::new(vec::UselessVec { too_large_for_stack }));
+ 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(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));
+ store.register_late_pass(|_| Box::new(regex::Regex));
+ store.register_late_pass(|_| Box::new(copies::CopyAndPaste));
+ store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
+ store.register_late_pass(|_| Box::new(format::UselessFormat));
+ store.register_late_pass(|_| Box::new(swap::Swap));
+ store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
+ store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
+ let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
+ let too_many_arguments_threshold = conf.too_many_arguments_threshold;
+ let too_many_lines_threshold = conf.too_many_lines_threshold;
+ let large_error_threshold = conf.large_error_threshold;
+ store.register_late_pass(move |_| {
+ Box::new(functions::Functions::new(
+ too_many_arguments_threshold,
+ too_many_lines_threshold,
+ large_error_threshold,
+ ))
+ });
+ let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
+ store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
+ store.register_late_pass(|_| Box::new(mem_forget::MemForget));
+ store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
+ store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
+ store.register_late_pass(|_| Box::new(missing_doc::MissingDoc::new()));
+ store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
+ store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
+ store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
+ store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
+ store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
+ let enum_variant_size_threshold = conf.enum_variant_size_threshold;
+ store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
+ store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite));
+ store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
+ let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
+ conf.trivial_copy_size_limit,
+ conf.pass_by_value_size_limit,
+ conf.avoid_breaking_exported_api,
+ &sess.target,
+ );
+ store.register_late_pass(move |_| Box::new(pass_by_ref_or_value));
+ store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
+ store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
+ store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
+ store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
+ store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
+ store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
+ store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
+ store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
+ store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
+ store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
+ 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(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));
+ store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
+ store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
+ store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants));
+ store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
+ store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
+ 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));
+ 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()));
+ store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
+ store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
+ store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
+ store.register_early_pass(|| Box::new(formatting::Formatting));
+ store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
+ store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
+ store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
+ store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
+ store.register_late_pass(|_| Box::new(returns::Return));
+ store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
+ store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
+ store.register_early_pass(|| Box::new(precedence::Precedence));
+ store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
+ store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
+ store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
+ store.register_late_pass(|_| Box::new(create_dir::CreateDir));
+ store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
+ let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
+ store.register_early_pass(move || {
+ Box::new(literal_representation::LiteralDigitGrouping::new(
+ literal_representation_lint_fraction_readability,
+ ))
+ });
+ let literal_representation_threshold = conf.literal_representation_threshold;
+ store.register_early_pass(move || {
+ Box::new(literal_representation::DecimalLiteralRepresentation::new(
+ literal_representation_threshold,
+ ))
+ });
+ let enum_variant_name_threshold = conf.enum_variant_name_threshold;
+ store.register_late_pass(move |_| {
+ Box::new(enum_variants::EnumVariantNames::new(
+ enum_variant_name_threshold,
+ avoid_breaking_exported_api,
+ ))
+ });
+ store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
+ let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
+ store.register_late_pass(move |_| {
+ Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
+ avoid_breaking_exported_api,
+ upper_case_acronyms_aggressive,
+ ))
+ });
+ store.register_late_pass(|_| Box::<default::Default>::default());
+ store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
+ store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
+ store.register_late_pass(|_| Box::new(exit::Exit));
+ store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome));
+ let array_size_threshold = conf.array_size_threshold;
+ store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
+ store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
+ 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::<single_component_path_imports::SingleComponentPathImports>::default());
+ let max_fn_params_bools = conf.max_fn_params_bools;
+ let max_struct_bools = conf.max_struct_bools;
+ store.register_late_pass(move |_| {
+ Box::new(excessive_bools::ExcessiveBools::new(
+ max_struct_bools,
+ max_fn_params_bools,
+ ))
+ });
+ store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
+ let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
+ store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
+ store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::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(|_| 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));
+ store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
+ store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
+ store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn));
+ store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn));
+ let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
+ store.register_early_pass(move || {
+ Box::new(non_expressive_names::NonExpressiveNames {
+ single_char_binding_names_threshold,
+ })
+ });
+ let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
+ store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
+ store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
+ store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
+ store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
+ store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
+ store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
+ let disallowed_macros = conf.disallowed_macros.clone();
+ store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone())));
+ let disallowed_methods = conf.disallowed_methods.clone();
+ store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
+ store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
+ store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
+ store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop));
+ store.register_late_pass(|_| Box::new(strings::StrToString));
+ store.register_late_pass(|_| Box::new(strings::StringToString));
+ store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues));
+ store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::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(|_| 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));
+ let disallowed_types = conf.disallowed_types.clone();
+ store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
+ let import_renames = conf.enforced_import_renames.clone();
+ store.register_late_pass(move |_| {
+ Box::new(missing_enforced_import_rename::ImportRename::new(
+ import_renames.clone(),
+ ))
+ });
+ let scripts = conf.allowed_scripts.clone();
+ store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
+ store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings));
+ store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors));
+ store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator));
+ store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert));
+ let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
+ store.register_late_pass(move |_| {
+ Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
+ enable_raw_pointer_heuristic_for_send,
+ ))
+ });
+ store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
+ 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(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
+ store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::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_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));
+ store.register_early_pass(|| Box::new(pub_use::PubUse));
+ store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
+ let max_include_file_size = conf.max_include_file_size;
+ store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
+ store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
+ store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
+ store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
+ store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
+ 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())));
+ 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::<std_instead_of_core::StdReexports>::default());
+ 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(|_| 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));
+ // add lints here, do not remove this comment, it's used in `new_lint`
+}
+
+#[rustfmt::skip]
+fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
+ store.register_removed(
+ "should_assert_eq",
+ "`assert!()` will be more flexible with RFC 2011",
+ );
+ store.register_removed(
+ "extend_from_slice",
+ "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
+ );
+ store.register_removed(
+ "range_step_by_zero",
+ "`iterator.step_by(0)` panics nowadays",
+ );
+ store.register_removed(
+ "unstable_as_slice",
+ "`Vec::as_slice` has been stabilized in 1.7",
+ );
+ store.register_removed(
+ "unstable_as_mut_slice",
+ "`Vec::as_mut_slice` has been stabilized in 1.7",
+ );
+ store.register_removed(
+ "misaligned_transmute",
+ "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
+ );
+ store.register_removed(
+ "assign_ops",
+ "using compound assignment operators (e.g., `+=`) is harmless",
+ );
+ store.register_removed(
+ "if_let_redundant_pattern_matching",
+ "this lint has been changed to redundant_pattern_matching",
+ );
+ store.register_removed(
+ "unsafe_vector_initialization",
+ "the replacement suggested by this lint had substantially different behavior",
+ );
+ store.register_removed(
+ "reverse_range_loop",
+ "this lint is now included in reversed_empty_ranges",
+ );
+}
+
+/// Register renamed lints.
+///
+/// Used in `./src/driver.rs`.
+pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
+ for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
+ ls.register_renamed(old_name, new_name);
+ }
+}
+
+// only exists to let the dogfood integration test works.
+// Don't run clippy as an executable directly
+#[allow(dead_code)]
+fn main() {
+ panic!("Please use the cargo-clippy executable");
+}
--- /dev/null
- #[clippy::version = "1.66.0"]
+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::<Vec<_>>();
+
+ 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
+}
--- /dev/null
- #[clippy::version = "1.66.0"]
+mod bind_instead_of_map;
+mod bytecount;
+mod bytes_count_to_len;
+mod bytes_nth;
+mod case_sensitive_file_extension_comparisons;
+mod chars_cmp;
+mod chars_cmp_with_unwrap;
+mod chars_last_cmp;
+mod chars_last_cmp_with_unwrap;
+mod chars_next_cmp;
+mod chars_next_cmp_with_unwrap;
+mod clone_on_copy;
+mod clone_on_ref_ptr;
+mod cloned_instead_of_copied;
+mod collapsible_str_replace;
+mod err_expect;
+mod expect_fun_call;
+mod expect_used;
+mod extend_with_drain;
+mod filetype_is_file;
+mod filter_map;
+mod filter_map_identity;
+mod filter_map_next;
+mod filter_next;
+mod flat_map_identity;
+mod flat_map_option;
+mod from_iter_instead_of_collect;
+mod get_first;
+mod get_last_with_len;
+mod get_unwrap;
+mod implicit_clone;
+mod inefficient_to_string;
+mod inspect_for_each;
+mod into_iter_on_ref;
+mod is_digit_ascii_radix;
+mod iter_cloned_collect;
+mod iter_count;
+mod iter_kv_map;
+mod iter_next_slice;
+mod iter_nth;
+mod iter_nth_zero;
+mod iter_on_single_or_empty_collections;
+mod iter_overeager_cloned;
+mod iter_skip_next;
+mod iter_with_drain;
+mod iterator_step_by_zero;
+mod manual_ok_or;
+mod manual_saturating_arithmetic;
+mod manual_str_repeat;
+mod map_clone;
+mod map_collect_result_unit;
+mod map_err_ignore;
+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;
+mod obfuscated_if_else;
+mod ok_expect;
+mod open_options;
+mod option_as_ref_deref;
+mod option_map_or_none;
+mod option_map_unwrap_or;
+mod or_fun_call;
+mod or_then_unwrap;
+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;
+mod single_char_push_string;
+mod skip_while_next;
+mod stable_sort_primitive;
+mod str_splitn;
+mod string_extend_chars;
+mod suspicious_map;
+mod suspicious_splitn;
+mod suspicious_to_owned;
+mod uninit_assumed_init;
+mod unit_hash;
+mod unnecessary_filter_map;
+mod unnecessary_fold;
+mod unnecessary_iter_cloned;
+mod unnecessary_join;
+mod unnecessary_lazy_eval;
+mod unnecessary_sort_by;
+mod unnecessary_to_owned;
+mod unwrap_or_else_default;
+mod unwrap_used;
+mod useless_asref;
+mod utils;
+mod vec_resize_to_zero;
+mod verbose_file_reads;
+mod wrong_self_convention;
+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::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::{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_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// [1, 2, 3].iter().cloned();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// [1, 2, 3].iter().copied();
+ /// ```
+ #[clippy::version = "1.53.0"]
+ pub CLONED_INSTEAD_OF_COPIED,
+ pedantic,
+ "used `cloned` where `copied` could be used instead"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let hello = "hesuo worpd"
+ /// .replace('s', "l")
+ /// .replace("u", "l")
+ /// .replace('p', "l");
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l");
+ /// ```
+ #[clippy::version = "1.65.0"]
+ pub COLLAPSIBLE_STR_REPLACE,
+ perf,
+ "collapse consecutive calls to str::replace (2 or more) into a single call"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.cloned().<func>()` 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
+ /// ```rust
+ /// # let vec = vec!["string".to_string()];
+ /// vec.iter().cloned().take(10);
+ /// vec.iter().cloned().last();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let vec = vec!["string".to_string()];
+ /// vec.iter().take(10).cloned();
+ /// vec.iter().last().cloned();
+ /// ```
+ #[clippy::version = "1.60.0"]
+ pub ITER_OVEREAGER_CLONED,
+ perf,
+ "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
+ /// ```
+ #[clippy::version = "1.53.0"]
+ pub FLAT_MAP_OPTION,
+ pedantic,
+ "used `flat_map` where `filter_map` could be used instead"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let option = Some(1);
+ /// # let result: Result<usize, ()> = Ok(1);
+ /// option.unwrap();
+ /// result.unwrap();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let option = Some(1);
+ /// # let result: Result<usize, ()> = Ok(1);
+ /// option.expect("more helpful message");
+ /// result.expect("more helpful message");
+ /// ```
+ ///
+ /// If [expect_used](#expect_used) is enabled, instead:
+ /// ```rust,ignore
+ /// # let option = Some(1);
+ /// # let result: Result<usize, ()> = Ok(1);
+ /// option?;
+ ///
+ /// // or
+ ///
+ /// result?;
+ /// ```
+ #[clippy::version = "1.45.0"]
+ pub UNWRAP_USED,
+ restriction,
+ "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// # let option = Some(1);
+ /// # let result: Result<usize, ()> = Ok(1);
+ /// option.expect("one");
+ /// result.expect("one");
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// # let option = Some(1);
+ /// # let result: Result<usize, ()> = Ok(1);
+ /// option?;
+ ///
+ /// // or
+ ///
+ /// result?;
+ /// ```
+ #[clippy::version = "1.45.0"]
+ pub EXPECT_USED,
+ restriction,
+ "using `.expect()` on `Result` or `Option`, which might be better handled"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// struct X;
+ /// impl X {
+ /// fn add(&self, other: &X) -> X {
+ /// // ..
+ /// # X
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub SHOULD_IMPLEMENT_TRAIT,
+ style,
+ "defining a method that should be implementing a std trait"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # struct X;
+ /// impl X {
+ /// fn as_str(self) -> &'static str {
+ /// // ..
+ /// # ""
+ /// }
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub WRONG_SELF_CONVENTION,
+ style,
+ "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let x = Ok::<_, ()>(());
+ /// x.ok().expect("why did I do this again?");
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let x = Ok::<_, ()>(());
+ /// x.expect("why did I do this again?");
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub OK_EXPECT,
+ style,
+ "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```should_panic
+ /// let x: Result<u32, &str> = Ok(10);
+ /// x.err().expect("Testing err().expect()");
+ /// ```
+ /// Use instead:
+ /// ```should_panic
+ /// let x: Result<u32, &str> = Ok(10);
+ /// x.expect_err("Testing expect_err");
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub ERR_EXPECT,
+ style,
+ r#"using `.err().expect("")` when `.expect_err("")` can be used"#
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let x = Some(1);
+ /// x.unwrap_or_else(Default::default);
+ /// x.unwrap_or_else(u32::default);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let x = Some(1);
+ /// x.unwrap_or_default();
+ /// ```
+ #[clippy::version = "1.56.0"]
+ pub UNWRAP_OR_ELSE_DEFAULT,
+ style,
+ "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let option = Some(1);
+ /// # let result: Result<usize, ()> = Ok(1);
+ /// # fn some_function(foo: ()) -> usize { 1 }
+ /// option.map(|a| a + 1).unwrap_or(0);
+ /// result.map(|a| a + 1).unwrap_or_else(some_function);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let option = Some(1);
+ /// # let result: Result<usize, ()> = Ok(1);
+ /// # fn some_function(foo: ()) -> usize { 1 }
+ /// option.map_or(0, |a| a + 1);
+ /// result.map_or_else(some_function, |a| a + 1);
+ /// ```
+ #[clippy::version = "1.45.0"]
+ pub MAP_UNWRAP_OR,
+ pedantic,
+ "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let opt = Some(1);
+ /// opt.map_or(None, |a| Some(a + 1));
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let opt = Some(1);
+ /// opt.and_then(|a| Some(a + 1));
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub OPTION_MAP_OR_NONE,
+ style,
+ "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let r: Result<u32, &str> = Ok(1);
+ /// assert_eq!(Some(1), r.map_or(None, Some));
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let r: Result<u32, &str> = Ok(1);
+ /// assert_eq!(Some(1), r.ok());
+ /// ```
+ #[clippy::version = "1.44.0"]
+ pub RESULT_MAP_OR_INTO_OPTION,
+ style,
+ "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # fn opt() -> Option<&'static str> { Some("42") }
+ /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
+ /// 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:
+ ///
+ /// ```rust
+ /// # fn opt() -> Option<&'static str> { Some("42") }
+ /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
+ /// 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 });
+ /// ```
+ #[clippy::version = "1.45.0"]
+ pub BIND_INSTEAD_OF_MAP,
+ complexity,
+ "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.filter(_).next()`.
+ ///
+ /// ### Why is this bad?
+ /// Readability, this can be written more concisely as
+ /// `_.find(_)`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().filter(|x| **x == 0).next();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().find(|x| **x == 0);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub FILTER_NEXT,
+ complexity,
+ "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().skip_while(|x| **x == 0).next();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let vec = vec![1];
+ /// vec.iter().find(|x| **x != 0);
+ /// ```
+ #[clippy::version = "1.42.0"]
+ pub SKIP_WHILE_NEXT,
+ complexity,
+ "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// # let vec = vec![vec![1]];
+ /// # let opt = Some(5);
+ /// vec.iter().flat_map(|x| x.iter());
+ /// opt.and_then(|x| Some(x * 2));
+ /// ```
+ #[clippy::version = "1.31.0"]
+ pub MAP_FLATTEN,
+ complexity,
+ "using combinations of `flatten` and `map` which can usually be written as a single method call"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # #![allow(unused)]
+ /// (0_i32..10)
+ /// .filter(|n| n.checked_add(1).is_some())
+ /// .map(|n| n.checked_add(1).unwrap());
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # #[allow(unused)]
+ /// (0_i32..10).filter_map(|n| n.checked_add(1));
+ /// ```
+ #[clippy::version = "1.51.0"]
+ pub MANUAL_FILTER_MAP,
+ complexity,
+ "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// (0_i32..10)
+ /// .find(|n| n.checked_add(1).is_some())
+ /// .map(|n| n.checked_add(1).unwrap());
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// (0_i32..10).find_map(|n| n.checked_add(1));
+ /// ```
+ #[clippy::version = "1.51.0"]
+ pub MANUAL_FIND_MAP,
+ complexity,
+ "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
+ /// ```
+ /// Can be written as
+ ///
+ /// ```rust
+ /// (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
+ /// ```
+ #[clippy::version = "1.36.0"]
+ pub FILTER_MAP_NEXT,
+ pedantic,
+ "using combination of `filter_map` and `next` which can usually be written as a single method call"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let iter = vec![vec![0]].into_iter();
+ /// iter.flat_map(|x| x);
+ /// ```
+ /// Can be written as
+ /// ```rust
+ /// # let iter = vec![vec![0]].into_iter();
+ /// iter.flatten();
+ /// ```
+ #[clippy::version = "1.39.0"]
+ pub FLAT_MAP_IDENTITY,
+ complexity,
+ "call to `flat_map` where `flatten` is sufficient"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # #![allow(unused)]
+ /// let vec = vec![1];
+ /// vec.iter().find(|x| **x == 0).is_some();
+ ///
+ /// "hello world".find("world").is_none();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let vec = vec![1];
+ /// vec.iter().any(|x| *x == 0);
+ ///
+ /// # #[allow(unused)]
+ /// !"hello world".contains("world");
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub SEARCH_IS_SOME,
+ complexity,
+ "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let name = "foo";
+ /// if name.chars().next() == Some('_') {};
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let name = "foo";
+ /// if name.starts_with('_') {};
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CHARS_NEXT_CMP,
+ style,
+ "using `.chars().next()` to check if a string starts with a char"
+}
+
+declare_clippy_lint! {
+ /// ### 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. 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.
+ ///
+ /// 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::from("empty"));
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// foo.unwrap_or_else(|| String::from("empty"));
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub OR_FUN_CALL,
+ nursery,
+ "using any `*or` method with a function call, which suggests `*or_else`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let fallback = "fallback";
+ /// // Result
+ /// # type Error = &'static str;
+ /// # let result: Result<&str, Error> = Err("error");
+ /// let value = result.or::<Error>(Ok(fallback)).unwrap();
+ ///
+ /// // Option
+ /// # let option: Option<&str> = None;
+ /// let value = option.or(Some(fallback)).unwrap();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let fallback = "fallback";
+ /// // Result
+ /// # let result: Result<&str, &str> = Err("error");
+ /// let value = result.unwrap_or(fallback);
+ ///
+ /// // Option
+ /// # let option: Option<&str> = None;
+ /// let value = option.unwrap_or(fallback);
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub OR_THEN_UNWRAP,
+ complexity,
+ "checks for `.or(…).unwrap()` calls to Options and Results."
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// # let err_code = "418";
+ /// # let err_msg = "I'm a teapot";
+ /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
+ ///
+ /// // or
+ ///
+ /// # let foo = Some(String::new());
+ /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let foo = Some(String::new());
+ /// # let err_code = "418";
+ /// # let err_msg = "I'm a teapot";
+ /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub EXPECT_FUN_CALL,
+ perf,
+ "using any `expect` method with a function call"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 42u64.clone();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CLONE_ON_COPY,
+ complexity,
+ "using `clone` on a `Copy` type"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # use std::rc::Rc;
+ /// let x = Rc::new(1);
+ ///
+ /// x.clone();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # use std::rc::Rc;
+ /// # let x = Rc::new(1);
+ /// Rc::clone(&x);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CLONE_ON_REF_PTR,
+ restriction,
+ "using 'clone' on a ref-counted pointer"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn main() {
+ /// let x = vec![1];
+ /// let y = &&x;
+ /// let z = y.clone();
+ /// println!("{:p} {:p}", *y, z); // prints out the same pointer
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CLONE_DOUBLE_REF,
+ correctness,
+ "using `clone` on `&&T`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// // 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());
+ /// ```
+ #[clippy::version = "1.40.0"]
+ pub INEFFICIENT_TO_STRING,
+ pedantic,
+ "using `to_string` on `&&T` where `T: ToString`"
+}
+
+declare_clippy_lint! {
+ /// ### 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:
+ /// ```rust
+ /// # struct Foo;
+ /// # struct NotAFoo;
+ /// impl Foo {
+ /// fn new() -> NotAFoo {
+ /// # NotAFoo
+ /// }
+ /// }
+ /// ```
+ ///
+ /// ```rust
+ /// # struct Foo;
+ /// struct Bar(Foo);
+ /// impl Foo {
+ /// // Bad. The type name must contain `Self`
+ /// fn new() -> Bar {
+ /// # Bar(Foo)
+ /// }
+ /// }
+ /// ```
+ ///
+ /// ```rust
+ /// # struct Foo;
+ /// # struct FooError;
+ /// impl Foo {
+ /// // Good. Return type contains `Self`
+ /// fn new() -> Result<Foo, FooError> {
+ /// # Ok(Foo)
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Or in a trait definition:
+ /// ```rust
+ /// pub trait Trait {
+ /// // Bad. The type name must contain `Self`
+ /// fn new();
+ /// }
+ /// ```
+ ///
+ /// ```rust
+ /// pub trait Trait {
+ /// // Good. Return type contains `Self`
+ /// fn new() -> Self;
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub NEW_RET_NO_SELF,
+ style,
+ "not returning type containing `Self` in a `new` method"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// _.split("x");
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// _.split('x');
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub SINGLE_CHAR_PATTERN,
+ perf,
+ "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,should_panic
+ /// for x in (0..100).step_by(0) {
+ /// //..
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub ITERATOR_STEP_BY_ZERO,
+ correctness,
+ "using `Iterator::step_by(0)`, which will panic at runtime"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let _ = std::iter::empty::<Option<i32>>().flatten();
+ /// ```
+ #[clippy::version = "1.53.0"]
+ pub OPTION_FILTER_MAP,
+ complexity,
+ "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # use std::collections::HashSet;
+ /// # let mut s = HashSet::new();
+ /// # s.insert(1);
+ /// let x = s.iter().nth(0);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # use std::collections::HashSet;
+ /// # let mut s = HashSet::new();
+ /// # s.insert(1);
+ /// let x = s.iter().next();
+ /// ```
+ #[clippy::version = "1.42.0"]
+ pub ITER_NTH_ZERO,
+ style,
+ "replace `iter.nth(0)` with `iter.next()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let bad_vec = some_vec.get(3);
+ /// let bad_slice = &some_vec[..].get(3);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub ITER_NTH,
+ perf,
+ "using `.iter().nth()` on a standard library type with O(1) element access"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `.skip(x).next()` on iterators.
+ ///
+ /// ### Why is this bad?
+ /// `.nth(x)` is cleaner
+ ///
+ /// ### Example
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// let some_vec = vec![0, 1, 2, 3];
+ /// let bad_vec = some_vec.iter().nth(3);
+ /// let bad_slice = &some_vec[..].iter().nth(3);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub ITER_SKIP_NEXT,
+ style,
+ "using `.skip(x).next()` on an iterator"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # use std::collections::HashSet;
+ /// let mut foo = vec![0, 1, 2, 3];
+ /// let bar: HashSet<usize> = foo.drain(..).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::collections::HashSet;
+ /// let foo = vec![0, 1, 2, 3];
+ /// let bar: HashSet<usize> = foo.into_iter().collect();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub ITER_WITH_DRAIN,
+ nursery,
+ "replace `.drain(..)` with `.into_iter()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = vec![2, 3, 5];
+ /// let last_element = x.get(x.len() - 1);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let x = vec![2, 3, 5];
+ /// let last_element = x.last();
+ /// ```
+ #[clippy::version = "1.37.0"]
+ pub GET_LAST_WITH_LEN,
+ complexity,
+ "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// let mut some_vec = vec![0, 1, 2, 3];
+ /// let last = some_vec[3];
+ /// some_vec[0] = 1;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub GET_UNWRAP,
+ restriction,
+ "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let mut a = vec![1, 2, 3];
+ /// let mut b = vec![4, 5, 6];
+ ///
+ /// a.extend(b.drain(..));
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let mut a = vec![1, 2, 3];
+ /// let mut b = vec![4, 5, 6];
+ ///
+ /// a.append(&mut b);
+ /// ```
+ #[clippy::version = "1.55.0"]
+ pub EXTEND_WITH_DRAIN,
+ perf,
+ "using vec.append(&mut vec) to move the full range of a vector to another"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// let abc = "abc";
+ /// let def = String::from("def");
+ /// let mut s = String::new();
+ /// s.push_str(abc);
+ /// s.push_str(&def);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub STRING_EXTEND_CHARS,
+ style,
+ "using `x.extend(s.chars())` where s is a `&str` or `String`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let s = [1, 2, 3, 4, 5];
+ /// let s2: Vec<isize> = s[..].iter().cloned().collect();
+ /// ```
+ /// The better use would be:
+ /// ```rust
+ /// let s = [1, 2, 3, 4, 5];
+ /// let s2: Vec<isize> = s.to_vec();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub ITER_CLONED_COLLECT,
+ style,
+ "using `.cloned().collect()` on slice to create a `Vec`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let name = "_";
+ /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let name = "_";
+ /// name.ends_with('_') || name.ends_with('-');
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CHARS_LAST_CMP,
+ style,
+ "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # fn do_stuff(x: &[i32]) {}
+ /// let x: &[i32] = &[1, 2, 3, 4, 5];
+ /// do_stuff(x.as_ref());
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// # fn do_stuff(x: &[i32]) {}
+ /// let x: &[i32] = &[1, 2, 3, 4, 5];
+ /// do_stuff(x);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub USELESS_ASREF,
+ complexity,
+ "using `as_ref` where the types before and after the call are the same"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # #[allow(unused)]
+ /// (0..3).fold(false, |acc, x| acc || x > 2);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// (0..3).any(|x| x > 2);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub UNNECESSARY_FOLD,
+ style,
+ "using `fold` when a more succinct alternative exists"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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);
+ /// ```
+ ///
+ /// ```rust
+ /// 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);
+ /// ```
+ #[clippy::version = "1.31.0"]
+ pub UNNECESSARY_FILTER_MAP,
+ complexity,
+ "using `filter_map` when a more succinct alternative exists"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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);
+ /// ```
+ ///
+ /// ```rust
+ /// 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();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub UNNECESSARY_FIND_MAP,
+ complexity,
+ "using `find_map` when a more succinct alternative exists"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let vec = vec![3, 4, 5];
+ /// (&vec).into_iter();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let vec = vec![3, 4, 5];
+ /// (&vec).iter();
+ /// ```
+ #[clippy::version = "1.32.0"]
+ pub INTO_ITER_ON_REF,
+ style,
+ "using `.into_iter()` on a reference"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let _ = (0..3).map(|x| x + 2).count();
+ /// ```
+ #[clippy::version = "1.39.0"]
+ pub SUSPICIOUS_MAP,
+ suspicious,
+ "suspicious usage of map"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// // Beware the UB
+ /// use std::mem::MaybeUninit;
+ ///
+ /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+ /// ```
+ ///
+ /// Note that the following is OK:
+ ///
+ /// ```rust
+ /// use std::mem::MaybeUninit;
+ ///
+ /// let _: [MaybeUninit<bool>; 5] = unsafe {
+ /// MaybeUninit::uninit().assume_init()
+ /// };
+ /// ```
+ #[clippy::version = "1.39.0"]
+ pub UNINIT_ASSUMED_INIT,
+ correctness,
+ "`MaybeUninit::uninit().assume_init()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let y: u32 = 0;
+ /// # let x: u32 = 100;
+ /// 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:
+ ///
+ /// ```rust
+ /// # let y: u32 = 0;
+ /// # let x: u32 = 100;
+ /// let add = x.saturating_add(y);
+ /// let sub = x.saturating_sub(y);
+ /// ```
+ #[clippy::version = "1.39.0"]
+ pub MANUAL_SATURATING_ARITHMETIC,
+ style,
+ "`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// unsafe { (&() as *const ()).offset(1) };
+ /// ```
+ #[clippy::version = "1.41.0"]
+ pub ZST_OFFSET,
+ correctness,
+ "Check for offset calculations on raw pointers to zero-sized types"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # || {
+ /// let metadata = std::fs::metadata("foo.txt")?;
+ /// let filetype = metadata.file_type();
+ ///
+ /// if filetype.is_file() {
+ /// // read file
+ /// }
+ /// # Ok::<_, std::io::Error>(())
+ /// # };
+ /// ```
+ ///
+ /// should be written as:
+ ///
+ /// ```rust
+ /// # || {
+ /// let metadata = std::fs::metadata("foo.txt")?;
+ /// let filetype = metadata.file_type();
+ ///
+ /// if !filetype.is_dir() {
+ /// // read file
+ /// }
+ /// # Ok::<_, std::io::Error>(())
+ /// # };
+ /// ```
+ #[clippy::version = "1.42.0"]
+ pub FILETYPE_IS_FILE,
+ restriction,
+ "`FileType::is_file` is not recommended to test for readable file type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// 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
+ /// `_.as_deref()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let opt = Some("".to_string());
+ /// opt.as_ref().map(String::as_str)
+ /// # ;
+ /// ```
+ /// Can be written as
+ /// ```rust
+ /// # let opt = Some("".to_string());
+ /// opt.as_deref()
+ /// # ;
+ /// ```
+ #[clippy::version = "1.42.0"]
+ pub OPTION_AS_REF_DEREF,
+ complexity,
+ "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let a = [1, 2, 3];
+ /// # let b = vec![1, 2, 3];
+ /// a[2..].iter().next();
+ /// b.iter().next();
+ /// ```
+ /// should be written as:
+ /// ```rust
+ /// # let a = [1, 2, 3];
+ /// # let b = vec![1, 2, 3];
+ /// a.get(2);
+ /// b.get(0);
+ /// ```
+ #[clippy::version = "1.46.0"]
+ pub ITER_NEXT_SLICE,
+ style,
+ "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let mut string = String::new();
+ /// string.insert_str(0, "R");
+ /// string.push_str("R");
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let mut string = String::new();
+ /// string.insert(0, 'R');
+ /// string.push('R');
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub SINGLE_CHAR_ADD_STR,
+ style,
+ "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// // example code where clippy issues a warning
+ /// let opt: Option<u32> = None;
+ ///
+ /// opt.unwrap_or_else(|| 42);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let opt: Option<u32> = None;
+ ///
+ /// opt.unwrap_or(42);
+ /// ```
+ #[clippy::version = "1.48.0"]
+ pub UNNECESSARY_LAZY_EVALUATIONS,
+ style,
+ "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
+ ///
+ /// ### Why is this bad?
+ /// Using `try_for_each` instead is more readable and idiomatic.
+ ///
+ /// ### Example
+ /// ```rust
+ /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// (0..3).try_for_each(|t| Err(t));
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub MAP_COLLECT_RESULT_UNIT,
+ style,
+ "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// let five_fives = std::iter::repeat(5).take(5);
+ ///
+ /// let v: Vec<i32> = five_fives.collect();
+ ///
+ /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub FROM_ITER_INSTEAD_OF_COLLECT,
+ pedantic,
+ "use `.collect()` instead of `::from_iter()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// [1,2,3,4,5].iter()
+ /// .inspect(|&x| println!("inspect the number: {}", x))
+ /// .for_each(|&x| {
+ /// assert!(x >= 0);
+ /// });
+ /// ```
+ /// Can be written as
+ /// ```rust
+ /// [1,2,3,4,5].iter()
+ /// .for_each(|&x| {
+ /// println!("inspect the number: {}", x);
+ /// assert!(x >= 0);
+ /// });
+ /// ```
+ #[clippy::version = "1.51.0"]
+ pub INSPECT_FOR_EACH,
+ complexity,
+ "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let iter = vec![Some(1)].into_iter();
+ /// iter.filter_map(|x| x);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let iter = vec![Some(1)].into_iter();
+ /// iter.flatten();
+ /// ```
+ #[clippy::version = "1.52.0"]
+ pub FILTER_MAP_IDENTITY,
+ complexity,
+ "call to `filter_map` where `flatten` is sufficient"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = [1, 2, 3];
+ /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x = [1, 2, 3];
+ /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
+ /// ```
+ #[clippy::version = "1.47.0"]
+ pub MAP_IDENTITY,
+ complexity,
+ "using iterator.map(|x| x)"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `.bytes().nth()`.
+ ///
+ /// ### Why is this bad?
+ /// `.as_bytes().get()` is more efficient and more
+ /// readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # #[allow(unused)]
+ /// "Hello".bytes().nth(3);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # #[allow(unused)]
+ /// "Hello".as_bytes().get(3);
+ /// ```
+ #[clippy::version = "1.52.0"]
+ pub BYTES_NTH,
+ style,
+ "replace `.bytes().nth()` with `.as_bytes().get()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let a = vec![1, 2, 3];
+ /// let b = a.to_vec();
+ /// let c = a.to_owned();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let a = vec![1, 2, 3];
+ /// let b = a.clone();
+ /// let c = a.clone();
+ /// ```
+ #[clippy::version = "1.52.0"]
+ pub IMPLICIT_CLONE,
+ pedantic,
+ "implicitly cloning a value by invoking a function on its dereferenced type"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the use of `.iter().count()`.
+ ///
+ /// ### Why is this bad?
+ /// `.len()` is more efficient and more
+ /// readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # #![allow(unused)]
+ /// let some_vec = vec![0, 1, 2, 3];
+ ///
+ /// some_vec.iter().count();
+ /// &some_vec[..].iter().count();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let some_vec = vec![0, 1, 2, 3];
+ ///
+ /// some_vec.len();
+ /// &some_vec[..].len();
+ /// ```
+ #[clippy::version = "1.52.0"]
+ pub ITER_COUNT,
+ complexity,
+ "replace `.iter().count()` with `.len()`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # use std::borrow::Cow;
+ /// let s = "Hello world!";
+ /// let cow = Cow::Borrowed(s);
+ ///
+ /// let data = cow.to_owned();
+ /// assert!(matches!(data, Cow::Borrowed(_)))
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::borrow::Cow;
+ /// let s = "Hello world!";
+ /// let cow = Cow::Borrowed(s);
+ ///
+ /// let data = cow.clone();
+ /// assert!(matches!(data, Cow::Borrowed(_)))
+ /// ```
+ /// or
+ /// ```rust
+ /// # use std::borrow::Cow;
+ /// let s = "Hello world!";
+ /// let cow = Cow::Borrowed(s);
+ ///
+ /// let _data: String = cow.into_owned();
+ /// ```
+ #[clippy::version = "1.65.0"]
+ pub SUSPICIOUS_TO_OWNED,
+ suspicious,
+ "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let s = "";
+ /// for x in s.splitn(1, ":") {
+ /// // ..
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let s = "";
+ /// for x in s.splitn(2, ":") {
+ /// // ..
+ /// }
+ /// ```
+ #[clippy::version = "1.54.0"]
+ pub SUSPICIOUS_SPLITN,
+ correctness,
+ "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x: String = std::iter::repeat('x').take(10).collect();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let x: String = "x".repeat(10);
+ /// ```
+ #[clippy::version = "1.54.0"]
+ pub MANUAL_STR_REPEAT,
+ perf,
+ "manual implementation of `str::repeat`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// 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:
+ /// ```rust,ignore
+ /// 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()`
+ #[clippy::version = "1.57.0"]
+ pub MANUAL_SPLIT_ONCE,
+ complexity,
+ "replace `.splitn(2, pat)` with `.split_once(pat)`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let str = "key=value=add";
+ /// let _ = str.splitn(3, '=').next().unwrap();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let str = "key=value=add";
+ /// let _ = str.split('=').next().unwrap();
+ /// ```
+ #[clippy::version = "1.59.0"]
+ pub NEEDLESS_SPLITN,
+ complexity,
+ "usages of `str::splitn` that can be replaced with `str::split`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let path = std::path::Path::new("x");
+ /// foo(&path.to_string_lossy().to_string());
+ /// fn foo(s: &str) {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let path = std::path::Path::new("x");
+ /// foo(&path.to_string_lossy());
+ /// fn foo(s: &str) {}
+ /// ```
+ #[clippy::version = "1.59.0"]
+ pub UNNECESSARY_TO_OWNED,
+ perf,
+ "unnecessary calls to `to_owned`-like functions"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
+ ///
+ /// ### Why is this bad?
+ /// `.collect::<String>()` is more concise and might be more performant
+ ///
+ /// ### Example
+ /// ```rust
+ /// let vector = vec!["hello", "world"];
+ /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
+ /// println!("{}", output);
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// let vector = vec!["hello", "world"];
+ /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+ /// println!("{}", output);
+ /// ```
+ /// ### Known problems
+ /// While `.collect::<String>()` is sometimes more performant, there are cases where
+ /// using `.collect::<String>()` over `.collect::<Vec<String>>().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::<String>()`
+ #[clippy::version = "1.61.0"]
+ pub UNNECESSARY_JOIN,
+ pedantic,
+ "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let a = Some(&1);
+ /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let a = Some(&1);
+ /// let b = a;
+ /// ```
+ #[clippy::version = "1.57.0"]
+ pub NEEDLESS_OPTION_AS_DEREF,
+ complexity,
+ "no-op use of `deref` or `deref_mut` method to `Option`."
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let c: char = '6';
+ /// c.is_digit(10);
+ /// c.is_digit(16);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let c: char = '6';
+ /// c.is_ascii_digit();
+ /// c.is_ascii_hexdigit();
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub IS_DIGIT_ASCII_RADIX,
+ style,
+ "use of `char::is_digit(..)` with literal radix of 10 or 16"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = Some(3);
+ /// x.as_ref().take();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x = Some(3);
+ /// x.as_ref();
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub NEEDLESS_OPTION_TAKE,
+ complexity,
+ "using `.as_ref().take()` on a temporary value"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `replace` statements which have no effect.
+ ///
+ /// ### Why is this bad?
+ /// It's either a mistake or confusing.
+ ///
+ /// ### Example
+ /// ```rust
+ /// "1234".replace("12", "12");
+ /// "1234".replacen("12", "12", 1);
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub NO_EFFECT_REPLACE,
+ suspicious,
+ "replace with no effect"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = true;
+ /// x.then_some("a").unwrap_or("b");
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x = true;
+ /// if x { "a" } else { "b" };
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub OBFUSCATED_IF_ELSE,
+ style,
+ "use of `.then_some(..).unwrap_or(..)` can be written \
+ more clearly with `if .. else ..`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ ///
+ /// ```rust
+ /// let a = [123].iter();
+ /// let b = Some(123).into_iter();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// 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
+ #[clippy::version = "1.65.0"]
+ pub ITER_ON_SINGLE_ITEMS,
+ nursery,
+ "Iterator for array of length 1"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ ///
+ /// ```rust
+ /// use std::{slice, option};
+ /// let a: slice::Iter<i32> = [].iter();
+ /// let f: option::IntoIter<i32> = None.into_iter();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::iter;
+ /// let a: iter::Empty<i32> = iter::empty();
+ /// let b: iter::Empty<i32> = iter::empty();
+ /// ```
+ ///
+ /// ### Known problems
+ ///
+ /// The type of the resulting iterator might become incompatible with its usage
+ #[clippy::version = "1.65.0"]
+ pub ITER_ON_EMPTY_COLLECTIONS,
+ nursery,
+ "Iterator for empty array"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let vec = vec![1_u8];
+ /// let count = vec.iter().filter(|x| **x == 0u8).count();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// # let vec = vec![1_u8];
+ /// let count = bytecount::count(&vec, 0u8);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub NAIVE_BYTECOUNT,
+ pedantic,
+ "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// "hello".bytes().count();
+ /// String::from("hello").bytes().count();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// "hello".len();
+ /// String::from("hello").len();
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub BYTES_COUNT_TO_LEN,
+ complexity,
+ "Using `bytes().count()` when `len()` performs the same functionality"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn is_rust_file(filename: &str) -> bool {
+ /// filename.ends_with(".rs")
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// 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"))
+ /// }
+ /// ```
+ #[clippy::version = "1.51.0"]
+ pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ pedantic,
+ "Checks for calls to ends_with with case-sensitive file extensions"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = vec![2, 3, 5];
+ /// let first_element = x.get(0);
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let x = vec![2, 3, 5];
+ /// let first_element = x.first();
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub GET_FIRST,
+ style,
+ "Using `x.get(0)` when `x.first()` is simpler"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Finds patterns that reimplement `Option::ok_or`.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// Concise code helps focusing on behavior instead of boilerplate.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// let foo: Option<i32> = None;
+ /// foo.map_or(Err("error"), |v| Ok(v));
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// let foo: Option<i32> = None;
+ /// foo.ok_or("error");
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub MANUAL_OK_OR,
+ pedantic,
+ "finds patterns that can be encoded more concisely with `Option::ok_or`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = vec![42, 43];
+ /// let y = x.iter();
+ /// let z = y.map(|i| *i);
+ /// ```
+ ///
+ /// The correct use would be:
+ ///
+ /// ```rust
+ /// let x = vec![42, 43];
+ /// let y = x.iter();
+ /// let z = y.cloned();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MAP_CLONE,
+ style,
+ "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for instances of `map_err(|_| Some::Enum)`
+ ///
+ /// ### Why is this bad?
+ /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
+ ///
+ /// ### Example
+ /// Before:
+ /// ```rust
+ /// use std::fmt;
+ ///
+ /// #[derive(Debug)]
+ /// enum Error {
+ /// Indivisible,
+ /// Remainder(u8),
+ /// }
+ ///
+ /// impl fmt::Display for Error {
+ /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// match self {
+ /// Error::Indivisible => write!(f, "could not divide input by three"),
+ /// Error::Remainder(remainder) => write!(
+ /// f,
+ /// "input is not divisible by three, remainder = {}",
+ /// remainder
+ /// ),
+ /// }
+ /// }
+ /// }
+ ///
+ /// impl std::error::Error for Error {}
+ ///
+ /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+ /// input
+ /// .parse::<i32>()
+ /// .map_err(|_| Error::Indivisible)
+ /// .map(|v| v % 3)
+ /// .and_then(|remainder| {
+ /// if remainder == 0 {
+ /// Ok(())
+ /// } else {
+ /// Err(Error::Remainder(remainder as u8))
+ /// }
+ /// })
+ /// }
+ /// ```
+ ///
+ /// After:
+ /// ```rust
+ /// use std::{fmt, num::ParseIntError};
+ ///
+ /// #[derive(Debug)]
+ /// enum Error {
+ /// Indivisible(ParseIntError),
+ /// Remainder(u8),
+ /// }
+ ///
+ /// impl fmt::Display for Error {
+ /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// match self {
+ /// Error::Indivisible(_) => write!(f, "could not divide input by three"),
+ /// Error::Remainder(remainder) => write!(
+ /// f,
+ /// "input is not divisible by three, remainder = {}",
+ /// remainder
+ /// ),
+ /// }
+ /// }
+ /// }
+ ///
+ /// impl std::error::Error for Error {
+ /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ /// match self {
+ /// Error::Indivisible(source) => Some(source),
+ /// _ => None,
+ /// }
+ /// }
+ /// }
+ ///
+ /// fn divisible_by_3(input: &str) -> Result<(), Error> {
+ /// input
+ /// .parse::<i32>()
+ /// .map_err(Error::Indivisible)
+ /// .map(|v| v % 3)
+ /// .and_then(|remainder| {
+ /// if remainder == 0 {
+ /// Ok(())
+ /// } else {
+ /// Err(Error::Remainder(remainder as u8))
+ /// }
+ /// })
+ /// }
+ /// ```
+ #[clippy::version = "1.48.0"]
+ pub MAP_ERR_IGNORE,
+ restriction,
+ "`map_err` should not ignore the original error"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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:
+ /// ```rust
+ /// 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;
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub MUT_MUTEX_LOCK,
+ style,
+ "`&mut Mutex::lock` does unnecessary locking"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// use std::fs::OpenOptions;
+ ///
+ /// OpenOptions::new().read(true).truncate(true);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub NONSENSICAL_OPEN_OPTIONS,
+ correctness,
+ "nonsensical combination of options for opening a file"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// use std::path::PathBuf;
+ ///
+ /// let mut x = PathBuf::from("/foo");
+ /// x.push("/bar");
+ /// assert_eq!(x, PathBuf::from("/bar"));
+ /// ```
+ /// Could be written:
+ ///
+ /// ```rust
+ /// use std::path::PathBuf;
+ ///
+ /// let mut x = PathBuf::from("/foo");
+ /// x.push("bar");
+ /// assert_eq!(x, PathBuf::from("/foo/bar"));
+ /// ```
+ #[clippy::version = "1.36.0"]
+ pub PATH_BUF_PUSH_OVERWRITE,
+ nursery,
+ "calling `push` with file system root on `PathBuf` can overwrite it"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # let x = vec![1];
+ /// let _ = x.iter().zip(0..x.len());
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// # let x = vec![1];
+ /// let _ = x.iter().enumerate();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub RANGE_ZIP_WITH_LEN,
+ complexity,
+ "zipping iterator with a range when `enumerate()` would do"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn main() {
+ /// let x = String::from("hello world").repeat(1);
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn main() {
+ /// let x = String::from("hello world").clone();
+ /// }
+ /// ```
+ #[clippy::version = "1.47.0"]
+ pub REPEAT_ONCE,
+ complexity,
+ "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let mut vec = vec![2, 1, 3];
+ /// vec.sort();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let mut vec = vec![2, 1, 3];
+ /// vec.sort_unstable();
+ /// ```
+ #[clippy::version = "1.47.0"]
+ pub STABLE_SORT_PRIMITIVE,
+ pedantic,
+ "use of sort() when sort_unstable() is equivalent"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # use std::hash::Hash;
+ /// # use std::collections::hash_map::DefaultHasher;
+ /// # enum Foo { Empty, WithValue(u8) }
+ /// # use Foo::*;
+ /// # let mut state = DefaultHasher::new();
+ /// # let my_enum = Foo::Empty;
+ /// match my_enum {
+ /// Empty => ().hash(&mut state),
+ /// WithValue(x) => x.hash(&mut state),
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::hash::Hash;
+ /// # use std::collections::hash_map::DefaultHasher;
+ /// # enum Foo { Empty, WithValue(u8) }
+ /// # use Foo::*;
+ /// # let mut state = DefaultHasher::new();
+ /// # let my_enum = Foo::Empty;
+ /// match my_enum {
+ /// Empty => 0_u8.hash(&mut state),
+ /// WithValue(x) => x.hash(&mut state),
+ /// }
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub UNIT_HASH,
+ correctness,
+ "hashing a unit value, which does nothing"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// # struct A;
+ /// # impl A { fn foo(&self) {} }
+ /// # let mut vec: Vec<A> = Vec::new();
+ /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # struct A;
+ /// # impl A { fn foo(&self) {} }
+ /// # let mut vec: Vec<A> = Vec::new();
+ /// vec.sort_by_key(|a| a.foo());
+ /// ```
+ #[clippy::version = "1.46.0"]
+ pub UNNECESSARY_SORT_BY,
+ complexity,
+ "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Finds occurrences of `Vec::resize(0, an_int)`
+ ///
+ /// ### Why is this bad?
+ /// This is probably an argument inversion mistake.
+ ///
+ /// ### Example
+ /// ```rust
+ /// vec!(1, 2, 3, 4, 5).resize(0, 5)
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust
+ /// vec!(1, 2, 3, 4, 5).clear()
+ /// ```
+ #[clippy::version = "1.46.0"]
+ pub VEC_RESIZE_TO_ZERO,
+ correctness,
+ "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,no_run
+ /// # use std::io::Read;
+ /// # use std::fs::File;
+ /// 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
+ /// ```rust,no_run
+ /// # use std::fs;
+ /// let mut bytes = fs::read("foo.txt").unwrap();
+ /// ```
+ #[clippy::version = "1.44.0"]
+ pub VERBOSE_FILE_READS,
+ restriction,
+ "use of `File::read_to_end` or `File::read_to_string`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ ///
+ /// ```
+ /// # use std::collections::HashMap;
+ /// let map: HashMap<u32, u32> = HashMap::new();
+ /// let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
+ /// ```
+ ///
+ /// Use instead:
+ /// ```
+ /// # use std::collections::HashMap;
+ /// let map: HashMap<u32, u32> = HashMap::new();
+ /// let values = map.values().collect::<Vec<_>>();
+ /// ```
+ #[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.66.0"]
++ #[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: io::Seek>(t: &mut T) {
+ /// t.seek(io::SeekFrom::Start(0));
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::io;
+ /// fn foo<T: io::Seek>(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::<Vec<_>>().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: Msrv,
+ allow_expect_in_tests: bool,
+ allow_unwrap_in_tests: bool,
+}
+
+impl Methods {
+ #[must_use]
+ pub fn new(
+ avoid_breaking_exported_api: bool,
+ msrv: Msrv,
+ allow_expect_in_tests: bool,
+ allow_unwrap_in_tests: bool,
+ ) -> Self {
+ Self {
+ avoid_breaking_exported_api,
+ msrv,
+ allow_expect_in_tests,
+ allow_unwrap_in_tests,
+ }
+ }
+}
+
+impl_lint_pass!(Methods => [
+ UNWRAP_USED,
+ EXPECT_USED,
+ SHOULD_IMPLEMENT_TRAIT,
+ WRONG_SELF_CONVENTION,
+ OK_EXPECT,
+ UNWRAP_OR_ELSE_DEFAULT,
+ MAP_UNWRAP_OR,
+ RESULT_MAP_OR_INTO_OPTION,
+ OPTION_MAP_OR_NONE,
+ BIND_INSTEAD_OF_MAP,
+ OR_FUN_CALL,
+ OR_THEN_UNWRAP,
+ EXPECT_FUN_CALL,
+ CHARS_NEXT_CMP,
+ CHARS_LAST_CMP,
+ CLONE_ON_COPY,
+ CLONE_ON_REF_PTR,
+ CLONE_DOUBLE_REF,
+ COLLAPSIBLE_STR_REPLACE,
+ ITER_OVEREAGER_CLONED,
+ CLONED_INSTEAD_OF_COPIED,
+ FLAT_MAP_OPTION,
+ INEFFICIENT_TO_STRING,
+ NEW_RET_NO_SELF,
+ SINGLE_CHAR_PATTERN,
+ SINGLE_CHAR_ADD_STR,
+ SEARCH_IS_SOME,
+ FILTER_NEXT,
+ SKIP_WHILE_NEXT,
+ FILTER_MAP_IDENTITY,
+ MAP_IDENTITY,
+ MANUAL_FILTER_MAP,
+ MANUAL_FIND_MAP,
+ OPTION_FILTER_MAP,
+ FILTER_MAP_NEXT,
+ FLAT_MAP_IDENTITY,
+ MAP_FLATTEN,
+ ITERATOR_STEP_BY_ZERO,
+ ITER_NEXT_SLICE,
+ ITER_COUNT,
+ ITER_NTH,
+ ITER_NTH_ZERO,
+ BYTES_NTH,
+ ITER_SKIP_NEXT,
+ GET_UNWRAP,
+ GET_LAST_WITH_LEN,
+ STRING_EXTEND_CHARS,
+ ITER_CLONED_COLLECT,
+ ITER_WITH_DRAIN,
+ USELESS_ASREF,
+ UNNECESSARY_FOLD,
+ UNNECESSARY_FILTER_MAP,
+ UNNECESSARY_FIND_MAP,
+ INTO_ITER_ON_REF,
+ SUSPICIOUS_MAP,
+ UNINIT_ASSUMED_INIT,
+ MANUAL_SATURATING_ARITHMETIC,
+ ZST_OFFSET,
+ FILETYPE_IS_FILE,
+ OPTION_AS_REF_DEREF,
+ UNNECESSARY_LAZY_EVALUATIONS,
+ MAP_COLLECT_RESULT_UNIT,
+ FROM_ITER_INSTEAD_OF_COLLECT,
+ INSPECT_FOR_EACH,
+ IMPLICIT_CLONE,
+ SUSPICIOUS_TO_OWNED,
+ SUSPICIOUS_SPLITN,
+ MANUAL_STR_REPEAT,
+ EXTEND_WITH_DRAIN,
+ MANUAL_SPLIT_ONCE,
+ NEEDLESS_SPLITN,
+ UNNECESSARY_TO_OWNED,
+ UNNECESSARY_JOIN,
+ ERR_EXPECT,
+ NEEDLESS_OPTION_AS_DEREF,
+ IS_DIGIT_ASCII_RADIX,
+ NEEDLESS_OPTION_TAKE,
+ NO_EFFECT_REPLACE,
+ OBFUSCATED_IF_ELSE,
+ ITER_ON_SINGLE_ITEMS,
+ ITER_ON_EMPTY_COLLECTIONS,
+ NAIVE_BYTECOUNT,
+ BYTES_COUNT_TO_LEN,
+ CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ GET_FIRST,
+ MANUAL_OK_OR,
+ MAP_CLONE,
+ MAP_ERR_IGNORE,
+ MUT_MUTEX_LOCK,
+ NONSENSICAL_OPEN_OPTIONS,
+ PATH_BUF_PUSH_OVERWRITE,
+ RANGE_ZIP_WITH_LEN,
+ REPEAT_ONCE,
+ STABLE_SORT_PRIMITIVE,
+ UNIT_HASH,
+ UNNECESSARY_SORT_BY,
+ 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, 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, call_span));
+ }
+ }
+ None
+}
+
+impl<'tcx> LateLintPass<'tcx> for Methods {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ self.check_methods(cx, expr);
+
+ match expr.kind {
+ hir::ExprKind::Call(func, args) => {
+ from_iter_instead_of_collect::check(cx, expr, args, func);
+ },
+ hir::ExprKind::MethodCall(method_call, receiver, args, _) => {
+ let method_span = method_call.ident.span;
+ or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
+ expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
+ clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
+ clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
+ inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
+ 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);
+ },
+ hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
+ let mut info = BinaryExprInfo {
+ expr,
+ chain: lhs,
+ other: rhs,
+ eq: op.node == hir::BinOpKind::Eq,
+ };
+ lint_binary_expr_with_method_call(cx, &mut info);
+ },
+ _ => (),
+ }
+ }
+
+ #[allow(clippy::too_many_lines)]
+ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
+ if in_external_macro(cx.sess(), impl_item.span) {
+ return;
+ }
+ 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.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.owner_id).subst_identity();
+ 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.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
+ && sig.decl.inputs.len() == method_config.param_count
+ && method_config.output_type.matches(&sig.decl.output)
+ // in case there is no first arg, since we already have checked the number of arguments
+ // it's should be always true
+ && first_arg_ty_opt.map_or(true, |first_arg_ty| method_config
+ .self_kind.matches(cx, self_ty, first_arg_ty)
+ )
+ && fn_header_equals(method_config.fn_header, sig.header)
+ && method_config.lifetime_param_cond(impl_item)
+ {
+ span_lint_and_help(
+ cx,
+ SHOULD_IMPLEMENT_TRAIT,
+ impl_item.span,
+ &format!(
+ "method `{}` can be confused for the standard trait method `{}::{}`",
+ method_config.method_name, method_config.trait_name, method_config.method_name
+ ),
+ None,
+ &format!(
+ "consider implementing the trait `{}` or choosing a less ambiguous method name",
+ method_config.trait_name
+ ),
+ );
+ }
+ }
+ }
+
+ if sig.decl.implicit_self.has_implicit_self()
+ && !(self.avoid_breaking_exported_api
+ && 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
+ {
+ wrong_self_convention::check(
+ cx,
+ name,
+ self_ty,
+ first_arg_ty,
+ first_arg.pat.span,
+ implements_trait,
+ false
+ );
+ }
+ }
+
+ // if this impl block implements a trait, lint in trait definition instead
+ if implements_trait {
+ return;
+ }
+
+ if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
+ let ret_ty = return_ty(cx, impl_item.hir_id());
+
+ if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
+ return;
+ }
+
+ if name == "new" && ret_ty != self_ty {
+ span_lint(
+ cx,
+ NEW_RET_NO_SELF,
+ impl_item.span,
+ "methods called `new` usually return `Self`",
+ );
+ }
+ }
+ }
+
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+ if in_external_macro(cx.tcx.sess, item.span) {
+ return;
+ }
+
+ if_chain! {
+ if let TraitItemKind::Fn(ref sig, _) = item.kind;
+ if sig.decl.implicit_self.has_implicit_self();
+ if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
+
+ 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.owner_id.to_def_id())
+ .self_ty()
+ .skip_binder();
+ wrong_self_convention::check(
+ cx,
+ item.ident.name.as_str(),
+ self_ty,
+ first_arg_ty,
+ first_arg_span,
+ false,
+ true,
+ );
+ }
+ }
+
+ if_chain! {
+ 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.owner_id.to_def_id())
+ .self_ty()
+ .skip_binder();
+ if !ret_ty.contains(self_ty);
+
+ then {
+ span_lint(
+ cx,
+ NEW_RET_NO_SELF,
+ item.span,
+ "methods called `new` usually return `Self`",
+ );
+ }
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
+
+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, call_span)) = method_call(expr) {
+ match (name, args) {
+ ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
+ zst_offset::check(cx, expr, recv);
+ },
+ ("and_then", [arg]) => {
+ let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
+ let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
+ if !biom_option_linted && !biom_result_linted {
+ unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
+ }
+ },
+ ("as_deref" | "as_deref_mut", []) => {
+ needless_option_as_deref::check(cx, expr, recv, name);
+ },
+ ("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", []) 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, [], _, _)) => {
+ 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),
+ _ => {},
+ },
+ ("drain", [arg]) => {
+ iter_with_drain::check(cx, expr, recv, span, arg);
+ },
+ ("ends_with", [arg]) => {
+ if let ExprKind::MethodCall(.., span) = expr.kind {
+ case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
+ }
+ },
+ ("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, 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),
+ ("extend", [arg]) => {
+ string_extend_chars::check(cx, expr, recv, arg);
+ extend_with_drain::check(cx, expr, recv, arg);
+ },
+ ("filter_map", [arg]) => {
+ unnecessary_filter_map::check(cx, expr, arg, name);
+ filter_map_identity::check(cx, expr, arg, span);
+ },
+ ("find_map", [arg]) => {
+ unnecessary_filter_map::check(cx, expr, arg, name);
+ },
+ ("flat_map", [arg]) => {
+ flat_map_identity::check(cx, expr, arg, span);
+ 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),
+ _ => {},
+ },
+ ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
+ ("for_each", [_]) => {
+ if let Some(("inspect", _, [_], span2, _)) = method_call(recv) {
+ inspect_for_each::check(cx, expr, span2);
+ }
+ },
+ ("get", [arg]) => {
+ get_first::check(cx, expr, recv, arg);
+ get_last_with_len::check(cx, expr, recv, arg);
+ },
+ ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
+ ("hash", [arg]) => {
+ 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_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) {
+ unnecessary_join::check(cx, expr, recv, join_arg, span);
+ }
+ },
+ ("last", []) | ("skip", [_]) => {
+ 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);
+ }
+ }
+ },
+ ("lock", []) => {
+ mut_mutex_lock::check(cx, expr, recv, span);
+ },
+ (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) {
+ 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) {
+ 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),
+ ("filter", [f_arg]) => {
+ filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
+ },
+ ("find", [f_arg]) => {
+ filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true);
+ },
+ _ => {},
+ }
+ }
+ map_identity::check(cx, expr, recv, m_arg, name, span);
+ },
+ ("map_or", [def, map]) => {
+ option_map_or_none::check(cx, expr, recv, def, map);
+ manual_ok_or::check(cx, expr, recv, def, map);
+ },
+ ("next", []) => {
+ 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),
+ ("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),
+ _ => {},
+ }
+ }
+ },
+ ("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),
+ _ => iter_nth_zero::check(cx, expr, recv, n_arg),
+ },
+ ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
+ ("open", [_]) => {
+ open_options::check(cx, expr, recv);
+ },
+ ("or_else", [arg]) => {
+ if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
+ unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
+ }
+ },
+ ("push", [arg]) => {
+ path_buf_push_overwrite::check(cx, expr, arg);
+ },
+ ("read_to_end", [_]) => {
+ verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
+ },
+ ("read_to_string", [_]) => {
+ verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
+ },
+ ("repeat", [arg]) => {
+ repeat_once::check(cx, expr, recv, arg);
+ },
+ (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
+ no_effect_replace::check(cx, expr, arg1, arg2);
+
+ // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
+ 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);
+ },
+ ("sort_by", [arg]) => {
+ unnecessary_sort_by::check(cx, expr, recv, arg, false);
+ },
+ ("sort_unstable_by", [arg]) => {
+ unnecessary_sort_by::check(cx, expr, recv, arg, true);
+ },
+ ("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);
+ }
+ },
+ ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
+ if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
+ suspicious_splitn::check(cx, name, expr, recv, count);
+ }
+ },
+ ("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 ("cloned", []) = (name2, args2) {
+ iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
+ }
+ }
+ },
+ ("take", []) => needless_option_take::check(cx, expr, recv),
+ ("then", [arg]) => {
+ if !self.msrv.meets(msrvs::BOOL_THEN_SOME) {
+ return;
+ }
+ unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
+ },
+ ("to_owned", []) => {
+ if !suspicious_to_owned::check(cx, expr, recv) {
+ implicit_clone::check(cx, name, expr, recv);
+ }
+ },
+ ("to_os_string" | "to_path_buf" | "to_vec", []) => {
+ implicit_clone::check(cx, name, expr, recv);
+ },
+ ("unwrap", []) => {
+ match method_call(recv) {
+ Some(("get", recv, [get_arg], _, _)) => {
+ get_unwrap::check(cx, expr, recv, get_arg, false);
+ },
+ Some(("get_mut", recv, [get_arg], _, _)) => {
+ get_unwrap::check(cx, expr, recv, get_arg, true);
+ },
+ Some(("or", recv, [or_arg], or_span, _)) => {
+ or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
+ },
+ _ => {},
+ }
+ unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
+ },
+ ("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], _, _)) => {
+ manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
+ },
+ 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], _, _)) => {
+ 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) => {},
+ _ => {
+ unwrap_or_else_default::check(cx, expr, recv, u_arg);
+ unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
+ },
+ },
+ ("zip", [arg]) => {
+ if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
+ && name.ident.name == sym::iter
+ {
+ range_zip_with_len::check(cx, expr, iter_recv, arg);
+ }
+ },
+ _ => {},
+ }
+ }
+ }
+}
+
+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) {
+ search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
+ }
+}
+
+/// Used for `lint_binary_expr_with_method_call`.
+#[derive(Copy, Clone)]
+struct BinaryExprInfo<'a> {
+ expr: &'a hir::Expr<'a>,
+ chain: &'a hir::Expr<'a>,
+ other: &'a hir::Expr<'a>,
+ eq: bool,
+}
+
+/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
+fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
+ macro_rules! lint_with_both_lhs_and_rhs {
+ ($func:expr, $cx:expr, $info:ident) => {
+ if !$func($cx, $info) {
+ ::std::mem::swap(&mut $info.chain, &mut $info.other);
+ if $func($cx, $info) {
+ return;
+ }
+ }
+ };
+ }
+
+ lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
+ lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
+}
+
+const FN_HEADER: hir::FnHeader = hir::FnHeader {
+ unsafety: hir::Unsafety::Normal,
+ constness: hir::Constness::NotConst,
+ asyncness: hir::IsAsync::NotAsync,
+ abi: rustc_target::spec::abi::Abi::Rust,
+};
+
+struct ShouldImplTraitCase {
+ trait_name: &'static str,
+ method_name: &'static str,
+ param_count: usize,
+ fn_header: hir::FnHeader,
+ // implicit self kind expected (none, self, &self, ...)
+ self_kind: SelfKind,
+ // checks against the output type
+ output_type: OutType,
+ // certain methods with explicit lifetimes can't implement the equivalent trait method
+ lint_explicit_lifetime: bool,
+}
+impl ShouldImplTraitCase {
+ const fn new(
+ trait_name: &'static str,
+ method_name: &'static str,
+ param_count: usize,
+ fn_header: hir::FnHeader,
+ self_kind: SelfKind,
+ output_type: OutType,
+ lint_explicit_lifetime: bool,
+ ) -> ShouldImplTraitCase {
+ ShouldImplTraitCase {
+ trait_name,
+ method_name,
+ param_count,
+ fn_header,
+ self_kind,
+ output_type,
+ lint_explicit_lifetime,
+ }
+ }
+
+ fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
+ self.lint_explicit_lifetime
+ || !impl_item.generics.params.iter().any(|p| {
+ matches!(
+ p.kind,
+ hir::GenericParamKind::Lifetime {
+ kind: hir::LifetimeParamKind::Explicit
+ }
+ )
+ })
+ }
+}
+
+#[rustfmt::skip]
+const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
+ ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true),
+ ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true),
+ ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true),
+ ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true),
+ ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true),
+ ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true),
+ ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true),
+ ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
+ ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false),
+ ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+ ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
+];
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+enum SelfKind {
+ Value,
+ Ref,
+ RefMut,
+ No, // When we want the first argument type to be different than `Self`
+}
+
+impl SelfKind {
+ fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+ fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+ if ty == parent_ty {
+ true
+ } else if ty.is_box() {
+ ty.boxed_ty() == parent_ty
+ } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
+ if let ty::Adt(_, substs) = ty.kind() {
+ substs.types().next().map_or(false, |t| t == parent_ty)
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
+
+ fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+ if let ty::Ref(_, t, m) = *ty.kind() {
+ return m == mutability && t == parent_ty;
+ }
+
+ let trait_sym = match mutability {
+ hir::Mutability::Not => sym::AsRef,
+ hir::Mutability::Mut => sym::AsMut,
+ };
+
+ 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()])
+ }
+
+ fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+ !matches_value(cx, parent_ty, ty)
+ && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
+ && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
+ }
+
+ match self {
+ Self::Value => matches_value(cx, parent_ty, ty),
+ Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
+ Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
+ Self::No => matches_none(cx, parent_ty, ty),
+ }
+ }
+
+ #[must_use]
+ fn description(self) -> &'static str {
+ match self {
+ Self::Value => "`self` by value",
+ Self::Ref => "`self` by reference",
+ Self::RefMut => "`self` by mutable reference",
+ Self::No => "no `self`",
+ }
+ }
+}
+
+#[derive(Clone, Copy)]
+enum OutType {
+ Unit,
+ Bool,
+ Any,
+ Ref,
+}
+
+impl OutType {
+ fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
+ let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
+ match (self, ty) {
+ (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
+ (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::Ref(_, _)),
+ _ => false,
+ }
+ }
+}
+
+fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
+ expected.constness == actual.constness
+ && expected.unsafety == actual.unsafety
+ && expected.asyncness == actual.asyncness
+}
--- /dev/null
- })
+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",
+ );
+ }
++ });
+ }
+ }
+}
--- /dev/null
--- /dev/null
++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_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::<u32>()) }
++ /// }
++ /// ```
++ /// 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::<u32>() };
++ ///
++ /// // 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(_)) {
++ 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.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.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.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)
++ });
++}
--- /dev/null
- use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
+use core::cell::Cell;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::Applicability;
+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};
- && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(|t| t.subst_identity())
++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};
+use rustc_span::Span;
+use std::iter;
+
+declare_clippy_lint! {
+ /// ### 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.
+ ///
+ /// ```rust
+ /// 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
+ /// ```rust
+ /// fn f(a: usize, b: usize) -> usize {
+ /// if a == 0 {
+ /// 1
+ /// } else {
+ /// f(a - 1, b + 1)
+ /// }
+ /// }
+ /// # fn main() {
+ /// # print!("{}", f(1, 1));
+ /// # }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// fn f(a: usize) -> usize {
+ /// if a == 0 {
+ /// 1
+ /// } else {
+ /// f(a - 1)
+ /// }
+ /// }
+ /// # fn main() {
+ /// # print!("{}", f(1));
+ /// # }
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub ONLY_USED_IN_RECURSION,
+ complexity,
+ "arguments that is only used in recursion can be removed"
+}
+impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
+
+#[derive(Clone, Copy)]
+enum FnKind {
+ Fn,
+ TraitFn,
+ // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
+ // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
+ // equality.
+ ImplTraitFn(usize),
+}
+
+struct Param {
+ /// The function this is a parameter for.
+ fn_id: DefId,
+ fn_kind: FnKind,
+ /// The index of this parameter.
+ idx: usize,
+ ident: Ident,
+ /// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
+ apply_lint: Cell<bool>,
+ /// All the uses of this parameter.
+ uses: Vec<Usage>,
+}
+impl Param {
+ fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
+ Self {
+ fn_id,
+ fn_kind,
+ idx,
+ ident,
+ apply_lint: Cell::new(true),
+ uses: Vec::new(),
+ }
+ }
+}
+
+#[derive(Debug)]
+struct Usage {
+ span: Span,
+ idx: usize,
+}
+impl Usage {
+ fn new(span: Span, idx: usize) -> Self {
+ Self { span, idx }
+ }
+}
+
+/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
+/// `DefId` of the function paired with the parameter's index.
+#[derive(Default)]
+struct Params {
+ params: Vec<Param>,
+ by_id: HirIdMap<usize>,
+ by_fn: FxHashMap<(DefId, usize), usize>,
+}
+impl Params {
+ fn insert(&mut self, param: Param, id: HirId) {
+ let idx = self.params.len();
+ self.by_id.insert(id, idx);
+ self.by_fn.insert((param.fn_id, param.idx), idx);
+ self.params.push(param);
+ }
+
+ fn remove_by_id(&mut self, id: HirId) {
+ if let Some(param) = self.get_by_id_mut(id) {
+ param.uses = Vec::new();
+ let key = (param.fn_id, param.idx);
+ self.by_fn.remove(&key);
+ self.by_id.remove(&id);
+ }
+ }
+
+ fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
+ self.params.get_mut(*self.by_id.get(&id)?)
+ }
+
+ fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
+ self.params.get(*self.by_fn.get(&(id, idx))?)
+ }
+
+ fn clear(&mut self) {
+ self.params.clear();
+ self.by_id.clear();
+ self.by_fn.clear();
+ }
+
+ /// Sets the `apply_lint` flag on each parameter.
+ fn flag_for_linting(&mut self) {
+ // Stores the list of parameters currently being resolved. Needed to avoid cycles.
+ let mut eval_stack = Vec::new();
+ for param in &self.params {
+ self.try_disable_lint_for_param(param, &mut eval_stack);
+ }
+ }
+
+ // Use by calling `flag_for_linting`.
+ fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
+ if !param.apply_lint.get() {
+ true
+ } else if param.uses.is_empty() {
+ // Don't lint on unused parameters.
+ param.apply_lint.set(false);
+ true
+ } else if eval_stack.contains(¶m.idx) {
+ // Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
+ false
+ } else {
+ eval_stack.push(param.idx);
+ // Check all cases when used at a different parameter index.
+ // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
+ for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
+ if self
+ .get_by_fn(param.fn_id, usage.idx)
+ // If the parameter can't be found, then it's used for more than just recursion.
+ .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
+ {
+ param.apply_lint.set(false);
+ eval_stack.pop();
+ return true;
+ }
+ }
+ eval_stack.pop();
+ false
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct OnlyUsedInRecursion {
+ /// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
+ entered_body: Option<HirId>,
+ params: Params,
+}
+
+impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
+ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+ if body.value.span.from_expansion() {
+ return;
+ }
+ // `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.owner_id.to_def_id(), FnKind::Fn, 0),
+ Some(Node::TraitItem(&TraitItem {
+ kind: TraitItemKind::Fn(ref sig, _),
+ owner_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, _),
+ owner_id,
+ ..
+ })) => {
+ #[allow(trivial_casts)]
+ 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,
+ FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
+ usize::from(sig.decl.implicit_self.has_implicit_self()),
+ )
+ } else {
+ (owner_id.to_def_id(), FnKind::Fn, 0)
+ }
+ },
+ _ => return,
+ };
+ body.params
+ .iter()
+ .enumerate()
+ .skip(skip_params)
+ .filter_map(|(idx, p)| match p.pat.kind {
+ PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
+ Some((id, Param::new(fn_id, fn_kind, idx, ident)))
+ },
+ _ => None,
+ })
+ .for_each(|(id, param)| self.params.insert(param, id));
+ if self.entered_body.is_none() {
+ self.entered_body = Some(body.value.hir_id);
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
+ if let Some(id) = path_to_local(e)
+ && let Some(param) = self.params.get_by_id_mut(id)
+ {
+ let typeck = cx.typeck_results();
+ let span = e.span;
+ let mut e = e;
+ loop {
+ match get_expr_use_or_unification_node(cx.tcx, e) {
+ None | Some((Node::Stmt(_), _)) => return,
+ Some((Node::Expr(parent), child_id)) => match parent.kind {
+ // Recursive call. Track which index the parameter is used in.
+ ExprKind::Call(callee, args)
+ if path_def_id(cx, callee).map_or(false, |id| {
+ id == param.fn_id
+ && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
+ }) =>
+ {
+ if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+ param.uses.push(Usage::new(span, idx));
+ }
+ return;
+ },
+ ExprKind::MethodCall(_, receiver, args, _)
+ if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
+ id == param.fn_id
+ && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
+ }) =>
+ {
+ if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
+ param.uses.push(Usage::new(span, idx));
+ }
+ return;
+ },
+ // Assignment to a parameter is fine.
+ ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
+ return;
+ },
+ // Parameter update e.g. `x = x + 1`
+ ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
+ if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
+ {
+ return;
+ },
+ // Side-effect free expressions. Walk to the parent expression.
+ ExprKind::Binary(_, lhs, rhs)
+ if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
+ {
+ e = parent;
+ continue;
+ },
+ ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
+ e = parent;
+ continue;
+ },
+ ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
+ e = parent;
+ continue;
+ },
+ // Only allow field accesses without auto-deref
+ ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
+ e = parent;
+ continue
+ }
+ _ => (),
+ },
+ _ => (),
+ }
+ self.params.remove_by_id(id);
+ return;
+ }
+ }
+ }
+
+ fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
+ if self.entered_body == Some(body.value.hir_id) {
+ self.entered_body = None;
+ self.params.flag_for_linting();
+ for param in &self.params.params {
+ if param.apply_lint.get() {
+ span_lint_and_then(
+ cx,
+ ONLY_USED_IN_RECURSION,
+ param.ident.span,
+ "parameter is only used in recursion",
+ |diag| {
+ if param.ident.name != kw::SelfLower {
+ diag.span_suggestion(
+ param.ident.span,
+ "if this is intentional, prefix it with an underscore",
+ format!("_{}", param.ident.name),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ diag.span_note(
+ param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
+ "parameter used here",
+ );
+ },
+ );
+ }
+ }
+ self.params.clear();
+ }
+ }
+}
+
+fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
+ match kind {
+ FnKind::Fn => true,
+ FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
+ GenericArgKind::Lifetime(_) => true,
+ GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
+ GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
+ }),
+ #[allow(trivial_casts)]
+ FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
+ }
+}
--- /dev/null
- use clippy_utils::{fn_def_id, path_to_local_id};
+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};
- _: Span,
++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, 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
+ /// 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
+ /// ```rust
+ /// fn foo() -> String {
+ /// let x = String::new();
+ /// x
+ /// }
+ /// ```
+ /// instead, use
+ /// ```
+ /// fn foo() -> String {
+ /// String::new()
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub LET_AND_RETURN,
+ style,
+ "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// fn foo(x: usize) -> usize {
+ /// return x;
+ /// }
+ /// ```
+ /// simplify to
+ /// ```rust
+ /// fn foo(x: usize) -> usize {
+ /// x
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub NEEDLESS_RETURN,
+ style,
+ "using a return statement like `return expr;` where an expression would suffice"
+}
+
+#[derive(PartialEq, Eq, Copy, Clone)]
+enum RetReplacement {
+ Empty,
+ Block,
+ Unit,
+}
+
+impl RetReplacement {
+ fn sugg_help(self) -> &'static str {
+ match self {
+ Self::Empty => "remove `return`",
+ Self::Block => "replace `return` with an empty block",
+ Self::Unit => "replace `return` with a unit value",
+ }
+ }
+}
+
+impl ToString for RetReplacement {
+ fn to_string(&self) -> String {
+ match *self {
+ Self::Empty => "",
+ Self::Block => "{}",
+ Self::Unit => "()",
+ }
+ .to_string()
+ }
+}
+
+declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
+
+impl<'tcx> LateLintPass<'tcx> for Return {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
+ // we need both a let-binding stmt and an expr
+ if_chain! {
+ if let Some(retexpr) = block.expr;
+ if let Some(stmt) = block.stmts.iter().last();
+ if let StmtKind::Local(local) = &stmt.kind;
+ if local.ty.is_none();
+ if cx.tcx.hir().attrs(local.hir_id).is_empty();
+ if let Some(initexpr) = &local.init;
+ if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
+ if path_to_local_id(retexpr, local_id);
+ if !last_statement_borrows(cx, initexpr);
+ if !in_external_macro(cx.sess(), initexpr.span);
+ if !in_external_macro(cx.sess(), retexpr.span);
+ if !local.span.from_expansion();
+ then {
+ span_lint_hir_and_then(
+ cx,
+ LET_AND_RETURN,
+ retexpr.hir_id,
+ retexpr.span,
+ "returning the result of a `let` binding from a block",
+ |err| {
+ err.span_label(local.span, "unnecessary `let` binding");
+
+ if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
+ if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
+ snippet.push_str(" as _");
+ }
+ err.multipart_suggestion(
+ "return the expression directly",
+ vec![
+ (local.span, String::new()),
+ (retexpr.span, snippet),
+ ],
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_help(initexpr.span, "this expression can be directly returned");
+ }
+ },
+ );
+ }
+ }
+ }
+
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ kind: FnKind<'tcx>,
+ _: &'tcx FnDecl<'tcx>,
+ body: &'tcx Body<'tcx>,
- check_block_return(cx, &body.value.kind, vec![]);
++ sp: Span,
+ _: HirId,
+ ) {
+ match kind {
+ FnKind::Closure => {
+ // when returning without value in closure, replace this `return`
+ // with an empty block to prevent invalid suggestion (see #6501)
+ let replacement = if let ExprKind::Ret(None) = &body.value.kind {
+ RetReplacement::Block
+ } else {
+ RetReplacement::Empty
+ };
+ check_final_expr(cx, body.value, vec![], replacement);
+ },
+ FnKind::ItemFn(..) | FnKind::Method(..) => {
- fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec<Span>) {
++ check_block_return(cx, &body.value.kind, sp, vec![]);
+ },
+ }
+ }
+}
+
+// if `expr` is a block, check if there are needless returns in it
- 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);
++fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
+ if let ExprKind::Block(block, _) = expr_kind {
+ if let Some(block_expr) = block.expr {
+ check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
+ } else if let Some(stmt) = block.stmts.iter().last() {
+ match stmt.kind {
+ StmtKind::Expr(expr) => {
+ check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
+ },
+ StmtKind::Semi(semi_expr) => {
- check_block_return(cx, &then.kind, semi_spans.clone());
++ // 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);
+ },
+ _ => (),
+ }
+ }
+ }
+}
+
+fn check_final_expr<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
+ * needless return */
+ replacement: RetReplacement,
+) {
+ let peeled_drop_expr = expr.peel_drop_temps();
+ match &peeled_drop_expr.kind {
+ // simple return is always "bad"
+ ExprKind::Ret(ref inner) => {
+ // 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, &else_clause.kind, semi_spans);
++ check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
+ if let Some(else_clause) = else_clause_opt {
- other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans),
++ check_block_return(cx, &else_clause.kind, peeled_drop_expr.span, semi_spans);
+ }
+ },
+ // a match expr, check all arms
+ // an if/if let expr, check both exprs
+ // note, if without else is going to be a type checking error anyways
+ // (except for unit type functions) so we don't match it
+ ExprKind::Match(_, arms, MatchSource::Normal) => {
+ for arm in arms.iter() {
+ check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit);
+ }
+ },
+ // if it's a whole block, check it
++ other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans),
+ }
+}
+
+fn emit_return_lint(
+ cx: &LateContext<'_>,
+ ret_span: Span,
+ semi_spans: Vec<Span>,
+ inner_span: Option<Span>,
+ replacement: RetReplacement,
+) {
+ if ret_span.from_expansion() {
+ return;
+ }
+ let mut applicability = Applicability::MachineApplicable;
+ let return_replacement = inner_span.map_or_else(
+ || replacement.to_string(),
+ |inner_span| {
+ let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
+ snippet.to_string()
+ },
+ );
+ let sugg_help = if inner_span.is_some() {
+ "remove `return`"
+ } else {
+ replacement.sugg_help()
+ };
+ span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
+ diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability);
+ // for each parent statement, we need to remove the semicolon
+ for semi_stmt_span in semi_spans {
+ diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability);
+ }
+ });
+}
+
+fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ for_each_expr(expr, |e| {
+ if let Some(def_id) = fn_def_id(cx, e)
+ && cx
+ .tcx
+ .fn_sig(def_id)
+ .subst_identity()
+ .skip_binder()
+ .output()
+ .walk()
+ .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+ {
+ ControlFlow::Break(())
+ } else {
+ 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
+}
--- /dev/null
- /// It's most probably a typo and may lead to unexpected behaviours.
+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?
- #[clippy::version = "1.66.0"]
++ /// 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,
+ );
+ }
+ }
+}
--- /dev/null
- let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
+mod crosspointer_transmute;
+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;
+mod transmute_ref_to_ref;
+mod transmute_undefined_repr;
+mod transmutes_expressible_as_ptr_casts;
+mod transmuting_null;
+mod unsound_collection_transmute;
+mod useless_transmute;
+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_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```ignore
+ /// let ptr: *const T = core::intrinsics::transmute('x')
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub WRONG_TRANSMUTE,
+ correctness,
+ "transmutes that are confusing at best, undefined behavior at worst and always useless"
+}
+
+// FIXME: Move this to `complexity` again, after #5343 is fixed
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub USELESS_TRANSMUTE,
+ complexity,
+ "transmutes that have the same to and from types or could be a cast/coercion"
+}
+
+// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
+declare_clippy_lint! {
+ /// ### 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
+ ///
+ /// ```rust
+ /// # let p: *const [i32] = &[];
+ /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let p: *const [i32] = &[];
+ /// p as *const [u16];
+ /// ```
+ #[clippy::version = "1.47.0"]
+ pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
+ complexity,
+ "transmutes that could be a pointer cast"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// core::intrinsics::transmute(t) // where the result type is the same as
+ /// // `*t` or `&t`'s
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub CROSSPOINTER_TRANSMUTE,
+ complexity,
+ "transmutes that have to or from types that are a pointer to the other"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust,ignore
+ /// unsafe {
+ /// let _: &T = std::mem::transmute(p); // where p: *const T
+ /// }
+ ///
+ /// // can be written:
+ /// let _: &T = &*p;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub TRANSMUTE_PTR_TO_REF,
+ complexity,
+ "transmutes from a pointer to a reference type"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = 1_u32;
+ /// unsafe {
+ /// let _: char = std::mem::transmute(x); // where x: u32
+ /// }
+ ///
+ /// // should be:
+ /// let _ = std::char::from_u32(x).unwrap();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub TRANSMUTE_INT_TO_CHAR,
+ complexity,
+ "transmutes from an integer to a `char`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub TRANSMUTE_BYTES_TO_STR,
+ complexity,
+ "transmutes from a `&[u8]` to a `&str`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let x = 1_u8;
+ /// unsafe {
+ /// let _: bool = std::mem::transmute(x); // where x: u8
+ /// }
+ ///
+ /// // should be:
+ /// let _: bool = x != 0;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub TRANSMUTE_INT_TO_BOOL,
+ complexity,
+ "transmutes from an integer to a `bool`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// unsafe {
+ /// let _: f32 = std::mem::transmute(1_u32); // where x: u32
+ /// }
+ ///
+ /// // should be:
+ /// let _: f32 = f32::from_bits(1_u32);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub TRANSMUTE_INT_TO_FLOAT,
+ complexity,
+ "transmutes from an integer to a float"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// unsafe {
+ /// let _: u32 = std::mem::transmute(1f32);
+ /// }
+ ///
+ /// // should be:
+ /// let _: u32 = 1f32.to_bits();
+ /// ```
+ #[clippy::version = "1.41.0"]
+ pub TRANSMUTE_FLOAT_TO_INT,
+ complexity,
+ "transmutes from a float to an integer"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// unsafe {
+ /// let x: [u8; 8] = std::mem::transmute(1i64);
+ /// }
+ ///
+ /// // should be
+ /// let x: [u8; 8] = 0i64.to_ne_bytes();
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub TRANSMUTE_NUM_TO_BYTES,
+ complexity,
+ "transmutes from a number to an array of `u8`"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// 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) };
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub TRANSMUTE_PTR_TO_PTR,
+ pedantic,
+ "transmutes from a pointer to a pointer / a reference to a reference"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// // different size, therefore likely out-of-bounds memory access
+ /// // You absolutely do not want this in your code!
+ /// unsafe {
+ /// std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
+ /// };
+ /// ```
+ ///
+ /// You must always iterate, map and collect the values:
+ ///
+ /// ```rust
+ /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
+ /// ```
+ #[clippy::version = "1.40.0"]
+ pub UNSOUND_COLLECTION_TRANSMUTE,
+ correctness,
+ "transmute between collections of layout-incompatible types"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// struct Foo<T>(u32, T);
+ /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// #[repr(C)]
+ /// struct Foo<T>(u32, T);
+ /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
+ /// ```
+ #[clippy::version = "1.60.0"]
+ pub TRANSMUTE_UNDEFINED_REPR,
+ nursery,
+ "transmute to or from a type with an undefined representation"
+}
+
+declare_clippy_lint! {
+ /// ### 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
+ /// ```rust
+ /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
+ /// ```
+ #[clippy::version = "1.35.0"]
+ pub TRANSMUTING_NULL,
+ correctness,
+ "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<fn()> = 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: Msrv,
+}
+impl_lint_pass!(Transmute => [
+ CROSSPOINTER_TRANSMUTE,
+ TRANSMUTE_PTR_TO_REF,
+ TRANSMUTE_PTR_TO_PTR,
+ USELESS_TRANSMUTE,
+ WRONG_TRANSMUTE,
+ TRANSMUTE_INT_TO_CHAR,
+ TRANSMUTE_BYTES_TO_STR,
+ TRANSMUTE_INT_TO_BOOL,
+ TRANSMUTE_INT_TO_FLOAT,
+ TRANSMUTE_FLOAT_TO_INT,
+ TRANSMUTE_NUM_TO_BYTES,
+ UNSOUND_COLLECTION_TRANSMUTE,
+ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
+ TRANSMUTE_UNDEFINED_REPR,
+ TRANSMUTING_NULL,
+ TRANSMUTE_NULL_TO_FN,
+]);
+impl Transmute {
+ #[must_use]
+ pub fn new(msrv: Msrv) -> Self {
+ Self { msrv }
+ }
+}
+impl<'tcx> LateLintPass<'tcx> for Transmute {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Call(path_expr, [arg]) = e.kind;
+ if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
+ if let Some(def_id) = path.res.opt_def_id();
+ if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
+ then {
+ // Avoid suggesting non-const operations in const contexts:
+ // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
+ // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
+ // - char conversions (https://github.com/rust-lang/rust/issues/89259)
+ let const_context = in_constant(cx, e.hir_id);
+
- transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, 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);
+
+ // If useless_transmute is triggered, the other lints can be skipped.
+ if useless_transmute::check(cx, e, from_ty, to_ty, arg) {
+ return;
+ }
+
+ 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_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)
+ | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
+ | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
+ | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
+ | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
+ | (
+ unsound_collection_transmute::check(cx, e, from_ty, to_ty)
+ || transmute_undefined_repr::check(cx, e, from_ty, to_ty)
+ );
+
+ if !linted {
++ transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg);
+ }
+ }
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
--- /dev/null
- 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`.
+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
+}
--- /dev/null
- /// 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)
- )
- }
-
+use rustc_hir as hir;
+use rustc_hir::Expr;
+use rustc_hir_typeck::{cast, FnCtxt, Inherited};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{cast::CastKind, Ty};
+use rustc_span::DUMMY_SP;
+
+// check if the component types of the transmuted collection and the result have different ABI,
+// size or alignment
+pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
+ if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
+ && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
+ && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
+ && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
+ {
+ from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
+ } else {
+ // no idea about layout, so don't lint
+ false
+ }
+}
+
- fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
+/// 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.
++pub(super) fn check_cast<'tcx>(
++ cx: &LateContext<'tcx>,
++ e: &'tcx Expr<'_>,
++ from_ty: Ty<'tcx>,
++ to_ty: Ty<'tcx>,
++) -> Option<CastKind> {
+ 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, local_def_id);
+
+ // If we already have errors, we can't be sure we can pointer cast.
+ assert!(
+ !fn_ctxt.errors_reported_since_creation(),
+ "Newly created FnCtxt contained errors"
+ );
+
+ 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,
+ hir::Constness::NotConst,
+ ) {
+ let res = check.do_check(&fn_ctxt);
+
+ // do_check's documentation says that it might return Ok and create
+ // errors in the fcx instead of returning Err in some cases. Those cases
+ // should be filtered out before getting here.
+ assert!(
+ !fn_ctxt.errors_reported_since_creation(),
+ "`fn_ctxt` contained errors after cast check!"
+ );
+
+ res.ok()
+ } else {
+ None
+ }
+ })
+}
--- /dev/null
+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};
+use rustc_lexer::{tokenize, TokenKind};
+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::{BytePos, Pos, Span, SyntaxContext};
+
+declare_clippy_lint! {
+ /// ### 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:
+ /// ```ignore
+ /// foo(
+ /// // SAFETY:
+ /// // This is a valid safety comment
+ /// unsafe { *x }
+ /// )
+ /// ```
+ /// But neither of these are:
+ /// ```ignore
+ /// // 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
+ /// ```rust
+ /// use std::ptr::NonNull;
+ /// let a = &mut 42;
+ ///
+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::ptr::NonNull;
+ /// let a = &mut 42;
+ ///
+ /// // SAFETY: references are guaranteed to be non-null.
+ /// let ptr = unsafe { NonNull::new_unchecked(a) };
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub UNDOCUMENTED_UNSAFE_BLOCKS,
+ 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, UNNECESSARY_SAFETY_COMMENT]);
+
+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.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) {
+ source_map.span_until_char(block.span, '\n')
+ } else {
+ block.span
+ };
+
+ span_lint_and_help(
+ cx,
+ UNDOCUMENTED_UNSAFE_BLOCKS,
+ span,
+ "unsafe block missing a safety comment",
+ None,
+ "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_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)
+ };
+
+ 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<Span> {
++ 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());
+ file_pos
+ .sf
+ .src
+ .as_deref()
+ .and_then(|src| src.get(file_pos.pos.to_usize()..))
+ .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<'_>, span: Span) -> bool {
+ // This intentionally ignores text before the start of a function so something like:
+ // ```
+ // // SAFETY: reason
+ // fn foo() { unsafe { .. } }
+ // ```
+ // won't work. This is to avoid dealing with where such a comment should be place relative to
+ // attributes and doc comments.
+
+ 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<'_>) -> 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() {
+ 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 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()
+ {
+ 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
+}
+
+/// 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,
+ item: &hir::Item<'_>,
+) -> Option<BytePos> {
+ parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
+ if *item_id == item.item_id() {
+ if idx == 0 {
+ // mod A { /* comment */ unsafe impl T {} ... }
+ // ^------------------------------------------^ returns the start of this span
+ // ^---------------------^ finally checks comments in this range
+ if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) {
+ return Some(sp.lo());
+ }
+ } else {
+ // some_item /* comment */ unsafe impl T {}
+ // ^-------^ returns the end of this span
+ // ^---------------^ finally checks comments in this range
+ let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]);
+ if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) {
+ return Some(sp.hi());
+ }
+ }
+ }
+ None
+ })
+}
+
+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() {
+ HasSafetyComment::Maybe
+ } else {
+ // From a macro expansion. Get the text from the start of the macro declaration to start of the
+ // unsafe block.
+ // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
+ // ^--------------------------------------------^
+ if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
+ && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
+ && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf)
+ && let Some(src) = unsafe_line.sf.src.as_deref()
+ {
+ unsafe_line.sf.lines(|lines| {
+ 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.
+ HasSafetyComment::Maybe
+ }
+ }
+}
+
+fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
+ let body = cx.enclosing_body?;
+ let map = cx.tcx.hir();
+ let mut span = map.body(body).value.span;
+ for (_, node) in map.parent_iter(body.hir_id) {
+ match node {
+ Node::Expr(e) => span = e.span,
+ Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (),
+ _ => break,
+ }
+ }
+ Some(span)
+}
+
+fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
+ let source_map = cx.sess().source_map();
+ let ctxt = span.ctxt();
+ if ctxt == SyntaxContext::root()
+ && let Some(search_span) = get_body_search_span(cx)
+ {
+ if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
+ && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root())
+ && let Ok(body_line) = source_map.lookup_line(body_span.lo())
+ && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf)
+ && let Some(src) = unsafe_line.sf.src.as_deref()
+ {
+ // Get the text from the start of function body to the unsafe block.
+ // fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
+ // ^-------------^
+ unsafe_line.sf.lines(|lines| {
+ body_line.line < unsafe_line.line && text_has_safety_comment(
+ 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.
+ true
+ }
+ } else {
+ false
+ }
+}
+
+/// 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) -> Option<BytePos> {
+ 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;
+ 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 None;
+ };
+ // Check for a sequence of line comments.
+ if line.starts_with("//") {
+ let (mut line, mut line_start) = (line, line_start);
+ loop {
+ if line.to_ascii_uppercase().contains("SAFETY:") {
+ return Some(BytePos(
+ u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
+ ));
+ }
+ match lines.next() {
+ Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s),
+ _ => return None,
+ }
+ }
+ }
+ // No line comments; look for the start of a block comment.
+ // This will only find them if they are at the start of a line.
+ 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];
+ let mut tokens = tokenize(src);
+ return (src[..tokens.next().unwrap().len as usize]
+ .to_ascii_uppercase()
+ .contains("SAFETY:")
+ && 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 None,
+ }
+ }
+}
--- /dev/null
- /// A type, say `SomeType`, listed in this configuration has the same behavior of `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
+//! Read configurations files.
+
+#![allow(clippy::module_name_repetitions)]
+
+use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
+use serde::Deserialize;
+use std::error::Error;
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+use std::{cmp, env, fmt, fs, io, iter};
+
+#[rustfmt::skip]
+const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
+ "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",
+];
+const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
+
+/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
+#[derive(Clone, Debug, Deserialize)]
+pub struct Rename {
+ pub path: String,
+ pub rename: String,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+#[serde(untagged)]
+pub enum DisallowedPath {
+ Simple(String),
+ WithReason { path: String, reason: Option<String> },
+}
+
+impl DisallowedPath {
+ pub fn path(&self) -> &str {
+ let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
+
+ path
+ }
+
+ pub fn reason(&self) -> Option<String> {
+ match self {
+ Self::WithReason {
+ reason: Some(reason), ..
+ } => Some(format!("{reason} (from clippy.toml)")),
+ _ => None,
+ }
+ }
+}
+
+/// Conf with parse errors
+#[derive(Default)]
+pub struct TryConf {
+ pub conf: Conf,
+ pub errors: Vec<Box<dyn Error>>,
+ pub warnings: Vec<Box<dyn Error>>,
+}
+
+impl TryConf {
+ fn from_error(error: impl Error + 'static) -> Self {
+ Self {
+ conf: Conf::default(),
+ errors: vec![Box::new(error)],
+ warnings: vec![],
+ }
+ }
+}
+
+#[derive(Debug)]
+struct ConfError(String);
+
+impl fmt::Display for ConfError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ <String as fmt::Display>::fmt(&self.0, f)
+ }
+}
+
+impl Error for ConfError {}
+
+fn conf_error(s: impl Into<String>) -> Box<dyn Error> {
+ Box::new(ConfError(s.into()))
+}
+
+macro_rules! define_Conf {
+ ($(
+ $(#[doc = $doc:literal])+
+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])?
+ ($name:ident: $ty:ty = $default:expr),
+ )*) => {
+ /// Clippy lint configuration
+ pub struct Conf {
+ $($(#[doc = $doc])+ pub $name: $ty,)*
+ }
+
+ mod defaults {
+ $(pub fn $name() -> $ty { $default })*
+ }
+
+ impl Default for Conf {
+ fn default() -> Self {
+ Self { $($name: defaults::$name(),)* }
+ }
+ }
+
+ impl<'de> Deserialize<'de> for TryConf {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
+ deserializer.deserialize_map(ConfVisitor)
+ }
+ }
+
+ #[derive(Deserialize)]
+ #[serde(field_identifier, rename_all = "kebab-case")]
+ #[allow(non_camel_case_types)]
+ enum Field { $($name,)* third_party, }
+
+ struct ConfVisitor;
+
+ impl<'de> Visitor<'de> for ConfVisitor {
+ type Value = TryConf;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("Conf")
+ }
+
+ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
+ let mut errors = Vec::new();
+ let mut warnings = Vec::new();
+ $(let mut $name = None;)*
+ // could get `Field` here directly, but get `str` first for diagnostics
+ while let Some(name) = map.next_key::<&str>()? {
+ match Field::deserialize(name.into_deserializer())? {
+ $(Field::$name => {
+ $(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)?
+ match map.next_value() {
+ Err(e) => errors.push(conf_error(e.to_string())),
+ Ok(value) => match $name {
+ Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))),
+ None => {
+ $name = Some(value);
+ // $new_conf is the same as one of the defined `$name`s, so
+ // this variable is defined in line 2 of this function.
+ $(match $new_conf {
+ Some(_) => errors.push(conf_error(concat!(
+ "duplicate field `", stringify!($new_conf),
+ "` (provided as `", stringify!($name), "`)"
+ ))),
+ None => $new_conf = $name.clone(),
+ })?
+ },
+ }
+ }
+ })*
+ // white-listed; ignore
+ Field::third_party => drop(map.next_value::<IgnoredAny>())
+ }
+ }
+ let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
+ Ok(TryConf { conf, errors, warnings })
+ }
+ }
+
+ #[cfg(feature = "internal")]
+ pub mod metadata {
+ use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
+
+ macro_rules! wrap_option {
+ () => (None);
+ ($x:literal) => (Some($x));
+ }
+
+ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
+ vec![
+ $(
+ {
+ let deprecation_reason = wrap_option!($($dep)?);
+
+ ClippyConfiguration::new(
+ stringify!($name),
+ stringify!($ty),
+ format!("{:?}", super::defaults::$name()),
+ concat!($($doc, '\n',)*),
+ deprecation_reason,
+ )
+ },
+ )+
+ ]
+ }
+ }
+ };
+}
+
+define_Conf! {
+ /// Lint: ARITHMETIC_SIDE_EFFECTS.
+ ///
+ /// 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<String> = <_>::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<String> = <_>::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, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION.
+ ///
+ /// The minimum rust version that the project supports
+ (msrv: Option<String> = None),
+ /// DEPRECATED LINT: BLACKLISTED_NAME.
+ ///
+ /// Use the Disallowed Names lint instead
+ #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)]
+ (blacklisted_names: Vec<String> = Vec::new()),
+ /// Lint: COGNITIVE_COMPLEXITY.
+ ///
+ /// The maximum cognitive complexity a function can have
+ (cognitive_complexity_threshold: u64 = 25),
+ /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
+ ///
+ /// Use the Cognitive Complexity lint instead.
+ #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
+ (cyclomatic_complexity_threshold: u64 = 25),
+ /// Lint: 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.
+ (disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
+ /// Lint: DOC_MARKDOWN.
+ ///
+ /// 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:
+ (doc_valid_idents: Vec<String> = super::DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
+ /// Lint: TOO_MANY_ARGUMENTS.
+ ///
+ /// The maximum number of argument a function or method can have
+ (too_many_arguments_threshold: u64 = 7),
+ /// Lint: TYPE_COMPLEXITY.
+ ///
+ /// The maximum complexity a type can have
+ (type_complexity_threshold: u64 = 250),
+ /// Lint: MANY_SINGLE_CHAR_NAMES.
+ ///
+ /// The maximum number of single char bindings a scope may have
+ (single_char_binding_names_threshold: u64 = 4),
+ /// Lint: BOXED_LOCAL, USELESS_VEC.
+ ///
+ /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
+ (too_large_for_stack: u64 = 200),
+ /// Lint: ENUM_VARIANT_NAMES.
+ ///
+ /// The minimum number of enum variants for the lints about variant names to trigger
+ (enum_variant_name_threshold: u64 = 3),
+ /// Lint: LARGE_ENUM_VARIANT.
+ ///
+ /// The maximum size of an enum's variant to avoid box suggestion
+ (enum_variant_size_threshold: u64 = 200),
+ /// Lint: VERBOSE_BIT_MASK.
+ ///
+ /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
+ (verbose_bit_mask_threshold: u64 = 1),
+ /// Lint: DECIMAL_LITERAL_REPRESENTATION.
+ ///
+ /// The lower bound for linting decimal literals
+ (literal_representation_threshold: u64 = 16384),
+ /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
+ ///
+ /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
+ (trivial_copy_size_limit: Option<u64> = None),
+ /// Lint: LARGE_TYPE_PASS_BY_MOVE.
+ ///
+ /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
+ (pass_by_value_size_limit: u64 = 256),
+ /// Lint: TOO_MANY_LINES.
+ ///
+ /// The maximum number of lines a function or method can have
+ (too_many_lines_threshold: u64 = 100),
+ /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
+ ///
+ /// The maximum allowed size for arrays on the stack
+ (array_size_threshold: u128 = 512_000),
+ /// Lint: VEC_BOX.
+ ///
+ /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
+ (vec_box_size_threshold: u64 = 4096),
+ /// Lint: TYPE_REPETITION_IN_BOUNDS.
+ ///
+ /// The maximum number of bounds a trait can have to be linted
+ (max_trait_bounds: u64 = 3),
+ /// Lint: STRUCT_EXCESSIVE_BOOLS.
+ ///
+ /// The maximum number of bool fields a struct can have
+ (max_struct_bools: u64 = 3),
+ /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
+ ///
+ /// The maximum number of bool parameters a function can have
+ (max_fn_params_bools: u64 = 3),
+ /// Lint: WILDCARD_IMPORTS.
+ ///
+ /// Whether to allow certain wildcard imports (prelude, super in tests).
+ (warn_on_all_wildcard_imports: bool = false),
+ /// Lint: DISALLOWED_MACROS.
+ ///
+ /// The list of disallowed macros, written as fully qualified paths.
+ (disallowed_macros: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ /// Lint: DISALLOWED_METHODS.
+ ///
+ /// The list of disallowed methods, written as fully qualified paths.
+ (disallowed_methods: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ /// Lint: DISALLOWED_TYPES.
+ ///
+ /// The list of disallowed types, written as fully qualified paths.
+ (disallowed_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ /// Lint: UNREADABLE_LITERAL.
+ ///
+ /// Should the fraction of a decimal be linted to include separators.
+ (unreadable_literal_lint_fractions: bool = true),
+ /// Lint: UPPER_CASE_ACRONYMS.
+ ///
+ /// 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.
+ (cargo_ignore_publish: bool = false),
+ /// Lint: NONSTANDARD_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.
+ (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
+ /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
+ ///
+ /// The list of imports to always rename, a fully qualified path followed by the rename.
+ (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
+ /// Lint: DISALLOWED_SCRIPT_IDENTS.
+ ///
+ /// The list of unicode scripts allowed to be used in the scope.
+ (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
+ /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
+ ///
+ /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
+ (enable_raw_pointer_heuristic_for_send: bool = true),
+ /// Lint: INDEX_REFUTABLE_SLICE.
+ ///
+ /// 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.
+ (max_suggested_slice_pattern_length: u64 = 3),
+ /// Lint: AWAIT_HOLDING_INVALID_TYPE
+ (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
+ /// Lint: LARGE_INCLUDE_FILE.
+ ///
+ /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
+ (max_include_file_size: u64 = 1_000_000),
+ /// Lint: EXPECT_USED.
+ ///
+ /// Whether `expect` should be allowed within `#[cfg(test)]`
+ (allow_expect_in_tests: bool = false),
+ /// Lint: UNWRAP_USED.
+ ///
+ /// 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: 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.
+ ///
+ /// 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<String> = 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<Option<PathBuf>> {
+ /// Possible filename to search for.
+ const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
+
+ // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
+ // If neither of those exist, use ".".
+ let mut current = env::var_os("CLIPPY_CONF_DIR")
+ .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
+ .map_or_else(|| PathBuf::from("."), PathBuf::from);
+
+ let mut found_config: Option<PathBuf> = None;
+
+ loop {
+ for config_file_name in &CONFIG_FILE_NAMES {
+ if let Ok(config_file) = current.join(config_file_name).canonicalize() {
+ match fs::metadata(&config_file) {
+ Err(e) if e.kind() == io::ErrorKind::NotFound => {},
+ Err(e) => return Err(e),
+ Ok(md) if md.is_dir() => {},
+ Ok(_) => {
+ // warn if we happen to find two config files #8323
+ if let Some(ref found_config_) = found_config {
+ eprintln!(
+ "Using config file `{}`\nWarning: `{}` will be ignored.",
+ found_config_.display(),
+ config_file.display(),
+ );
+ } else {
+ found_config = Some(config_file);
+ }
+ },
+ }
+ }
+ }
+
+ if found_config.is_some() {
+ return Ok(found_config);
+ }
+
+ // If the current directory has no parent, we're done searching.
+ if !current.pop() {
+ return Ok(None);
+ }
+ }
+}
+
+/// Read the `toml` configuration file.
+///
+/// In case of error, the function tries to continue as much as possible.
+pub fn read(path: &Path) -> TryConf {
+ let content = match fs::read_to_string(path) {
+ Err(e) => return TryConf::from_error(e),
+ Ok(content) => content,
+ };
+ match toml::from_str::<TryConf>(&content) {
+ Ok(mut conf) => {
+ extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
+ extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
+
+ conf
+ },
+ Err(e) => TryConf::from_error(e),
+ }
+}
+
+fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
+ if vec.contains(&"..".to_string()) {
+ vec.extend(default.iter().map(ToString::to_string));
+ }
+}
+
+const SEPARATOR_WIDTH: usize = 4;
+
+// Check whether the error is "unknown field" and, if so, list the available fields sorted and at
+// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it.
+pub fn format_error(error: Box<dyn Error>) -> String {
+ let s = error.to_string();
+
+ if_chain! {
+ if error.downcast::<toml::de::Error>().is_ok();
+ if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s);
+ then {
+ use fmt::Write;
+
+ fields.sort_unstable();
+
+ let (rows, column_widths) = calculate_dimensions(&fields);
+
+ let mut msg = String::from(prefix);
+ for row in 0..rows {
+ writeln!(msg).unwrap();
+ for (column, column_width) in column_widths.iter().copied().enumerate() {
+ let index = column * rows + row;
+ let field = fields.get(index).copied().unwrap_or_default();
+ write!(
+ msg,
+ "{:SEPARATOR_WIDTH$}{field:column_width$}",
+ " "
+ )
+ .unwrap();
+ }
+ }
+ write!(msg, "\n{suffix}").unwrap();
+ msg
+ } else {
+ s
+ }
+ }
+}
+
+// `parse_unknown_field_message` will become unnecessary if
+// https://github.com/alexcrichton/toml-rs/pull/364 is merged.
+fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> {
+ // An "unknown field" message has the following form:
+ // unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y
+ // ^^ ^^^^ ^^
+ if_chain! {
+ if s.starts_with("unknown field");
+ let slices = s.split("`, `").collect::<Vec<_>>();
+ let n = slices.len();
+ if n >= 2;
+ if let Some((prefix, first_field)) = slices[0].rsplit_once(" `");
+ if let Some((last_field, suffix)) = slices[n - 1].split_once("` ");
+ then {
+ let fields = iter::once(first_field)
+ .chain(slices[1..n - 1].iter().copied())
+ .chain(iter::once(last_field))
+ .collect::<Vec<_>>();
+ Some((prefix, fields, suffix))
+ } else {
+ None
+ }
+ }
+}
+
+fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
+ let columns = env::var("CLIPPY_TERMINAL_WIDTH")
+ .ok()
+ .and_then(|s| <usize as FromStr>::from_str(&s).ok())
+ .map_or(1, |terminal_width| {
+ let max_field_width = fields.iter().map(|field| field.len()).max().unwrap();
+ cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width))
+ });
+
+ let rows = (fields.len() + (columns - 1)) / columns;
+
+ let column_widths = (0..columns)
+ .map(|column| {
+ if column < columns - 1 {
+ (0..rows)
+ .map(|row| {
+ let index = column * rows + row;
+ let field = fields.get(index).copied().unwrap_or_default();
+ field.len()
+ })
+ .max()
+ .unwrap()
+ } else {
+ // Avoid adding extra space to the last column.
+ 0
+ }
+ })
+ .collect::<Vec<_>>();
+
+ (rows, column_widths)
+}
--- /dev/null
- /// This is the output file of the lint collector.
- const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
+//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
+//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
+//!
+//! This module and therefore the entire lint is guarded by a feature flag called `internal`
+//!
+//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
+//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
+//! a simple mistake)
+
+use crate::renamed_lints::RENAMED_LINTS;
+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::{
+ self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
+};
+use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
+use rustc_middle::hir::nested_filter;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Loc, Span, Symbol};
+use serde::{ser::SerializeStruct, Serialize, Serializer};
+use std::collections::BinaryHeap;
+use std::fmt;
+use std::fmt::Write as _;
+use std::fs::{self, OpenOptions};
+use std::io::prelude::*;
+use std::path::Path;
+use std::path::PathBuf;
+use std::process::Command;
+
- // Outputting
- if Path::new(OUTPUT_FILE).exists() {
- fs::remove_file(OUTPUT_FILE).unwrap();
++/// 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
+/// `clippy::all`
+const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
+/// Lints within this group will be excluded from the collection. These groups
+/// have to be defined without the `clippy::` prefix.
+const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
+/// Collected deprecated lint will be assigned to this group in the JSON output
+const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
+/// This is the lint level for deprecated lints that will be displayed in the lint list
+const DEPRECATED_LINT_LEVEL: &str = "none";
+/// This array holds Clippy's lint groups with their corresponding default lint level. The
+/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
+const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
+ ("correctness", "deny"),
+ ("suspicious", "warn"),
+ ("restriction", "allow"),
+ ("style", "warn"),
+ ("pedantic", "allow"),
+ ("complexity", "warn"),
+ ("perf", "warn"),
+ ("cargo", "allow"),
+ ("nursery", "allow"),
+];
+/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
+/// to only keep the actual lint group in the output.
+const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
+const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
+ &["clippy_utils", "diagnostics", "span_lint"],
+ &["clippy_utils", "diagnostics", "span_lint_and_help"],
+ &["clippy_utils", "diagnostics", "span_lint_and_note"],
+ &["clippy_utils", "diagnostics", "span_lint_hir"],
+ &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
+ &["clippy_utils", "diagnostics", "span_lint_and_then"],
+ &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
+];
+const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
+ ("span_suggestion", false),
+ ("span_suggestion_short", false),
+ ("span_suggestion_verbose", false),
+ ("span_suggestion_hidden", false),
+ ("tool_only_span_suggestion", false),
+ ("multipart_suggestion", true),
+ ("multipart_suggestions", true),
+ ("tool_only_multipart_suggestion", true),
+ ("span_suggestions", true),
+];
+const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
+ &["clippy_utils", "diagnostics", "multispan_sugg"],
+ &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
+];
+const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
+
+/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
+const APPLICABILITY_NAME_INDEX: usize = 2;
+/// This applicability will be set for unresolved applicability values.
+const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
+/// The version that will be displayed if none has been defined
+const VERSION_DEFAULT_STR: &str = "Unknown";
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Collects metadata about clippy lints for the website.
+ ///
+ /// This lint will be used to report problems of syntax parsing. You should hopefully never
+ /// see this but never say never I guess ^^
+ ///
+ /// ### Why is this bad?
+ /// This is not a bad thing but definitely a hacky way to do it. See
+ /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
+ /// about the implementation.
+ ///
+ /// ### Known problems
+ /// Hopefully none. It would be pretty uncool to have a problem here :)
+ ///
+ /// ### Example output
+ /// ```json,ignore
+ /// {
+ /// "id": "internal_metadata_collector",
+ /// "id_span": {
+ /// "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
+ /// "line": 1
+ /// },
+ /// "group": "clippy::internal",
+ /// "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
+ /// }
+ /// ```
+ #[clippy::version = "1.56.0"]
+ pub INTERNAL_METADATA_COLLECTOR,
+ internal_warn,
+ "A busy bee collection metadata about lints"
+}
+
+impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
+
+#[allow(clippy::module_name_repetitions)]
+#[derive(Debug, Clone)]
+pub struct MetadataCollector {
+ /// All collected lints
+ ///
+ /// We use a Heap here to have the lints added in alphabetic order in the export
+ lints: BinaryHeap<LintMetadata>,
+ applicability_info: FxHashMap<String, ApplicabilityInfo>,
+ config: Vec<ClippyConfiguration>,
+ clippy_project_root: PathBuf,
+}
+
+impl MetadataCollector {
+ pub fn new() -> Self {
+ Self {
+ lints: BinaryHeap::<LintMetadata>::default(),
+ applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
+ config: collect_configs(),
+ clippy_project_root: std::env::current_dir()
+ .expect("failed to get current dir")
+ .ancestors()
+ .nth(1)
+ .expect("failed to get project root")
+ .to_path_buf(),
+ }
+ }
+
+ fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
+ self.config
+ .iter()
+ .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
+ .map(ToString::to_string)
+ .reduce(|acc, x| acc + &x)
+ .map(|configurations| {
+ format!(
+ r#"
+### Configuration
+This lint has the following configuration variables:
+
+{configurations}
+"#
+ )
+ })
+ }
++
++ 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| <div style=\"width:290px\">Option</div> | 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 {
+ /// You might ask: How hacky is this?
+ /// My answer: YES
+ fn drop(&mut self) {
+ // The metadata collector gets dropped twice, this makes sure that we only write
+ // when the list is full
+ if self.lints.is_empty() {
+ return;
+ }
+
+ let mut applicability_info = std::mem::take(&mut self.applicability_info);
+
+ // Mapping the final data
+ let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
+ for x in &mut lints {
+ x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
+ replace_produces(&x.id, &mut x.docs, &self.clippy_project_root);
+ }
+
+ collect_renames(&mut lints);
+
- let mut file = OpenOptions::new().write(true).create(true).open(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(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,
++ "<!--
++This file is generated by `cargo collect-metadata`.
++Please use that command to update the file and do not edit it by hand.
++-->
++
++{}",
++ self.get_markdown_docs(),
++ )
++ .unwrap();
+ }
+}
+
+#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+struct LintMetadata {
+ id: String,
+ id_span: SerializableSpan,
+ group: String,
+ level: String,
+ docs: String,
+ version: String,
+ /// This field is only used in the output and will only be
+ /// mapped shortly before the actual output.
+ applicability: Option<ApplicabilityInfo>,
+}
+
+impl LintMetadata {
+ fn new(
+ id: String,
+ id_span: SerializableSpan,
+ group: String,
+ level: &'static str,
+ version: String,
+ docs: String,
+ ) -> Self {
+ Self {
+ id,
+ id_span,
+ group,
+ level: level.to_string(),
+ version,
+ docs,
+ applicability: None,
+ }
+ }
+}
+
+fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
+ let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>();
+ let mut lines = doc_lines.iter_mut();
+
+ 'outer: loop {
+ // Find the start of the example
+
+ // ```rust
+ loop {
+ match lines.next() {
+ Some(line) if line.trim_start().starts_with("```rust") => {
+ if line.contains("ignore") || line.contains("no_run") {
+ // A {{produces}} marker may have been put on a ignored code block by mistake,
+ // just seek to the end of the code block and continue checking.
+ if lines.any(|line| line.trim_start().starts_with("```")) {
+ continue;
+ }
+
+ panic!("lint `{lint_name}` has an unterminated code block")
+ }
+
+ break;
+ },
+ Some(line) if line.trim_start() == "{{produces}}" => {
+ panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block")
+ },
+ Some(line) => {
+ let line = line.trim();
+ // These are the two most common markers of the corrections section
+ if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") {
+ break 'outer;
+ }
+ },
+ None => break 'outer,
+ }
+ }
+
+ // Collect the example
+ let mut example = Vec::new();
+ loop {
+ match lines.next() {
+ Some(line) if line.trim_start() == "```" => break,
+ Some(line) => example.push(line),
+ None => panic!("lint `{lint_name}` has an unterminated code block"),
+ }
+ }
+
+ // Find the {{produces}} and attempt to generate the output
+ loop {
+ match lines.next() {
+ Some(line) if line.is_empty() => {},
+ Some(line) if line.trim() == "{{produces}}" => {
+ let output = get_lint_output(lint_name, &example, clippy_project_root);
+ line.replace_range(
+ ..,
+ &format!(
+ "<details>\
+ <summary>Produces</summary>\n\
+ \n\
+ ```text\n\
+ {output}\n\
+ ```\n\
+ </details>"
+ ),
+ );
+
+ break;
+ },
+ // No {{produces}}, we can move on to the next example
+ Some(_) => break,
+ None => break 'outer,
+ }
+ }
+ }
+
+ *docs = cleanup_docs(&doc_lines);
+}
+
+fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String {
+ let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}"));
+ let file = dir.path().join("lint_example.rs");
+
+ let mut source = String::new();
+ let unhidden = example
+ .iter()
+ .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line));
+
+ // Get any attributes
+ let mut lines = unhidden.peekable();
+ while let Some(line) = lines.peek() {
+ if line.starts_with("#!") {
+ source.push_str(line);
+ source.push('\n');
+ lines.next();
+ } else {
+ break;
+ }
+ }
+
+ let needs_main = !example.iter().any(|line| line.contains("fn main"));
+ if needs_main {
+ source.push_str("fn main() {\n");
+ }
+
+ for line in lines {
+ source.push_str(line);
+ source.push('\n');
+ }
+
+ if needs_main {
+ source.push_str("}\n");
+ }
+
+ if let Err(e) = fs::write(&file, &source) {
+ panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy());
+ }
+
+ let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}");
+
+ let mut cmd = Command::new("cargo");
+
+ cmd.current_dir(clippy_project_root)
+ .env("CARGO_INCREMENTAL", "0")
+ .env("CLIPPY_ARGS", "")
+ .env("CLIPPY_DISABLE_DOCS_LINKS", "1")
+ // We need to disable this to enable all lints
+ .env("ENABLE_METADATA_COLLECTION", "0")
+ .args(["run", "--bin", "clippy-driver"])
+ .args(["--target-dir", "./clippy_lints/target"])
+ .args(["--", "--error-format=json"])
+ .args(["--edition", "2021"])
+ .arg("-Cdebuginfo=0")
+ .args(["-A", "clippy::all"])
+ .args(["-W", &prefixed_name])
+ .args(["-L", "./target/debug"])
+ .args(["-Z", "no-codegen"]);
+
+ let output = cmd
+ .arg(file.as_path())
+ .output()
+ .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}"));
+
+ let tmp_file_path = file.to_string_lossy();
+ let stderr = std::str::from_utf8(&output.stderr).unwrap();
+ let msgs = stderr
+ .lines()
+ .filter(|line| line.starts_with('{'))
+ .map(|line| serde_json::from_str(line).unwrap())
+ .collect::<Vec<serde_json::Value>>();
+
+ let mut rendered = String::new();
+ let iter = msgs
+ .iter()
+ .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name));
+
+ for message in iter {
+ let rendered_part = message["rendered"].as_str().expect("rendered field should exist");
+ rendered.push_str(rendered_part);
+ }
+
+ if rendered.is_empty() {
+ let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
+ let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect();
+ panic!(
+ "did not find lint `{lint_name}` in output of example, got:\n{}\n{}",
+ non_json.join("\n"),
+ rendered.join("\n")
+ );
+ }
+
+ // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :)
+ rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs")
+}
+
+#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
+struct SerializableSpan {
+ path: String,
+ line: usize,
+}
+
+impl fmt::Display for SerializableSpan {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
+ }
+}
+
+impl SerializableSpan {
+ fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
+ Self::from_span(cx, item.ident.span)
+ }
+
+ fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
+ let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
+
+ Self {
+ path: format!("{}", loc.file.name.prefer_remapped()),
+ line: loc.line,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
+struct ApplicabilityInfo {
+ /// Indicates if any of the lint emissions uses multiple spans. This is related to
+ /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
+ /// currently not be applied automatically.
+ is_multi_part_suggestion: bool,
+ applicability: Option<usize>,
+}
+
+impl Serialize for ApplicabilityInfo {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
+ s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
+ if let Some(index) = self.applicability {
+ s.serialize_field(
+ "applicability",
+ &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
+ )?;
+ } else {
+ s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
+ }
+ s.end()
+ }
+}
+
+// ==================================================================
+// Configuration
+// ==================================================================
+#[derive(Debug, Clone, Default)]
+pub struct ClippyConfiguration {
+ name: String,
+ config_type: &'static str,
+ default: String,
+ lints: Vec<String>,
+ doc: String,
+ #[allow(dead_code)]
+ deprecation_reason: Option<&'static str>,
+}
+
+impl ClippyConfiguration {
+ pub fn new(
+ name: &'static str,
+ config_type: &'static str,
+ default: String,
+ doc_comment: &'static str,
+ deprecation_reason: Option<&'static str>,
+ ) -> Self {
+ let (lints, doc) = parse_config_field_doc(doc_comment)
+ .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
+
+ Self {
+ name: to_kebab(name),
+ lints,
+ doc,
+ config_type,
+ default,
+ 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<ClippyConfiguration> {
+ crate::utils::conf::metadata::get_configuration_metadata()
+}
+
+/// This parses the field documentation of the config struct.
+///
+/// ```rust, ignore
+/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
+/// ```
+///
+/// Would yield:
+/// ```rust, ignore
+/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
+/// ```
+fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
+ const DOC_START: &str = " Lint: ";
+ if_chain! {
+ if doc_comment.starts_with(DOC_START);
+ if let Some(split_pos) = doc_comment.find('.');
+ then {
+ let mut doc_comment = doc_comment.to_string();
+ let mut documentation = doc_comment.split_off(split_pos);
+
+ // Extract lints
+ doc_comment.make_ascii_lowercase();
+ let lints: Vec<String> = 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
+ documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
+
+ Some((lints, documentation))
+ } else {
+ None
+ }
+ }
+}
+
+/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
+fn to_kebab(config_name: &str) -> String {
+ config_name.replace('_', "-")
+}
+
+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.default, self.doc
+ )
+ }
+}
+
+// ==================================================================
+// Lint pass
+// ==================================================================
+impl<'hir> LateLintPass<'hir> for MetadataCollector {
+ /// Collecting lint declarations like:
+ /// ```rust, ignore
+ /// declare_clippy_lint! {
+ /// /// ### What it does
+ /// /// Something IDK.
+ /// pub SOME_LINT,
+ /// internal,
+ /// "Who am I?"
+ /// }
+ /// ```
+ fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
+ if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
+ // Normal lint
+ if_chain! {
+ // item validation
+ if is_lint_ref_type(cx, ty);
+ // disallow check
+ let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
+ if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
+ // metadata extraction
+ if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
+ if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item);
+ then {
+ if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
+ raw_docs.push_str(&configuration_section);
+ }
+ let version = get_lint_version(cx, item);
+
+ self.lints.push(LintMetadata::new(
+ lint_name,
+ SerializableSpan::from_item(cx, item),
+ group,
+ level,
+ version,
+ raw_docs,
+ ));
+ }
+ }
+
+ if_chain! {
+ if is_deprecated_lint(cx, ty);
+ // disallow check
+ let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
+ if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
+ // Metadata the little we can get from a deprecated lint
+ if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item);
+ then {
+ let version = get_lint_version(cx, item);
+
+ self.lints.push(LintMetadata::new(
+ lint_name,
+ SerializableSpan::from_item(cx, item),
+ DEPRECATED_LINT_GROUP_STR.to_string(),
+ DEPRECATED_LINT_LEVEL,
+ version,
+ raw_docs,
+ ));
+ }
+ }
+ }
+ }
+
+ /// Collecting constant applicability from the actual lint emissions
+ ///
+ /// Example:
+ /// ```rust, ignore
+ /// span_lint_and_sugg(
+ /// cx,
+ /// SOME_LINT,
+ /// item.span,
+ /// "Le lint message",
+ /// "Here comes help:",
+ /// "#![allow(clippy::all)]",
+ /// Applicability::MachineApplicable, // <-- Extracts this constant value
+ /// );
+ /// ```
+ fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
+ if let Some(args) = match_lint_emission(cx, expr) {
+ let emission_info = extract_emission_info(cx, args);
+ if emission_info.is_empty() {
+ // See:
+ // - src/misc.rs:734:9
+ // - src/methods/mod.rs:3545:13
+ // - src/methods/mod.rs:3496:13
+ // We are basically unable to resolve the lint name itself.
+ return;
+ }
+
+ for (lint_name, applicability, is_multi_part) in emission_info {
+ let app_info = self.applicability_info.entry(lint_name).or_default();
+ app_info.applicability = applicability;
+ app_info.is_multi_part_suggestion = is_multi_part;
+ }
+ }
+ }
+}
+
+// ==================================================================
+// Lint definition extraction
+// ==================================================================
+fn sym_to_string(sym: Symbol) -> String {
+ sym.as_str().to_string()
+}
+
+fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
+ extract_attr_docs(cx, item).or_else(|| {
+ lint_collection_error_item(cx, item, "could not collect the lint documentation");
+ None
+ })
+}
+
+/// This function collects all documentation that has been added to an item using
+/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
+///
+/// ```ignore
+/// #[doc = r"Hello world!"]
+/// #[doc = r"=^.^="]
+/// struct SomeItem {}
+/// ```
+///
+/// Would result in `Hello world!\n=^.^=\n`
+fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
+
+ if let Some(line) = lines.next() {
+ let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n");
+ return Some(raw_docs);
+ }
+
+ None
+}
+
+/// This function may modify the doc comment to ensure that the string can be displayed using a
+/// markdown viewer in Clippy's lint list. The following modifications could be applied:
+/// * Removal of leading space after a new line. (Important to display tables)
+/// * Ensures that code blocks only contain language information
+fn cleanup_docs(docs_collection: &Vec<String>) -> String {
+ let mut in_code_block = false;
+ let mut is_code_block_rust = false;
+
+ let mut docs = String::new();
+ for line in docs_collection {
+ // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
+ if is_code_block_rust && line.trim_start().starts_with("# ") {
+ continue;
+ }
+
+ // The line should be represented in the lint list, even if it's just an empty line
+ docs.push('\n');
+ if let Some(info) = line.trim_start().strip_prefix("```") {
+ in_code_block = !in_code_block;
+ is_code_block_rust = false;
+ if in_code_block {
+ let lang = info
+ .trim()
+ .split(',')
+ // remove rustdoc directives
+ .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
+ // if no language is present, fill in "rust"
+ .unwrap_or("rust");
+ docs.push_str("```");
+ docs.push_str(lang);
+
+ is_code_block_rust = lang == "rust";
+ continue;
+ }
+ }
+ // This removes the leading space that the macro translation introduces
+ if let Some(stripped_doc) = line.strip_prefix(' ') {
+ docs.push_str(stripped_doc);
+ } else if !line.is_empty() {
+ docs.push_str(line);
+ }
+ }
+
+ docs
+}
+
+fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
+ extract_clippy_version_value(cx, item).map_or_else(
+ || VERSION_DEFAULT_STR.to_string(),
+ |version| version.as_str().to_string(),
+ )
+}
+
+fn get_lint_group_and_level_or_lint(
+ cx: &LateContext<'_>,
+ lint_name: &str,
+ item: &Item<'_>,
+) -> Option<(String, &'static str)> {
+ let result = cx.lint_store.check_lint_name(
+ lint_name,
+ Some(sym::clippy),
+ &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
+ );
+ if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
+ if let Some(group) = get_lint_group(cx, lint_lst[0]) {
+ if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
+ return None;
+ }
+
+ if let Some(level) = get_lint_level_from_group(&group) {
+ Some((group, level))
+ } else {
+ lint_collection_error_item(
+ cx,
+ item,
+ &format!("Unable to determine lint level for found group `{group}`"),
+ );
+ None
+ }
+ } else {
+ lint_collection_error_item(cx, item, "Unable to determine lint group");
+ None
+ }
+ } else {
+ lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
+ None
+ }
+}
+
+fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
+ for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
+ if IGNORED_LINT_GROUPS.contains(&group_name) {
+ continue;
+ }
+
+ if lints.iter().any(|group_lint| *group_lint == lint_id) {
+ let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
+ return Some((*group).to_string());
+ }
+ }
+
+ None
+}
+
+fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
+ DEFAULT_LINT_LEVELS
+ .iter()
+ .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
+}
+
+pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+ if let hir::TyKind::Path(ref path) = ty.kind {
+ if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
+ return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
+ }
+ }
+
+ false
+}
+
+fn collect_renames(lints: &mut Vec<LintMetadata>) {
+ for lint in lints {
+ let mut collected = String::new();
+ let mut names = vec![lint.id.clone()];
+
+ loop {
+ if let Some(lint_name) = names.pop() {
+ for (k, v) in RENAMED_LINTS {
+ if_chain! {
+ if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
+ if name == lint_name;
+ if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
+ then {
+ writeln!(collected, "* `{past_name}`").unwrap();
+ names.push(past_name.to_string());
+ }
+ }
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ if !collected.is_empty() {
+ write!(
+ &mut lint.docs,
+ r#"
+### Past names
+
+{collected}
+"#
+ )
+ .unwrap();
+ }
+ }
+}
+
+// ==================================================================
+// Lint emission
+// ==================================================================
+fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
+ span_lint(
+ cx,
+ INTERNAL_METADATA_COLLECTOR,
+ item.ident.span,
+ &format!("metadata collection error for `{}`: {message}", item.ident.name),
+ );
+}
+
+// ==================================================================
+// Applicability
+// ==================================================================
+/// This function checks if a given expression is equal to a simple lint emission function call.
+/// It will return the function arguments if the emission matched any function.
+fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
+ LINT_EMISSION_FUNCTIONS
+ .iter()
+ .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
+}
+
+fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
+ a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
+}
+
+fn extract_emission_info<'hir>(
+ cx: &LateContext<'hir>,
+ args: &'hir [hir::Expr<'hir>],
+) -> Vec<(String, Option<usize>, bool)> {
+ let mut lints = Vec::new();
+ let mut applicability = None;
+ let mut multi_part = false;
+
+ for arg in args {
+ let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
+
+ if match_type(cx, arg_ty, &paths::LINT) {
+ // If we found the lint arg, extract the lint name
+ let mut resolved_lints = resolve_lints(cx, arg);
+ lints.append(&mut resolved_lints);
+ } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
+ applicability = resolve_applicability(cx, arg);
+ } else if arg_ty.is_closure() {
+ multi_part |= check_is_multi_part(cx, arg);
+ applicability = applicability.or_else(|| resolve_applicability(cx, arg));
+ }
+ }
+
+ lints
+ .into_iter()
+ .map(|lint_name| (lint_name, applicability, multi_part))
+ .collect()
+}
+
+/// Resolves the possible lints that this expression could reference
+fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
+ let mut resolver = LintResolver::new(cx);
+ resolver.visit_expr(expr);
+ resolver.lints
+}
+
+/// This function tries to resolve the linked applicability to the given expression.
+fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
+ let mut resolver = ApplicabilityResolver::new(cx);
+ resolver.visit_expr(expr);
+ resolver.complete()
+}
+
+fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
+ if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
+ let mut scanner = IsMultiSpanScanner::new(cx);
+ intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
+ return scanner.is_multi_part();
+ } else if let Some(local) = get_parent_local(cx, closure_expr) {
+ if let Some(local_init) = local.init {
+ return check_is_multi_part(cx, local_init);
+ }
+ }
+
+ false
+}
+
+struct LintResolver<'a, 'hir> {
+ cx: &'a LateContext<'hir>,
+ lints: Vec<String>,
+}
+
+impl<'a, 'hir> LintResolver<'a, 'hir> {
+ fn new(cx: &'a LateContext<'hir>) -> Self {
+ Self {
+ cx,
+ lints: Vec::<String>::default(),
+ }
+ }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
+ type NestedFilter = nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+ if_chain! {
+ if let ExprKind::Path(qpath) = &expr.kind;
+ if let QPath::Resolved(_, path) = qpath;
+
+ let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
+ if match_type(self.cx, expr_ty, &paths::LINT);
+ then {
+ if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
+ let lint_name = last_path_segment(qpath).ident.name;
+ self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
+ } else if let Some(local) = get_parent_local(self.cx, expr) {
+ if let Some(local_init) = local.init {
+ intravisit::walk_expr(self, local_init);
+ }
+ }
+ }
+ }
+
+ intravisit::walk_expr(self, expr);
+ }
+}
+
+/// This visitor finds the highest applicability value in the visited expressions
+struct ApplicabilityResolver<'a, 'hir> {
+ cx: &'a LateContext<'hir>,
+ /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
+ applicability_index: Option<usize>,
+}
+
+impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
+ fn new(cx: &'a LateContext<'hir>) -> Self {
+ Self {
+ cx,
+ applicability_index: None,
+ }
+ }
+
+ fn add_new_index(&mut self, new_index: usize) {
+ self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
+ }
+
+ fn complete(self) -> Option<usize> {
+ self.applicability_index
+ }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
+ type NestedFilter = nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ 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);
+ return;
+ }
+ }
+ }
+
+ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+ let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
+
+ if_chain! {
+ if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
+ if let Some(local) = get_parent_local(self.cx, expr);
+ if let Some(local_init) = local.init;
+ then {
+ intravisit::walk_expr(self, local_init);
+ }
+ };
+
+ intravisit::walk_expr(self, expr);
+ }
+}
+
+/// This returns the parent local node if the expression is a reference one
+fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
+ if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
+ if let hir::def::Res::Local(local_hir) = path.res {
+ return get_parent_local_hir_id(cx, local_hir);
+ }
+ }
+
+ None
+}
+
+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_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,
+ }
+}
+
+/// This visitor finds the highest applicability value in the visited expressions
+struct IsMultiSpanScanner<'a, 'hir> {
+ cx: &'a LateContext<'hir>,
+ suggestion_count: usize,
+}
+
+impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
+ fn new(cx: &'a LateContext<'hir>) -> Self {
+ Self {
+ cx,
+ suggestion_count: 0,
+ }
+ }
+
+ /// Add a new single expression suggestion to the counter
+ fn add_single_span_suggestion(&mut self) {
+ self.suggestion_count += 1;
+ }
+
+ /// Signals that a suggestion with possible multiple spans was found
+ fn add_multi_part_suggestion(&mut self) {
+ self.suggestion_count += 2;
+ }
+
+ /// Checks if the suggestions include multiple spans
+ fn is_multi_part(&self) -> bool {
+ self.suggestion_count > 1
+ }
+}
+
+impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
+ type NestedFilter = nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+ // Early return if the lint is already multi span
+ if self.is_multi_part() {
+ return;
+ }
+
+ match &expr.kind {
+ ExprKind::Call(fn_expr, _args) => {
+ let found_function = SUGGESTION_FUNCTIONS
+ .iter()
+ .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
+ if found_function {
+ // These functions are all multi part suggestions
+ self.add_single_span_suggestion();
+ }
+ },
+ ExprKind::MethodCall(path, recv, _, _arg_span) => {
+ let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
+ if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
+ let called_method = path.ident.name.as_str().to_string();
+ for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
+ if *method_name == called_method {
+ if *is_multi_part {
+ self.add_multi_part_suggestion();
+ } else {
+ self.add_single_span_suggestion();
+ }
+ break;
+ }
+ }
+ }
+ },
+ _ => {},
+ }
+
+ intravisit::walk_expr(self, expr);
+ }
+}
--- /dev/null
- version = "0.1.68"
+[package]
+name = "clippy_utils"
++version = "0.1.69"
+edition = "2021"
+publish = false
+
+[dependencies]
+arrayvec = { version = "0.7", default-features = false }
+if_chain = "1.0"
+itertools = "0.10.1"
+rustc-semver = "1.1"
+
+[features]
+deny-warnings = []
+internal = []
+
+[package.metadata.rust-analyzer]
+# This crate uses #[feature(rustc_private)]
+rustc_private = true
--- /dev/null
+#![feature(array_chunks)]
+#![feature(box_patterns)]
+#![feature(control_flow_enum)]
+#![feature(let_chains)]
+#![feature(lint_reasons)]
+#![feature(never_type)]
+#![feature(once_cell)]
+#![feature(rustc_private)]
+#![recursion_limit = "512"]
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
+// warn on the same lints as `clippy_lints`
+#![warn(trivial_casts, trivial_numeric_casts)]
+// warn on lints, that are included in `rust-lang/rust`s bootstrap
+#![warn(rust_2018_idioms, unused_lifetimes)]
+// warn on rustc internal lints
+#![warn(rustc::internal)]
+
+// FIXME: switch to something more ergonomic here, once available.
+// (Currently there is no way to opt into sysroot crates without `extern crate`.)
+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_typeck;
+extern crate rustc_index;
+extern crate rustc_infer;
+extern crate rustc_lexer;
+extern crate rustc_lint;
+extern crate rustc_middle;
+extern crate rustc_mir_dataflow;
+extern crate rustc_parse_format;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_target;
+extern crate rustc_trait_selection;
+
+#[macro_use]
+pub mod sym_helper;
+
+pub mod ast_utils;
+pub mod attrs;
+mod check_proc_macro;
+pub mod comparisons;
+pub mod consts;
+pub mod diagnostics;
+pub mod eager_or_lazy;
+pub mod higher;
+mod hir_utils;
+pub mod macros;
+pub mod mir;
+pub mod msrvs;
+pub mod numeric_literal;
+pub mod paths;
+pub mod ptr;
+pub mod qualify_min_const_fn;
+pub mod source;
+pub mod str_utils;
+pub mod sugg;
+pub mod ty;
+pub mod usage;
+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, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
+};
+
+use core::ops::ControlFlow;
+use std::collections::hash_map::Entry;
+use std::hash::BuildHasherDefault;
+use std::sync::OnceLock;
+use std::sync::{Mutex, MutexGuard};
+
+use if_chain::if_chain;
+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::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::{
+ 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};
+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::SimplifiedType::{
+ ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
+ PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
+};
+use rustc_middle::ty::{
+ layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
+};
+use rustc_middle::ty::{FloatTy, IntTy, UintTy};
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::source_map::SourceMap;
+use rustc_span::sym;
+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;
+
+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);
+ 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);
+ }
+ };
+}
+
+/// If the given expression is a local binding, find the initializer expression.
+/// If that initializer expression is another local binding, find its initializer again.
+/// This process repeats as long as possible (but usually no more than once). Initializer
+/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
+/// instead.
+///
+/// Examples:
+/// ```
+/// let abc = 1;
+/// // ^ output
+/// let def = abc;
+/// dbg!(def);
+/// // ^^^ input
+///
+/// // or...
+/// let abc = 1;
+/// let def = abc + 2;
+/// // ^^^^^^^ output
+/// dbg!(def);
+/// // ^^^ input
+/// ```
+pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
+ while let Some(init) = path_to_local(expr)
+ .and_then(|id| find_binding_init(cx, id))
+ .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
+ {
+ expr = init;
+ }
+ expr
+}
+
+/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
+/// By only considering immutable bindings, we guarantee that the returned expression represents the
+/// value of the binding wherever it is referenced.
+///
+/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
+/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
+/// canonical binding `HirId`.
+pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
+ let hir = cx.tcx.hir();
+ if_chain! {
+ if let Some(Node::Pat(pat)) = hir.find(hir_id);
+ if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..));
+ let parent = hir.parent_id(hir_id);
+ if let Some(Node::Local(local)) = hir.find(parent);
+ then {
+ return local.init;
+ }
+ }
+ None
+}
+
+/// Returns `true` if the given `NodeId` is inside a constant context
+///
+/// # Example
+///
+/// ```rust,ignore
+/// if in_constant(cx, expr.hir_id) {
+/// // Do something
+/// }
+/// ```
+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(..) | ItemKind::Enum(..),
+ ..
+ })
+ | Node::TraitItem(&TraitItem {
+ kind: TraitItemKind::Const(..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Const(..),
+ ..
+ })
+ | Node::AnonConst(_) => true,
+ Node::Item(&Item {
+ kind: ItemKind::Fn(ref sig, ..),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(ref sig, _),
+ ..
+ }) => sig.header.constness == Constness::Const,
+ _ => false,
+ }
+}
+
+/// Checks if a `Res` refers to a constructor of a `LangItem`
+/// 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 Some(lang_id) = cx.tcx.lang_items().get(lang_item)
+ && let Some(id) = cx.tcx.opt_parent(id)
+ {
+ id == lang_id
+ } else {
+ false
+ }
+}
+
+pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool {
+ if let Res::Def(DefKind::Ctor(..), id) = res
+ && let Some(id) = cx.tcx.opt_parent(id)
+ {
+ cx.tcx.is_diagnostic_item(diag_item, id)
+ } else {
+ false
+ }
+}
+
+/// Checks if a `QPath` resolves to a constructor of a diagnostic item.
+pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
+ if let QPath::Resolved(_, path) = qpath {
+ if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
+ return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id));
+ }
+ }
+ false
+}
+
+/// Checks if the `DefId` matches the given diagnostic item or it's constructor.
+pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
+ let did = match cx.tcx.def_kind(did) {
+ DefKind::Ctor(..) => cx.tcx.parent(did),
+ // Constructors for types in external crates seem to have `DefKind::Variant`
+ DefKind::Variant => match cx.tcx.opt_parent(did) {
+ Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
+ _ => did,
+ },
+ _ => did,
+ };
+
+ cx.tcx.is_diagnostic_item(item, did)
+}
+
+/// Checks if the `DefId` matches the given `LangItem` or it's constructor.
+pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
+ let did = match cx.tcx.def_kind(did) {
+ DefKind::Ctor(..) => cx.tcx.parent(did),
+ // Constructors for types in external crates seem to have `DefKind::Variant`
+ DefKind::Variant => match cx.tcx.opt_parent(did) {
+ Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
+ _ => did,
+ },
+ _ => did,
+ };
+
+ cx.tcx.lang_items().get(item) == Some(did)
+}
+
+pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
+ matches!(
+ expr.kind,
+ ExprKind::Block(
+ Block {
+ stmts: [],
+ expr: None,
+ ..
+ },
+ _
+ ) | ExprKind::Tup([])
+ )
+}
+
+/// Checks if given pattern is a wildcard (`_`)
+pub fn is_wild(pat: &Pat<'_>) -> bool {
+ matches!(pat.kind, PatKind::Wild)
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+/// This is a deprecated function, consider using [`is_trait_method`].
+pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
+ let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
+ let trt_id = cx.tcx.trait_of_item(def_id);
+ trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
+}
+
+/// Checks if a method is defined in an impl of a diagnostic item
+pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ 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 cx.tcx.is_diagnostic_item(diag_item, adt.did());
+ }
+ }
+ false
+}
+
+/// Checks if a method is in a diagnostic item trait
+pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
+ if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
+ return cx.tcx.is_diagnostic_item(diag_item, trait_did);
+ }
+ false
+}
+
+/// Checks if the method call given in `expr` belongs to the given trait.
+pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ cx.typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
+}
+
+/// Checks if the given expression is a path referring an item on the trait
+/// that is marked with the given diagnostic item.
+///
+/// For checking method call expressions instead of path expressions, use
+/// [`is_trait_method`].
+///
+/// For example, this can be used to find if an expression like `u64::default`
+/// refers to an item of the trait `Default`, which is associated with the
+/// `diag_item` of `sym::Default`.
+pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+ if let hir::ExprKind::Path(ref qpath) = expr.kind {
+ cx.qpath_res(qpath, expr.hir_id)
+ .opt_def_id()
+ .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
+ } else {
+ false
+ }
+}
+
+pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
+ match *path {
+ QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
+ QPath::TypeRelative(_, seg) => seg,
+ QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
+ }
+}
+
+pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
+ last_path_segment(qpath)
+ .args
+ .map_or(&[][..], |a| a.args)
+ .iter()
+ .filter_map(|a| match a {
+ hir::GenericArg::Type(ty) => Some(*ty),
+ _ => None,
+ })
+}
+
+/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
+/// `QPath::Resolved.1.res.opt_def_id()`.
+///
+/// Matches a `QPath` against a slice of segment string literals.
+///
+/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
+/// `rustc_hir::QPath`.
+///
+/// # Examples
+/// ```rust,ignore
+/// match_qpath(path, &["std", "rt", "begin_unwind"])
+/// ```
+pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
+ match *path {
+ QPath::Resolved(_, path) => match_path(path, segments),
+ QPath::TypeRelative(ty, segment) => match ty.kind {
+ TyKind::Path(ref inner_path) => {
+ if let [prefix @ .., end] = segments {
+ if match_qpath(inner_path, prefix) {
+ return segment.ident.name.as_str() == *end;
+ }
+ }
+ false
+ },
+ _ => false,
+ },
+ QPath::LangItem(..) => false,
+ }
+}
+
+/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
+///
+/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
+pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
+ 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>(
+ cx: &LateContext<'_>,
+ maybe_path: &impl MaybePath<'tcx>,
+ diag_item: Symbol,
+) -> bool {
+ path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+}
+
+/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
+/// `QPath::Resolved.1.res.opt_def_id()`.
+///
+/// Matches a `Path` against a slice of segment string literals.
+///
+/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
+/// `rustc_hir::Path`.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// if match_path(&trait_ref.path, &paths::HASH) {
+/// // This is the `std::hash::Hash` trait.
+/// }
+///
+/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
+/// // This is a `rustc_middle::lint::Lint`.
+/// }
+/// ```
+pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
+ path.segments
+ .iter()
+ .rev()
+ .zip(segments.iter().rev())
+ .all(|(a, b)| a.ident.name.as_str() == *b)
+}
+
+/// If the expression is a path to a local, returns the canonical `HirId` of the local.
+pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
+ if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
+ if let Res::Local(id) = path.res {
+ return Some(id);
+ }
+ }
+ None
+}
+
+/// Returns true if the expression is a path to a local with the specified `HirId`.
+/// Use this function to see if an expression matches a function argument or a match binding.
+pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
+ path_to_local(expr) == Some(id)
+}
+
+pub trait MaybePath<'hir> {
+ fn hir_id(&self) -> HirId;
+ fn qpath_opt(&self) -> Option<&QPath<'hir>>;
+}
+
+macro_rules! maybe_path {
+ ($ty:ident, $kind:ident) => {
+ impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
+ fn hir_id(&self) -> HirId {
+ self.hir_id
+ }
+ fn qpath_opt(&self) -> Option<&QPath<'hir>> {
+ match &self.kind {
+ hir::$kind::Path(qpath) => Some(qpath),
+ _ => None,
+ }
+ }
+ }
+ };
+}
+maybe_path!(Expr, ExprKind);
+maybe_path!(Pat, PatKind);
+maybe_path!(Ty, TyKind);
+
+/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
+pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
+ match maybe_path.qpath_opt() {
+ None => Res::Err,
+ Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
+ }
+}
+
+/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
+pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
+ path_res(cx, maybe_path).opt_def_id()
+}
+
+fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + '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" => 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<Res> {
+ 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<Res> {
+ 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 item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
+ 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<Res> {
+ fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
+ tcx.crates(())
+ .iter()
+ .copied()
+ .filter(move |&num| tcx.crate_name(num) == name)
+ .map(CrateNum::as_def_id)
+ }
+
+ let tcx = cx.tcx;
+
+ let (base, mut path) = match *path {
+ [primitive] => {
+ return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
+ },
+ [base, ref path @ ..] => (base, path),
+ _ => return Vec::new(),
+ };
+
+ 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));
+
+ let mut resolutions: Vec<Res> = starts.collect();
+
+ 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();
+ }
+
+ 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<Item = DefId> {
+ 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.
+/// It could be a trait or trait alias.
+///
+/// This function is expensive and should be used sparingly.
+pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
+ 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.
+///
+/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
+///
+/// ```rust
+/// struct Point(isize, isize);
+///
+/// impl std::ops::Add for Point {
+/// type Output = Self;
+///
+/// fn add(self, other: Self) -> Self {
+/// Point(0, 0)
+/// }
+/// }
+/// ```
+pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
+ // Get the implemented trait for the current function
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
+ let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
+ if_chain! {
+ if parent_impl != hir::CRATE_OWNER_ID;
+ if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id);
+ if let hir::ItemKind::Impl(impl_) = &item.kind;
+ then {
+ return impl_.of_trait.as_ref();
+ }
+ }
+ None
+}
+
+/// This method will return tuple of projection stack and root of the expression,
+/// used in `can_mut_borrow_both`.
+///
+/// For example, if `e` represents the `v[0].a.b[x]`
+/// this method will return a tuple, composed of a `Vec`
+/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
+/// and an `Expr` for root of them, `v`
+fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
+ let mut result = vec![];
+ let root = loop {
+ match e.kind {
+ ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
+ result.push(e);
+ e = ep;
+ },
+ _ => break e,
+ };
+ };
+ result.reverse();
+ (result, root)
+}
+
+/// Gets the mutability of the custom deref adjustment, if any.
+pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
+ cx.typeck_results()
+ .expr_adjustments(e)
+ .iter()
+ .find_map(|a| match a.kind {
+ Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
+ Adjust::Deref(None) => None,
+ _ => Some(None),
+ })
+ .and_then(|x| x)
+}
+
+/// Checks if two expressions can be mutably borrowed simultaneously
+/// and they aren't dependent on borrowing same thing twice
+pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
+ let (s1, r1) = projection_stack(e1);
+ let (s2, r2) = projection_stack(e2);
+ if !eq_expr_value(cx, r1, r2) {
+ return true;
+ }
+ if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
+ return false;
+ }
+
+ for (x1, x2) in s1.iter().zip(s2.iter()) {
+ if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
+ return false;
+ }
+
+ match (&x1.kind, &x2.kind) {
+ (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
+ if i1 != i2 {
+ return true;
+ }
+ },
+ (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
+ if !eq_expr_value(cx, i1, i2) {
+ return false;
+ }
+ },
+ _ => return false,
+ }
+ }
+ false
+}
+
+/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
+/// constructor from the std library
+fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
+ let std_types_symbols = &[
+ sym::Vec,
+ sym::VecDeque,
+ sym::LinkedList,
+ sym::HashMap,
+ sym::BTreeMap,
+ sym::HashSet,
+ sym::BTreeSet,
+ sym::BinaryHeap,
+ ];
+
+ if let QPath::TypeRelative(_, method) = path {
+ 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()) || Some(adt.did()) == cx.tcx.lang_items().string()
+ });
+ }
+ }
+ }
+ }
+ false
+}
+
+/// Return true if the expr is equal to `Default::default` when evaluated.
+pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
+ if_chain! {
+ if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
+ if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
+ if is_diag_trait_item(cx, repl_def_id, sym::Default)
+ || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
+ then { true } else { false }
+ }
+}
+
+/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
+/// It doesn't cover all cases, for example indirect function calls (some of std
+/// functions are supported) but it is the best we have.
+pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ match &e.kind {
+ ExprKind::Lit(lit) => match lit.node {
+ LitKind::Bool(false) | LitKind::Int(0, _) => true,
+ LitKind::Str(s, _) => s.is_empty(),
+ _ => false,
+ },
+ ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
+ ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
+ if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
+ if let LitKind::Int(v, _) = const_lit.node;
+ if v <= 32 && is_default_equivalent(cx, x);
+ then {
+ true
+ }
+ else {
+ false
+ }
+ },
+ ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
+ ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
+ ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
+ ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
+ _ => false,
+ }
+}
+
+fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
+ if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind &&
+ seg.ident.name == sym::from
+ {
+ match arg.kind {
+ ExprKind::Lit(hir::Lit {
+ node: LitKind::Str(ref sym, _),
+ ..
+ }) => 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 &&
+ let LitKind::Int(v, _) = const_lit.node
+ {
+ return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
+ }
+ }
+ _ => (),
+ }
+ }
+ false
+}
+
+/// Checks if the top level expression can be moved into a closure as is.
+/// Currently checks for:
+/// * Break/Continue outside the given loop HIR ids.
+/// * Yield/Return statements.
+/// * Inline assembly.
+/// * Usages of a field of a local where the type of the local can be partially moved.
+///
+/// For example, given the following function:
+///
+/// ```
+/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
+/// for item in iter {
+/// let s = item.1;
+/// if item.0 > 10 {
+/// continue;
+/// } else {
+/// s.clear();
+/// }
+/// }
+/// }
+/// ```
+///
+/// When called on the expression `item.0` this will return false unless the local `item` is in the
+/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
+/// isn't always safe to move into a closure when only a single field is needed.
+///
+/// When called on the `continue` expression this will return false unless the outer loop expression
+/// is in the `loop_ids` set.
+///
+/// Note that this check is not recursive, so passing the `if` expression will always return true
+/// even though sub-expressions might return false.
+pub fn can_move_expr_to_closure_no_visit<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ loop_ids: &[HirId],
+ ignore_locals: &HirIdSet,
+) -> bool {
+ match expr.kind {
+ ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
+ | ExprKind::Continue(Destination { target_id: Ok(id), .. })
+ if loop_ids.contains(&id) =>
+ {
+ true
+ },
+ ExprKind::Break(..)
+ | ExprKind::Continue(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Yield(..)
+ | ExprKind::InlineAsm(_) => false,
+ // Accessing a field of a local value can only be done if the type isn't
+ // partially moved.
+ ExprKind::Field(
+ &Expr {
+ hir_id,
+ kind:
+ ExprKind::Path(QPath::Resolved(
+ _,
+ Path {
+ res: Res::Local(local_id),
+ ..
+ },
+ )),
+ ..
+ },
+ _,
+ ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
+ // TODO: check if the local has been partially moved. Assume it has for now.
+ false
+ },
+ _ => true,
+ }
+}
+
+/// How a local is captured by a closure
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureKind {
+ Value,
+ Ref(Mutability),
+}
+impl CaptureKind {
+ pub fn is_imm_ref(self) -> bool {
+ self == Self::Ref(Mutability::Not)
+ }
+}
+impl std::ops::BitOr for CaptureKind {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self::Output {
+ match (self, rhs) {
+ (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
+ (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
+ | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
+ (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
+ }
+ }
+}
+impl std::ops::BitOrAssign for CaptureKind {
+ fn bitor_assign(&mut self, rhs: Self) {
+ *self = *self | rhs;
+ }
+}
+
+/// Given an expression referencing a local, determines how it would be captured in a closure.
+/// 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(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
+ .typeck_results()
+ .extract_binding_mode(cx.sess(), id, span)
+ .unwrap()
+ {
+ BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
+ capture = CaptureKind::Value;
+ },
+ BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
+ capture = CaptureKind::Ref(Mutability::Mut);
+ },
+ _ => (),
+ });
+ capture
+ }
+
+ debug_assert!(matches!(
+ e.kind,
+ ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
+ ));
+
+ let mut child_id = e.hir_id;
+ let mut capture = CaptureKind::Value;
+ let mut capture_expr_ty = e;
+
+ for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
+ if let [
+ Adjustment {
+ kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
+ target,
+ },
+ ref adjust @ ..,
+ ] = *cx
+ .typeck_results()
+ .adjustments()
+ .get(child_id)
+ .map_or(&[][..], |x| &**x)
+ {
+ if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
+ *adjust.last().map_or(target, |a| a.target).kind()
+ {
+ return CaptureKind::Ref(mutability);
+ }
+ }
+
+ match parent {
+ Node::Expr(e) => match e.kind {
+ ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
+ ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
+ ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
+ return CaptureKind::Ref(Mutability::Mut);
+ },
+ ExprKind::Field(..) => {
+ if capture == CaptureKind::Value {
+ capture_expr_ty = e;
+ }
+ },
+ ExprKind::Let(let_expr) => {
+ let mutability = match pat_capture_kind(cx, let_expr.pat) {
+ CaptureKind::Value => Mutability::Not,
+ CaptureKind::Ref(m) => m,
+ };
+ return CaptureKind::Ref(mutability);
+ },
+ ExprKind::Match(_, arms, _) => {
+ let mut mutability = Mutability::Not;
+ for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
+ match capture {
+ CaptureKind::Value => break,
+ CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
+ CaptureKind::Ref(Mutability::Not) => (),
+ }
+ }
+ return CaptureKind::Ref(mutability);
+ },
+ _ => break,
+ },
+ Node::Local(l) => match pat_capture_kind(cx, l.pat) {
+ CaptureKind::Value => break,
+ capture @ CaptureKind::Ref(_) => return capture,
+ },
+ _ => break,
+ }
+
+ child_id = parent_id;
+ }
+
+ if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
+ // Copy types are never automatically captured by value.
+ CaptureKind::Ref(Mutability::Not)
+ } else {
+ capture
+ }
+}
+
+/// Checks if the expression can be moved into a closure as is. This will return a list of captures
+/// if so, otherwise, `None`.
+pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ // Stack of potential break targets contained in the expression.
+ loops: Vec<HirId>,
+ /// Local variables created in the expression. These don't need to be captured.
+ locals: HirIdSet,
+ /// Whether this expression can be turned into a closure.
+ allow_closure: bool,
+ /// Locals which need to be captured, and whether they need to be by value, reference, or
+ /// mutable reference.
+ captures: HirIdMap<CaptureKind>,
+ }
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if !self.allow_closure {
+ return;
+ }
+
+ match e.kind {
+ ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
+ if !self.locals.contains(&l) {
+ let cap = capture_local_usage(self.cx, e);
+ self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
+ }
+ },
+ ExprKind::Closure { .. } => {
+ let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
+ for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
+ let local_id = match capture.place.base {
+ PlaceBase::Local(id) => id,
+ PlaceBase::Upvar(var) => var.var_path.hir_id,
+ _ => continue,
+ };
+ if !self.locals.contains(&local_id) {
+ let capture = match capture.info.capture_kind {
+ UpvarCapture::ByValue => CaptureKind::Value,
+ UpvarCapture::ByRef(kind) => match kind {
+ BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
+ BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
+ CaptureKind::Ref(Mutability::Mut)
+ },
+ },
+ };
+ self.captures
+ .entry(local_id)
+ .and_modify(|e| *e |= capture)
+ .or_insert(capture);
+ }
+ }
+ },
+ ExprKind::Loop(b, ..) => {
+ self.loops.push(e.hir_id);
+ self.visit_block(b);
+ self.loops.pop();
+ },
+ _ => {
+ self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
+ walk_expr(self, e);
+ },
+ }
+ }
+
+ fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
+ p.each_binding_or_first(&mut |_, id, _, _| {
+ self.locals.insert(id);
+ });
+ }
+ }
+
+ let mut v = V {
+ cx,
+ allow_closure: true,
+ loops: Vec::new(),
+ locals: HirIdSet::default(),
+ captures: HirIdMap::default(),
+ };
+ v.visit_expr(expr);
+ v.allow_closure.then_some(v.captures)
+}
+
+/// Arguments of a method: the receiver and all the additional arguments.
+pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
+
+/// Returns the method names and argument list of nested method call expressions that make up
+/// `expr`. method/span lists are sorted with the most recent call first.
+pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
+ let mut method_names = Vec::with_capacity(max_depth);
+ let mut arg_lists = Vec::with_capacity(max_depth);
+ let mut spans = Vec::with_capacity(max_depth);
+
+ let mut current = expr;
+ for _ in 0..max_depth {
+ if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
+ if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
+ break;
+ }
+ method_names.push(path.ident.name);
+ arg_lists.push((*receiver, &**args));
+ spans.push(path.ident.span);
+ current = receiver;
+ } else {
+ break;
+ }
+ }
+
+ (method_names, arg_lists, spans)
+}
+
+/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
+///
+/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
+/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
+/// containing the `Expr`s for
+/// `.bar()` and `.baz()`
+pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
+ let mut current = expr;
+ let mut matched = Vec::with_capacity(methods.len());
+ for method_name in methods.iter().rev() {
+ // method chains are stored last -> first
+ if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
+ if path.ident.name.as_str() == *method_name {
+ if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
+ return None;
+ }
+ matched.push((receiver, args)); // build up `matched` backwards
+ current = receiver; // go to parent expression
+ } else {
+ return None;
+ }
+ } else {
+ return None;
+ }
+ }
+ // Reverse `matched` so that it is in the same order as `methods`.
+ matched.reverse();
+ Some(matched)
+}
+
+/// Returns `true` if the provided `def_id` is an entrypoint to a program.
+pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
+ cx.tcx
+ .entry_fn(())
+ .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
+}
+
+/// Returns `true` if the expression is in the program's `#[panic_handler]`.
+pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ let parent = cx.tcx.hir().get_parent_item(e.hir_id);
+ Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
+}
+
+/// Gets the name of the item the expression is in, if available.
+pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
+ let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
+ match cx.tcx.hir().find_by_def_id(parent_id) {
+ Some(
+ Node::Item(Item { ident, .. })
+ | Node::TraitItem(TraitItem { ident, .. })
+ | Node::ImplItem(ImplItem { ident, .. }),
+ ) => Some(ident.name),
+ _ => None,
+ }
+}
+
+pub struct ContainsName<'a, 'tcx> {
+ pub cx: &'a LateContext<'tcx>,
+ pub name: Symbol,
+ pub result: bool,
+}
+
+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<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
+ let mut cn = ContainsName {
+ name,
+ result: false,
+ cx,
+ };
+ cn.visit_expr(expr);
+ cn.result
+}
+
+/// Returns `true` if `expr` contains a return expression
+pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
+ for_each_expr(expr, |e| {
+ if matches!(e.kind, hir::ExprKind::Ret(..)) {
+ ControlFlow::Break(())
+ } else {
+ ControlFlow::Continue(())
+ }
+ })
+ .is_some()
+}
+
+/// Gets the parent node, if any.
+pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
+ tcx.hir().find_parent(id)
+}
+
+/// Gets the parent expression, if any –- this is useful to constrain a lint.
+pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+ get_parent_expr_for_hir(cx, e.hir_id)
+}
+
+/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
+/// constraint lints
+pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
+ match get_parent_node(cx.tcx, hir_id) {
+ Some(Node::Expr(parent)) => Some(parent),
+ _ => None,
+ }
+}
+
+/// 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
+ .get_enclosing_scope(hir_id)
+ .and_then(|enclosing_id| map.find(enclosing_id));
+ enclosing_node.and_then(|node| match node {
+ Node::Block(block) => Some(block),
+ Node::Item(&Item {
+ kind: ItemKind::Fn(_, _, eid),
+ ..
+ })
+ | Node::ImplItem(&ImplItem {
+ kind: ImplItemKind::Fn(_, eid),
+ ..
+ }) => match cx.tcx.hir().body(eid).value.kind {
+ ExprKind::Block(block, _) => Some(block),
+ _ => None,
+ },
+ _ => None,
+ })
+}
+
+/// Gets the loop or closure enclosing the given expression, if any.
+pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &Expr<'_>,
+) -> Option<&'tcx Expr<'tcx>> {
+ for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
+ match node {
+ Node::Expr(e) => match e.kind {
+ ExprKind::Closure { .. } => {
+ if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
+ && subs.as_closure().kind() == ClosureKind::FnOnce
+ {
+ continue;
+ }
+ let is_once = walk_to_expr_usage(cx, e, |node, id| {
+ let Node::Expr(e) = node else {
+ return None;
+ };
+ match e.kind {
+ ExprKind::Call(f, _) if f.hir_id == id => Some(()),
+ ExprKind::Call(f, args) => {
+ let i = args.iter().position(|arg| arg.hir_id == id)?;
+ let sig = expr_sig(cx, f)?;
+ let predicates = sig
+ .predicates_id()
+ .map_or(cx.param_env, |id| cx.tcx.param_env(id))
+ .caller_bounds();
+ sig.input(i).and_then(|ty| {
+ ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
+ })
+ },
+ ExprKind::MethodCall(_, receiver, args, _) => {
+ let i = std::iter::once(receiver)
+ .chain(args.iter())
+ .position(|arg| arg.hir_id == id)?;
+ let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
+ let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i];
+ ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
+ },
+ _ => None,
+ }
+ })
+ .is_some();
+ if !is_once {
+ return Some(e);
+ }
+ },
+ ExprKind::Loop(..) => return Some(e),
+ _ => (),
+ },
+ Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
+ _ => break,
+ }
+ }
+ None
+}
+
+/// Gets the parent node if it's an impl block.
+pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
+ match tcx.hir().parent_iter(id).next() {
+ Some((
+ _,
+ Node::Item(Item {
+ kind: ItemKind::Impl(imp),
+ ..
+ }),
+ )) => Some(imp),
+ _ => None,
+ }
+}
+
+/// Removes blocks around an expression, only if the block contains just one expression
+/// and no statements. Unsafe blocks are not removed.
+///
+/// Examples:
+/// * `{}` -> `{}`
+/// * `{ x }` -> `x`
+/// * `{{ x }}` -> `x`
+/// * `{ x; }` -> `{ x; }`
+/// * `{ x; y }` -> `{ x; y }`
+/// * `{ unsafe { x } }` -> `unsafe { x }`
+pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
+ while let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr: Some(inner),
+ rules: BlockCheckMode::DefaultBlock,
+ ..
+ },
+ _,
+ ) = expr.kind
+ {
+ expr = inner;
+ }
+ expr
+}
+
+/// Removes blocks around an expression, only if the block contains just one expression
+/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
+///
+/// Examples:
+/// * `{}` -> `{}`
+/// * `{ x }` -> `x`
+/// * `{ x; }` -> `x`
+/// * `{{ x; }}` -> `x`
+/// * `{ x; y }` -> `{ x; y }`
+/// * `{ unsafe { x } }` -> `unsafe { x }`
+pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
+ while let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr: Some(inner),
+ rules: BlockCheckMode::DefaultBlock,
+ ..
+ }
+ | Block {
+ stmts:
+ [
+ Stmt {
+ kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
+ ..
+ },
+ ],
+ expr: None,
+ rules: BlockCheckMode::DefaultBlock,
+ ..
+ },
+ _,
+ ) = expr.kind
+ {
+ expr = inner;
+ }
+ expr
+}
+
+/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
+pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ let mut iter = tcx.hir().parent_iter(expr.hir_id);
+ match iter.next() {
+ Some((
+ _,
+ Node::Expr(Expr {
+ kind: ExprKind::If(_, _, Some(else_expr)),
+ ..
+ }),
+ )) => else_expr.hir_id == expr.hir_id,
+ _ => false,
+ }
+}
+
+/// Checks whether the given expression is a constant integer of the given value.
+/// unlike `is_integer_literal`, this version does const folding
+pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
+ if is_integer_literal(e, value) {
+ return true;
+ }
+ let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
+ if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
+ return value == v;
+ }
+ false
+}
+
+/// Checks whether the given expression is a constant literal of the given value.
+pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
+ // FIXME: use constant folding
+ if let ExprKind::Lit(ref spanned) = expr.kind {
+ if let LitKind::Int(v, _) = spanned.node {
+ return v == value;
+ }
+ }
+ false
+}
+
+/// Returns `true` if the given `Expr` has been coerced before.
+///
+/// Examples of coercions can be found in the Nomicon at
+/// <https://doc.rust-lang.org/nomicon/coercions.html>.
+///
+/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for
+/// more information on adjustments and coercions.
+pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ cx.typeck_results().adjustments().get(e.hir_id).is_some()
+}
+
+/// Returns the pre-expansion span if this comes from an expansion of the
+/// macro `name`.
+/// See also [`is_direct_expn_of`].
+#[must_use]
+pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
+ loop {
+ if span.from_expansion() {
+ let data = span.ctxt().outer_expn_data();
+ let new_span = data.call_site;
+
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+ if mac_name.as_str() == name {
+ return Some(new_span);
+ }
+ }
+
+ span = new_span;
+ } else {
+ return None;
+ }
+ }
+}
+
+/// Returns the pre-expansion span if the span directly comes from an expansion
+/// of the macro `name`.
+/// The difference with [`is_expn_of`] is that in
+/// ```rust
+/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
+/// # macro_rules! bar { ($e:expr) => { $e } }
+/// foo!(bar!(42));
+/// ```
+/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
+/// from `bar!` by `is_direct_expn_of`.
+#[must_use]
+pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
+ if span.from_expansion() {
+ let data = span.ctxt().outer_expn_data();
+ let new_span = data.call_site;
+
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
+ if mac_name.as_str() == name {
+ return Some(new_span);
+ }
+ }
+ }
+
+ None
+}
+
+/// Convenience function to get the return type of a function.
+pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
+ let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
+ let ret_ty = cx.tcx.fn_sig(fn_def_id).subst_identity().output();
+ cx.tcx.erase_late_bound_regions(ret_ty)
+}
+
+/// Convenience function to get the nth argument type of a function.
+pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
+ let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
+ let arg = cx.tcx.fn_sig(fn_def_id).subst_identity().input(nth);
+ cx.tcx.erase_late_bound_regions(arg)
+}
+
+/// Checks if an expression is constructing a tuple-like enum variant or struct
+pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let ExprKind::Call(fun, _) = expr.kind {
+ if let ExprKind::Path(ref qp) = fun.kind {
+ let res = cx.qpath_res(qp, fun.hir_id);
+ return match res {
+ def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
+ def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
+ _ => false,
+ };
+ }
+ }
+ false
+}
+
+/// Returns `true` if a pattern is refutable.
+// TODO: should be implemented using rustc/mir_build/thir machinery
+pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
+ fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
+ matches!(
+ cx.qpath_res(qpath, id),
+ def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
+ )
+ }
+
+ fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
+ i.into_iter().any(|pat| is_refutable(cx, pat))
+ }
+
+ match pat.kind {
+ PatKind::Wild => false,
+ PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
+ PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
+ PatKind::Lit(..) | PatKind::Range(..) => true,
+ PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
+ PatKind::Or(pats) => {
+ // TODO: should be the honest check, that pats is exhaustive set
+ are_refutable(cx, pats)
+ },
+ PatKind::Tuple(pats, _) => are_refutable(cx, pats),
+ PatKind::Struct(ref qpath, fields, _) => {
+ is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
+ },
+ PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
+ PatKind::Slice(head, middle, tail) => {
+ match &cx.typeck_results().node_type(pat.hir_id).kind() {
+ rustc_ty::Slice(..) => {
+ // [..] is the only irrefutable slice pattern.
+ !head.is_empty() || middle.is_none() || !tail.is_empty()
+ },
+ rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
+ _ => {
+ // unreachable!()
+ true
+ },
+ }
+ },
+ }
+}
+
+/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
+/// the function once on the given pattern.
+pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
+ if let PatKind::Or(pats) = pat.kind {
+ pats.iter().for_each(f);
+ } else {
+ f(pat);
+ }
+}
+
+pub fn is_self(slf: &Param<'_>) -> bool {
+ if let PatKind::Binding(.., name, _) = slf.pat.kind {
+ name.name == kw::SelfLower
+ } else {
+ false
+ }
+}
+
+pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
+ if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
+ if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
+ return true;
+ }
+ }
+ false
+}
+
+pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
+ (0..decl.inputs.len()).map(move |i| &body.params[i])
+}
+
+/// Checks if a given expression is a match expression expanded from the `?`
+/// operator or the `try` macro.
+pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+ fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ if_chain! {
+ if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind;
+ if ddpos.as_opt_usize().is_none();
+ if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk);
+ if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
+ if path_to_local_id(arm.body, hir_id);
+ then {
+ return true;
+ }
+ }
+ false
+ }
+
+ fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
+ is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
+ } else {
+ false
+ }
+ }
+
+ if let ExprKind::Match(_, arms, ref source) = expr.kind {
+ // desugared from a `?` operator
+ if *source == MatchSource::TryDesugar {
+ return Some(expr);
+ }
+
+ if_chain! {
+ if arms.len() == 2;
+ if arms[0].guard.is_none();
+ if arms[1].guard.is_none();
+ if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
+ then {
+ return Some(expr);
+ }
+ }
+ }
+
+ None
+}
+
+/// Returns `true` if the lint is allowed in the current context. This is useful for
+/// skipping long running code when it's unnecessary
+///
+/// This function should check the lint level for the same node, that the lint will
+/// be emitted at. If the information is buffered to be emitted at a later point, please
+/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
+/// expectations at the checked nodes will be fulfilled.
+pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
+ cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
+}
+
+pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
+ while let PatKind::Ref(subpat, _) = pat.kind {
+ pat = subpat;
+ }
+ pat
+}
+
+pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
+ Integer::from_int_ty(&tcx, ity).size().bits()
+}
+
+#[expect(clippy::cast_possible_wrap)]
+/// Turn a constant int byte representation into an i128
+pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
+ let amt = 128 - int_bits(tcx, ity);
+ ((u as i128) << amt) >> amt
+}
+
+#[expect(clippy::cast_sign_loss)]
+/// clip unused bytes
+pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
+ let amt = 128 - int_bits(tcx, ity);
+ ((u as u128) << amt) >> amt
+}
+
+/// clip unused bytes
+pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
+ let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
+ let amt = 128 - bits;
+ (u << amt) >> amt
+}
+
+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;
+ let mut enclosing_node = node;
+ while Some(enclosing_node) != prev_enclosing_node {
+ if has_attr(map.attrs(enclosing_node), symbol) {
+ return true;
+ }
+ prev_enclosing_node = Some(enclosing_node);
+ enclosing_node = map.get_parent_item(enclosing_node).into();
+ }
+
+ false
+}
+
+pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+ any_parent_has_attr(tcx, node, sym::automatically_derived)
+}
+
+/// Matches a function call with the given path and returns the arguments.
+///
+/// Usage:
+///
+/// ```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<'_>,
+ path: &[&str],
+) -> Option<&'tcx [Expr<'tcx>]> {
+ if_chain! {
+ if let ExprKind::Call(fun, args) = expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ if match_def_path(cx, fun_def_id, path);
+ then {
+ return Some(args);
+ }
+ };
+ 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.
+///
+/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
+pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
+ let search_path = cx.get_def_path(did);
+ paths
+ .iter()
+ .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
+}
+
+/// Checks if the given `DefId` matches the path.
+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())
+}
+
+/// Checks if the given `DefId` matches the `libc` item.
+pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
+ let path = cx.get_def_path(did);
+ // libc is meant to be used as a flat list of names, but they're all actually defined in different
+ // modules based on the target platform. Ignore everything but crate name and the item name.
+ path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
+}
+
+/// Returns the list of condition expressions and the list of blocks in a
+/// sequence of `if/else`.
+/// E.g., this returns `([a, b], [c, d, e])` for the expression
+/// `if a { c } else if b { d } else { e }`.
+pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
+ let mut conds = Vec::new();
+ let mut blocks: Vec<&Block<'_>> = Vec::new();
+
+ while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
+ conds.push(cond);
+ if let ExprKind::Block(block, _) = then.kind {
+ blocks.push(block);
+ } else {
+ panic!("ExprKind::If node is not an ExprKind::Block");
+ }
+
+ if let Some(else_expr) = r#else {
+ expr = else_expr;
+ } else {
+ break;
+ }
+ }
+
+ // final `else {..}`
+ if !blocks.is_empty() {
+ if let ExprKind::Block(block, _) = expr.kind {
+ blocks.push(block);
+ }
+ }
+
+ (conds, blocks)
+}
+
+/// Checks if the given function kind is an async function.
+pub fn is_async_fn(kind: FnKind<'_>) -> bool {
+ 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,
+pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+ if let ExprKind::Call(
+ _,
+ &[
+ Expr {
+ kind: ExprKind::Closure(&Closure { body, .. }),
+ ..
+ },
+ ],
+ ) = body.value.kind
+ {
+ if let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr:
+ Some(Expr {
+ kind: ExprKind::DropTemps(expr),
+ ..
+ }),
+ ..
+ },
+ _,
+ ) = tcx.hir().body(body).value.kind
+ {
+ return Some(expr);
+ }
+ };
+ None
+}
+
+// check if expr is calling method or function with #[must_use] attribute
+pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let did = match expr.kind {
+ ExprKind::Call(path, _) => if_chain! {
+ if let ExprKind::Path(ref qpath) = path.kind;
+ if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
+ then {
+ Some(did)
+ } else {
+ None
+ }
+ },
+ ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+ _ => None,
+ };
+
+ did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
+}
+
+/// Checks if an expression represents the identity function
+/// Only examines closures and `std::convert::identity`
+pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ /// Checks if a function's body represents the identity function. Looks for bodies of the form:
+ /// * `|x| x`
+ /// * `|x| return x`
+ /// * `|x| { return x }`
+ /// * `|x| { return x; }`
+ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
+ let id = if_chain! {
+ if let [param] = func.params;
+ if let PatKind::Binding(_, id, _, _) = param.pat.kind;
+ then {
+ id
+ } else {
+ return false;
+ }
+ };
+
+ let mut expr = func.value;
+ loop {
+ match expr.kind {
+ #[rustfmt::skip]
+ ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
+ | ExprKind::Ret(Some(e)) => expr = e,
+ #[rustfmt::skip]
+ ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
+ if_chain! {
+ if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
+ if let ExprKind::Ret(Some(ret_val)) = e.kind;
+ then {
+ expr = ret_val;
+ } else {
+ return false;
+ }
+ }
+ },
+ _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
+ }
+ }
+ }
+
+ match expr.kind {
+ ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
+ _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
+ }
+}
+
+/// Gets the node where an expression is either used, or it's type is unified with another branch.
+/// Returns both the node and the `HirId` of the closest child node.
+pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
+ let mut child_id = expr.hir_id;
+ let mut iter = tcx.hir().parent_iter(child_id);
+ loop {
+ match iter.next() {
+ None => break None,
+ Some((id, Node::Block(_))) => child_id = id,
+ Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
+ Some((_, Node::Expr(expr))) => match expr.kind {
+ ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
+ ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
+ ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
+ _ => break Some((Node::Expr(expr), child_id)),
+ },
+ Some((_, node)) => break Some((node, child_id)),
+ }
+ }
+}
+
+/// Checks if the result of an expression is used, or it's type is unified with another branch.
+pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ !matches!(
+ get_expr_use_or_unification_node(tcx, expr),
+ None | Some((
+ Node::Stmt(Stmt {
+ kind: StmtKind::Expr(_)
+ | StmtKind::Semi(_)
+ | StmtKind::Local(Local {
+ pat: Pat {
+ kind: PatKind::Wild,
+ ..
+ },
+ ..
+ }),
+ ..
+ }),
+ _
+ ))
+ )
+}
+
+/// Checks if the expression is the final expression returned from a block.
+pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+ matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
+}
+
+pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
+ if !is_no_std_crate(cx) {
+ Some("std")
+ } else if !is_no_core_crate(cx) {
+ Some("core")
+ } else {
+ None
+ }
+}
+
+pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
+ cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
+ if let ast::AttrKind::Normal(ref normal) = attr.kind {
+ normal.item.path == sym::no_std
+ } else {
+ false
+ }
+ })
+}
+
+pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
+ cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
+ if let ast::AttrKind::Normal(ref normal) = attr.kind {
+ normal.item.path == sym::no_core
+ } else {
+ false
+ }
+ })
+}
+
+/// Check if parent of a hir node is a trait implementation block.
+/// For example, `f` in
+/// ```rust
+/// # struct S;
+/// # trait Trait { fn f(); }
+/// impl Trait for S {
+/// fn f() {}
+/// }
+/// ```
+pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+ 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
+ }
+}
+
+/// Check if it's even possible to satisfy the `where` clause for the item.
+///
+/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
+///
+/// ```ignore
+/// fn foo() where i32: Iterator {
+/// for _ in 2i32 {}
+/// }
+/// ```
+pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
+ use rustc_trait_selection::traits;
+ let predicates = cx
+ .tcx
+ .predicates_of(did)
+ .predicates
+ .iter()
+ .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
+ traits::impossible_predicates(
+ cx.tcx,
+ traits::elaborate_predicates(cx.tcx, predicates)
+ .map(|o| o.predicate)
+ .collect::<Vec<_>>(),
+ )
+}
+
+/// Returns the `DefId` of the callee if the given expression is a function or method call.
+pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
+ match &expr.kind {
+ ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(qpath),
+ hir_id: path_hir_id,
+ ..
+ },
+ ..,
+ ) => {
+ // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
+ // deref to fn pointers, dyn Fn, impl Fn - #8850
+ if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
+ cx.typeck_results().qpath_res(qpath, *path_hir_id)
+ {
+ Some(id)
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+}
+
+/// Returns `Option<String>` 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<String> {
+ let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
+ let expr_kind = expr_type.kind();
+ let is_primitive = match expr_kind {
+ rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
+ rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
+ if let rustc_ty::Slice(element_type) = inner_ty.kind() {
+ is_recursively_primitive_type(*element_type)
+ } else {
+ unreachable!()
+ }
+ },
+ _ => false,
+ };
+
+ if is_primitive {
+ // if we have wrappers like Array, Slice or Tuple, print these
+ // and get the type enclosed in the slice ref
+ match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
+ rustc_ty::Slice(..) => return Some("slice".into()),
+ rustc_ty::Array(..) => return Some("array".into()),
+ rustc_ty::Tuple(..) => return Some("tuple".into()),
+ _ => {
+ // is_recursively_primitive_type() should have taken care
+ // of the rest and we can rely on the type that is found
+ let refs_peeled = expr_type.peel_refs();
+ return Some(refs_peeled.walk().last().unwrap().to_string());
+ },
+ }
+ }
+ None
+}
+
+/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
+/// `hash` must be comformed with `eq`
+pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
+where
+ Hash: Fn(&T) -> u64,
+ Eq: Fn(&T, &T) -> bool,
+{
+ match exprs {
+ [a, b] if eq(a, b) => return vec![(a, b)],
+ _ if exprs.len() <= 2 => return vec![],
+ _ => {},
+ }
+
+ let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
+
+ let mut map: UnhashMap<u64, Vec<&_>> =
+ UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
+
+ for expr in exprs {
+ match map.entry(hash(expr)) {
+ Entry::Occupied(mut o) => {
+ for o in o.get() {
+ if eq(o, expr) {
+ match_expr_list.push((o, expr));
+ }
+ }
+ o.get_mut().push(expr);
+ },
+ Entry::Vacant(v) => {
+ v.insert(vec![expr]);
+ },
+ }
+ }
+
+ match_expr_list
+}
+
+/// Peels off all references on the pattern. Returns the underlying pattern and the number of
+/// references removed.
+pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
+ fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
+ if let PatKind::Ref(pat, _) = pat.kind {
+ peel(pat, count + 1)
+ } else {
+ (pat, count)
+ }
+ }
+ peel(pat, 0)
+}
+
+/// Peels of expressions while the given closure returns `Some`.
+pub fn peel_hir_expr_while<'tcx>(
+ mut expr: &'tcx Expr<'tcx>,
+ mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
+) -> &'tcx Expr<'tcx> {
+ while let Some(e) = f(expr) {
+ expr = e;
+ }
+ expr
+}
+
+/// Peels off up to the given number of references on the expression. Returns the underlying
+/// expression and the number of references removed.
+pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
+ let mut remaining = count;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
+ remaining -= 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (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) {
+ let mut count = 0;
+ let e = peel_hir_expr_while(expr, |e| match e.kind {
+ ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
+ count += 1;
+ Some(e)
+ },
+ _ => None,
+ });
+ (e, count)
+}
+
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+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::Ref(_, ref_ty) => {
+ ty = ref_ty.ty;
+ count += 1;
+ },
+ _ => break (ty, count),
+ }
+ }
+}
+
+/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
+/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
+pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
+ loop {
+ match expr.kind {
+ ExprKind::AddrOf(_, _, e) => expr = e,
+ ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
+ _ => break,
+ }
+ }
+ expr
+}
+
+pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+ if let Res::Def(_, def_id) = path.res {
+ return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
+ }
+ }
+ false
+}
+
+static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
+
+fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
+ let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
+ let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
+ let value = map.entry(module);
+ match value {
+ Entry::Occupied(entry) => f(entry.get()),
+ Entry::Vacant(entry) => {
+ let mut names = Vec::new();
+ for id in tcx.hir().module_items(module) {
+ 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 {
+ // We could also check for the type name `test::TestDescAndFn`
+ if let Res::Def(DefKind::Struct, _) = path.res {
+ let has_test_marker = tcx
+ .hir()
+ .attrs(item.hir_id())
+ .iter()
+ .any(|a| a.has_name(sym::rustc_test_marker));
+ if has_test_marker {
+ names.push(item.ident.name);
+ }
+ }
+ }
+ }
+ }
+ names.sort_unstable();
+ f(entry.insert(names))
+ },
+ }
+}
+
+/// Checks if the function containing the given `HirId` is a `#[test]` function
+///
+/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
+pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
+ with_test_item_names(tcx, tcx.parent_module(id), |names| {
+ tcx.hir()
+ .parent_iter(id)
+ // Since you can nest functions we need to collect all until we leave
+ // function scope
+ .any(|(_id, node)| {
+ if let Node::Item(item) = node {
+ if let ItemKind::Fn(_, _, _) = item.kind {
+ // Note that we have sorted the item names in the visitor,
+ // so the binary_search gets the same as `contains`, but faster.
+ return names.binary_search(&item.ident.name).is_ok();
+ }
+ }
+ false
+ })
+ })
+}
+
+/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
+///
+/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
+pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
+ fn is_cfg_test(attr: &Attribute) -> bool {
+ if attr.has_name(sym::cfg)
+ && let Some(items) = attr.meta_item_list()
+ && let [item] = &*items
+ && item.has_name(sym::test)
+ {
+ true
+ } else {
+ false
+ }
+ }
+ tcx.hir()
+ .parent_iter(id)
+ .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
+ .any(is_cfg_test)
+}
+
+/// Checks whether item either has `test` attribute applied, or
+/// is a module with `test` in its name.
+///
+/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
+pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
+ is_in_test_function(tcx, item.hir_id())
+ || matches!(item.kind, ItemKind::Mod(..))
+ && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
+}
+
+/// Walks the HIR tree from the given expression, up to the node where the value produced by the
+/// expression is consumed. Calls the function for every node encountered this way until it returns
+/// `Some`.
+///
+/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
+/// produced by the expression is consumed.
+pub fn walk_to_expr_usage<'tcx, T>(
+ cx: &LateContext<'tcx>,
+ e: &Expr<'tcx>,
+ mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
+) -> Option<T> {
+ let map = cx.tcx.hir();
+ let mut iter = map.parent_iter(e.hir_id);
+ let mut child_id = e.hir_id;
+
+ while let Some((parent_id, parent)) = iter.next() {
+ if let Some(x) = f(parent, child_id) {
+ return Some(x);
+ }
+ let parent = match parent {
+ Node::Expr(e) => e,
+ Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
+ child_id = parent_id;
+ continue;
+ },
+ Node::Arm(a) if a.body.hir_id == child_id => {
+ child_id = parent_id;
+ continue;
+ },
+ _ => return None,
+ };
+ match parent.kind {
+ ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
+ ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
+ child_id = id;
+ iter = map.parent_iter(id);
+ },
+ ExprKind::Block(..) => child_id = parent_id,
+ _ => return None,
+ }
+ }
+ None
+}
+
+/// Checks whether a given span has any comment token
+/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
+pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
+ let Ok(snippet) = sm.span_to_snippet(span) else { return false };
+ return tokenize(&snippet).any(|token| {
+ matches!(
+ token.kind,
+ TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
+ )
+ });
+}
+
+/// Return all the comments a given span contains
+/// Comments are returned wrapped with their relevant delimiters
+pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
+ let snippet = sm.span_to_snippet(span).unwrap_or_default();
+ let mut comments_buf: Vec<String> = Vec::new();
+ let mut index: usize = 0;
+
+ for token in tokenize(&snippet) {
+ let token_range = index..(index + token.len as usize);
+ index += token.len as usize;
+ match token.kind {
+ TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => {
+ if let Some(comment) = snippet.get(token_range) {
+ comments_buf.push(comment.to_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`
+ pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
+
+ /// Operator-Assign traits like `LangItem::AddAssign`
+ pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
+
+ /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
+ pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
+ match kind {
+ $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
+ _ => None,
+ }
+ }
+ };
+}
+
+op_utils! {
+ Add AddAssign
+ Sub SubAssign
+ Mul MulAssign
+ Div DivAssign
+ Rem RemAssign
+ BitXor BitXorAssign
+ BitAnd BitAndAssign
+ BitOr BitOrAssign
+ Shl ShlAssign
+ Shr ShrAssign
+}
--- /dev/null
- 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))
- },
+//! Util methods for [`rustc_middle::ty`]
+
+#![allow(clippy::module_name_repetitions)]
+
+use core::ops::ControlFlow;
+use rustc_ast::ast::Mutability;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+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::{
+ type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
+ TyCtxtInferExt,
+};
+use rustc_lint::LateContext;
+use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::ty::{
+ 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::QueryNormalizeExt;
+use std::iter;
+
+use crate::{match_def_path, path_res, paths};
+
+/// 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, cx.param_env)
+}
+
+/// This checks whether a given type is known to implement Debug.
+pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ cx.tcx
+ .get_diagnostic_item(sym::Debug)
+ .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
+}
+
+/// Checks whether a type can be partially moved.
+pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ if has_drop(cx, ty) || is_copy(cx, ty) {
+ return false;
+ }
+ match ty.kind() {
+ ty::Param(_) => false,
+ ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
+ _ => true,
+ }
+}
+
+/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
+/// constructor.
+pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
+ ty.walk().any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+}
+
+/// 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<U>` 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<DefId>,
+ ) -> 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<U>`, it will register a predicate of `T: Trait<U>`, 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<Assoc=U>`, it will register a predicate of `<T as Trait>::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 `<T as Iterator>::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<Ty<'tcx>> {
+ cx.tcx
+ .get_diagnostic_item(sym::Iterator)
+ .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
+/// implements a trait marked with a diagnostic item use [`implements_trait`].
+///
+/// For a further exploitation what diagnostic items are see [diagnostic items] in
+/// rustc-dev-guide.
+///
+/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
+pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
+ _ => None,
+ }
+}
+
+/// Returns true if ty has `iter` or `iter_mut` methods
+pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
+ // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
+ // exists and has the desired signature. Unfortunately FnCtxt is not exported
+ // so we can't use its `lookup_method` method.
+ let into_iter_collections: &[Symbol] = &[
+ sym::Vec,
+ sym::Option,
+ sym::Result,
+ sym::BTreeMap,
+ sym::BTreeSet,
+ sym::VecDeque,
+ sym::LinkedList,
+ sym::BinaryHeap,
+ sym::HashSet,
+ sym::HashMap,
+ sym::PathBuf,
+ sym::Path,
+ sym::Receiver,
+ ];
+
+ let ty_to_check = match probably_ref_ty.kind() {
+ ty::Ref(_, ty_to_check, _) => *ty_to_check,
+ _ => probably_ref_ty,
+ };
+
+ let def_id = match ty_to_check.kind() {
+ ty::Array(..) => return Some(sym::array),
+ ty::Slice(..) => return Some(sym::slice),
+ ty::Adt(adt, _) => adt.did(),
+ _ => return None,
+ };
+
+ for &name in into_iter_collections {
+ if cx.tcx.is_diagnostic_item(name, def_id) {
+ return Some(cx.tcx.item_name(def_id));
+ }
+ }
+ None
+}
+
+/// Checks whether a type implements a trait.
+/// The function returns false in case the type contains an inference variable.
+///
+/// See:
+/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
+/// * [Common tools for writing lints] for an example how to use this function and other options.
+///
+/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
+pub fn implements_trait<'tcx>(
+ cx: &LateContext<'tcx>,
+ ty: Ty<'tcx>,
+ trait_id: DefId,
+ ty_params: &[GenericArg<'tcx>],
+) -> bool {
+ 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.
+pub fn implements_trait_with_env<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ trait_id: DefId,
+ ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
+) -> bool {
+ // Clippy shouldn't have infer types
+ assert!(!ty.needs_infer());
+
+ let ty = tcx.erase_regions(ty);
+ if ty.has_escaping_bound_vars() {
+ return false;
+ }
+ 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`.
+pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.ty_adt_def() {
+ Some(def) => def.has_dtor(cx.tcx),
+ None => false,
+ }
+}
+
+// Returns whether the type has #[must_use] attribute
+pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
+ ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
+ ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
+ // for the Array case we don't need to care for the len == 0 case
+ // because we don't want to lint functions returning empty arrays
+ is_must_use_ty(cx, *ty)
+ },
+ ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
+ for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
+ 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;
+ }
+ }
+ }
+ false
+ },
+ ty::Dynamic(binder, _, _) => {
+ for predicate in binder.iter() {
+ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
+ if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
+ return true;
+ }
+ }
+ }
+ false
+ },
+ _ => false,
+ }
+}
+
+// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
+// this function can be removed once the `normalize` method does not panic when normalization does
+// not succeed
+/// Checks if `Ty` is normalizable. This function is useful
+/// to avoid crashes on `layout_of`.
+pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+ is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
+}
+
+fn is_normalizable_helper<'tcx>(
+ cx: &LateContext<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ cache: &mut FxHashMap<Ty<'tcx>, bool>,
+) -> bool {
+ if let Some(&cached_result) = cache.get(&ty) {
+ return cached_result;
+ }
+ // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
+ cache.insert(ty, 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
+}
+
+/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
+/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
+/// tuples and slices of primitive type) see `is_recursively_primitive_type`
+pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
+ matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
+}
+
+/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
+/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
+pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
+ match *ty.kind() {
+ ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
+ ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
+ ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+ ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
+ _ => false,
+ }
+}
+
+/// Checks if the type is a reference equals to a diagnostic item
+pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+ match ty.kind() {
+ ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
+ _ => false,
+ },
+ _ => false,
+ }
+}
+
+/// Checks if the type is equal to a diagnostic item. To check if a type implements a
+/// trait marked with a diagnostic item use [`implements_trait`].
+///
+/// For a further exploitation what diagnostic items are see [diagnostic items] in
+/// rustc-dev-guide.
+///
+/// ---
+///
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+///
+/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
+pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
+ _ => false,
+ }
+}
+
+/// Checks if the type is equal to a lang item.
+///
+/// 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().get(lang_item) == Some(adt.did()),
+ _ => false,
+ }
+}
+
+/// Return `true` if the passed `typ` is `isize` or `usize`.
+pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
+ matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
+}
+
+/// Checks if type is struct, enum or union type with the given def path.
+///
+/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
+ match ty.kind() {
+ ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
+ _ => false,
+ }
+}
+
+/// Checks if the drop order for a type matters. Some std types implement drop solely to
+/// deallocate memory. For these types, and composites containing them, changing the drop order
+/// won't result in any observable side effects.
+pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+ if !seen.insert(ty) {
+ return false;
+ }
+ if !ty.has_significant_drop(cx.tcx, cx.param_env) {
+ false
+ }
+ // Check for std types which implement drop, but only for memory allocation.
+ else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
+ || matches!(
+ get_type_diagnostic_name(cx, ty),
+ Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
+ )
+ || match_type(cx, ty, &paths::WEAK_RC)
+ || match_type(cx, ty, &paths::WEAK_ARC)
+ {
+ // Check all of the generic arguments.
+ if let ty::Adt(_, subs) = ty.kind() {
+ subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
+ } else {
+ true
+ }
+ } else if !cx
+ .tcx
+ .lang_items()
+ .drop_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // This type doesn't implement drop, so no side effects here.
+ // Check if any component type has any.
+ match ty.kind() {
+ ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
+ ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
+ ty::Adt(adt, subs) => adt
+ .all_fields()
+ .map(|f| f.ty(cx.tcx, subs))
+ .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
+ _ => true,
+ }
+ } else {
+ true
+ }
+ }
+
+ needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+}
+
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
+ fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
+ if let ty::Ref(_, ty, _) = ty.kind() {
+ peel(*ty, count + 1)
+ } else {
+ (ty, count)
+ }
+ }
+ peel(ty, 0)
+}
+
+/// Peels off all references on the type. Returns the underlying type, the number of references
+/// removed, and whether the pointer is ultimately mutable or not.
+pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
+ fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
+ match ty.kind() {
+ ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
+ ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
+ _ => (ty, count, mutability),
+ }
+ }
+ f(ty, 0, Mutability::Mut)
+}
+
+/// Returns `true` if the given type is an `unsafe` function.
+pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ match ty.kind() {
+ ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
+ _ => false,
+ }
+}
+
+/// 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::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
+ _ => ty,
+ }
+}
+
+/// Returns the base type for references and raw pointers, and count reference
+/// depth.
+pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
+ fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
+ match ty.kind() {
+ ty::Ref(_, ty, _) => inner(*ty, depth + 1),
+ _ => (ty, depth),
+ }
+ }
+ inner(ty, 0)
+}
+
+/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
+/// otherwise returns `false`
+pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+ match (&a.kind(), &b.kind()) {
+ (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
+ if did_a != did_b {
+ return false;
+ }
+
+ substs_a
+ .iter()
+ .zip(substs_b.iter())
+ .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
+ (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
+ (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
+ same_type_and_consts(type_a, type_b)
+ },
+ _ => true,
+ })
+ },
+ _ => a == b,
+ }
+}
+
+/// Checks if a given type looks safe to be uninitialized.
+pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+ match *ty.kind() {
+ ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
+ ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
+ ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
+ _ => false,
+ }
+}
+
+/// Gets an iterator over all predicates which apply to the given item.
+pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
+ let mut next_id = Some(id);
+ iter::from_fn(move || {
+ next_id.take().map(|id| {
+ let preds = tcx.predicates_of(id);
+ next_id = preds.parent;
+ preds.predicates.iter()
+ })
+ })
+ .flatten()
+}
+
+/// A signature for a function like type.
+#[derive(Clone, Copy)]
+pub enum ExprFnSig<'tcx> {
+ Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
+ Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
+ Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
+}
+impl<'tcx> ExprFnSig<'tcx> {
+ /// Gets the argument type at the given offset. This will return `None` when the index is out of
+ /// bounds only for variadic functions, otherwise this will panic.
+ pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
+ match self {
+ Self::Sig(sig, _) => {
+ if sig.c_variadic() {
+ sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
+ } else {
+ Some(sig.input(i))
+ }
+ },
+ Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
+ Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
+ }
+ }
+
+ /// Gets the argument type at the given offset. For closures this will also get the type as
+ /// written. This will return `None` when the index is out of bounds only for variadic
+ /// functions, otherwise this will panic.
+ pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
+ match self {
+ Self::Sig(sig, _) => {
+ if sig.c_variadic() {
+ sig.inputs()
+ .map_bound(|inputs| inputs.get(i).copied())
+ .transpose()
+ .map(|arg| (None, arg))
+ } else {
+ Some((None, sig.input(i)))
+ }
+ },
+ Self::Closure(decl, sig) => Some((
+ decl.and_then(|decl| decl.inputs.get(i)),
+ sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
+ )),
+ Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
+ }
+ }
+
+ /// Gets the result type, if one could be found. Note that the result type of a trait may not be
+ /// specified.
+ pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
+ match self {
+ Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
+ Self::Trait(_, output, _) => output,
+ }
+ }
+
+ pub fn predicates_id(&self) -> Option<DefId> {
+ if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
+ id
+ } else {
+ None
+ }
+ }
+}
+
+/// If the expression is function like, get the signature for it.
+pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
+ if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
+ Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst_identity(), Some(id)))
+ } else {
+ ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
+ }
+}
+
+/// If the type is function like, get the signature for it.
+pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
+ if ty.is_box() {
+ return ty_sig(cx, ty.boxed_ty());
+ }
+ match *ty.kind() {
+ ty::Closure(id, subs) => {
+ let decl = id
+ .as_local()
+ .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
+ Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
+ },
+ ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs), Some(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();
+ match bounds.principal() {
+ Some(bound)
+ if Some(bound.def_id()) == lang_items.fn_trait()
+ || Some(bound.def_id()) == lang_items.fn_once_trait()
+ || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
+ {
+ let output = bounds
+ .projection_bounds()
+ .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
+ .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
+ Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None))
+ },
+ _ => None,
+ }
+ },
+ 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)),
+ },
+ ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
+ _ => None,
+ }
+}
+
+fn sig_from_bounds<'tcx>(
+ cx: &LateContext<'tcx>,
+ ty: Ty<'tcx>,
+ predicates: &'tcx [Predicate<'tcx>],
+ predicates_id: Option<DefId>,
+) -> Option<ExprFnSig<'tcx>> {
+ let mut inputs = None;
+ let mut output = None;
+ let lang_items = cx.tcx.lang_items();
+
+ for pred in predicates {
+ 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()))
+ && p.self_ty() == ty =>
+ {
+ let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
+ if inputs.map_or(false, |inputs| i != inputs) {
+ // Multiple different fn trait impls. Is this even allowed?
+ return None;
+ }
+ inputs = Some(i);
+ },
+ 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?
+ return None;
+ }
+ output = Some(pred.kind().rebind(p.term.ty().unwrap()));
+ },
+ _ => (),
+ }
+ }
+
+ inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
+}
+
+fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
+ let mut inputs = None;
+ let mut output = None;
+ let lang_items = cx.tcx.lang_items();
+
+ for (pred, _) in cx
+ .tcx
+ .bound_explicit_item_bounds(ty.def_id)
+ .subst_iter_copied(cx.tcx, ty.substs)
+ {
+ 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.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?
+ return None;
+ }
+ inputs = Some(i);
+ },
+ 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 = pred.kind().rebind(p.term.ty()).transpose();
+ },
+ _ => (),
+ }
+ }
+
+ inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
+}
+
+#[derive(Clone, Copy)]
+pub enum EnumValue {
+ Unsigned(u128),
+ Signed(i128),
+}
+impl core::ops::Add<u32> for EnumValue {
+ type Output = Self;
+ fn add(self, n: u32) -> Self::Output {
+ match self {
+ Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
+ Self::Signed(x) => Self::Signed(x + i128::from(n)),
+ }
+ }
+}
+
+/// 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<EnumValue> {
+ if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
+ match tcx.type_of(id).kind() {
+ ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
+ 1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
+ 2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
+ 4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
+ 8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
+ 16 => value.assert_bits(Size::from_bytes(16)) as i128,
+ _ => return None,
+ })),
+ ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
+ 1 => value.assert_bits(Size::from_bytes(1)),
+ 2 => value.assert_bits(Size::from_bytes(2)),
+ 4 => value.assert_bits(Size::from_bytes(4)),
+ 8 => value.assert_bits(Size::from_bytes(8)),
+ 16 => value.assert_bits(Size::from_bytes(16)),
+ _ => return None,
+ })),
+ _ => None,
+ }
+ } else {
+ None
+ }
+}
+
+/// Gets the value of the given variant.
+pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
+ let variant = &adt.variant(i);
+ match variant.discr {
+ VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
+ VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
+ VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
+ VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
+ },
+ }
+}
+
+/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
+/// platform specific `libc::<platform>::c_void` types in libc.
+pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+ if let ty::Adt(adt, _) = ty.kind()
+ && let &[krate, .., name] = &*cx.get_def_path(adt.did())
+ && let sym::libc | sym::core | sym::std = krate
+ && name.as_str() == "c_void"
+ {
+ true
+ } else {
+ false
+ }
+}
+
+pub fn for_each_top_level_late_bound_region<B>(
+ ty: Ty<'_>,
+ f: impl FnMut(BoundRegion) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+ struct V<F> {
+ index: u32,
+ f: F,
+ }
+ impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<'tcx> for V<F> {
+ type BreakTy = B;
+ fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
+ (self.f)(bound)
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+ fn visit_binder<T: TypeVisitable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
+ self.index += 1;
+ let res = t.super_visit_with(self);
+ self.index -= 1;
+ res
+ }
+ }
+ 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<GenericArg<'tcx>>) -> Vec<Self> {
+ 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::<Vec<_>>();
+ 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::<Vec<_>>();
+ 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 {
+ Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
+ Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
+ Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
+ Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
+ let var_id = cx.tcx.parent(id);
+ Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
+ },
+ Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
+ _ => None,
+ }
+}
+
+/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
+pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
+ let ty::Param(ty) = *ty.kind() else {
+ return false;
+ };
+ let lang = tcx.lang_items();
+ let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id))
+ = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
+ else {
+ return false;
+ };
+ predicates
+ .iter()
+ .try_fold(false, |found, p| {
+ 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
+ {
+ // This should use `super_traits_of`, but that's a private function.
+ if p.trait_ref.def_id == fn_once_id {
+ return Some(true);
+ } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
+ return None;
+ }
+ }
+ Some(found)
+ })
+ .unwrap_or(false)
+}
+
+/// Comes up with an "at least" guesstimate for the type's size, not taking into
+/// account the layout of type parameters.
+pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
+ use rustc_middle::ty::layout::LayoutOf;
+ if !is_normalizable(cx, cx.param_env, ty) {
+ return 0;
+ }
+ match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
+ (Ok(size), _) => size,
+ (Err(_), ty::Tuple(list)) => list.as_substs().types().map(|t| approx_ty_size(cx, t)).sum(),
+ (Err(_), ty::Array(t, n)) => {
+ n.try_eval_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
+ },
+ (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
+ .variants()
+ .iter()
+ .map(|v| {
+ v.fields
+ .iter()
+ .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+ .sum::<u64>()
+ })
+ .sum(),
+ (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
+ .variants()
+ .iter()
+ .map(|v| {
+ v.fields
+ .iter()
+ .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+ .sum::<u64>()
+ })
+ .max()
+ .unwrap_or_default(),
+ (Err(_), ty::Adt(def, subst)) if def.is_union() => def
+ .variants()
+ .iter()
+ .map(|v| {
+ v.fields
+ .iter()
+ .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
+ .max()
+ .unwrap_or_default()
+ })
+ .max()
+ .unwrap_or_default(),
+ (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<Item = impl Into<GenericArg<'tcx>>>,
+) -> Option<AliasTy<'tcx>> {
+ fn helper<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ container_id: DefId,
+ assoc_ty: Symbol,
+ substs: SubstsRef<'tcx>,
+ ) -> Option<AliasTy<'tcx>> {
+ 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::<Vec<_>>(),
+ );
+
+ 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::<Vec<_>>()
+ );
+ }
+ }
+
+ 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<Item = impl Into<GenericArg<'tcx>>>,
+) -> Option<Ty<'tcx>> {
+ fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
+ #[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)?)
+}
--- /dev/null
- version = "0.1.68"
+[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 = []
--- /dev/null
- channel = "nightly-2023-01-12"
+[toolchain]
++channel = "nightly-2023-01-27"
+components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
--- /dev/null
- You can use tool lints to allow or deny lints from your code, eg.:
+#![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 std::env;
+use std::path::PathBuf;
+use std::process::{self, Command};
+
+const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
+
+Usage:
+ cargo clippy [options] [--] [<opts>...]
+
+Common options:
+ --no-deps Run Clippy only on the given crate, without linting the dependencies
+ --fix Automatically apply lint suggestions. This flag implies `--no-deps`
+ -h, --help Print this message
+ -V, --version Print version info and exit
+ --explain LINT Print the documentation for a given lint
+
+Other options are the same as `cargo check`.
+
+To allow or deny a lint from the command line you can use `cargo clippy --`
+with:
+
+ -W --warn OPT Set lint warnings
+ -A --allow OPT Set lint allowed
+ -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, e.g.:
+
+ #[allow(clippy::needless_lifetimes)]
+"#;
+
+fn show_help() {
+ println!("{CARGO_CLIPPY_HELP}");
+}
+
+fn show_version() {
+ let version_info = rustc_tools_util::get_version_info!();
+ println!("{version_info}");
+}
+
+pub fn main() {
+ // Check for version and help flags even when invoked as 'cargo-clippy'
+ if env::args().any(|a| a == "--help" || a == "-h") {
+ show_help();
+ return;
+ }
+
+ if env::args().any(|a| a == "--version" || a == "-V") {
+ show_version();
+ return;
+ }
+
+ if let Some(pos) = env::args().position(|a| a == "--explain") {
+ if let Some(mut lint) = env::args().nth(pos + 1) {
+ lint.make_ascii_lowercase();
+ clippy_lints::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_"));
+ } else {
+ show_help();
+ }
+ return;
+ }
+
+ if let Err(code) = process(env::args().skip(2)) {
+ process::exit(code);
+ }
+}
+
+struct ClippyCmd {
+ cargo_subcommand: &'static str,
+ args: Vec<String>,
+ clippy_args: Vec<String>,
+}
+
+impl ClippyCmd {
+ fn new<I>(mut old_args: I) -> Self
+ where
+ I: Iterator<Item = String>,
+ {
+ let mut cargo_subcommand = "check";
+ let mut args = vec![];
+ let mut clippy_args: Vec<String> = vec![];
+
+ for arg in old_args.by_ref() {
+ match arg.as_str() {
+ "--fix" => {
+ cargo_subcommand = "fix";
+ continue;
+ },
+ "--no-deps" => {
+ clippy_args.push("--no-deps".into());
+ continue;
+ },
+ "--" => break,
+ _ => {},
+ }
+
+ args.push(arg);
+ }
+
+ clippy_args.append(&mut (old_args.collect()));
+ if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
+ clippy_args.push("--no-deps".into());
+ }
+
+ Self {
+ cargo_subcommand,
+ args,
+ clippy_args,
+ }
+ }
+
+ fn path() -> PathBuf {
+ let mut path = env::current_exe()
+ .expect("current executable path invalid")
+ .with_file_name("clippy-driver");
+
+ if cfg!(windows) {
+ path.set_extension("exe");
+ }
+
+ path
+ }
+
+ fn into_std_cmd(self) -> Command {
+ let mut cmd = Command::new("cargo");
+ let clippy_args: String = self
+ .clippy_args
+ .iter()
+ .map(|arg| format!("{arg}__CLIPPY_HACKERY__"))
+ .collect();
+
+ // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages.
+ let terminal_width = termize::dimensions().map_or(0, |(w, _)| w);
+
+ cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
+ .env("CLIPPY_ARGS", clippy_args)
+ .env("CLIPPY_TERMINAL_WIDTH", terminal_width.to_string())
+ .arg(self.cargo_subcommand)
+ .args(&self.args);
+
+ cmd
+ }
+}
+
+fn process<I>(old_args: I) -> Result<(), i32>
+where
+ I: Iterator<Item = String>,
+{
+ let cmd = ClippyCmd::new(old_args);
+
+ let mut cmd = cmd.into_std_cmd();
+
+ let exit_status = cmd
+ .spawn()
+ .expect("could not run cargo")
+ .wait()
+ .expect("failed to wait for cargo?");
+
+ if exit_status.success() {
+ Ok(())
+ } else {
+ Err(exit_status.code().unwrap_or(-1))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::ClippyCmd;
+
+ #[test]
+ fn fix() {
+ let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert_eq!("fix", cmd.cargo_subcommand);
+ assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
+ }
+
+ #[test]
+ fn fix_implies_no_deps() {
+ let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
+ }
+
+ #[test]
+ fn no_deps_not_duplicated_with_fix() {
+ let args = "cargo clippy --fix -- --no-deps"
+ .split_whitespace()
+ .map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
+ }
+
+ #[test]
+ fn check() {
+ let args = "cargo clippy".split_whitespace().map(ToString::to_string);
+ let cmd = ClippyCmd::new(args);
+ assert_eq!("check", cmd.cargo_subcommand);
+ }
+}
--- /dev/null
- 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: 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
+
--- /dev/null
--- /dev/null
++// 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<bool> 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<bool> 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<bool> 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);
++}
--- /dev/null
- #[derive(Debug)]
++// 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)]
++#[derive(Debug, Clone, Copy)]
+enum ImplNotTraitWithoutBool {
+ VariantX(bool),
+ VariantY(u32),
+}
+
+impl PartialEq<bool> 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<bool> 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<bool> 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_eq!("a".is_empty(), false);
+ assert_eq!("".is_empty(), true);
+ assert_eq!(true, "".is_empty());
+ assert_eq!(a!(), b!());
+ assert_eq!(a!(), "".is_empty());
+ assert_eq!("".is_empty(), b!());
+ assert_eq!(a, true);
+ assert_eq!(b, true);
+
+ assert_ne!("a".len(), 1);
+ assert_ne!("a".is_empty(), false);
+ assert_ne!("".is_empty(), true);
+ assert_ne!(true, "".is_empty());
+ assert_ne!(a!(), b!());
+ assert_ne!(a!(), "".is_empty());
+ assert_ne!("".is_empty(), b!());
+ assert_ne!(a, true);
+ assert_ne!(b, true);
+
+ debug_assert_eq!("a".len(), 1);
+ debug_assert_eq!("a".is_empty(), false);
+ debug_assert_eq!("".is_empty(), true);
+ debug_assert_eq!(true, "".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_eq!(b, true);
+
+ debug_assert_ne!("a".len(), 1);
+ debug_assert_ne!("a".is_empty(), false);
+ debug_assert_ne!("".is_empty(), true);
+ debug_assert_ne!(true, "".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_ne!(b, true);
+
+ // assert with error messages
+ assert_eq!("a".len(), 1, "tadam {}", 1);
+ assert_eq!("a".len(), 1, "tadam {}", true);
+ assert_eq!("a".is_empty(), false, "tadam {}", 1);
+ assert_eq!("a".is_empty(), false, "tadam {}", true);
+ assert_eq!(false, "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_eq!("a".is_empty(), false, "tadam {}", 1);
+ 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);
+}
--- /dev/null
- --> $DIR/bool_assert_comparison.rs:69:5
+error: used `assert_eq!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:89:5
+ |
+LL | assert_eq!("a".is_empty(), false);
- --> $DIR/bool_assert_comparison.rs:70:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:90:5
+ |
+LL | assert_eq!("".is_empty(), true);
- --> $DIR/bool_assert_comparison.rs:71:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: replace it with `assert!(..)`
++ |
++LL - assert_eq!("".is_empty(), true);
++LL + assert!("".is_empty());
++ |
+
+error: used `assert_eq!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:91:5
+ |
+LL | assert_eq!(true, "".is_empty());
- --> $DIR/bool_assert_comparison.rs:76:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: replace it with `assert!(..)`
++ |
++LL - assert_eq!(true, "".is_empty());
++LL + assert!("".is_empty());
++ |
+
+error: used `assert_eq!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:96:5
+ |
+LL | assert_eq!(b, true);
- --> $DIR/bool_assert_comparison.rs:79:5
++ | ^^^^^^^^^^^^^^^^^^^
++ |
++help: replace it with `assert!(..)`
++ |
++LL - assert_eq!(b, true);
++LL + assert!(b);
++ |
+
+error: used `assert_ne!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:99:5
+ |
+LL | assert_ne!("a".is_empty(), false);
- --> $DIR/bool_assert_comparison.rs:80:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:100:5
+ |
+LL | assert_ne!("".is_empty(), true);
- --> $DIR/bool_assert_comparison.rs:81:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: replace it with `assert!(..)`
++ |
++LL - assert_ne!("".is_empty(), true);
++LL + assert!("".is_empty());
++ |
+
+error: used `assert_ne!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:101:5
+ |
+LL | assert_ne!(true, "".is_empty());
- --> $DIR/bool_assert_comparison.rs:86:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: replace it with `assert!(..)`
++ |
++LL - assert_ne!(true, "".is_empty());
++LL + assert!("".is_empty());
++ |
+
+error: used `assert_ne!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:106:5
+ |
+LL | assert_ne!(b, true);
- --> $DIR/bool_assert_comparison.rs:89:5
++ | ^^^^^^^^^^^^^^^^^^^
++ |
++help: replace it with `assert!(..)`
++ |
++LL - assert_ne!(b, true);
++LL + assert!(b);
++ |
+
+error: used `debug_assert_eq!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:109:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false);
- --> $DIR/bool_assert_comparison.rs:90:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:110:5
+ |
+LL | debug_assert_eq!("".is_empty(), true);
- --> $DIR/bool_assert_comparison.rs:91:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:111:5
+ |
+LL | debug_assert_eq!(true, "".is_empty());
- --> $DIR/bool_assert_comparison.rs:96:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:116:5
+ |
+LL | debug_assert_eq!(b, true);
- --> $DIR/bool_assert_comparison.rs:99:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:119:5
+ |
+LL | debug_assert_ne!("a".is_empty(), false);
- --> $DIR/bool_assert_comparison.rs:100:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:120:5
+ |
+LL | debug_assert_ne!("".is_empty(), true);
- --> $DIR/bool_assert_comparison.rs:101:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:121:5
+ |
+LL | debug_assert_ne!(true, "".is_empty());
- --> $DIR/bool_assert_comparison.rs:106:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:126:5
+ |
+LL | debug_assert_ne!(b, true);
- --> $DIR/bool_assert_comparison.rs:111:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++help: replace it with `debug_assert!(..)`
++ |
++LL - debug_assert_ne!(b, true);
++LL + debug_assert!(b);
++ |
+
+error: used `assert_eq!` with a literal bool
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:131:5
+ |
+LL | assert_eq!("a".is_empty(), false, "tadam {}", 1);
- --> $DIR/bool_assert_comparison.rs:112:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:132:5
+ |
+LL | assert_eq!("a".is_empty(), false, "tadam {}", true);
- --> $DIR/bool_assert_comparison.rs:113:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:133:5
+ |
+LL | assert_eq!(false, "a".is_empty(), "tadam {}", true);
- --> $DIR/bool_assert_comparison.rs:118:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:138:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
- --> $DIR/bool_assert_comparison.rs:119:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:139:5
+ |
+LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
- --> $DIR/bool_assert_comparison.rs:120:5
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
++ --> $DIR/bool_assert_comparison.rs:140:5
+ |
+LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
- error: aborting due to 22 previous errors
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++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 25 previous errors
+
--- /dev/null
+#![feature(repr128)]
+#![allow(incomplete_features)]
+#![warn(
+ clippy::cast_precision_loss,
+ clippy::cast_possible_truncation,
+ clippy::cast_sign_loss,
+ clippy::cast_possible_wrap
+)]
+#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+
+fn main() {
+ // Test clippy::cast_precision_loss
+ let x0 = 1i32;
+ x0 as f32;
+ let x1 = 1i64;
+ x1 as f32;
+ x1 as f64;
+ let x2 = 1u32;
+ x2 as f32;
+ let x3 = 1u64;
+ x3 as f32;
+ x3 as f64;
+ // Test clippy::cast_possible_truncation
+ 1f32 as i32;
+ 1f32 as u32;
+ 1f64 as f32;
+ 1i32 as i8;
+ 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;
+ 1u32 as i32;
+ 1u64 as i64;
+ 1usize as isize;
+ // Test clippy::cast_sign_loss
+ 1i32 as u32;
+ -1i32 as u32;
+ 1isize as usize;
+ -1isize as usize;
+ 0i8 as u8;
+ i8::MAX as u8;
+ i16::MAX as u16;
+ i32::MAX as u32;
+ i64::MAX as u64;
+ i128::MAX as u128;
+
+ (-1i8).abs() as u8;
+ (-1i16).abs() as u16;
+ (-1i32).abs() as u32;
+ (-1i64).abs() as u64;
+ (-1isize).abs() as usize;
+
+ (-1i8).checked_abs().unwrap() as u8;
+ (-1i16).checked_abs().unwrap() as u16;
+ (-1i32).checked_abs().unwrap() as u32;
+ (-1i64).checked_abs().unwrap() as u64;
+ (-1isize).checked_abs().unwrap() as usize;
+
+ (-1i8).rem_euclid(1i8) as u8;
+ (-1i8).rem_euclid(1i8) as u16;
+ (-1i16).rem_euclid(1i16) as u16;
+ (-1i16).rem_euclid(1i16) as u32;
+ (-1i32).rem_euclid(1i32) as u32;
+ (-1i32).rem_euclid(1i32) as u64;
+ (-1i64).rem_euclid(1i64) as u64;
+ (-1i64).rem_euclid(1i64) as u128;
+ (-1isize).rem_euclid(1isize) as usize;
+ (1i8).rem_euclid(-1i8) as u8;
+ (1i8).rem_euclid(-1i8) as u16;
+ (1i16).rem_euclid(-1i16) as u16;
+ (1i16).rem_euclid(-1i16) as u32;
+ (1i32).rem_euclid(-1i32) as u32;
+ (1i32).rem_euclid(-1i32) as u64;
+ (1i64).rem_euclid(-1i64) as u64;
+ (1i64).rem_euclid(-1i64) as u128;
+ (1isize).rem_euclid(-1isize) as usize;
+
+ (-1i8).checked_rem_euclid(1i8).unwrap() as u8;
+ (-1i8).checked_rem_euclid(1i8).unwrap() as u16;
+ (-1i16).checked_rem_euclid(1i16).unwrap() as u16;
+ (-1i16).checked_rem_euclid(1i16).unwrap() as u32;
+ (-1i32).checked_rem_euclid(1i32).unwrap() as u32;
+ (-1i32).checked_rem_euclid(1i32).unwrap() as u64;
+ (-1i64).checked_rem_euclid(1i64).unwrap() as u64;
+ (-1i64).checked_rem_euclid(1i64).unwrap() as u128;
+ (-1isize).checked_rem_euclid(1isize).unwrap() as usize;
+ (1i8).checked_rem_euclid(-1i8).unwrap() as u8;
+ (1i8).checked_rem_euclid(-1i8).unwrap() as u16;
+ (1i16).checked_rem_euclid(-1i16).unwrap() as u16;
+ (1i16).checked_rem_euclid(-1i16).unwrap() as u32;
+ (1i32).checked_rem_euclid(-1i32).unwrap() as u32;
+ (1i32).checked_rem_euclid(-1i32).unwrap() as u64;
+ (1i64).checked_rem_euclid(-1i64).unwrap() as u64;
+ (1i64).checked_rem_euclid(-1i64).unwrap() as u128;
+ (1isize).checked_rem_euclid(-1isize).unwrap() as usize;
+
+ // no lint for `cast_possible_truncation`
+ // with `signum` method call (see issue #5395)
+ let x: i64 = 5;
+ let _ = x.signum() as i32;
+
+ let s = x.signum();
+ let _ = s as i32;
+
+ // Test for signed min
+ (-99999999999i64).min(1) as i8; // should be linted because signed
+
+ // Test for various operations that remove enough bits for the result to fit
+ (999999u64 & 1) as u8;
+ (999999u64 % 15) as u8;
+ (999999u64 / 0x1_0000_0000_0000) as u16;
+ ({ 999999u64 >> 56 }) as u8;
+ ({
+ let x = 999999u64;
+ x.min(1)
+ }) as u8;
+ 999999u64.clamp(0, 255) as u8;
+ 999999u64.clamp(0, 256) as u8; // should still be linted
+
+ #[derive(Clone, Copy)]
+ enum E1 {
+ A,
+ B,
+ C,
+ }
+ impl E1 {
+ fn test(self) {
+ let _ = self as u8; // Don't lint. `0..=2` fits in u8
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E2 {
+ A = 255,
+ B,
+ }
+ impl E2 {
+ fn test(self) {
+ let _ = self as u8;
+ let _ = Self::B as u8;
+ let _ = self as i16; // Don't lint. `255..=256` fits in i16
+ let _ = Self::A as u8; // Don't lint.
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E3 {
+ A = -1,
+ B,
+ C = 50,
+ }
+ impl E3 {
+ fn test(self) {
+ let _ = self as i8; // Don't lint. `-1..=50` fits in i8
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E4 {
+ A = -128,
+ B,
+ }
+ impl E4 {
+ fn test(self) {
+ let _ = self as i8; // Don't lint. `-128..=-127` fits in i8
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ enum E5 {
+ A = -129,
+ B = 127,
+ }
+ impl E5 {
+ fn test(self) {
+ let _ = self as i8;
+ let _ = Self::A as i8;
+ let _ = self as i16; // Don't lint. `-129..=127` fits in i16
+ let _ = Self::B as u8; // Don't lint.
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(u32)]
+ enum E6 {
+ A = u16::MAX as u32,
+ B,
+ }
+ impl E6 {
+ fn test(self) {
+ let _ = self as i16;
+ let _ = Self::A as u16; // Don't lint. `2^16-1` fits in u16
+ let _ = self as u32; // Don't lint. `2^16-1..=2^16` fits in u32
+ let _ = Self::A as u16; // Don't lint.
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(u64)]
+ enum E7 {
+ A = u32::MAX as u64,
+ B,
+ }
+ impl E7 {
+ fn test(self) {
+ let _ = self as usize;
+ let _ = Self::A as usize; // Don't lint.
+ let _ = self as u64; // Don't lint. `2^32-1..=2^32` fits in u64
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(i128)]
+ enum E8 {
+ A = i128::MIN,
+ B,
+ C = 0,
+ D = i128::MAX,
+ }
+ impl E8 {
+ fn test(self) {
+ let _ = self as i128; // Don't lint. `-(2^127)..=2^127-1` fits it i128
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(u128)]
+ enum E9 {
+ A,
+ B = u128::MAX,
+ }
+ impl E9 {
+ fn test(self) {
+ let _ = Self::A as u8; // Don't lint.
+ let _ = self as u128; // Don't lint. `0..=2^128-1` fits in u128
+ }
+ }
+
+ #[derive(Clone, Copy)]
+ #[repr(usize)]
+ enum E10 {
+ A,
+ B = u32::MAX as usize,
+ }
+ impl E10 {
+ fn test(self) {
+ let _ = self as u16;
+ let _ = Self::B as u32; // Don't lint.
+ let _ = self as u64; // Don't lint.
+ }
+ }
+}
+
+fn avoid_subtract_overflow(q: u32) {
+ let c = (q >> 16) as u8;
+ c as usize;
+
+ let c = (q / 1000) as u8;
+ c as usize;
+}
--- /dev/null
- --> $DIR/cast.rs:32:5
+error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:14:5
+ |
+LL | x0 as f32;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
+
+error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:16:5
+ |
+LL | x1 as f32;
+ | ^^^^^^^^^
+
+error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast.rs:17:5
+ |
+LL | x1 as f64;
+ | ^^^^^^^^^
+
+error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:19:5
+ |
+LL | x2 as f32;
+ | ^^^^^^^^^
+
+error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast.rs:21:5
+ |
+LL | x3 as f32;
+ | ^^^^^^^^^
+
+error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast.rs:22:5
+ |
+LL | x3 as f64;
+ | ^^^^^^^^^
+
+error: casting `f32` to `i32` may truncate the value
+ --> $DIR/cast.rs:24:5
+ |
+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
+ |
+LL | 1f32 as u32;
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
+
+error: casting `f64` to `f32` may truncate the value
+ --> $DIR/cast.rs:26:5
+ |
+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
+ |
+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:33:5
++ --> $DIR/cast.rs:33:5
+ |
+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:34:5
++ --> $DIR/cast.rs:34:5
+ |
+LL | 1u16 as i16;
+ | ^^^^^^^^^^^
+
+error: casting `u32` to `i32` may wrap around the value
- --> $DIR/cast.rs:35:5
++ --> $DIR/cast.rs:35:5
+ |
+LL | 1u32 as i32;
+ | ^^^^^^^^^^^
+
+error: casting `u64` to `i64` may wrap around the value
- --> $DIR/cast.rs:36:5
++ --> $DIR/cast.rs:36:5
+ |
+LL | 1u64 as i64;
+ | ^^^^^^^^^^^
+
+error: casting `usize` to `isize` may wrap around the value
- --> $DIR/cast.rs:39: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:41: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:108:5
++ --> $DIR/cast.rs:42:5
+ |
+LL | -1isize as usize;
+ | ^^^^^^^^^^^^^^^^
+
+error: casting `i64` to `i8` may truncate the value
- --> $DIR/cast.rs:120: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:141:21
++ --> $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:142: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:178:21
++ --> $DIR/cast.rs:143:21
+ |
+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:179: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:193: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:208: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:249: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:257:13
++ --> $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:260: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
- error: aborting due to 33 previous errors
++ --> $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 36 previous errors
+
--- /dev/null
+error: casting `isize` to `i8` may truncate the value
+ --> $DIR/cast_size.rs:12:5
+ |
+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
+ |
+LL | x0 as f64;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
+
+error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast_size.rs:16:5
+ |
+LL | x1 as f64;
+ | ^^^^^^^^^
+
+error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size.rs:17:5
+ |
+LL | x0 as f32;
+ | ^^^^^^^^^
+
+error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size.rs:18:5
+ |
+LL | x1 as f32;
+ | ^^^^^^^^^
+
+error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers
+ --> $DIR/cast_size.rs:19:5
+ |
+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
+ |
+LL | 1usize as i32;
+ | ^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
+
+error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:24:5
+ |
+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
+ |
+LL | 1u64 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers
+ --> $DIR/cast_size.rs:27:5
+ |
+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
+ |
+LL | 1u32 as isize;
+ | ^^^^^^^^^^^^^
+
+error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
+ --> $DIR/cast_size.rs:33:5
+ |
+LL | 999_999_999 as f32;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
+ --> $DIR/cast_size.rs:34:5
+ |
+LL | 9_999_999_999_999_999usize as f64;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 18 previous errors
+
--- /dev/null
- --> $DIR/module_name_repetitions.rs:8:5
+error: item name starts with its containing module's name
- | ^^^^^^^^^^^^^^^^^^^
++ --> $DIR/module_name_repetitions.rs:8:12
+ |
+LL | pub fn foo_bar() {}
- --> $DIR/module_name_repetitions.rs:9:5
++ | ^^^^^^^
+ |
+ = 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:12
+ |
+LL | pub fn bar_foo() {}
- --> $DIR/module_name_repetitions.rs:10:5
++ | ^^^^^^^
+
+error: item name starts with its containing module's name
- | ^^^^^^^^^^^^^^^^^^^
++ --> $DIR/module_name_repetitions.rs:10:16
+ |
+LL | pub struct FooCake;
- --> $DIR/module_name_repetitions.rs:11:5
++ | ^^^^^^^
+
+error: item name ends with its containing module's name
- | ^^^^^^^^^^^^^^^^^^^
++ --> $DIR/module_name_repetitions.rs:11:14
+ |
+LL | pub enum CakeFoo {}
- --> $DIR/module_name_repetitions.rs:12:5
++ | ^^^^^^^
+
+error: item name starts with its containing module's name
- | ^^^^^^^^^^^^^^^^^^^
++ --> $DIR/module_name_repetitions.rs:12:16
+ |
+LL | pub struct Foo7Bar;
++ | ^^^^^^^
+
+error: aborting due to 5 previous errors
+
--- /dev/null
--- /dev/null
++#![allow(unused)]
++#![allow(deref_nullptr)]
++#![allow(clippy::unnecessary_operation)]
++#![allow(clippy::drop_copy)]
++#![warn(clippy::multiple_unsafe_ops_per_block)]
++
++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::<u32>()) }
++}
++
++// no lint
++unsafe fn read_char_good(ptr: *const u8) -> char {
++ let int_value = unsafe { *ptr.cast::<u32>() };
++ unsafe { core::char::from_u32_unchecked(int_value) }
++}
++
++fn main() {}
--- /dev/null
--- /dev/null
++error: this `unsafe` block contains 2 unsafe operations, expected only one
++ --> $DIR/multiple_unsafe_ops_per_block.rs:32: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:33:9
++ |
++LL | STATIC += 1;
++ | ^^^^^^^^^^^
++note: unsafe function call occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:34: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:41:5
++ |
++LL | / unsafe {
++LL | | drop(u.u);
++LL | | *raw_ptr();
++LL | | }
++ | |_____^
++ |
++note: union field access occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:42:14
++ |
++LL | drop(u.u);
++ | ^^^
++note: raw pointer dereference occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:43:9
++ |
++LL | *raw_ptr();
++ | ^^^^^^^^^^
++
++error: this `unsafe` block contains 3 unsafe operations, expected only one
++ --> $DIR/multiple_unsafe_ops_per_block.rs:48: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:49:9
++ |
++LL | asm!("nop");
++ | ^^^^^^^^^^^
++note: unsafe method call occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:50:9
++ |
++LL | sample.not_very_safe();
++ | ^^^^^^^^^^^^^^^^^^^^^^
++note: modification of a mutable static occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:51:9
++ |
++LL | STATIC = 0;
++ | ^^^^^^^^^^
++
++error: this `unsafe` block contains 6 unsafe operations, expected only one
++ --> $DIR/multiple_unsafe_ops_per_block.rs:57: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:58:14
++ |
++LL | drop(u.u);
++ | ^^^
++note: access of a mutable static occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:59:14
++ |
++LL | drop(STATIC);
++ | ^^^^^^
++note: unsafe method call occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:60:9
++ |
++LL | sample.not_very_safe();
++ | ^^^^^^^^^^^^^^^^^^^^^^
++note: unsafe function call occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:61:9
++ |
++LL | not_very_safe();
++ | ^^^^^^^^^^^^^^^
++note: raw pointer dereference occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:62:9
++ |
++LL | *raw_ptr();
++ | ^^^^^^^^^^
++note: inline assembly used here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:63:9
++ |
++LL | asm!("nop");
++ | ^^^^^^^^^^^
++
++error: this `unsafe` block contains 2 unsafe operations, expected only one
++ --> $DIR/multiple_unsafe_ops_per_block.rs:101:5
++ |
++LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ |
++note: unsafe function call occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:101:14
++ |
++LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
++ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++note: raw pointer dereference occurs here
++ --> $DIR/multiple_unsafe_ops_per_block.rs:101:39
++ |
++LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
++ | ^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 5 previous errors
++
--- /dev/null
+// run-rustfix
+
+#![feature(lint_reasons)]
+#![feature(yeet_expr)]
+#![allow(unused)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::single_match,
+ clippy::needless_bool,
+ clippy::equatable_if_let
+)]
+#![warn(clippy::needless_return)]
+
+use std::cell::RefCell;
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+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
+ } else {
+ false
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+fn test_macro_call() -> i32 {
+ the_answer!()
+}
+
+fn test_void_fun() {
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+ } else {
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => (),
+ }
+}
+
+fn test_nested_match(x: u32) {
+ match x {
+ 0 => (),
+ 1 => {
+ let _ = 42;
+ },
+ _ => (),
+ }
+}
+
+fn temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| {})
+ }
+
+ fn test_closure() {
+ let _ = || {
+ };
+ let _ = || {};
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ true
+}
+
+async fn async_test_no_semicolon() -> bool {
+ true
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ true
+ } else {
+ false
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => false,
+ false => {
+ true
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ true
+ };
+ let _ = || true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ the_answer!()
+}
+
+async fn async_test_void_fun() {
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+ } else {
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => (),
+ }
+}
+
+async fn async_temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ String::from("test")
+ } else {
+ String::new()
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn needless_return_macro() -> String {
+ let _ = "foo";
+ let _ = "bar";
+ format!("Hello {}", "world!")
+}
+
+fn issue_9361() -> i32 {
+ #[allow(clippy::integer_arithmetic)]
+ return 1 + 2;
+}
+
+fn issue8336(x: i32) -> bool {
+ if x > 0 {
+ println!("something");
+ true
+ } else {
+ false
+ }
+}
+
+fn issue8156(x: u8) -> u64 {
+ match x {
+ 80 => {
+ 10
+ },
+ _ => {
+ 100
+ },
+ }
+}
+
+// Ideally the compiler should throw `unused_braces` in this case
+fn issue9192() -> i32 {
+ {
+ 0
+ }
+}
+
+fn issue9503(x: usize) -> isize {
+ unsafe {
+ if x > 12 {
+ *(x as *const isize)
+ } else {
+ !*(x as *const 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<String, String> {
+ if true {
+ Ok(format!("ok!"))
+ } else {
+ Err(format!("err!"))
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![feature(lint_reasons)]
+#![feature(yeet_expr)]
+#![allow(unused)]
+#![allow(
+ clippy::if_same_then_else,
+ clippy::single_match,
+ clippy::needless_bool,
+ clippy::equatable_if_let
+)]
+#![warn(clippy::needless_return)]
+
+use std::cell::RefCell;
+
+macro_rules! the_answer {
+ () => {
+ 42
+ };
+}
+
+fn test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+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;
+ } else {
+ return false;
+ }
+}
+
+fn test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+fn test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+fn test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+fn test_void_fun() {
+ return;
+}
+
+fn test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+fn test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+fn test_nested_match(x: u32) {
+ match x {
+ 0 => (),
+ 1 => {
+ let _ = 42;
+ return;
+ },
+ _ => return,
+ }
+}
+
+fn temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+fn borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+macro_rules! needed_return {
+ ($e:expr) => {
+ if $e > 3 {
+ return;
+ }
+ };
+}
+
+fn test_return_in_macro() {
+ // This will return and the macro below won't be executed. Removing the `return` from the macro
+ // will change semantics.
+ needed_return!(10);
+ needed_return!(0);
+}
+
+mod issue6501 {
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn foo(bar: Result<(), ()>) {
+ bar.unwrap_or_else(|_| return)
+ }
+
+ fn test_closure() {
+ let _ = || {
+ return;
+ };
+ let _ = || return;
+ }
+
+ struct Foo;
+ #[allow(clippy::unnecessary_lazy_evaluations)]
+ fn bar(res: Result<Foo, u8>) -> Foo {
+ res.unwrap_or_else(|_| return Foo)
+ }
+}
+
+async fn async_test_end_of_fn() -> bool {
+ if true {
+ // no error!
+ return true;
+ }
+ return true;
+}
+
+async fn async_test_no_semicolon() -> bool {
+ return true;
+}
+
+async fn async_test_if_block() -> bool {
+ if true {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+async fn async_test_match(x: bool) -> bool {
+ match x {
+ true => return false,
+ false => {
+ return true;
+ },
+ }
+}
+
+async fn async_test_closure() {
+ let _ = || {
+ return true;
+ };
+ let _ = || return true;
+}
+
+async fn async_test_macro_call() -> i32 {
+ return the_answer!();
+}
+
+async fn async_test_void_fun() {
+ return;
+}
+
+async fn async_test_void_if_fun(b: bool) {
+ if b {
+ return;
+ } else {
+ return;
+ }
+}
+
+async fn async_test_void_match(x: u32) {
+ match x {
+ 0 => (),
+ _ => return,
+ }
+}
+
+async fn async_temporary_outlives_local() -> String {
+ let x = RefCell::<String>::default();
+ return x.borrow().clone();
+}
+
+async fn async_borrows_but_not_last(value: bool) -> String {
+ if value {
+ let x = RefCell::<String>::default();
+ let _a = x.borrow().clone();
+ return String::from("test");
+ } else {
+ return String::new();
+ }
+}
+
+async fn async_test_return_in_macro() {
+ needed_return!(10);
+ needed_return!(0);
+}
+
+fn let_else() {
+ let Some(1) = Some(1) else { return };
+}
+
+fn needless_return_macro() -> String {
+ let _ = "foo";
+ let _ = "bar";
+ return format!("Hello {}", "world!");
+}
+
+fn issue_9361() -> i32 {
+ #[allow(clippy::integer_arithmetic)]
+ return 1 + 2;
+}
+
+fn issue8336(x: i32) -> bool {
+ if x > 0 {
+ println!("something");
+ return true;
+ } else {
+ return false;
+ };
+}
+
+fn issue8156(x: u8) -> u64 {
+ match x {
+ 80 => {
+ return 10;
+ },
+ _ => {
+ return 100;
+ },
+ };
+}
+
+// Ideally the compiler should throw `unused_braces` in this case
+fn issue9192() -> i32 {
+ {
+ return 0;
+ };
+}
+
+fn issue9503(x: usize) -> isize {
+ unsafe {
+ if x > 12 {
+ return *(x as *const isize);
+ } else {
+ return !*(x as *const 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<String, String> {
+ if true {
+ return Ok(format!("ok!"));
+ } else {
+ return Err(format!("err!"));
+ }
+}
+
+fn main() {}
--- /dev/null
- --> $DIR/needless_return.rs:36:9
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:27:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::needless-return` implied by `-D warnings`
+ = help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:31:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:38: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;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:44:17
++ --> $DIR/needless_return.rs:48:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:46:13
++ --> $DIR/needless_return.rs:54:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:53:9
++ --> $DIR/needless_return.rs:56:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:55:16
++ --> $DIR/needless_return.rs:63:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:59:5
++ --> $DIR/needless_return.rs:65:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:62:21
++ --> $DIR/needless_return.rs:69:5
+ |
+LL | return the_answer!();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:67:11
++ --> $DIR/needless_return.rs:72:21
+ |
+LL | fn test_void_fun() {
+ | _____________________^
+LL | | return;
+ | |__________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:69:13
++ --> $DIR/needless_return.rs:77:11
+ |
+LL | if b {
+ | ___________^
+LL | | return;
+ | |______________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:77:14
++ --> $DIR/needless_return.rs:79:13
+ |
+LL | } else {
+ | _____________^
+LL | | return;
+ | |______________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:85:24
++ --> $DIR/needless_return.rs:87:14
+ |
+LL | _ => return,
+ | ^^^^^^
+ |
+ = help: replace `return` with a unit value
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:88:14
++ --> $DIR/needless_return.rs:95:24
+ |
+LL | let _ = 42;
+ | ________________________^
+LL | | return;
+ | |__________________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:101:9
++ --> $DIR/needless_return.rs:98:14
+ |
+LL | _ => return,
+ | ^^^^^^
+ |
+ = help: replace `return` with a unit value
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:103:9
++ --> $DIR/needless_return.rs:111:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:125:32
++ --> $DIR/needless_return.rs:113:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:129:21
++ --> $DIR/needless_return.rs:135:32
+ |
+LL | bar.unwrap_or_else(|_| return)
+ | ^^^^^^
+ |
+ = help: replace `return` with an empty block
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:132:20
++ --> $DIR/needless_return.rs:139:21
+ |
+LL | let _ = || {
+ | _____________________^
+LL | | return;
+ | |__________________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:138:32
++ --> $DIR/needless_return.rs:142:20
+ |
+LL | let _ = || return;
+ | ^^^^^^
+ |
+ = help: replace `return` with an empty block
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:147:5
++ --> $DIR/needless_return.rs:148:32
+ |
+LL | res.unwrap_or_else(|_| return Foo)
+ | ^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:151:5
++ --> $DIR/needless_return.rs:157:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:156:9
++ --> $DIR/needless_return.rs:161:5
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:158:9
++ --> $DIR/needless_return.rs:166:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:164:17
++ --> $DIR/needless_return.rs:168:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:166:13
++ --> $DIR/needless_return.rs:174:17
+ |
+LL | true => return false,
+ | ^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:173:9
++ --> $DIR/needless_return.rs:176:13
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:175:16
++ --> $DIR/needless_return.rs:183:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:179:5
++ --> $DIR/needless_return.rs:185:16
+ |
+LL | let _ = || return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:182:33
++ --> $DIR/needless_return.rs:189:5
+ |
+LL | return the_answer!();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:187:11
++ --> $DIR/needless_return.rs:192:33
+ |
+LL | async fn async_test_void_fun() {
+ | _________________________________^
+LL | | return;
+ | |__________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:189:13
++ --> $DIR/needless_return.rs:197:11
+ |
+LL | if b {
+ | ___________^
+LL | | return;
+ | |______________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:197:14
++ --> $DIR/needless_return.rs:199:13
+ |
+LL | } else {
+ | _____________^
+LL | | return;
+ | |______________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:210:9
++ --> $DIR/needless_return.rs:207:14
+ |
+LL | _ => return,
+ | ^^^^^^
+ |
+ = help: replace `return` with a unit value
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:212:9
++ --> $DIR/needless_return.rs:220:9
+ |
+LL | return String::from("test");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:228:5
++ --> $DIR/needless_return.rs:222:9
+ |
+LL | return String::new();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:239:9
++ --> $DIR/needless_return.rs:238:5
+ |
+LL | return format!("Hello {}", "world!");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:241:9
++ --> $DIR/needless_return.rs:249:9
+ |
+LL | return true;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:248:13
++ --> $DIR/needless_return.rs:251:9
+ |
+LL | return false;
+ | ^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:251:13
++ --> $DIR/needless_return.rs:258:13
+ |
+LL | return 10;
+ | ^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:259:9
++ --> $DIR/needless_return.rs:261:13
+ |
+LL | return 100;
+ | ^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:266:13
++ --> $DIR/needless_return.rs:269:9
+ |
+LL | return 0;
+ | ^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:268:13
++ --> $DIR/needless_return.rs:276:13
+ |
+LL | return *(x as *const isize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:275:20
++ --> $DIR/needless_return.rs:278:13
+ |
+LL | return !*(x as *const isize);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:282:20
++ --> $DIR/needless_return.rs:285:20
+ |
+LL | let _ = 42;
+ | ____________________^
+LL | |
+LL | | return;
+ | |______________^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:294:9
++ --> $DIR/needless_return.rs:292:20
+ |
+LL | let _ = 42; return;
+ | ^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- --> $DIR/needless_return.rs:296:9
++ --> $DIR/needless_return.rs:304:9
+ |
+LL | return Ok(format!("ok!"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
- error: aborting due to 48 previous errors
++ --> $DIR/needless_return.rs:306:9
+ |
+LL | return Err(format!("err!"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
++error: aborting due to 50 previous errors
+
--- /dev/null
+// run-rustfix
+#![warn(clippy::transmutes_expressible_as_ptr_casts)]
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
+// would otherwise be responsible for
+#![warn(clippy::useless_transmute)]
+#![warn(clippy::transmute_ptr_to_ptr)]
+#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+
+use std::mem::{size_of, transmute};
+
+// rustc_hir_analysis::check::cast contains documentation about when a cast `e as U` is
+// valid, which we quote from below.
+fn main() {
+ // We should see an error message for each transmute, and no error messages for
+ // the casts, since the casts are the recommended fixes.
+
+ // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
+ let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 };
+ let ptr_i32 = usize::MAX as *const i32;
+
+ // e has type *T, U is *U_0, and either U_0: Sized ...
+ let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 };
+ let _ptr_i8 = ptr_i32 as *const i8;
+
+ let slice_ptr = &[0, 1, 2, 3] as *const [i32];
+
+ // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
+ let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
+ let _ptr_to_unsized = slice_ptr as *const [u32];
+ // TODO: We could try testing vtable casts here too, but maybe
+ // we should wait until std::raw::TraitObject is stabilized?
+
+ // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
+ let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize };
+ let _usize_from_int_ptr = ptr_i32 as usize;
+
+ let array_ref: &[i32; 4] = &[1, 2, 3, 4];
+
+ // e has type &[T; n] and U is *const T; array-ptr-cast
+ let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] };
+ let _array_ptr = array_ref as *const [i32; 4];
+
+ fn foo(_: usize) -> u8 {
+ 42
+ }
+
+ // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
+ let _usize_ptr_transmute = unsafe { foo as *const usize };
+ let _usize_ptr_transmute = foo as *const usize;
+
+ // 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
+// than the referenced type, calling `CastCheck::do_check` has been observed to
+// cause an ICE error message. `do_check` is currently called inside the
+// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
+// currently prevent it from being called in these cases. This test is meant to
+// fail if the ordering of the checks ever changes enough to cause these cases to
+// fall through into `do_check`.
+fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
+ unsafe { in_param as *const [i32; 1] as *const u8 }
+}
+
+#[repr(C)]
+struct Single(u64);
+
+#[repr(C)]
+struct Pair(u32, u32);
+
+fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
+ assert_eq!(size_of::<Single>(), size_of::<Pair>());
+
+ unsafe { transmute::<Single, Pair>(in_param) }
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::transmutes_expressible_as_ptr_casts)]
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
+// would otherwise be responsible for
+#![warn(clippy::useless_transmute)]
+#![warn(clippy::transmute_ptr_to_ptr)]
+#![allow(dead_code, unused_unsafe, clippy::borrow_as_ptr)]
+
+use std::mem::{size_of, transmute};
+
+// rustc_hir_analysis::check::cast contains documentation about when a cast `e as U` is
+// valid, which we quote from below.
+fn main() {
+ // We should see an error message for each transmute, and no error messages for
+ // the casts, since the casts are the recommended fixes.
+
+ // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
+ let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
+ let ptr_i32 = usize::MAX as *const i32;
+
+ // e has type *T, U is *U_0, and either U_0: Sized ...
+ let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
+ let _ptr_i8 = ptr_i32 as *const i8;
+
+ let slice_ptr = &[0, 1, 2, 3] as *const [i32];
+
+ // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
+ let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
+ let _ptr_to_unsized = slice_ptr as *const [u32];
+ // TODO: We could try testing vtable casts here too, but maybe
+ // we should wait until std::raw::TraitObject is stabilized?
+
+ // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast
+ let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
+ let _usize_from_int_ptr = ptr_i32 as usize;
+
+ let array_ref: &[i32; 4] = &[1, 2, 3, 4];
+
+ // e has type &[T; n] and U is *const T; array-ptr-cast
+ let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
+ let _array_ptr = array_ref as *const [i32; 4];
+
+ fn foo(_: usize) -> u8 {
+ 42
+ }
+
+ // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast
+ let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
+ let _usize_ptr_transmute = foo as *const usize;
+
+ // e is a function pointer type and U is an integer; fptr-addr-cast
+ let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> 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
+// than the referenced type, calling `CastCheck::do_check` has been observed to
+// cause an ICE error message. `do_check` is currently called inside the
+// `transmutes_expressible_as_ptr_casts` check, but other, more specific lints
+// currently prevent it from being called in these cases. This test is meant to
+// fail if the ordering of the checks ever changes enough to cause these cases to
+// fall through into `do_check`.
+fn trigger_do_check_to_emit_error(in_param: &[i32; 1]) -> *const u8 {
+ unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
+}
+
+#[repr(C)]
+struct Single(u64);
+
+#[repr(C)]
+struct Pair(u32, u32);
+
+fn cannot_be_expressed_as_pointer_cast(in_param: Single) -> Pair {
+ assert_eq!(size_of::<Single>(), size_of::<Pair>());
+
+ unsafe { transmute::<Single, Pair>(in_param) }
+}
--- /dev/null
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14
+error: transmute from an integer to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:18:39
+ |
+LL | let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
+ |
+ = note: `-D clippy::useless-transmute` implied by `-D warnings`
+
+error: transmute from a pointer to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:22:38
+ |
+LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
+ |
+ = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
+
+error: transmute from a pointer to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46
+ |
+LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u32]`
+
+error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50
+ |
+LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
+ |
+ = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings`
+
+error: transmute from a reference to a pointer
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:40:41
+ |
+LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
+
+error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:48:41
+ |
+LL | let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
+
+error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:52:49
+ |
+LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> 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
- error: aborting due to 8 previous errors
++ --> $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 9 previous errors
+
--- /dev/null
+#![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() {}