Auto merge of #139983 - flip1995:clippy-subtree-update, r=Manishearth
Clippy subtree update r? `@Manishearth` Cargo.lock update due to the Clippy version bump and because Clippy moved from rinja (unmaintained) to askama. Last sync was skipped due to the askama issue and me not getting to fixing this in time.
This commit is contained in:
commit
df35ff6c35
748 changed files with 13948 additions and 7255 deletions
85
Cargo.lock
85
Cargo.lock
|
|
@ -546,12 +546,14 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
|||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.1.87"
|
||||
version = "0.1.88"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"askama",
|
||||
"cargo_metadata 0.18.1",
|
||||
"clippy_config",
|
||||
"clippy_lints",
|
||||
"clippy_lints_internal",
|
||||
"clippy_utils",
|
||||
"color-print",
|
||||
"filetime",
|
||||
|
|
@ -562,7 +564,6 @@ dependencies = [
|
|||
"pulldown-cmark 0.11.3",
|
||||
"quote",
|
||||
"regex",
|
||||
"rinja",
|
||||
"rustc_tools_util 0.4.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -577,7 +578,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_config"
|
||||
version = "0.1.87"
|
||||
version = "0.1.88"
|
||||
dependencies = [
|
||||
"clippy_utils",
|
||||
"itertools",
|
||||
|
|
@ -602,7 +603,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.87"
|
||||
version = "0.1.88"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cargo_metadata 0.18.1",
|
||||
|
|
@ -610,12 +611,9 @@ dependencies = [
|
|||
"clippy_utils",
|
||||
"itertools",
|
||||
"quine-mc_cluskey",
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"toml 0.7.8",
|
||||
"unicode-normalization",
|
||||
"unicode-script",
|
||||
|
|
@ -623,9 +621,19 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy_lints_internal"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clippy_config",
|
||||
"clippy_utils",
|
||||
"regex",
|
||||
"rustc-semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.87"
|
||||
version = "0.1.88"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"itertools",
|
||||
|
|
@ -2244,22 +2252,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minifier"
|
||||
version = "0.3.5"
|
||||
|
|
@ -3084,45 +3076,6 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinja"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"rinja_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinja_derive"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b"
|
||||
dependencies = [
|
||||
"basic-toml",
|
||||
"memchr",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rinja_parser",
|
||||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rinja_parser"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"nom",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "run_make_support"
|
||||
version = "0.2.0"
|
||||
|
|
@ -3196,6 +3149,12 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-semver"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be1bdc7edf596692617627bbfeaba522131b18e06ca4df2b6b689e3c5d5ce84"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-stable-hash"
|
||||
version = "0.1.2"
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ pub static CRATES: &[&str] = &[
|
|||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"minimal-lexical",
|
||||
"nom",
|
||||
"once_cell",
|
||||
|
|
@ -38,7 +36,6 @@ pub static CRATES: &[&str] = &[
|
|||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rinja_parser",
|
||||
"rustc-hash",
|
||||
"self_cell",
|
||||
"serde",
|
||||
|
|
@ -56,7 +53,6 @@ pub static CRATES: &[&str] = &[
|
|||
"unic-langid",
|
||||
"unic-langid-impl",
|
||||
"unic-langid-macros",
|
||||
"unicase",
|
||||
"unicode-ident",
|
||||
"unicode-width",
|
||||
"version_check",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
- name: Check Changelog
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \
|
||||
body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | \
|
||||
python -c "import sys, json; print(json.load(sys.stdin)['body'])")
|
||||
output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g")
|
||||
if [ -z "$output" ]; then
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ jobs:
|
|||
run: cargo test --features internal -- --skip dogfood
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features internal
|
||||
run: cargo test
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test clippy_utils
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
run: cargo test --features internal
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features internal
|
||||
run: cargo test
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test clippy_utils
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ on:
|
|||
tags:
|
||||
- rust-1.**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
TARGET_BRANCH: 'gh-pages'
|
||||
SHA: '${{ github.sha }}'
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ jobs:
|
|||
|
||||
- name: Run lintcheck
|
||||
if: steps.cache-json.outputs.cache-hit != 'true'
|
||||
run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
|
||||
run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
|
||||
|
||||
- name: Upload base JSON
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
@ -97,7 +97,7 @@ jobs:
|
|||
run: cargo build --manifest-path=lintcheck/Cargo.toml
|
||||
|
||||
- name: Run lintcheck
|
||||
run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
|
||||
run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
|
||||
|
||||
- name: Upload head JSON
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
|
|||
|
|
@ -6,11 +6,68 @@ document.
|
|||
|
||||
## Unreleased / Beta / In Rust Nightly
|
||||
|
||||
[609cd310...master](https://github.com/rust-lang/rust-clippy/compare/609cd310...master)
|
||||
[3e3715c3...master](https://github.com/rust-lang/rust-clippy/compare/3e3715c3...master)
|
||||
|
||||
## Rust 1.86
|
||||
|
||||
Current stable, released 2025-04-03
|
||||
|
||||
[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-12-27T15%3A11%3A38Z..2025-02-06T13%3A57%3A58Z+base%3Amaster)
|
||||
|
||||
### New Lints
|
||||
|
||||
* Added [`unneeded_struct_pattern`] to `style` [#13465](https://github.com/rust-lang/rust-clippy/pull/13465)
|
||||
* Added [`doc_overindented_list_items`] to `style` [#13711](https://github.com/rust-lang/rust-clippy/pull/13711)
|
||||
* Added [`manual_ok_err`] to `complexity` [#13740](https://github.com/rust-lang/rust-clippy/pull/13740)
|
||||
* Added [`non_std_lazy_statics`] to `pedantic` [#13770](https://github.com/rust-lang/rust-clippy/pull/13770)
|
||||
* Added [`manual_repeat_n`] to `style` [#13858](https://github.com/rust-lang/rust-clippy/pull/13858)
|
||||
* Added [`manual_option_as_slice`] to `complexity` [#13901](https://github.com/rust-lang/rust-clippy/pull/13901)
|
||||
* Added [`double_ended_iterator_last`] to `perf` [#13922](https://github.com/rust-lang/rust-clippy/pull/13922)
|
||||
* Added [`useless_nonzero_new_unchecked`] to `complexity` [#13993](https://github.com/rust-lang/rust-clippy/pull/13993)
|
||||
* Added [`sliced_string_as_bytes`] to `perf` [#14002](https://github.com/rust-lang/rust-clippy/pull/14002)
|
||||
* Added [`unnecessary_semicolon`] to `pedantic` [#14032](https://github.com/rust-lang/rust-clippy/pull/14032)
|
||||
* Added [`return_and_then`] to `restriction` [#14051](https://github.com/rust-lang/rust-clippy/pull/14051)
|
||||
* Added [`manual_slice_fill`] to `style` [#14082](https://github.com/rust-lang/rust-clippy/pull/14082)
|
||||
* Added [`precedence_bits`] to `restriction` [#14115](https://github.com/rust-lang/rust-clippy/pull/14115)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Moved [`redundant_locals`] to `suspicious` (from `correctness`, now warn-by-default)
|
||||
[#13747](https://github.com/rust-lang/rust-clippy/pull/13747)
|
||||
* Moved [`format_push_string`] to `pedantic` (from `restriction`)
|
||||
[#13894](https://github.com/rust-lang/rust-clippy/pull/13894)
|
||||
* Moved [`format_collect`] to `pedantic` (from `perf`, now allow-by-default)
|
||||
[#13894](https://github.com/rust-lang/rust-clippy/pull/13894)
|
||||
* Moved [`mutex_integer`] to `restriction` (from `nursery`) [#14110](https://github.com/rust-lang/rust-clippy/pull/14110)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Add `lint-inconsistent-struct-field-initializers` configuration option to [`inconsistent_struct_constructor`]
|
||||
[#13737](https://github.com/rust-lang/rust-clippy/pull/13737)
|
||||
* [`len_zero`] now also triggers if deref target implements `is_empty()`
|
||||
[#13871](https://github.com/rust-lang/rust-clippy/pull/13871)
|
||||
* [`obfuscated_if_else`] now also triggers for the `.then(..).unwrap_or(..)` pattern
|
||||
[#14021](https://github.com/rust-lang/rust-clippy/pull/14021)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`trailing_empty_array`] no longer triggers in tests [#13844](https://github.com/rust-lang/rust-clippy/pull/13844)
|
||||
* [`missing_const_for_fn`] no longer triggers in tests [#13945](https://github.com/rust-lang/rust-clippy/pull/13945)
|
||||
* [`significant_drop_in_scrutinee`]: do not falsely warn for temporaries created by `.await` expansion
|
||||
[#13985](https://github.com/rust-lang/rust-clippy/pull/13985)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* [`borrow_interior_mutable_const`] Fix an ICE that can occur when taking a reference to a tuple/`struct` field of an
|
||||
interior mutable `const` [#13877](https://github.com/rust-lang/rust-clippy/pull/13877)
|
||||
|
||||
### Others
|
||||
|
||||
* Clippy now uses Rust edition 2024 [#13751](https://github.com/rust-lang/rust-clippy/pull/13751)
|
||||
|
||||
## Rust 1.85
|
||||
|
||||
Current stable, released 2025-02-20
|
||||
Released 2025-02-20
|
||||
|
||||
[View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-11-15T19%3A31%3A08Z..2024-12-26T13%3A59%3A48Z+base%3Amaster)
|
||||
|
||||
|
|
@ -5516,6 +5573,7 @@ Released 2018-09-13
|
|||
[`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
|
||||
[`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test
|
||||
[`char_indices_as_byte_indices`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_indices_as_byte_indices
|
||||
[`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
|
||||
|
|
@ -5681,6 +5739,7 @@ Released 2018-09-13
|
|||
[`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
|
||||
[`ignore_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignore_without_reason
|
||||
[`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns
|
||||
[`impl_hash_borrow_with_str_and_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_hash_borrow_with_str_and_bytes
|
||||
[`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params
|
||||
|
|
@ -5783,12 +5842,14 @@ Released 2018-09-13
|
|||
[`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe
|
||||
[`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_abs_diff`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff
|
||||
[`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_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
|
||||
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
|
||||
[`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains
|
||||
[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr
|
||||
[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
|
||||
[`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
|
||||
|
|
@ -6055,6 +6116,7 @@ Released 2018-09-13
|
|||
[`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
|
||||
[`redundant_test_prefix`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_test_prefix
|
||||
[`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations
|
||||
[`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr
|
||||
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
|
||||
|
|
@ -6156,6 +6218,7 @@ Released 2018-09-13
|
|||
[`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
|
||||
[`swap_with_temporary`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_with_temporary
|
||||
[`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
|
||||
|
|
@ -6346,6 +6409,7 @@ Released 2018-09-13
|
|||
[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
|
||||
[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
|
||||
[`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests
|
||||
[`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers
|
||||
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
|
||||
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
|
||||
[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros
|
||||
|
|
@ -6362,7 +6426,7 @@ Released 2018-09-13
|
|||
[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
|
||||
[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
|
||||
[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold
|
||||
[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers
|
||||
[`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code
|
||||
[`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold
|
||||
[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else
|
||||
[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
# begin autogenerated version
|
||||
version = "0.1.87"
|
||||
version = "0.1.88"
|
||||
# end autogenerated version
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
|
|
@ -27,6 +27,7 @@ clippy_config = { path = "clippy_config" }
|
|||
clippy_lints = { path = "clippy_lints" }
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
|
||||
clippy_lints_internal = { path = "clippy_lints_internal", optional = true }
|
||||
tempfile = { version = "3.3", optional = true }
|
||||
termize = "0.1"
|
||||
color-print = "0.3.4"
|
||||
|
|
@ -43,7 +44,7 @@ walkdir = "2.3"
|
|||
filetime = "0.2.9"
|
||||
itertools = "0.12"
|
||||
pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] }
|
||||
rinja = { version = "0.3", default-features = false, features = ["config"] }
|
||||
askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] }
|
||||
|
||||
# UI test dependencies
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
|
|
@ -58,8 +59,8 @@ tokio = { version = "1", features = ["io-util"] }
|
|||
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
|
||||
|
||||
[features]
|
||||
integration = ["tempfile"]
|
||||
internal = ["clippy_lints/internal", "tempfile"]
|
||||
integration = ["dep:tempfile"]
|
||||
internal = ["dep:clippy_lints_internal", "dep:tempfile"]
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This package uses #[feature(rustc_private)]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
- [Updating the Changelog](development/infrastructure/changelog_update.md)
|
||||
- [Release a New Version](development/infrastructure/release.md)
|
||||
- [The Clippy Book](development/infrastructure/book.md)
|
||||
- [Benchmarking Clippy](development/infrastructure/benchmarking.md)
|
||||
- [Proposals](development/proposals/README.md)
|
||||
- [Roadmap 2021](development/proposals/roadmap-2021.md)
|
||||
- [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ struct A;
|
|||
impl A {
|
||||
pub fn fo(&self) {}
|
||||
pub fn foo(&self) {}
|
||||
//~^ foo_functions
|
||||
pub fn food(&self) {}
|
||||
}
|
||||
|
||||
|
|
@ -106,12 +107,14 @@ impl A {
|
|||
trait B {
|
||||
fn fo(&self) {}
|
||||
fn foo(&self) {}
|
||||
//~^ foo_functions
|
||||
fn food(&self) {}
|
||||
}
|
||||
|
||||
// Plain functions
|
||||
fn fo() {}
|
||||
fn foo() {}
|
||||
//~^ foo_functions
|
||||
fn food() {}
|
||||
|
||||
fn main() {
|
||||
|
|
@ -122,17 +125,24 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently
|
||||
this test is meaningless though.
|
||||
Note that we are adding comment annotations with the name of our lint to mark
|
||||
lines where we expect an error. Except for very specific situations
|
||||
(`//@check-pass`), at least one error marker must be present in a test file for
|
||||
it to be accepted.
|
||||
|
||||
Once we have implemented our lint we can run `TESTNAME=foo_functions cargo
|
||||
uibless` to generate the `.stderr` file. If our lint makes use of structured
|
||||
suggestions then this command will also generate the corresponding `.fixed`
|
||||
file.
|
||||
|
||||
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 by checking the
|
||||
`.stderr` file that gets updated on every test run.
|
||||
|
||||
Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we
|
||||
commit our lint, we need to commit the generated `.stderr` files, too. In
|
||||
general, you should only commit files changed by `cargo bless` for the
|
||||
specific lint you are creating/editing.
|
||||
Once we have implemented our lint running `TESTNAME=foo_functions cargo uitest`
|
||||
should pass on its own. When we commit our lint, we need to commit the generated
|
||||
`.stderr` and if applicable `.fixed` files, too. In general, you should only
|
||||
commit files changed by `cargo bless` for the specific lint you are creating/editing.
|
||||
|
||||
> _Note:_ you can run multiple test files by specifying a comma separated list:
|
||||
> `TESTNAME=foo_functions,test2,test3`.
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ following:
|
|||
|
||||
First, take note of the toolchain
|
||||
[override](https://rust-lang.github.io/rustup/overrides.html) in
|
||||
`/rust-toolchain`. We will use this override to install Clippy into the right
|
||||
`/rust-toolchain.toml`. We will use this override to install Clippy into the right
|
||||
toolchain.
|
||||
|
||||
> Tip: You can view the active toolchain for the current directory with `rustup
|
||||
|
|
|
|||
|
|
@ -159,19 +159,21 @@ paths for Clippy can be found in [paths.rs][paths]
|
|||
To check if our type defines a method called `some_method`:
|
||||
|
||||
```rust
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::return_ty;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use clippy_utils::{sym, return_ty};
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
||||
// Check if item is a method/function
|
||||
if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
|
||||
// Check the method is named `some_method`
|
||||
&& impl_item.ident.name.as_str() == "some_method"
|
||||
//
|
||||
// Add `some_method` to `clippy_utils::sym` if it's not already there
|
||||
&& impl_item.ident.name == sym::some_method
|
||||
// We can also check it has a parameter `self`
|
||||
&& signature.decl.implicit_self.has_implicit_self()
|
||||
// We can go further and even check if its return type is `String`
|
||||
&& is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type))
|
||||
&& is_type_lang_item(cx, return_ty(cx, impl_item.hir_id), LangItem::String)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ lint involves some boilerplate code.
|
|||
|
||||
A lint type is the category of items and expressions in which your lint focuses on.
|
||||
|
||||
As of the writing of this documentation update, there are 12 _types_ of lints
|
||||
As of the writing of this documentation update, there are 11 _types_ of lints
|
||||
besides the numerous standalone lints living under `clippy_lints/src/`:
|
||||
|
||||
- `cargo`
|
||||
|
|
@ -23,7 +23,6 @@ besides the numerous standalone lints living under `clippy_lints/src/`:
|
|||
- `transmute`
|
||||
- `types`
|
||||
- `unit_types`
|
||||
- `utils / internal` (Clippy internal lints)
|
||||
|
||||
These types group together lints that share some common behaviors. For instance,
|
||||
`functions` groups together lints that deal with some aspects of functions in
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
# Benchmarking Clippy
|
||||
|
||||
Benchmarking Clippy is similar to using our Lintcheck tool, in fact, it even
|
||||
uses the same tool! Just by adding a `--perf` flag it will transform Lintcheck
|
||||
into a very simple but powerful benchmarking tool!
|
||||
|
||||
It requires having the [`perf` tool][perf] installed, as `perf` is what's actually
|
||||
profiling Clippy under the hood.
|
||||
|
||||
The lintcheck `--perf` tool generates a series of `perf.data` in the
|
||||
`target/lintcheck/sources/<package>-<version>` directories. Each `perf.data`
|
||||
corresponds to the package which is contained.
|
||||
|
||||
Lintcheck uses the `-g` flag, meaning that you can get stack traces for richer
|
||||
analysis, including with tools such as [flamegraph][flamegraph-perf]
|
||||
(or [`flamegraph-rs`][flamegraph-rs]).
|
||||
|
||||
Currently, we only measure instruction count, as it's the most reproducible metric
|
||||
and [rustc-perf][rustc-perf] also considers it the main number to focus on.
|
||||
|
||||
## Benchmarking a PR
|
||||
|
||||
Having a benchmarking tool directly implemented into lintcheck gives us the
|
||||
ability to benchmark any given PR just by making a before and after
|
||||
|
||||
Here's the way you can get into any PR, benchmark it, and then benchmark
|
||||
`master`.
|
||||
|
||||
The first `perf.data` will not have any numbers appended, but any subsequent
|
||||
benchmark will be written to `perf.data.number` with a number growing for 0.
|
||||
All benchmarks are compressed so that you can
|
||||
|
||||
```bash
|
||||
git fetch upstream pull/<PR_NUMBER>/head:<BRANCH_NAME>
|
||||
git switch BRANCHNAME
|
||||
|
||||
# Bench
|
||||
cargo lintcheck --perf
|
||||
|
||||
# Get last common commit, checkout that
|
||||
LAST_COMMIT=$(git log BRANCHNAME..master --oneline | tail -1 | cut -c 1-11)
|
||||
git switch -c temporary $LAST_COMMIT
|
||||
|
||||
# We're now on master
|
||||
|
||||
# Bench
|
||||
cargo lintcheck --perf
|
||||
perf diff ./target/lintcheck/sources/CRATE/perf.data ./target/lintcheck/sources/CRATE/perf.data.0
|
||||
```
|
||||
|
||||
|
||||
[perf]: https://perfwiki.github.io/main/
|
||||
[flamegraph-perf]: https://github.com/brendangregg/FlameGraph
|
||||
[flamegraph-rs]: https://github.com/flamegraph-rs/flamegraph
|
||||
[rustc-perf]: https://github.com/rust-lang/rustc-perf
|
||||
|
|
@ -88,9 +88,6 @@ git push upstream stable
|
|||
After updating the `stable` branch, tag the HEAD commit and push it to the
|
||||
Clippy repo.
|
||||
|
||||
> Note: Only push the tag once the Deploy GitHub action of the `beta` branch is
|
||||
> finished. Otherwise the deploy for the tag might fail.
|
||||
|
||||
```bash
|
||||
git tag rust-1.XX.0 # XX should be exchanged with the corresponding version
|
||||
git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ to be run inside the `rust` directory):
|
|||
4. Bump the nightly version in the Clippy repository by running these commands:
|
||||
```bash
|
||||
cargo dev sync update_nightly
|
||||
git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain clippy_utils/README.md
|
||||
git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain.toml clippy_utils/README.md
|
||||
```
|
||||
5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
|
||||
accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
|
||||
|
|
|
|||
|
|
@ -41,20 +41,23 @@ Update the file with some examples to get started:
|
|||
struct A;
|
||||
impl A {
|
||||
pub fn fo(&self) {}
|
||||
pub fn foo(&self) {} //~ ERROR: function called "foo"
|
||||
pub fn foo(&self) {}
|
||||
//~^ foo_functions
|
||||
pub fn food(&self) {}
|
||||
}
|
||||
|
||||
// Default trait methods
|
||||
trait B {
|
||||
fn fo(&self) {}
|
||||
fn foo(&self) {} //~ ERROR: function called "foo"
|
||||
fn foo(&self) {}
|
||||
//~^ foo_functions
|
||||
fn food(&self) {}
|
||||
}
|
||||
|
||||
// Plain functions
|
||||
fn fo() {}
|
||||
fn foo() {} //~ ERROR: function called "foo"
|
||||
fn foo() {}
|
||||
//~^ foo_functions
|
||||
fn food() {}
|
||||
|
||||
fn main() {
|
||||
|
|
@ -66,19 +69,38 @@ fn main() {
|
|||
```
|
||||
|
||||
Without actual lint logic to emit the lint when we see a `foo` function name,
|
||||
this test will just pass, because no lint will be emitted. However, we can now
|
||||
run the test with the following command:
|
||||
this test will fail, because we expect errors at lines marked with
|
||||
`//~^ foo_functions`. However, we can now run the test with the following command:
|
||||
|
||||
```sh
|
||||
$ TESTNAME=foo_functions cargo uitest
|
||||
```
|
||||
|
||||
Clippy will compile and it will conclude with an `ok` for the tests:
|
||||
Clippy will compile and it will fail complaining it didn't receive any errors:
|
||||
|
||||
```
|
||||
...Clippy warnings and test outputs...
|
||||
test compile_test ... ok
|
||||
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s
|
||||
error: diagnostic code `clippy::foo_functions` not found on line 8
|
||||
--> tests/ui/foo_functions.rs:9:10
|
||||
|
|
||||
9 | //~^ foo_functions
|
||||
| ^^^^^^^^^^^^^ expected because of this pattern
|
||||
|
|
||||
|
||||
error: diagnostic code `clippy::foo_functions` not found on line 16
|
||||
--> tests/ui/foo_functions.rs:17:10
|
||||
|
|
||||
17 | //~^ foo_functions
|
||||
| ^^^^^^^^^^^^^ expected because of this pattern
|
||||
|
|
||||
|
||||
error: diagnostic code `clippy::foo_functions` not found on line 23
|
||||
--> tests/ui/foo_functions.rs:24:6
|
||||
|
|
||||
24 | //~^ foo_functions
|
||||
| ^^^^^^^^^^^^^ expected because of this pattern
|
||||
|
|
||||
|
||||
```
|
||||
|
||||
This is normal. After all, we wrote a bunch of Rust code but we haven't really
|
||||
|
|
|
|||
|
|
@ -425,6 +425,33 @@ Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
|
|||
* [`incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv)
|
||||
|
||||
|
||||
## `check-inconsistent-struct-field-initializers`
|
||||
Whether to suggest reordering constructor fields when initializers are present.
|
||||
|
||||
Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
|
||||
suggested code would compile, it can change semantics if the initializer expressions have side effects. The
|
||||
following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
|
||||
|
||||
```rust
|
||||
struct MyStruct {
|
||||
vector: Vec<u32>,
|
||||
length: usize
|
||||
}
|
||||
fn main() {
|
||||
let vector = vec![1,2,3];
|
||||
MyStruct { length: vector.len(), vector};
|
||||
}
|
||||
```
|
||||
|
||||
[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
|
||||
|
||||
**Default Value:** `false`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor)
|
||||
|
||||
|
||||
## `check-private-items`
|
||||
Whether to also run the listed lints on private items.
|
||||
|
||||
|
|
@ -613,31 +640,15 @@ The maximum size of the `Err`-variant in a `Result` returned from a function
|
|||
* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)
|
||||
|
||||
|
||||
## `lint-inconsistent-struct-field-initializers`
|
||||
Whether to suggest reordering constructor fields when initializers are present.
|
||||
|
||||
Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
|
||||
suggested code would compile, it can change semantics if the initializer expressions have side effects. The
|
||||
following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
|
||||
|
||||
```rust
|
||||
struct MyStruct {
|
||||
vector: Vec<u32>,
|
||||
length: usize
|
||||
}
|
||||
fn main() {
|
||||
let vector = vec![1,2,3];
|
||||
MyStruct { length: vector.len(), vector};
|
||||
}
|
||||
```
|
||||
|
||||
[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
|
||||
## `lint-commented-code`
|
||||
Whether collapsible `if` chains are linted if they contain comments inside the parts
|
||||
that would be collapsed.
|
||||
|
||||
**Default Value:** `false`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor)
|
||||
* [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if)
|
||||
|
||||
|
||||
## `literal-representation-threshold`
|
||||
|
|
@ -786,6 +797,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
|
||||
* [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants)
|
||||
* [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok)
|
||||
* [`manual_abs_diff`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff)
|
||||
* [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
|
||||
* [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals)
|
||||
* [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
|
||||
|
|
@ -793,6 +805,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`manual_flatten`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten)
|
||||
* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one)
|
||||
* [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check)
|
||||
* [`manual_is_power_of_two`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two)
|
||||
* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
|
||||
* [`manual_midpoint`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint)
|
||||
* [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
|
||||
|
|
@ -1059,7 +1072,8 @@ The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
|
|||
|
||||
|
||||
## `warn-on-all-wildcard-imports`
|
||||
Whether to allow certain wildcard imports (prelude, super in tests).
|
||||
Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests,
|
||||
or for `pub use` reexports.
|
||||
|
||||
**Default Value:** `false`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
avoid-breaking-exported-api = false
|
||||
|
||||
lint-inconsistent-struct-field-initializers = true
|
||||
check-inconsistent-struct-field-initializers = true
|
||||
|
||||
lint-commented-code = true
|
||||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_lint::context::LintContext::lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
allow-invalid = true
|
||||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_lint::context::LintContext::span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
allow-invalid = true
|
||||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
|
||||
allow-invalid = true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_config"
|
||||
# begin autogenerated version
|
||||
version = "0.1.87"
|
||||
version = "0.1.88"
|
||||
# end autogenerated version
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
|
|
|||
|
|
@ -120,12 +120,7 @@ impl ConfError {
|
|||
Self {
|
||||
message: message.into(),
|
||||
suggestion,
|
||||
span: Span::new(
|
||||
file.start_pos + BytePos::from_usize(span.start),
|
||||
file.start_pos + BytePos::from_usize(span.end),
|
||||
SyntaxContext::root(),
|
||||
None,
|
||||
),
|
||||
span: span_from_toml_range(file, span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -176,11 +171,61 @@ macro_rules! default_text {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! deserialize {
|
||||
($map:expr, $ty:ty, $errors:expr, $file:expr) => {{
|
||||
let raw_value = $map.next_value::<toml::Spanned<toml::Value>>()?;
|
||||
let value_span = raw_value.span();
|
||||
let value = match <$ty>::deserialize(raw_value.into_inner()) {
|
||||
Err(e) => {
|
||||
$errors.push(ConfError::spanned(
|
||||
$file,
|
||||
e.to_string().replace('\n', " ").trim(),
|
||||
None,
|
||||
value_span,
|
||||
));
|
||||
continue;
|
||||
},
|
||||
Ok(value) => value,
|
||||
};
|
||||
(value, value_span)
|
||||
}};
|
||||
|
||||
($map:expr, $ty:ty, $errors:expr, $file:expr, $replacements_allowed:expr) => {{
|
||||
let array = $map.next_value::<Vec<toml::Spanned<toml::Value>>>()?;
|
||||
let mut disallowed_paths_span = Range {
|
||||
start: usize::MAX,
|
||||
end: usize::MIN,
|
||||
};
|
||||
let mut disallowed_paths = Vec::new();
|
||||
for raw_value in array {
|
||||
let value_span = raw_value.span();
|
||||
let mut disallowed_path = match DisallowedPath::<$replacements_allowed>::deserialize(raw_value.into_inner())
|
||||
{
|
||||
Err(e) => {
|
||||
$errors.push(ConfError::spanned(
|
||||
$file,
|
||||
e.to_string().replace('\n', " ").trim(),
|
||||
None,
|
||||
value_span,
|
||||
));
|
||||
continue;
|
||||
},
|
||||
Ok(disallowed_path) => disallowed_path,
|
||||
};
|
||||
disallowed_paths_span = union(&disallowed_paths_span, &value_span);
|
||||
disallowed_path.set_span(span_from_toml_range($file, value_span));
|
||||
disallowed_paths.push(disallowed_path);
|
||||
}
|
||||
(disallowed_paths, disallowed_paths_span)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! define_Conf {
|
||||
($(
|
||||
$(#[doc = $doc:literal])+
|
||||
$(#[conf_deprecated($dep:literal, $new_conf:ident)])?
|
||||
$(#[default_text = $default_text:expr])?
|
||||
$(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])?
|
||||
$(#[lints($($for_lints:ident),* $(,)?)])?
|
||||
$name:ident: $ty:ty = $default:expr,
|
||||
)*) => {
|
||||
|
|
@ -218,42 +263,46 @@ macro_rules! define_Conf {
|
|||
let mut value_spans = HashMap::new();
|
||||
let mut errors = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
// Declare a local variable for each field available to a configuration file.
|
||||
$(let mut $name = None;)*
|
||||
|
||||
// could get `Field` here directly, but get `String` first for diagnostics
|
||||
while let Some(name) = map.next_key::<toml::Spanned<String>>()? {
|
||||
match Field::deserialize(name.get_ref().as_str().into_deserializer()) {
|
||||
let field = match Field::deserialize(name.get_ref().as_str().into_deserializer()) {
|
||||
Err(e) => {
|
||||
let e: FieldError = e;
|
||||
errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span()));
|
||||
continue;
|
||||
}
|
||||
$(Ok(Field::$name) => {
|
||||
Ok(field) => field
|
||||
};
|
||||
|
||||
match field {
|
||||
$(Field::$name => {
|
||||
// Is this a deprecated field, i.e., is `$dep` set? If so, push a warning.
|
||||
$(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)?
|
||||
let raw_value = map.next_value::<toml::Spanned<toml::Value>>()?;
|
||||
let value_span = raw_value.span();
|
||||
match <$ty>::deserialize(raw_value.into_inner()) {
|
||||
Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)),
|
||||
Ok(value) => match $name {
|
||||
Some(_) => {
|
||||
errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span()));
|
||||
}
|
||||
None => {
|
||||
$name = Some(value);
|
||||
value_spans.insert(name.get_ref().as_str().to_string(), value_span);
|
||||
// $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(ConfError::spanned(self.0, concat!(
|
||||
"duplicate field `", stringify!($new_conf),
|
||||
"` (provided as `", stringify!($name), "`)"
|
||||
), None, name.span())),
|
||||
None => $new_conf = $name.clone(),
|
||||
})?
|
||||
},
|
||||
}
|
||||
let (value, value_span) =
|
||||
deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?);
|
||||
// Was this field set previously?
|
||||
if $name.is_some() {
|
||||
errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span()));
|
||||
continue;
|
||||
}
|
||||
$name = Some(value);
|
||||
value_spans.insert(name.get_ref().as_str().to_string(), value_span);
|
||||
// If this is a deprecated field, was the new field (`$new_conf`) set previously?
|
||||
// Note that `$new_conf` is one of the defined `$name`s.
|
||||
$(match $new_conf {
|
||||
Some(_) => errors.push(ConfError::spanned(self.0, concat!(
|
||||
"duplicate field `", stringify!($new_conf),
|
||||
"` (provided as `", stringify!($name), "`)"
|
||||
), None, name.span())),
|
||||
None => $new_conf = $name.clone(),
|
||||
})?
|
||||
})*
|
||||
// ignore contents of the third_party key
|
||||
Ok(Field::third_party) => drop(map.next_value::<IgnoredAny>())
|
||||
Field::third_party => drop(map.next_value::<IgnoredAny>())
|
||||
}
|
||||
}
|
||||
let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
|
||||
|
|
@ -275,6 +324,22 @@ macro_rules! define_Conf {
|
|||
};
|
||||
}
|
||||
|
||||
fn union(x: &Range<usize>, y: &Range<usize>) -> Range<usize> {
|
||||
Range {
|
||||
start: cmp::min(x.start, y.start),
|
||||
end: cmp::max(x.end, y.end),
|
||||
}
|
||||
}
|
||||
|
||||
fn span_from_toml_range(file: &SourceFile, span: Range<usize>) -> Span {
|
||||
Span::new(
|
||||
file.start_pos + BytePos::from_usize(span.start),
|
||||
file.start_pos + BytePos::from_usize(span.end),
|
||||
SyntaxContext::root(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
define_Conf! {
|
||||
/// Which crates to allow absolute paths from
|
||||
#[lints(absolute_paths)]
|
||||
|
|
@ -461,6 +526,7 @@ define_Conf! {
|
|||
)]
|
||||
avoid_breaking_exported_api: bool = true,
|
||||
/// The list of types which may not be held across an await point.
|
||||
#[disallowed_paths_allow_replacements = false]
|
||||
#[lints(await_holding_invalid_type)]
|
||||
await_holding_invalid_types: Vec<DisallowedPathWithoutReplacement> = Vec::new(),
|
||||
/// DEPRECATED LINT: BLACKLISTED_NAME.
|
||||
|
|
@ -474,6 +540,26 @@ define_Conf! {
|
|||
/// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
|
||||
#[lints(incompatible_msrv)]
|
||||
check_incompatible_msrv_in_tests: bool = false,
|
||||
/// Whether to suggest reordering constructor fields when initializers are present.
|
||||
///
|
||||
/// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
|
||||
/// suggested code would compile, it can change semantics if the initializer expressions have side effects. The
|
||||
/// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
|
||||
///
|
||||
/// ```rust
|
||||
/// struct MyStruct {
|
||||
/// vector: Vec<u32>,
|
||||
/// length: usize
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// let vector = vec![1,2,3];
|
||||
/// MyStruct { length: vector.len(), vector};
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
|
||||
#[lints(inconsistent_struct_constructor)]
|
||||
check_inconsistent_struct_field_initializers: bool = false,
|
||||
/// Whether to also run the listed lints on private items.
|
||||
#[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)]
|
||||
check_private_items: bool = false,
|
||||
|
|
@ -486,9 +572,11 @@ define_Conf! {
|
|||
#[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
|
||||
cyclomatic_complexity_threshold: u64 = 25,
|
||||
/// The list of disallowed macros, written as fully qualified paths.
|
||||
#[disallowed_paths_allow_replacements = true]
|
||||
#[lints(disallowed_macros)]
|
||||
disallowed_macros: Vec<DisallowedPath> = Vec::new(),
|
||||
/// The list of disallowed methods, written as fully qualified paths.
|
||||
#[disallowed_paths_allow_replacements = true]
|
||||
#[lints(disallowed_methods)]
|
||||
disallowed_methods: Vec<DisallowedPath> = Vec::new(),
|
||||
/// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
|
||||
|
|
@ -497,6 +585,7 @@ define_Conf! {
|
|||
#[lints(disallowed_names)]
|
||||
disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(),
|
||||
/// The list of disallowed types, written as fully qualified paths.
|
||||
#[disallowed_paths_allow_replacements = true]
|
||||
#[lints(disallowed_types)]
|
||||
disallowed_types: Vec<DisallowedPath> = Vec::new(),
|
||||
/// The list of words this lint should not consider as identifiers needing ticks. The value
|
||||
|
|
@ -549,25 +638,15 @@ define_Conf! {
|
|||
/// The maximum size of the `Err`-variant in a `Result` returned from a function
|
||||
#[lints(result_large_err)]
|
||||
large_error_threshold: u64 = 128,
|
||||
/// Whether collapsible `if` chains are linted if they contain comments inside the parts
|
||||
/// that would be collapsed.
|
||||
#[lints(collapsible_if)]
|
||||
lint_commented_code: bool = false,
|
||||
/// Whether to suggest reordering constructor fields when initializers are present.
|
||||
/// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers
|
||||
///
|
||||
/// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
|
||||
/// suggested code would compile, it can change semantics if the initializer expressions have side effects. The
|
||||
/// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
|
||||
///
|
||||
/// ```rust
|
||||
/// struct MyStruct {
|
||||
/// vector: Vec<u32>,
|
||||
/// length: usize
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// let vector = vec![1,2,3];
|
||||
/// MyStruct { length: vector.len(), vector};
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
|
||||
#[lints(inconsistent_struct_constructor)]
|
||||
/// Use the `check-inconsistent-struct-field-initializers` configuration instead.
|
||||
#[conf_deprecated("Please use `check-inconsistent-struct-field-initializers` instead", check_inconsistent_struct_field_initializers)]
|
||||
lint_inconsistent_struct_field_initializers: bool = false,
|
||||
/// The lower bound for linting decimal literals
|
||||
#[lints(decimal_literal_representation)]
|
||||
|
|
@ -635,6 +714,7 @@ define_Conf! {
|
|||
iter_kv_map,
|
||||
legacy_numeric_constants,
|
||||
lines_filter_map_ok,
|
||||
manual_abs_diff,
|
||||
manual_bits,
|
||||
manual_c_str_literals,
|
||||
manual_clamp,
|
||||
|
|
@ -642,6 +722,7 @@ define_Conf! {
|
|||
manual_flatten,
|
||||
manual_hash_one,
|
||||
manual_is_ascii_check,
|
||||
manual_is_power_of_two,
|
||||
manual_let_else,
|
||||
manual_midpoint,
|
||||
manual_non_exhaustive,
|
||||
|
|
@ -760,7 +841,8 @@ define_Conf! {
|
|||
/// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
|
||||
#[lints(verbose_bit_mask)]
|
||||
verbose_bit_mask_threshold: u64 = 1,
|
||||
/// Whether to allow certain wildcard imports (prelude, super in tests).
|
||||
/// Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests,
|
||||
/// or for `pub use` reexports.
|
||||
#[lints(wildcard_imports)]
|
||||
warn_on_all_wildcard_imports: bool = false,
|
||||
/// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
|
||||
|
|
@ -981,7 +1063,23 @@ impl serde::de::Error for FieldError {
|
|||
// set and allows it.
|
||||
use fmt::Write;
|
||||
|
||||
let mut expected = expected.to_vec();
|
||||
let metadata = get_configuration_metadata();
|
||||
let deprecated = metadata
|
||||
.iter()
|
||||
.filter_map(|conf| {
|
||||
if conf.deprecation_reason.is_some() {
|
||||
Some(conf.name.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut expected = expected
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|name| !deprecated.contains(name))
|
||||
.collect::<Vec<_>>();
|
||||
expected.sort_unstable();
|
||||
|
||||
let (rows, column_widths) = calculate_dimensions(&expected);
|
||||
|
|
@ -1064,7 +1162,13 @@ mod tests {
|
|||
fn configs_are_tested() {
|
||||
let mut names: HashSet<String> = crate::get_configuration_metadata()
|
||||
.into_iter()
|
||||
.map(|meta| meta.name.replace('_', "-"))
|
||||
.filter_map(|meta| {
|
||||
if meta.deprecation_reason.is_none() {
|
||||
Some(meta.name.replace('_', "-"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let toml_files = WalkDir::new("../tests")
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
rustc::untranslatable_diagnostic
|
||||
)]
|
||||
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_middle;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use clippy_utils::def_path_def_ids;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::PrimTy;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -21,6 +23,17 @@ pub struct DisallowedPath<const REPLACEMENT_ALLOWED: bool = true> {
|
|||
path: String,
|
||||
reason: Option<String>,
|
||||
replacement: Option<String>,
|
||||
/// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing
|
||||
/// definition.
|
||||
///
|
||||
/// This could be useful when conditional compilation is used, or when a clippy.toml file is
|
||||
/// shared among multiple projects.
|
||||
allow_invalid: bool,
|
||||
/// The span of the `DisallowedPath`.
|
||||
///
|
||||
/// Used for diagnostics.
|
||||
#[serde(skip_serializing)]
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<REPLACEMENT_ALLOWED> {
|
||||
|
|
@ -36,6 +49,8 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<R
|
|||
path: enum_.path().to_owned(),
|
||||
reason: enum_.reason().map(ToOwned::to_owned),
|
||||
replacement: enum_.replacement().map(ToOwned::to_owned),
|
||||
allow_invalid: enum_.allow_invalid(),
|
||||
span: Span::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +65,8 @@ enum DisallowedPathEnum {
|
|||
path: String,
|
||||
reason: Option<String>,
|
||||
replacement: Option<String>,
|
||||
#[serde(rename = "allow-invalid")]
|
||||
allow_invalid: Option<bool>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +75,7 @@ impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
|
|||
&self.path
|
||||
}
|
||||
|
||||
pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_, REPLACEMENT_ALLOWED> {
|
||||
pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) {
|
||||
move |diag| {
|
||||
if let Some(replacement) = &self.replacement {
|
||||
diag.span_suggestion(
|
||||
|
|
@ -72,6 +89,14 @@ impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
self.span = span;
|
||||
}
|
||||
}
|
||||
|
||||
impl DisallowedPathEnum {
|
||||
|
|
@ -94,20 +119,87 @@ impl DisallowedPathEnum {
|
|||
Self::Simple(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_invalid(&self) -> bool {
|
||||
match &self {
|
||||
Self::WithReason { allow_invalid, .. } => allow_invalid.unwrap_or_default(),
|
||||
Self::Simple(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a map of disallowed items to the reason they were disallowed.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
|
||||
tcx: TyCtxt<'_>,
|
||||
disallowed: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
|
||||
) -> DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> {
|
||||
disallowed
|
||||
.iter()
|
||||
.map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x))
|
||||
.flat_map(|(name, path, disallowed_path)| {
|
||||
def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path)))
|
||||
})
|
||||
.collect()
|
||||
disallowed_paths: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
|
||||
def_kind_predicate: impl Fn(DefKind) -> bool,
|
||||
predicate_description: &str,
|
||||
allow_prim_tys: bool,
|
||||
) -> (
|
||||
DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
|
||||
FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
|
||||
) {
|
||||
let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> = DefIdMap::default();
|
||||
let mut prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> =
|
||||
FxHashMap::default();
|
||||
for disallowed_path in disallowed_paths {
|
||||
let path = disallowed_path.path();
|
||||
let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::<Vec<_>>());
|
||||
|
||||
let mut found_def_id = None;
|
||||
let mut found_prim_ty = false;
|
||||
resolutions.retain(|res| match res {
|
||||
Res::Def(def_kind, def_id) => {
|
||||
found_def_id = Some(*def_id);
|
||||
def_kind_predicate(*def_kind)
|
||||
},
|
||||
Res::PrimTy(_) => {
|
||||
found_prim_ty = true;
|
||||
allow_prim_tys
|
||||
},
|
||||
_ => false,
|
||||
});
|
||||
|
||||
if resolutions.is_empty() {
|
||||
let span = disallowed_path.span();
|
||||
|
||||
if let Some(def_id) = found_def_id {
|
||||
tcx.sess.dcx().span_warn(
|
||||
span,
|
||||
format!(
|
||||
"expected a {predicate_description}, found {} {}",
|
||||
tcx.def_descr_article(def_id),
|
||||
tcx.def_descr(def_id)
|
||||
),
|
||||
);
|
||||
} else if found_prim_ty {
|
||||
tcx.sess.dcx().span_warn(
|
||||
span,
|
||||
format!("expected a {predicate_description}, found a primitive type",),
|
||||
);
|
||||
} else if !disallowed_path.allow_invalid {
|
||||
tcx.sess.dcx().span_warn(
|
||||
span,
|
||||
format!("`{path}` does not refer to an existing {predicate_description}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for res in resolutions {
|
||||
match res {
|
||||
Res::Def(_, def_id) => {
|
||||
def_ids.insert(def_id, (path, disallowed_path));
|
||||
},
|
||||
Res::PrimTy(ty) => {
|
||||
prim_tys.insert(ty, (path, disallowed_path));
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(def_ids, prim_tys)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#[allow(unused_extern_crates)]
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_lexer;
|
||||
extern crate rustc_literal_escaper;
|
||||
|
||||
pub mod dogfood;
|
||||
pub mod fmt;
|
||||
|
|
|
|||
|
|
@ -170,7 +170,6 @@ enum DevCommand {
|
|||
"restriction",
|
||||
"cargo",
|
||||
"nursery",
|
||||
"internal",
|
||||
],
|
||||
default_value = "nursery",
|
||||
)]
|
||||
|
|
@ -334,7 +333,7 @@ struct SyncCommand {
|
|||
#[derive(Subcommand)]
|
||||
enum SyncSubcommand {
|
||||
#[command(name = "update_nightly")]
|
||||
/// Update nightly version in rust-toolchain and `clippy_utils`
|
||||
/// Update nightly version in `rust-toolchain.toml` and `clippy_utils`
|
||||
UpdateNightly,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) {
|
|||
|
||||
println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`");
|
||||
if !standalone {
|
||||
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
|
||||
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain.toml` changes");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub fn update_nightly() {
|
|||
let date = Utc::now().format("%Y-%m-%d").to_string();
|
||||
replace_region_in_file(
|
||||
UpdateMode::Change,
|
||||
Path::new("rust-toolchain"),
|
||||
Path::new("rust-toolchain.toml"),
|
||||
"# begin autogenerated nightly\n",
|
||||
"# end autogenerated nightly",
|
||||
|res| {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file};
|
||||
use aho_corasick::AhoCorasickBuilder;
|
||||
use itertools::Itertools;
|
||||
use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape};
|
||||
use rustc_lexer::{LiteralKind, TokenKind, tokenize};
|
||||
use rustc_literal_escaper::{Mode, unescape_unicode};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::{self, Write};
|
||||
|
|
@ -37,9 +38,8 @@ fn generate_lint_files(
|
|||
deprecated_lints: &[DeprecatedLint],
|
||||
renamed_lints: &[RenamedLint],
|
||||
) {
|
||||
let internal_lints = Lint::internal_lints(lints);
|
||||
let mut usable_lints = Lint::usable_lints(lints);
|
||||
usable_lints.sort_by_key(|lint| lint.name.clone());
|
||||
let mut lints = lints.to_owned();
|
||||
lints.sort_by_key(|lint| lint.name.clone());
|
||||
|
||||
replace_region_in_file(
|
||||
update_mode,
|
||||
|
|
@ -47,7 +47,7 @@ fn generate_lint_files(
|
|||
"[There are over ",
|
||||
" lints included in this crate!]",
|
||||
|res| {
|
||||
write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
|
||||
write!(res, "{}", round_to_fifty(lints.len())).unwrap();
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ fn generate_lint_files(
|
|||
"[There are over ",
|
||||
" lints included in this crate!]",
|
||||
|res| {
|
||||
write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
|
||||
write!(res, "{}", round_to_fifty(lints.len())).unwrap();
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ fn generate_lint_files(
|
|||
"<!-- begin autogenerated links to lint list -->\n",
|
||||
"<!-- end autogenerated links to lint list -->",
|
||||
|res| {
|
||||
for lint in usable_lints
|
||||
for lint in lints
|
||||
.iter()
|
||||
.map(|l| &*l.name)
|
||||
.chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
|
||||
|
|
@ -86,7 +86,7 @@ fn generate_lint_files(
|
|||
"// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
|
||||
"// end lints modules, do not remove this comment, it’s used in `update_lints`",
|
||||
|res| {
|
||||
for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
|
||||
for lint_mod in lints.iter().map(|l| &l.module).unique().sorted() {
|
||||
writeln!(res, "mod {lint_mod};").unwrap();
|
||||
}
|
||||
},
|
||||
|
|
@ -95,7 +95,7 @@ fn generate_lint_files(
|
|||
process_file(
|
||||
"clippy_lints/src/declared_lints.rs",
|
||||
update_mode,
|
||||
&gen_declared_lints(internal_lints.iter(), usable_lints.iter()),
|
||||
&gen_declared_lints(lints.iter()),
|
||||
);
|
||||
|
||||
let content = gen_deprecated_lints_test(deprecated_lints);
|
||||
|
|
@ -106,10 +106,9 @@ fn generate_lint_files(
|
|||
}
|
||||
|
||||
pub fn print_lints() {
|
||||
let (lint_list, _, _) = gather_all();
|
||||
let usable_lints = Lint::usable_lints(&lint_list);
|
||||
let usable_lint_count = usable_lints.len();
|
||||
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
|
||||
let (lints, _, _) = gather_all();
|
||||
let lint_count = lints.len();
|
||||
let grouped_by_lint_group = Lint::by_lint_group(lints.into_iter());
|
||||
|
||||
for (lint_group, mut lints) in grouped_by_lint_group {
|
||||
println!("\n## {lint_group}");
|
||||
|
|
@ -121,7 +120,7 @@ pub fn print_lints() {
|
|||
}
|
||||
}
|
||||
|
||||
println!("there are {usable_lint_count} lints");
|
||||
println!("there are {lint_count} lints");
|
||||
}
|
||||
|
||||
/// Runs the `rename_lint` command.
|
||||
|
|
@ -402,53 +401,53 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
|
|||
}
|
||||
}
|
||||
|
||||
if path.exists() {
|
||||
if let Some(lint) = lints.iter().find(|l| l.name == name) {
|
||||
if lint.module == name {
|
||||
// The lint name is the same as the file, we can just delete the entire file
|
||||
fs::remove_file(path)?;
|
||||
} else {
|
||||
// We can't delete the entire file, just remove the declaration
|
||||
if path.exists()
|
||||
&& let Some(lint) = lints.iter().find(|l| l.name == name)
|
||||
{
|
||||
if lint.module == name {
|
||||
// The lint name is the same as the file, we can just delete the entire file
|
||||
fs::remove_file(path)?;
|
||||
} else {
|
||||
// We can't delete the entire file, just remove the declaration
|
||||
|
||||
if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
|
||||
// Remove clippy_lints/src/some_mod/some_lint.rs
|
||||
let mut lint_mod_path = path.to_path_buf();
|
||||
lint_mod_path.set_file_name(name);
|
||||
lint_mod_path.set_extension("rs");
|
||||
if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
|
||||
// Remove clippy_lints/src/some_mod/some_lint.rs
|
||||
let mut lint_mod_path = path.to_path_buf();
|
||||
lint_mod_path.set_file_name(name);
|
||||
lint_mod_path.set_extension("rs");
|
||||
|
||||
let _ = fs::remove_file(lint_mod_path);
|
||||
}
|
||||
|
||||
let mut content =
|
||||
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
|
||||
eprintln!(
|
||||
"warn: you will have to manually remove any code related to `{name}` from `{}`",
|
||||
path.display()
|
||||
);
|
||||
|
||||
assert!(
|
||||
content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
|
||||
"error: `{}` does not contain lint `{}`'s declaration",
|
||||
path.display(),
|
||||
lint.name
|
||||
);
|
||||
|
||||
// Remove lint declaration (declare_clippy_lint!)
|
||||
content.replace_range(lint.declaration_range.clone(), "");
|
||||
|
||||
// Remove the module declaration (mod xyz;)
|
||||
let mod_decl = format!("\nmod {name};");
|
||||
content = content.replacen(&mod_decl, "", 1);
|
||||
|
||||
remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
|
||||
fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
|
||||
let _ = fs::remove_file(lint_mod_path);
|
||||
}
|
||||
|
||||
remove_test_assets(name);
|
||||
remove_lint(name, lints);
|
||||
return Ok(true);
|
||||
let mut content =
|
||||
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
|
||||
eprintln!(
|
||||
"warn: you will have to manually remove any code related to `{name}` from `{}`",
|
||||
path.display()
|
||||
);
|
||||
|
||||
assert!(
|
||||
content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
|
||||
"error: `{}` does not contain lint `{}`'s declaration",
|
||||
path.display(),
|
||||
lint.name
|
||||
);
|
||||
|
||||
// Remove lint declaration (declare_clippy_lint!)
|
||||
content.replace_range(lint.declaration_range.clone(), "");
|
||||
|
||||
// Remove the module declaration (mod xyz;)
|
||||
let mod_decl = format!("\nmod {name};");
|
||||
content = content.replacen(&mod_decl, "", 1);
|
||||
|
||||
remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
|
||||
fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
|
||||
}
|
||||
|
||||
remove_test_assets(name);
|
||||
remove_lint(name, lints);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
|
|
@ -527,22 +526,6 @@ impl Lint {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns all non-deprecated lints and non-internal lints
|
||||
#[must_use]
|
||||
fn usable_lints(lints: &[Self]) -> Vec<Self> {
|
||||
lints
|
||||
.iter()
|
||||
.filter(|l| !l.group.starts_with("internal"))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns all internal lints
|
||||
#[must_use]
|
||||
fn internal_lints(lints: &[Self]) -> Vec<Self> {
|
||||
lints.iter().filter(|l| l.group == "internal").cloned().collect()
|
||||
}
|
||||
|
||||
/// Returns the lints in a `HashMap`, grouped by the different lint groups
|
||||
#[must_use]
|
||||
fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
|
||||
|
|
@ -579,23 +562,14 @@ impl RenamedLint {
|
|||
|
||||
/// Generates the code for registering lints
|
||||
#[must_use]
|
||||
fn gen_declared_lints<'a>(
|
||||
internal_lints: impl Iterator<Item = &'a Lint>,
|
||||
usable_lints: impl Iterator<Item = &'a Lint>,
|
||||
) -> String {
|
||||
let mut details: Vec<_> = internal_lints
|
||||
.map(|l| (false, &l.module, l.name.to_uppercase()))
|
||||
.chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase())))
|
||||
.collect();
|
||||
fn gen_declared_lints<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
|
||||
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
|
||||
details.sort_unstable();
|
||||
|
||||
let mut output = GENERATED_FILE_COMMENT.to_string();
|
||||
output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
|
||||
|
||||
for (is_public, module_name, lint_name) in details {
|
||||
if !is_public {
|
||||
output.push_str(" #[cfg(feature = \"internal\")]\n");
|
||||
}
|
||||
for (module_name, lint_name) in details {
|
||||
let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,");
|
||||
}
|
||||
output.push_str("];\n");
|
||||
|
|
@ -830,7 +804,7 @@ fn remove_line_splices(s: &str) -> String {
|
|||
.and_then(|s| s.strip_suffix('"'))
|
||||
.unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
|
||||
let mut res = String::with_capacity(s.len());
|
||||
unescape::unescape_unicode(s, unescape::Mode::Str, &mut |range, ch| {
|
||||
unescape_unicode(s, Mode::Str, &mut |range, ch| {
|
||||
if ch.is_ok() {
|
||||
res.push_str(&s[range]);
|
||||
}
|
||||
|
|
@ -936,41 +910,6 @@ mod tests {
|
|||
assert_eq!(expected, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_usable_lints() {
|
||||
let lints = vec![
|
||||
Lint::new(
|
||||
"should_assert_eq2",
|
||||
"Not Deprecated",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
Lint::new(
|
||||
"should_assert_eq2",
|
||||
"internal",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
Lint::new(
|
||||
"should_assert_eq2",
|
||||
"internal_style",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
),
|
||||
];
|
||||
let expected = vec![Lint::new(
|
||||
"should_assert_eq2",
|
||||
"Not Deprecated",
|
||||
"\"abc\"",
|
||||
"module_name",
|
||||
Range::default(),
|
||||
)];
|
||||
assert_eq!(expected, Lint::usable_lints(&lints));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_by_lint_group() {
|
||||
let lints = vec![
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ pub fn clippy_project_root() -> PathBuf {
|
|||
let current_dir = std::env::current_dir().unwrap();
|
||||
for path in current_dir.ancestors() {
|
||||
let result = fs::read_to_string(path.join("Cargo.toml"));
|
||||
if let Err(err) = &result {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
continue;
|
||||
}
|
||||
if let Err(err) = &result
|
||||
&& err.kind() == io::ErrorKind::NotFound
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let content = result.unwrap();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
# begin autogenerated version
|
||||
version = "0.1.87"
|
||||
version = "0.1.88"
|
||||
# end autogenerated version
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
|
|
@ -19,10 +19,7 @@ itertools = "0.12"
|
|||
quine-mc_cluskey = "0.2"
|
||||
regex-syntax = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
tempfile = { version = "3.3.0", optional = true }
|
||||
toml = "0.7.3"
|
||||
regex = { version = "1.5", optional = true }
|
||||
unicode-normalization = "0.1"
|
||||
unicode-script = { version = "0.5", default-features = false }
|
||||
semver = "1.0"
|
||||
|
|
@ -31,10 +28,6 @@ url = "2.2"
|
|||
[dev-dependencies]
|
||||
walkdir = "2.3"
|
||||
|
||||
[features]
|
||||
# build clippy with internal lints enabled, off by default
|
||||
internal = ["serde_json", "tempfile", "regex"]
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This crate uses #[feature(rustc_private)]
|
||||
rustc_private = true
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use clippy_config::types::{
|
|||
SourceItemOrderingWithinModuleItemGroupings,
|
||||
};
|
||||
use clippy_utils::diagnostics::span_lint_and_note;
|
||||
use clippy_utils::is_cfg_test;
|
||||
use rustc_hir::{
|
||||
AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, Variant,
|
||||
VariantData,
|
||||
|
|
@ -263,10 +264,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(cur_v) = cur_v {
|
||||
if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span {
|
||||
Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
|
||||
}
|
||||
if let Some(cur_v) = cur_v
|
||||
&& cur_v.ident.name.as_str() > variant.ident.name.as_str()
|
||||
&& cur_v.span != variant.span
|
||||
{
|
||||
Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
|
||||
}
|
||||
cur_v = Some(variant);
|
||||
}
|
||||
|
|
@ -278,10 +280,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some(cur_f) = cur_f {
|
||||
if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span {
|
||||
Self::lint_member_name(cx, &field.ident, &cur_f.ident);
|
||||
}
|
||||
if let Some(cur_f) = cur_f
|
||||
&& cur_f.ident.name.as_str() > field.ident.name.as_str()
|
||||
&& cur_f.span != field.span
|
||||
{
|
||||
Self::lint_member_name(cx, &field.ident, &cur_f.ident);
|
||||
}
|
||||
cur_f = Some(field);
|
||||
}
|
||||
|
|
@ -342,7 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
|
|||
struct CurItem<'a> {
|
||||
item: &'a Item<'a>,
|
||||
order: usize,
|
||||
name: String,
|
||||
name: Option<String>,
|
||||
}
|
||||
let mut cur_t: Option<CurItem<'_>> = None;
|
||||
|
||||
|
|
@ -359,32 +362,36 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
|
|||
// as no sorting by source map/line of code has to be applied.
|
||||
//
|
||||
for item in items {
|
||||
if is_cfg_test(cx.tcx, item.hir_id()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if item.span.in_external_macro(cx.sess().source_map()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ident = if let Some(ident) = item.kind.ident() {
|
||||
ident
|
||||
} else if let ItemKind::Impl(_) = item.kind
|
||||
&& !get_item_name(item).is_empty()
|
||||
{
|
||||
rustc_span::Ident::empty() // FIXME: a bit strange, is there a better way to do it?
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if ident.name.as_str().starts_with('_') {
|
||||
// Filters out unnamed macro-like impls for various derives,
|
||||
// e.g. serde::Serialize or num_derive::FromPrimitive.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ident.name == rustc_span::sym::std && item.span.is_dummy() {
|
||||
if let ItemKind::ExternCrate(None, _) = item.kind {
|
||||
// Filters the auto-included Rust standard library.
|
||||
if let Some(ident) = item.kind.ident() {
|
||||
if ident.name.as_str().starts_with('_') {
|
||||
// Filters out unnamed macro-like impls for various derives,
|
||||
// e.g. serde::Serialize or num_derive::FromPrimitive.
|
||||
continue;
|
||||
}
|
||||
println!("Unknown item: {item:?}");
|
||||
|
||||
if ident.name == rustc_span::sym::std && item.span.is_dummy() {
|
||||
if let ItemKind::ExternCrate(None, _) = item.kind {
|
||||
// Filters the auto-included Rust standard library.
|
||||
continue;
|
||||
}
|
||||
if cfg!(debug_assertions) {
|
||||
rustc_middle::bug!("unknown item: {item:?}");
|
||||
}
|
||||
}
|
||||
} else if let ItemKind::Impl(_) = item.kind
|
||||
&& get_item_name(item).is_some()
|
||||
{
|
||||
// keep going below
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
let item_kind = convert_module_item_kind(&item.kind);
|
||||
|
|
@ -493,7 +500,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte
|
|||
/// further in the [Rust Reference, Paths Chapter][rust_ref].
|
||||
///
|
||||
/// [rust_ref]: https://doc.rust-lang.org/reference/paths.html#crate-1
|
||||
fn get_item_name(item: &Item<'_>) -> String {
|
||||
fn get_item_name(item: &Item<'_>) -> Option<String> {
|
||||
match item.kind {
|
||||
ItemKind::Impl(im) => {
|
||||
if let TyKind::Path(path) = im.self_ty.kind {
|
||||
|
|
@ -513,27 +520,19 @@ fn get_item_name(item: &Item<'_>) -> String {
|
|||
}
|
||||
|
||||
segs.push(String::new());
|
||||
segs.join("!!")
|
||||
Some(segs.join("!!"))
|
||||
},
|
||||
QPath::TypeRelative(_, _path_seg) => {
|
||||
// This case doesn't exist in the clippy tests codebase.
|
||||
String::new()
|
||||
None
|
||||
},
|
||||
QPath::LangItem(_, _) => String::new(),
|
||||
QPath::LangItem(_, _) => None,
|
||||
}
|
||||
} else {
|
||||
// Impls for anything that isn't a named type can be skipped.
|
||||
String::new()
|
||||
None
|
||||
}
|
||||
},
|
||||
// FIXME: `Ident::empty` for anonymous items is a bit strange, is there
|
||||
// a better way to do it?
|
||||
_ => item
|
||||
.kind
|
||||
.ident()
|
||||
.unwrap_or(rustc_span::Ident::empty())
|
||||
.name
|
||||
.as_str()
|
||||
.to_owned(),
|
||||
_ => item.kind.ident().map(|name| name.as_str().to_owned()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@ declare_clippy_lint! {
|
|||
/// regardless of whether good alternatives exist or not. If you want more
|
||||
/// precise lints for `as`, please consider using these separate lints:
|
||||
///
|
||||
/// - clippy::cast_lossless
|
||||
/// - clippy::cast_possible_truncation
|
||||
/// - clippy::cast_possible_wrap
|
||||
/// - clippy::cast_precision_loss
|
||||
/// - clippy::cast_sign_loss
|
||||
/// - clippy::char_lit_as_u8
|
||||
/// - clippy::fn_to_numeric_cast
|
||||
/// - clippy::fn_to_numeric_cast_with_truncation
|
||||
/// - clippy::ptr_as_ptr
|
||||
/// - clippy::unnecessary_cast
|
||||
/// - invalid_reference_casting
|
||||
/// - `clippy::cast_lossless`
|
||||
/// - `clippy::cast_possible_truncation`
|
||||
/// - `clippy::cast_possible_wrap`
|
||||
/// - `clippy::cast_precision_loss`
|
||||
/// - `clippy::cast_sign_loss`
|
||||
/// - `clippy::char_lit_as_u8`
|
||||
/// - `clippy::fn_to_numeric_cast`
|
||||
/// - `clippy::fn_to_numeric_cast_with_truncation`
|
||||
/// - `clippy::ptr_as_ptr`
|
||||
/// - `clippy::unnecessary_cast`
|
||||
/// - `invalid_reference_casting`
|
||||
///
|
||||
/// There is a good explanation the reason why this lint should work in this
|
||||
/// way and how it is useful [in this
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ fn build_sugg<'tcx>(
|
|||
// `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
|
||||
Sugg::hir_with_applicability(cx, lhs, "_", app)
|
||||
}
|
||||
.maybe_par();
|
||||
.maybe_paren();
|
||||
|
||||
// Determine whether we need to reference the argument to clone_from().
|
||||
let clone_receiver_type = cx.typeck_results().expr_ty(fn_arg);
|
||||
|
|
@ -284,7 +284,7 @@ fn build_sugg<'tcx>(
|
|||
let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
|
||||
// `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)`
|
||||
// `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)`
|
||||
let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_par();
|
||||
let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_paren();
|
||||
let inner_type = cx.typeck_results().expr_ty(ref_expr);
|
||||
// If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it
|
||||
// deref to a mutable reference.
|
||||
|
|
@ -296,7 +296,7 @@ fn build_sugg<'tcx>(
|
|||
} else {
|
||||
// `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)`
|
||||
// `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)`
|
||||
Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_par().mut_addr()
|
||||
Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_paren().mut_addr()
|
||||
};
|
||||
|
||||
match call_kind {
|
||||
|
|
|
|||
|
|
@ -8,17 +8,18 @@ use rustc_span::{DUMMY_SP, sym};
|
|||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) {
|
||||
for lint in items {
|
||||
if let Some(lint_name) = extract_clippy_lint(lint) {
|
||||
if lint_name.as_str() == "restriction" && name != sym::allow {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
lint.span(),
|
||||
"`clippy::restriction` is not meant to be enabled as a group",
|
||||
None,
|
||||
"enable the restriction lints you need individually",
|
||||
);
|
||||
}
|
||||
if let Some(lint_name) = extract_clippy_lint(lint)
|
||||
&& lint_name.as_str() == "restriction"
|
||||
&& name != sym::allow
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
lint.span(),
|
||||
"`clippy::restriction` is not meant to be enabled as a group",
|
||||
None,
|
||||
"enable the restriction lints you need individually",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ use rustc_span::Span;
|
|||
use semver::Version;
|
||||
|
||||
pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) {
|
||||
if let LitKind::Str(is, _) = lit.kind {
|
||||
if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() {
|
||||
return;
|
||||
}
|
||||
if let LitKind::Str(is, _) = lit.kind
|
||||
&& (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok())
|
||||
{
|
||||
return;
|
||||
}
|
||||
span_lint(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -36,10 +36,7 @@ fn check_duplicated_attr(
|
|||
}
|
||||
let Some(ident) = attr.ident() else { return };
|
||||
let name = ident.name;
|
||||
if name == sym::doc
|
||||
|| name == sym::cfg_attr_trace
|
||||
|| name == sym::rustc_on_unimplemented
|
||||
|| name == sym::reason {
|
||||
if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason {
|
||||
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
|
||||
// conditions are the same.
|
||||
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ mod useless_attribute;
|
|||
mod utils;
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::msrvs::{self, Msrv, MsrvStack};
|
||||
use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind};
|
||||
use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind};
|
||||
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -448,6 +449,31 @@ declare_clippy_lint! {
|
|||
"duplicated attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for ignored tests without messages.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The reason for ignoring the test may not be obvious.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[test]
|
||||
/// #[ignore]
|
||||
/// fn test() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[test]
|
||||
/// #[ignore = "Some good reason"]
|
||||
/// fn test() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub IGNORE_WITHOUT_REASON,
|
||||
pedantic,
|
||||
"ignored tests without messages"
|
||||
}
|
||||
|
||||
pub struct Attributes {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
|
@ -532,6 +558,7 @@ impl_lint_pass!(PostExpansionEarlyAttributes => [
|
|||
ALLOW_ATTRIBUTES,
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
DEPRECATED_SEMVER,
|
||||
IGNORE_WITHOUT_REASON,
|
||||
USELESS_ATTRIBUTE,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
|
|
@ -546,28 +573,27 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
|
|||
}
|
||||
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if let Some(items) = &attr.meta_item_list() {
|
||||
if let Some(ident) = attr.ident() {
|
||||
if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes::check(cx, attr);
|
||||
}
|
||||
if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION)
|
||||
if let Some(items) = &attr.meta_item_list()
|
||||
&& let Some(ident) = attr.ident()
|
||||
{
|
||||
if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes::check(cx, attr);
|
||||
}
|
||||
if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
|
||||
allow_attributes_without_reason::check(cx, ident.name, items, attr);
|
||||
}
|
||||
if is_lint_level(ident.name, attr.id) {
|
||||
blanket_clippy_restriction_lints::check(cx, ident.name, items);
|
||||
}
|
||||
if items.is_empty() || !attr.has_name(sym::deprecated) {
|
||||
return;
|
||||
}
|
||||
for item in items {
|
||||
if let MetaItemInner::MetaItem(mi) = &item
|
||||
&& let MetaItemKind::NameValue(lit) = &mi.kind
|
||||
&& mi.has_name(sym::since)
|
||||
{
|
||||
allow_attributes_without_reason::check(cx, ident.name, items, attr);
|
||||
}
|
||||
if is_lint_level(ident.name, attr.id) {
|
||||
blanket_clippy_restriction_lints::check(cx, ident.name, items);
|
||||
}
|
||||
if items.is_empty() || !attr.has_name(sym::deprecated) {
|
||||
return;
|
||||
}
|
||||
for item in items {
|
||||
if let MetaItemInner::MetaItem(mi) = &item
|
||||
&& let MetaItemKind::NameValue(lit) = &mi.kind
|
||||
&& mi.has_name(sym::since)
|
||||
{
|
||||
deprecated_semver::check(cx, item.span(), lit);
|
||||
}
|
||||
deprecated_semver::check(cx, item.span(), lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -575,6 +601,22 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
|
|||
if attr.has_name(sym::should_panic) {
|
||||
should_panic_without_expect::check(cx, attr);
|
||||
}
|
||||
|
||||
if attr.has_name(sym::ignore)
|
||||
&& match &attr.kind {
|
||||
AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }),
|
||||
AttrKind::DocComment(..) => true,
|
||||
}
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
IGNORE_WITHOUT_REASON,
|
||||
attr.span,
|
||||
"`#[ignore]` without reason",
|
||||
None,
|
||||
"add a reason with `= \"..\"`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute],
|
|||
diag.warn(
|
||||
"unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI",
|
||||
)
|
||||
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
|
||||
.help("qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
|
||||
.span_label(packed_span, "`packed` representation set here");
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,75 +14,75 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
|
|||
if attr.span.in_external_macro(cx.sess().source_map()) {
|
||||
return;
|
||||
}
|
||||
if let Some(lint_list) = &attr.meta_item_list() {
|
||||
if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) {
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else {
|
||||
return;
|
||||
};
|
||||
if let Some(lint_list) = &attr.meta_item_list()
|
||||
&& attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id))
|
||||
{
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if namespace.is_none()
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"ambiguous_glob_reexports"
|
||||
| "dead_code"
|
||||
| "deprecated"
|
||||
| "hidden_glob_reexports"
|
||||
| "unreachable_pub"
|
||||
| "unused"
|
||||
| "unused_braces"
|
||||
| "unused_import_braces"
|
||||
| "unused_imports"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if namespace.is_none()
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"ambiguous_glob_reexports"
|
||||
| "dead_code"
|
||||
| "deprecated"
|
||||
| "hidden_glob_reexports"
|
||||
| "unreachable_pub"
|
||||
| "unused"
|
||||
| "unused_braces"
|
||||
| "unused_import_braces"
|
||||
| "unused_imports"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if namespace == Some(sym::clippy)
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"wildcard_imports"
|
||||
| "enum_glob_use"
|
||||
| "redundant_pub_crate"
|
||||
| "macro_use_imports"
|
||||
| "unsafe_removed_from_name"
|
||||
| "module_name_repetitions"
|
||||
| "single_component_path_imports"
|
||||
| "disallowed_types"
|
||||
| "unused_trait_names"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
},
|
||||
ItemKind::ExternCrate(..) => {
|
||||
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
||||
return;
|
||||
}
|
||||
if is_word(lint, sym::unused_extern_crates) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
if namespace == Some(sym::clippy)
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"wildcard_imports"
|
||||
| "enum_glob_use"
|
||||
| "redundant_pub_crate"
|
||||
| "macro_use_imports"
|
||||
| "unsafe_removed_from_name"
|
||||
| "module_name_repetitions"
|
||||
| "single_component_path_imports"
|
||||
| "disallowed_types"
|
||||
| "unused_trait_names"
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
},
|
||||
ItemKind::ExternCrate(..) => {
|
||||
if is_word(lint, sym::unused_imports) && skip_unused_imports {
|
||||
return;
|
||||
}
|
||||
if is_word(lint, sym::unused_extern_crates) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
let line_span = first_line_of_span(cx, attr.span);
|
||||
}
|
||||
let line_span = first_line_of_span(cx, attr.span);
|
||||
|
||||
if let Some(src) = line_span.get_source_text(cx) {
|
||||
if src.contains("#[") {
|
||||
#[expect(clippy::collapsible_span_lint_calls)]
|
||||
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
|
||||
diag.span_suggestion(
|
||||
line_span,
|
||||
"if you just forgot a `!`, use",
|
||||
src.replacen("#[", "#![", 1),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(src) = line_span.get_source_text(cx)
|
||||
&& src.contains("#[")
|
||||
{
|
||||
#[expect(clippy::collapsible_span_lint_calls)]
|
||||
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
|
||||
diag.span_suggestion(
|
||||
line_span,
|
||||
"if you just forgot a `!`, use",
|
||||
src.replacen("#[", "#![", 1),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,9 +179,14 @@ pub struct AwaitHolding {
|
|||
|
||||
impl AwaitHolding {
|
||||
pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types),
|
||||
}
|
||||
let (def_ids, _) = create_disallowed_map(
|
||||
tcx,
|
||||
&conf.await_holding_invalid_types,
|
||||
crate::disallowed_types::def_kind_predicate,
|
||||
"type",
|
||||
false,
|
||||
);
|
||||
Self { def_ids }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,10 +197,9 @@ impl<'tcx> LateLintPass<'tcx> for AwaitHolding {
|
|||
def_id,
|
||||
..
|
||||
}) = expr.kind
|
||||
&& let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id)
|
||||
{
|
||||
if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) {
|
||||
self.check_interior_types(cx, coroutine_layout);
|
||||
}
|
||||
self.check_interior_types(cx, coroutine_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::HasSession;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{is_else_clause, is_in_const_context};
|
||||
use clippy_utils::{higher, is_else_clause, is_in_const_context, span_contains_comment};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
|
@ -46,18 +47,25 @@ declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::If(cond, then, Some(else_)) = expr.kind
|
||||
&& matches!(cond.kind, ExprKind::DropTemps(_))
|
||||
if !expr.span.from_expansion()
|
||||
&& let Some(higher::If {
|
||||
cond,
|
||||
then,
|
||||
r#else: Some(r#else),
|
||||
}) = higher::If::hir(expr)
|
||||
&& let Some(then_lit) = as_int_bool_lit(then)
|
||||
&& let Some(else_lit) = as_int_bool_lit(else_)
|
||||
&& let Some(else_lit) = as_int_bool_lit(r#else)
|
||||
&& then_lit != else_lit
|
||||
&& !expr.span.from_expansion()
|
||||
&& !is_in_const_context(cx)
|
||||
{
|
||||
let ty = cx.typeck_results().expr_ty(then);
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
let snippet = {
|
||||
let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
|
||||
let mut sugg = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "..", &mut applicability);
|
||||
if !then_lit {
|
||||
sugg = !sugg;
|
||||
}
|
||||
|
|
@ -72,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
|
|||
s
|
||||
};
|
||||
|
||||
let into_snippet = snippet.clone().maybe_par();
|
||||
let into_snippet = snippet.clone().maybe_paren();
|
||||
let as_snippet = snippet.as_ty(ty);
|
||||
|
||||
span_lint_and_then(
|
||||
|
|
@ -91,10 +99,11 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_int_bool_lit(e: &Expr<'_>) -> Option<bool> {
|
||||
if let ExprKind::Block(b, _) = e.kind
|
||||
fn as_int_bool_lit(expr: &Expr<'_>) -> Option<bool> {
|
||||
if let ExprKind::Block(b, _) = expr.kind
|
||||
&& b.stmts.is_empty()
|
||||
&& let Some(e) = b.expr
|
||||
&& !e.span.from_expansion()
|
||||
&& let ExprKind::Lit(lit) = e.kind
|
||||
&& let LitKind::Int(x, _) = lit.node
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
|
|||
use rustc_lint::{LateContext, LateLintPass, Level};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_span::{Span, SyntaxContext, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -242,11 +242,11 @@ struct Hir2Qmm<'a, 'tcx, 'v> {
|
|||
impl<'v> Hir2Qmm<'_, '_, 'v> {
|
||||
fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
|
||||
for a in a {
|
||||
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
|
||||
if binop.node == op {
|
||||
v = self.extract(op, &[lhs, rhs], v)?;
|
||||
continue;
|
||||
}
|
||||
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind
|
||||
&& binop.node == op
|
||||
{
|
||||
v = self.extract(op, &[lhs, rhs], v)?;
|
||||
continue;
|
||||
}
|
||||
v.push(self.run(a)?);
|
||||
}
|
||||
|
|
@ -349,9 +349,13 @@ impl SuggestContext<'_, '_, '_> {
|
|||
if let Some(str) = simplify_not(self.cx, self.msrv, terminal) {
|
||||
self.output.push_str(&str);
|
||||
} else {
|
||||
self.output.push('!');
|
||||
self.output
|
||||
.push_str(&Sugg::hir_opt(self.cx, terminal)?.maybe_par().to_string());
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = Sugg::hir_with_context(self.cx, terminal, SyntaxContext::root(), "", &mut app);
|
||||
// Ignore the case If the expression is inside a macro expansion, or the default snippet is used
|
||||
if app != Applicability::MachineApplicable {
|
||||
return None;
|
||||
}
|
||||
self.output.push_str(&(!snip).to_string());
|
||||
}
|
||||
},
|
||||
True | False | Not(_) => {
|
||||
|
|
@ -414,12 +418,12 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
|
|||
let lhs_snippet = lhs.span.get_source_text(cx)?;
|
||||
let rhs_snippet = rhs.span.get_source_text(cx)?;
|
||||
|
||||
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
|
||||
if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) {
|
||||
// e.g. `(a as u64) < b`. Without the parens the `<` is
|
||||
// interpreted as a start of generic arguments for `u64`
|
||||
return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
|
||||
}
|
||||
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')'))
|
||||
&& let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node)
|
||||
{
|
||||
// e.g. `(a as u64) < b`. Without the parens the `<` is
|
||||
// interpreted as a start of generic arguments for `u64`
|
||||
return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
|
||||
}
|
||||
|
||||
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::reference::DEREF_ADDROF;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed};
|
||||
use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -73,6 +73,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
|
|||
}
|
||||
})
|
||||
&& !is_from_proc_macro(cx, e)
|
||||
&& let e_ty = cx.typeck_results().expr_ty_adjusted(e)
|
||||
// check if the reference is coercing to a mutable reference
|
||||
&& (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target))
|
||||
&& let Some(deref_text) = deref_target.span.get_source_text(cx)
|
||||
{
|
||||
span_lint_and_then(
|
||||
|
|
@ -90,10 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
|
|||
|
||||
// has deref trait -> give 2 help
|
||||
// doesn't have deref trait -> give 1 help
|
||||
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() {
|
||||
if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
|
||||
return;
|
||||
}
|
||||
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait()
|
||||
&& !implements_trait(cx, *inner_ty, deref_trait_id, &[])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::{is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core};
|
||||
use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::{Adjust, AutoBorrow};
|
||||
use rustc_span::BytePos;
|
||||
|
||||
use super::BORROW_AS_PTR;
|
||||
|
|
@ -29,10 +30,6 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
let (suggestion, span) = if msrv.meets(cx, msrvs::RAW_REF_OP) {
|
||||
let operator_kind = match mutability {
|
||||
Mutability::Not => "const",
|
||||
Mutability::Mut => "mut",
|
||||
};
|
||||
// Make sure that the span to be replaced doesn't include parentheses, that could break the
|
||||
// suggestion.
|
||||
let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) {
|
||||
|
|
@ -42,7 +39,7 @@ pub(super) fn check<'tcx>(
|
|||
} else {
|
||||
expr.span
|
||||
};
|
||||
(format!("&raw {operator_kind} {snip}"), span)
|
||||
(format!("&raw {} {snip}", mutability.ptr_str()), span)
|
||||
} else {
|
||||
let Some(std_or_core) = std_or_core(cx) else {
|
||||
return false;
|
||||
|
|
@ -59,3 +56,25 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check for an implicit cast from reference to raw pointer outside an explicit `as`.
|
||||
pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if !expr.span.from_expansion()
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, _, pointee) = expr.kind
|
||||
&& !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..)))
|
||||
&& let [deref, borrow] = cx.typeck_results().expr_adjustments(expr)
|
||||
&& matches!(deref.kind, Adjust::Deref(..))
|
||||
&& let Adjust::Borrow(AutoBorrow::RawPtr(mutability)) = borrow.kind
|
||||
// Do not suggest taking a raw pointer to a temporary value
|
||||
&& !is_expr_temporary_value(cx, pointee)
|
||||
{
|
||||
span_lint_and_then(cx, BORROW_AS_PTR, expr.span, "implicit borrow as raw pointer", |diag| {
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span.until(pointee.span),
|
||||
"use a raw pointer instead",
|
||||
format!("&raw {} ", mutability.ptr_str()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub(super) fn check(
|
|||
span,
|
||||
format!("casting the result of `{cast_from}::abs()` to {cast_to}"),
|
||||
"replace with",
|
||||
format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()),
|
||||
format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_paren()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ pub(super) fn check(
|
|||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
"use `Into::into` instead",
|
||||
format!("{}.into()", from_sugg.maybe_par()),
|
||||
format!("{}.into()", from_sugg.maybe_paren()),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
|
|||
apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX))
|
||||
},
|
||||
ExprKind::MethodCall(method, _, [lo, hi], _) => {
|
||||
if method.ident.as_str() == "clamp" {
|
||||
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);
|
||||
}
|
||||
&& let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi))
|
||||
{
|
||||
return lo_bits.max(hi_bits);
|
||||
}
|
||||
nbits
|
||||
},
|
||||
|
|
@ -185,7 +185,7 @@ fn offer_suggestion(
|
|||
) {
|
||||
let cast_to_snip = snippet(cx, cast_to_span, "..");
|
||||
let suggestion = if cast_to_snip == "_" {
|
||||
format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par())
|
||||
format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_paren())
|
||||
} else {
|
||||
format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,16 +19,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
cx.typeck_results().expr_ty(expr),
|
||||
);
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind {
|
||||
if method_path.ident.name.as_str() == "cast"
|
||||
&& let Some(generic_args) = method_path.args
|
||||
&& let [GenericArg::Type(cast_to)] = generic_args.args
|
||||
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
|
||||
&& !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty())
|
||||
{
|
||||
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
}
|
||||
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind
|
||||
&& method_path.ident.name.as_str() == "cast"
|
||||
&& let Some(generic_args) = method_path.args
|
||||
&& let [GenericArg::Type(cast_to)] = generic_args.args
|
||||
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
|
||||
&& !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty())
|
||||
{
|
||||
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,42 +21,41 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv)
|
|||
start_ty,
|
||||
end_ty,
|
||||
}) = expr_cast_chain_tys(cx, expr)
|
||||
&& let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty))
|
||||
{
|
||||
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
|
||||
let from_size = from_layout.size.bytes();
|
||||
let to_size = to_layout.size.bytes();
|
||||
if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
expr.span,
|
||||
format!(
|
||||
"casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
|
||||
start_ty.ty, end_ty.ty,
|
||||
),
|
||||
|diag| {
|
||||
let ptr_snippet = source::snippet(cx, left_cast.span, "..");
|
||||
let from_size = from_layout.size.bytes();
|
||||
let to_size = to_layout.size.bytes();
|
||||
if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
expr.span,
|
||||
format!(
|
||||
"casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
|
||||
start_ty.ty, end_ty.ty,
|
||||
),
|
||||
|diag| {
|
||||
let ptr_snippet = source::snippet(cx, left_cast.span, "..");
|
||||
|
||||
let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
|
||||
Mutability::Mut => ("_mut", "mut"),
|
||||
Mutability::Not => ("", "const"),
|
||||
};
|
||||
let sugg = format!(
|
||||
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
|
||||
// get just the ty from the TypeAndMut so that the printed type isn't something like `mut
|
||||
// T`, extract just the `T`
|
||||
end_ty.ty
|
||||
);
|
||||
let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
|
||||
Mutability::Mut => ("_mut", "mut"),
|
||||
Mutability::Not => ("", "const"),
|
||||
};
|
||||
let sugg = format!(
|
||||
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
|
||||
// get just the ty from the TypeAndMut so that the printed type isn't something like `mut
|
||||
// T`, extract just the `T`
|
||||
end_ty.ty
|
||||
);
|
||||
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
sugg,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
sugg,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::is_normalizable;
|
||||
use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::MANUAL_DANGLING_PTR;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
|
||||
if let TyKind::Ptr(ref ptr_ty) = to.kind {
|
||||
let init_expr = expr_or_init(cx, from);
|
||||
if is_expr_const_aligned(cx, init_expr, ptr_ty.ty)
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
{
|
||||
let sugg_fn = match ptr_ty.mutbl {
|
||||
Mutability::Not => "ptr::dangling",
|
||||
Mutability::Mut => "ptr::dangling_mut",
|
||||
};
|
||||
|
||||
let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind {
|
||||
format!("{std_or_core}::{sugg_fn}()")
|
||||
} else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) {
|
||||
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_DANGLING_PTR,
|
||||
expr.span,
|
||||
"manual creation of a dangling pointer",
|
||||
"use",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the given expression is a call to `align_of` whose generic argument matches the target
|
||||
// type, or a positive constant literal that matches the target type's alignment.
|
||||
fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to),
|
||||
ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
|
||||
&& let Some(fun_id) = path_def_id(cx, fun)
|
||||
&& match_def_path(cx, fun_id, &paths::ALIGN_OF)
|
||||
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
|
||||
&& let [GenericArg::Type(generic_ty)] = args.args
|
||||
{
|
||||
let typeck = cx.typeck_results();
|
||||
return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned<LitKind>, to: &Ty<'_>) -> bool {
|
||||
let LitKind::Int(val, _) = lit.node else { return false };
|
||||
if val == 0 {
|
||||
return false;
|
||||
}
|
||||
let to_mid_ty = cx.typeck_results().node_type(to.hir_id);
|
||||
is_normalizable(cx, cx.param_env, to_mid_ty)
|
||||
&& cx
|
||||
.tcx
|
||||
.layout_of(cx.typing_env().as_query_input(to_mid_ty))
|
||||
.is_ok_and(|layout| {
|
||||
let align = u128::from(layout.align.abi.bytes());
|
||||
u128::from(val) <= align
|
||||
})
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ mod char_lit_as_u8;
|
|||
mod fn_to_numeric_cast;
|
||||
mod fn_to_numeric_cast_any;
|
||||
mod fn_to_numeric_cast_with_truncation;
|
||||
mod manual_dangling_ptr;
|
||||
mod ptr_as_ptr;
|
||||
mod ptr_cast_constness;
|
||||
mod ref_as_ptr;
|
||||
|
|
@ -71,7 +72,7 @@ declare_clippy_lint! {
|
|||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let y: i8 = -1;
|
||||
/// y as u128; // will return 18446744073709551615
|
||||
/// y as u64; // will return 18446744073709551615
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_SIGN_LOSS,
|
||||
|
|
@ -759,6 +760,32 @@ declare_clippy_lint! {
|
|||
"detects `as *mut _` and `as *const _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This creates a dangling pointer and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let ptr = 4 as *const u32;
|
||||
/// let aligned = std::mem::align_of::<u32>() as *const u32;
|
||||
/// let mut_ptr: *mut i64 = 8 as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let ptr = std::ptr::dangling::<u32>();
|
||||
/// let aligned = std::ptr::dangling::<u32>();
|
||||
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub MANUAL_DANGLING_PTR,
|
||||
style,
|
||||
"casting small constant literals to pointers to create dangling pointers"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
|
@ -795,6 +822,7 @@ impl_lint_pass!(Casts => [
|
|||
ZERO_PTR,
|
||||
REF_AS_PTR,
|
||||
AS_POINTER_UNDERSCORE,
|
||||
MANUAL_DANGLING_PTR,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
|
|
@ -823,6 +851,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
|
||||
if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) {
|
||||
manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
}
|
||||
|
||||
if cast_to.is_numeric() {
|
||||
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
|
||||
if cast_from.is_numeric() {
|
||||
|
|
@ -846,6 +878,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
}
|
||||
}
|
||||
|
||||
if self.msrv.meets(cx, msrvs::RAW_REF_OP) {
|
||||
borrow_as_ptr::check_implicit_cast(cx, expr);
|
||||
}
|
||||
cast_ptr_alignment::check(cx, expr);
|
||||
char_lit_as_u8::check(cx, expr);
|
||||
ptr_as_ptr::check(cx, expr, self.msrv);
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) {
|
|||
|
||||
(
|
||||
"try `pointer::cast`, a safer alternative",
|
||||
format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
|
||||
format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_paren()),
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) {
|
||||
let sugg = Sugg::hir(cx, cast_expr, "_");
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app);
|
||||
let constness = match *to_mutbl {
|
||||
Mutability::Not => "const",
|
||||
Mutability::Mut => "mut",
|
||||
|
|
@ -65,8 +66,8 @@ pub(super) fn check<'tcx>(
|
|||
expr.span,
|
||||
"`as` casting between raw pointers while changing only its constness",
|
||||
format!("try `pointer::cast_{constness}`, a safer alternative"),
|
||||
format!("{}.cast_{constness}()", sugg.maybe_par()),
|
||||
Applicability::MachineApplicable,
|
||||
format!("{}.cast_{constness}()", sugg.maybe_paren()),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,11 +130,11 @@ pub(super) fn check<'tcx>(
|
|||
| LitKind::Float(_, LitFloatType::Suffixed(_))
|
||||
if cast_from.kind() == cast_to.kind() =>
|
||||
{
|
||||
if let Some(src) = cast_expr.span.get_source_text(cx) {
|
||||
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
|
||||
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
|
||||
return true;
|
||||
}
|
||||
if let Some(src) = cast_expr.span.get_source_text(cx)
|
||||
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
|
||||
{
|
||||
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
|
|
|||
|
|
@ -253,11 +253,11 @@ fn get_types_from_cast<'a>(
|
|||
match limit.kind {
|
||||
// `from_type::from(_)`
|
||||
ExprKind::Call(path, _) => {
|
||||
if let ExprKind::Path(ref path) = path.kind {
|
||||
if let ExprKind::Path(ref path) = path.kind
|
||||
// `to_type`
|
||||
if let Some(to_type) = get_implementing_type(path, types, func) {
|
||||
return Some((from_type, to_type));
|
||||
}
|
||||
&& let Some(to_type) = get_implementing_type(path, types, func)
|
||||
{
|
||||
return Some((from_type, to_type));
|
||||
}
|
||||
},
|
||||
// `to_type::MAX`
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ impl CognitiveComplexity {
|
|||
|
||||
let mut cc = 1u64;
|
||||
let mut returns = 0u64;
|
||||
let mut prev_expr: Option<&ExprKind<'tcx>> = None;
|
||||
let _: Option<!> = for_each_expr_without_closures(expr, |e| {
|
||||
match e.kind {
|
||||
ExprKind::If(_, _, _) => {
|
||||
|
|
@ -73,9 +74,14 @@ impl CognitiveComplexity {
|
|||
}
|
||||
cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
|
||||
},
|
||||
ExprKind::Ret(_) => returns += 1,
|
||||
ExprKind::Ret(_) => {
|
||||
if !matches!(prev_expr, Some(ExprKind::Ret(_))) {
|
||||
returns += 1;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
prev_expr = Some(&e.kind);
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::ast;
|
||||
use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability};
|
||||
use rustc_ast::BinOpKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_hir::{Block, Expr, ExprKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -75,105 +77,152 @@ declare_clippy_lint! {
|
|||
"nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
|
||||
pub struct CollapsibleIf {
|
||||
let_chains_enabled: bool,
|
||||
lint_commented_code: bool,
|
||||
}
|
||||
|
||||
impl EarlyLintPass for CollapsibleIf {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
if let ast::ExprKind::If(cond, then, else_) = &expr.kind
|
||||
impl CollapsibleIf {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
let_chains_enabled: tcx.features().let_chains(),
|
||||
lint_commented_code: conf.lint_commented_code,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_collapsible_else_if(cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) {
|
||||
if !block_starts_with_comment(cx, else_block)
|
||||
&& let Some(else_) = expr_block(else_block)
|
||||
&& cx.tcx.hir_attrs(else_.hir_id).is_empty()
|
||||
&& !else_.span.from_expansion()
|
||||
&& let ExprKind::If(..) = else_.kind
|
||||
{
|
||||
// Prevent "elseif"
|
||||
// Check that the "else" is followed by whitespace
|
||||
let up_to_else = then_span.between(else_block.span);
|
||||
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
|
||||
!c.is_whitespace()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_ELSE_IF,
|
||||
else_block.span,
|
||||
"this `else { if .. }` block can be collapsed",
|
||||
"collapse nested if block",
|
||||
format!(
|
||||
"{}{}",
|
||||
if requires_space { " " } else { "" },
|
||||
snippet_block_with_applicability(cx, else_.span, "..", Some(else_block.span), &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) {
|
||||
if let Some(inner) = expr_block(then)
|
||||
&& cx.tcx.hir_attrs(inner.hir_id).is_empty()
|
||||
&& let ExprKind::If(check_inner, _, None) = &inner.kind
|
||||
&& self.eligible_condition(check_inner)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& inner.span.ctxt() == ctxt
|
||||
&& (self.lint_commented_code || !block_starts_with_comment(cx, then))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
COLLAPSIBLE_IF,
|
||||
expr.span,
|
||||
"this `if` statement can be collapsed",
|
||||
|diag| {
|
||||
let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span();
|
||||
let then_closing_bracket = {
|
||||
let end = then.span.shrink_to_hi();
|
||||
end.with_lo(end.lo() - rustc_span::BytePos(1))
|
||||
.with_leading_whitespace(cx)
|
||||
.into_span()
|
||||
};
|
||||
let inner_if = inner.span.split_at(2).0;
|
||||
let mut sugg = vec![
|
||||
// Remove the outer then block `{`
|
||||
(then_open_bracket, String::new()),
|
||||
// Remove the outer then block '}'
|
||||
(then_closing_bracket, String::new()),
|
||||
// Replace inner `if` by `&&`
|
||||
(inner_if, String::from("&&")),
|
||||
];
|
||||
sugg.extend(parens_around(check));
|
||||
sugg.extend(parens_around(check_inner));
|
||||
|
||||
diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool {
|
||||
self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..))
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
|
||||
|
||||
impl LateLintPass<'_> for CollapsibleIf {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::If(cond, then, else_) = &expr.kind
|
||||
&& !expr.span.from_expansion()
|
||||
{
|
||||
if let Some(else_) = else_ {
|
||||
check_collapsible_maybe_if_let(cx, then.span, else_);
|
||||
} else if !matches!(cond.kind, ast::ExprKind::Let(..)) {
|
||||
check_collapsible_no_if_let(cx, expr, cond, then);
|
||||
if let Some(else_) = else_
|
||||
&& let ExprKind::Block(else_, None) = else_.kind
|
||||
{
|
||||
Self::check_collapsible_else_if(cx, then.span, else_);
|
||||
} else if else_.is_none()
|
||||
&& self.eligible_condition(cond)
|
||||
&& let ExprKind::Block(then, None) = then.kind
|
||||
{
|
||||
self.check_collapsible_if_if(cx, expr, cond, then);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
|
||||
fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
|
||||
// We trim all opening braces and whitespaces and then check if the next string is a comment.
|
||||
let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
|
||||
let trimmed_block_text = snippet_block(cx, block.span, "..", None)
|
||||
.trim_start_matches(|c: char| c.is_whitespace() || c == '{')
|
||||
.to_owned();
|
||||
trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
|
||||
}
|
||||
|
||||
fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
|
||||
if let ast::ExprKind::Block(ref block, _) = else_.kind
|
||||
&& !block_starts_with_comment(cx, block)
|
||||
&& let Some(else_) = expr_block(block)
|
||||
&& else_.attrs.is_empty()
|
||||
&& !else_.span.from_expansion()
|
||||
&& let ast::ExprKind::If(..) = else_.kind
|
||||
{
|
||||
// Prevent "elseif"
|
||||
// Check that the "else" is followed by whitespace
|
||||
let up_to_else = then_span.between(block.span);
|
||||
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
|
||||
!c.is_whitespace()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_ELSE_IF,
|
||||
block.span,
|
||||
"this `else { if .. }` block can be collapsed",
|
||||
"collapse nested if block",
|
||||
format!(
|
||||
"{}{}",
|
||||
if requires_space { " " } else { "" },
|
||||
snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
/// If `block` is a block with either one expression or a statement containing an expression,
|
||||
/// return the expression. We don't peel blocks recursively, as extra blocks might be intentional.
|
||||
fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match block.stmts {
|
||||
[] => block.expr,
|
||||
[stmt] => {
|
||||
if let StmtKind::Semi(expr) = stmt.kind {
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
|
||||
if !block_starts_with_comment(cx, then)
|
||||
&& let Some(inner) = expr_block(then)
|
||||
&& inner.attrs.is_empty()
|
||||
&& let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind
|
||||
// Prevent triggering on `if c { if let a = b { .. } }`.
|
||||
&& !matches!(check_inner.kind, ast::ExprKind::Let(..))
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& inner.span.ctxt() == ctxt
|
||||
/// If the expression is a `||`, suggest parentheses around it.
|
||||
fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> {
|
||||
if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind
|
||||
&& op.node == BinOpKind::Or
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
COLLAPSIBLE_IF,
|
||||
expr.span,
|
||||
"this `if` statement can be collapsed",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
|
||||
let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"collapse nested if block",
|
||||
format!(
|
||||
"if {} {}",
|
||||
lhs.and(&rhs),
|
||||
snippet_block(cx, content.span, "..", Some(expr.span)),
|
||||
),
|
||||
app, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// If the block contains only one expression, return it.
|
||||
fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
|
||||
if let [stmt] = &*block.stmts
|
||||
&& let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind
|
||||
{
|
||||
Some(expr)
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), String::from("(")),
|
||||
(expr.span.shrink_to_hi(), String::from(")")),
|
||||
]
|
||||
} else {
|
||||
None
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
|
|||
let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else {
|
||||
unreachable!();
|
||||
};
|
||||
let lhs = Sugg::hir(cx, lhs, "..").maybe_par();
|
||||
let lhs = Sugg::hir(cx, lhs, "..").maybe_paren();
|
||||
let rhs = Sugg::hir(cx, rhs, "..").addr();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ fn lint_branches_sharing_code<'tcx>(
|
|||
let suggestion = reindent_multiline(&suggestion, true, indent);
|
||||
|
||||
let span = span.with_hi(last_block.span.hi());
|
||||
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
|
||||
// Improve formatting if the inner block has indentation (i.e. normal Rust formatting)
|
||||
let span = span
|
||||
.map_range(cx, |src, range| {
|
||||
(range.start > 4 && src.get(range.start - 4..range.start)? == " ")
|
||||
|
|
@ -539,10 +539,10 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
|
|||
.filter(|stmt| !ignore_span.overlaps(stmt.span))
|
||||
.try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
|
||||
|
||||
if let Some(expr) = block.expr {
|
||||
if res.is_continue() {
|
||||
res = intravisit::walk_expr(&mut walker, expr);
|
||||
}
|
||||
if let Some(expr) = block.expr
|
||||
&& res.is_continue()
|
||||
{
|
||||
res = intravisit::walk_expr(&mut walker, expr);
|
||||
}
|
||||
|
||||
res.is_break()
|
||||
|
|
|
|||
|
|
@ -165,17 +165,4 @@ macro_rules! declare_clippy_lint {
|
|||
$(, $eval_always)?
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
pub $lint_name:ident,
|
||||
internal,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Internal, $desc,
|
||||
None, "0.0.0"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,36 +3,6 @@
|
|||
// Manual edits will be overwritten.
|
||||
|
||||
pub static LINTS: &[&crate::LintInfo] = &[
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_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_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::slow_symbol_comparisons::SLOW_SYMBOL_COMPARISONS_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO,
|
||||
crate::absolute_paths::ABSOLUTE_PATHS_INFO,
|
||||
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
|
||||
crate::approx_const::APPROX_CONSTANT_INFO,
|
||||
|
|
@ -52,6 +22,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
|
||||
crate::attrs::DEPRECATED_SEMVER_INFO,
|
||||
crate::attrs::DUPLICATED_ATTRIBUTES_INFO,
|
||||
crate::attrs::IGNORE_WITHOUT_REASON_INFO,
|
||||
crate::attrs::INLINE_ALWAYS_INFO,
|
||||
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
|
||||
crate::attrs::NON_MINIMAL_CFG_INFO,
|
||||
|
|
@ -96,6 +67,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
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::MANUAL_DANGLING_PTR_INFO,
|
||||
crate::casts::PTR_AS_PTR_INFO,
|
||||
crate::casts::PTR_CAST_CONSTNESS_INFO,
|
||||
crate::casts::REF_AS_PTR_INFO,
|
||||
|
|
@ -286,6 +258,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::literal_representation::UNREADABLE_LITERAL_INFO,
|
||||
crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
|
||||
crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO,
|
||||
crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO,
|
||||
crate::loops::EMPTY_LOOP_INFO,
|
||||
crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
|
||||
crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,
|
||||
|
|
@ -312,6 +285,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO,
|
||||
crate::macro_use::MACRO_USE_IMPORTS_INFO,
|
||||
crate::main_recursion::MAIN_RECURSION_INFO,
|
||||
crate::manual_abs_diff::MANUAL_ABS_DIFF_INFO,
|
||||
crate::manual_assert::MANUAL_ASSERT_INFO,
|
||||
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
|
||||
crate::manual_bits::MANUAL_BITS_INFO,
|
||||
|
|
@ -334,7 +308,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
|
||||
crate::manual_string_new::MANUAL_STRING_NEW_INFO,
|
||||
crate::manual_strip::MANUAL_STRIP_INFO,
|
||||
crate::manual_unwrap_or_default::MANUAL_UNWRAP_OR_DEFAULT_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,
|
||||
|
|
@ -344,10 +317,10 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::matches::MANUAL_MAP_INFO,
|
||||
crate::matches::MANUAL_OK_ERR_INFO,
|
||||
crate::matches::MANUAL_UNWRAP_OR_INFO,
|
||||
crate::matches::MANUAL_UNWRAP_OR_DEFAULT_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,
|
||||
|
|
@ -488,6 +461,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO,
|
||||
crate::methods::SUSPICIOUS_SPLITN_INFO,
|
||||
crate::methods::SUSPICIOUS_TO_OWNED_INFO,
|
||||
crate::methods::SWAP_WITH_TEMPORARY_INFO,
|
||||
crate::methods::TYPE_ID_ON_BOX_INFO,
|
||||
crate::methods::UNBUFFERED_BYTES_INFO,
|
||||
crate::methods::UNINIT_ASSUMED_INIT_INFO,
|
||||
|
|
@ -664,6 +638,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::redundant_slicing::DEREF_BY_SLICING_INFO,
|
||||
crate::redundant_slicing::REDUNDANT_SLICING_INFO,
|
||||
crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO,
|
||||
crate::redundant_test_prefix::REDUNDANT_TEST_PREFIX_INFO,
|
||||
crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO,
|
||||
crate::ref_option_ref::REF_OPTION_REF_INFO,
|
||||
crate::ref_patterns::REF_PATTERNS_INFO,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_ty_alias;
|
||||
use clippy_utils::source::SpanRangeExt as _;
|
||||
use hir::ExprKind;
|
||||
use hir::def::Res;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -70,15 +71,26 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
|
|||
&& let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant()
|
||||
&& !var.is_field_list_non_exhaustive()
|
||||
&& !expr.span.from_expansion() && !qpath.span().from_expansion()
|
||||
// do not suggest replacing an expression by a type name with placeholders
|
||||
&& !base.is_suggestable_infer_ty()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())];
|
||||
if expr.span.with_source_text(cx, |s| s.starts_with('<')) == Some(true) {
|
||||
// Remove `<`, '>` has already been removed by the existing removal expression.
|
||||
removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new()));
|
||||
}
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
|
||||
expr.span.with_lo(qpath.qself_span().hi()),
|
||||
expr.span,
|
||||
"use of `default` to create a unit struct",
|
||||
"remove this call to `default`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"remove this call to `default`",
|
||||
removals,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[
|
|||
("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"),
|
||||
#[clippy::version = "1.86.0"]
|
||||
("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"),
|
||||
#[clippy::version = "1.86.0"]
|
||||
("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"),
|
||||
// end deprecated lints. used by `cargo dev deprecate_lint`
|
||||
]}
|
||||
|
||||
|
|
|
|||
|
|
@ -1133,61 +1133,60 @@ fn report<'tcx>(
|
|||
|
||||
impl<'tcx> Dereferencing<'tcx> {
|
||||
fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
|
||||
if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
|
||||
if let Some(pat) = outer_pat {
|
||||
// Check for auto-deref
|
||||
if !matches!(
|
||||
cx.typeck_results().expr_adjustments(e),
|
||||
[
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
},
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
},
|
||||
if let Some(outer_pat) = self.ref_locals.get_mut(&local)
|
||||
&& let Some(pat) = outer_pat
|
||||
// Check for auto-deref
|
||||
&& !matches!(
|
||||
cx.typeck_results().expr_adjustments(e),
|
||||
[
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
]
|
||||
) {
|
||||
match get_parent_expr(cx, e) {
|
||||
// Field accesses are the same no matter the number of references.
|
||||
Some(Expr {
|
||||
kind: ExprKind::Field(..),
|
||||
..
|
||||
}) => (),
|
||||
Some(&Expr {
|
||||
span,
|
||||
kind: ExprKind::Unary(UnOp::Deref, _),
|
||||
..
|
||||
}) if !span.from_expansion() => {
|
||||
// Remove explicit deref.
|
||||
let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((span, snip.into()));
|
||||
},
|
||||
Some(parent) if !parent.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
if parent.precedence() == ExprPrecedence::Unambiguous {
|
||||
// Parentheses would be needed here, don't lint.
|
||||
*outer_pat = None;
|
||||
} else {
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
}
|
||||
},
|
||||
_ if !e.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
},
|
||||
// Edge case for macros. The span of the identifier will usually match the context of the
|
||||
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
|
||||
// macros
|
||||
_ => *outer_pat = None,
|
||||
},
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
},
|
||||
..
|
||||
]
|
||||
)
|
||||
{
|
||||
match get_parent_expr(cx, e) {
|
||||
// Field accesses are the same no matter the number of references.
|
||||
Some(Expr {
|
||||
kind: ExprKind::Field(..),
|
||||
..
|
||||
}) => (),
|
||||
Some(&Expr {
|
||||
span,
|
||||
kind: ExprKind::Unary(UnOp::Deref, _),
|
||||
..
|
||||
}) if !span.from_expansion() => {
|
||||
// Remove explicit deref.
|
||||
let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((span, snip.into()));
|
||||
},
|
||||
Some(parent) if !parent.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
if parent.precedence() == ExprPrecedence::Unambiguous {
|
||||
// Parentheses would be needed here, don't lint.
|
||||
*outer_pat = None;
|
||||
} else {
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ if !e.span.from_expansion() => {
|
||||
// Double reference might be needed at this point.
|
||||
pat.always_deref = false;
|
||||
let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
|
||||
pat.replacements.push((e.span, format!("&{snip}")));
|
||||
},
|
||||
// Edge case for macros. The span of the identifier will usually match the context of the
|
||||
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
|
||||
// macros
|
||||
_ => *outer_pat = None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,18 +94,18 @@ fn check_struct<'tcx>(
|
|||
ty_args: GenericArgsRef<'_>,
|
||||
typeck_results: &'tcx TypeckResults<'tcx>,
|
||||
) {
|
||||
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
|
||||
if let Some(PathSegment { args, .. }) = p.segments.last() {
|
||||
let args = args.map(|a| a.args).unwrap_or(&[]);
|
||||
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind
|
||||
&& let Some(PathSegment { args, .. }) = p.segments.last()
|
||||
{
|
||||
let args = args.map(|a| a.args).unwrap_or(&[]);
|
||||
|
||||
// ty_args contains the generic parameters of the type declaration, while args contains the
|
||||
// arguments used at instantiation time. If both len are not equal, it means that some
|
||||
// parameters were not provided (which means that the default values were used); in this
|
||||
// case we will not risk suggesting too broad a rewrite. We won't either if any argument
|
||||
// is a type or a const.
|
||||
if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
|
||||
return;
|
||||
}
|
||||
// ty_args contains the generic parameters of the type declaration, while args contains the
|
||||
// arguments used at instantiation time. If both len are not equal, it means that some
|
||||
// parameters were not provided (which means that the default values were used); in this
|
||||
// case we will not risk suggesting too broad a rewrite. We won't either if any argument
|
||||
// is a type or a const.
|
||||
if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
|
|||
self_ty,
|
||||
..
|
||||
}) = item.kind
|
||||
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
|
||||
&& !cx.tcx.is_automatically_derived(item.owner_id.to_def_id())
|
||||
&& !item.span.from_expansion()
|
||||
&& let Some(def_id) = trait_ref.trait_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Default, def_id)
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
|
|||
}) = item.kind
|
||||
{
|
||||
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
|
||||
let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
|
||||
let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id());
|
||||
|
||||
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||
|
|
@ -235,7 +235,7 @@ fn check_hash_peq<'tcx>(
|
|||
{
|
||||
// Look for the PartialEq implementations for `ty`
|
||||
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
|
||||
let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
|
||||
let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);
|
||||
|
||||
if !hash_is_automatically_derived || peq_is_automatically_derived {
|
||||
return;
|
||||
|
|
@ -278,7 +278,7 @@ fn check_ord_partial_ord<'tcx>(
|
|||
{
|
||||
// Look for the PartialOrd implementations for `ty`
|
||||
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
|
||||
let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
|
||||
let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);
|
||||
|
||||
if partial_ord_is_automatically_derived == ord_is_automatically_derived {
|
||||
return;
|
||||
|
|
@ -349,6 +349,10 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
|
|||
{
|
||||
return;
|
||||
}
|
||||
// The presence of `unsafe` fields prevents deriving `Clone` automatically
|
||||
if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
|
|
@ -426,10 +430,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result {
|
||||
if let ExprKind::Block(block, _) = expr.kind {
|
||||
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
if let ExprKind::Block(block, _) = expr.kind
|
||||
&& block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
walk_expr(self, expr)
|
||||
|
|
@ -479,7 +483,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De
|
|||
tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some()
|
||||
}
|
||||
|
||||
/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
|
||||
/// Creates the `ParamEnv` used for the given type's derived `Eq` impl.
|
||||
fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> {
|
||||
// Initial map from generic index to param def.
|
||||
// Vec<(param_def, needs_eq)>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
|||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{
|
||||
AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
|
||||
|
|
@ -72,8 +73,15 @@ pub struct DisallowedMacros {
|
|||
|
||||
impl DisallowedMacros {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self {
|
||||
let (disallowed, _) = create_disallowed_map(
|
||||
tcx,
|
||||
&conf.disallowed_macros,
|
||||
|def_kind| matches!(def_kind, DefKind::Macro(_)),
|
||||
"macro",
|
||||
false,
|
||||
);
|
||||
Self {
|
||||
disallowed: create_disallowed_map(tcx, &conf.disallowed_macros),
|
||||
disallowed,
|
||||
seen: FxHashSet::default(),
|
||||
derive_src: None,
|
||||
earlies,
|
||||
|
|
|
|||
|
|
@ -63,9 +63,19 @@ pub struct DisallowedMethods {
|
|||
|
||||
impl DisallowedMethods {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
disallowed: create_disallowed_map(tcx, &conf.disallowed_methods),
|
||||
}
|
||||
let (disallowed, _) = create_disallowed_map(
|
||||
tcx,
|
||||
&conf.disallowed_methods,
|
||||
|def_kind| {
|
||||
matches!(
|
||||
def_kind,
|
||||
DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn
|
||||
)
|
||||
},
|
||||
"function",
|
||||
false,
|
||||
);
|
||||
Self { disallowed }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,12 +84,7 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
|
|||
impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (id, span) = match &expr.kind {
|
||||
ExprKind::Path(path)
|
||||
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) =
|
||||
cx.qpath_res(path, expr.hir_id) =>
|
||||
{
|
||||
(id, expr.span)
|
||||
},
|
||||
ExprKind::Path(path) if let Res::Def(_, id) = cx.qpath_res(path, expr.hir_id) => (id, expr.span),
|
||||
ExprKind::MethodCall(name, ..) if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => {
|
||||
(id, name.ident.span)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::DisallowedPath;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -60,22 +60,7 @@ pub struct DisallowedTypes {
|
|||
|
||||
impl DisallowedTypes {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
let mut def_ids = DefIdMap::default();
|
||||
let mut prim_tys = FxHashMap::default();
|
||||
for disallowed_path in &conf.disallowed_types {
|
||||
let path: Vec<_> = disallowed_path.path().split("::").collect::<Vec<_>>();
|
||||
for res in clippy_utils::def_path_res(tcx, &path) {
|
||||
match res {
|
||||
Res::Def(_, id) => {
|
||||
def_ids.insert(id, (disallowed_path.path(), disallowed_path));
|
||||
},
|
||||
Res::PrimTy(ty) => {
|
||||
prim_tys.insert(ty, (disallowed_path.path(), disallowed_path));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true);
|
||||
Self { def_ids, prim_tys }
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +80,19 @@ impl DisallowedTypes {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn def_kind_predicate(def_kind: DefKind) -> bool {
|
||||
matches!(
|
||||
def_kind,
|
||||
DefKind::Struct
|
||||
| DefKind::Union
|
||||
| DefKind::Enum
|
||||
| DefKind::Trait
|
||||
| DefKind::TyAlias
|
||||
| DefKind::ForeignTy
|
||||
| DefKind::AssocTy
|
||||
)
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
|
||||
|
|
|
|||
|
|
@ -113,20 +113,20 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
|
|||
s != "-" && s.contains('-')
|
||||
}
|
||||
|
||||
if let Ok(url) = Url::parse(word) {
|
||||
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_and_sugg(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
|
||||
"try",
|
||||
format!("<{word}>"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return;
|
||||
}
|
||||
&& !url.cannot_be_a_base()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOC_MARKDOWN,
|
||||
span,
|
||||
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
|
||||
"try",
|
||||
format!("<{word}>"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
|
||||
use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_doc_hidden, return_ty};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item};
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty};
|
||||
use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{Span, sym};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
|
|
@ -13,7 +16,6 @@ pub fn check(
|
|||
sig: FnSig<'_>,
|
||||
headers: DocHeaders,
|
||||
body_id: Option<BodyId>,
|
||||
panic_info: Option<(Span, bool)>,
|
||||
check_private_items: bool,
|
||||
) {
|
||||
if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) {
|
||||
|
|
@ -46,13 +48,16 @@ pub fn check(
|
|||
),
|
||||
_ => (),
|
||||
}
|
||||
if !headers.panics && panic_info.is_some_and(|el| !el.1) {
|
||||
if !headers.panics
|
||||
&& let Some(body_id) = body_id
|
||||
&& let Some(panic_span) = find_panic(cx, body_id)
|
||||
{
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
MISSING_PANICS_DOC,
|
||||
span,
|
||||
"docs for function which may panic missing `# Panics` section",
|
||||
panic_info.map(|el| el.0),
|
||||
Some(panic_span),
|
||||
"first possible panic found here",
|
||||
);
|
||||
}
|
||||
|
|
@ -89,3 +94,39 @@ pub fn check(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option<Span> {
|
||||
let mut panic_span = None;
|
||||
let typeck = cx.tcx.typeck_body(body_id);
|
||||
for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& (is_panic(cx, macro_call.def_id)
|
||||
|| matches!(
|
||||
cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||
Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro)
|
||||
))
|
||||
&& !cx.tcx.hir_is_inside_const_context(expr.hir_id)
|
||||
&& !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id])
|
||||
&& panic_span.is_none()
|
||||
{
|
||||
panic_span = Some(macro_call.span);
|
||||
}
|
||||
|
||||
// check for `unwrap` and `expect` for both `Option` and `Result`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or_else(|| method_chain_args(expr, &["expect"]))
|
||||
&& let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs()
|
||||
&& matches!(
|
||||
get_type_diagnostic_name(cx, receiver_ty),
|
||||
Some(sym::Option | sym::Result)
|
||||
)
|
||||
&& !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id])
|
||||
&& panic_span.is_none()
|
||||
{
|
||||
panic_span = Some(expr.span);
|
||||
}
|
||||
|
||||
// Visit all nodes to fulfill any `#[expect]`s after the first linted panic
|
||||
ControlFlow::<!>::Continue(())
|
||||
});
|
||||
panic_span
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,8 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::Visitable;
|
||||
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
|
||||
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item};
|
||||
use pulldown_cmark::Event::{
|
||||
Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,
|
||||
TaskListMarker, Text,
|
||||
|
|
@ -16,18 +13,15 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, It
|
|||
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};
|
||||
use rustc_hir::{Attribute, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty;
|
||||
use rustc_resolve::rustdoc::{
|
||||
DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range,
|
||||
span_of_fragments,
|
||||
};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{Span, sym};
|
||||
use std::ops::Range;
|
||||
use url::Url;
|
||||
|
||||
|
|
@ -194,6 +188,19 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Individual panics within a function can be ignored with `#[expect]` or
|
||||
/// `#[allow]`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::num::NonZeroUsize;
|
||||
/// pub fn will_not_panic(x: usize) {
|
||||
/// #[expect(clippy::missing_panics_doc, reason = "infallible")]
|
||||
/// let y = NonZeroUsize::new(1).unwrap();
|
||||
///
|
||||
/// // If any panics are added in the future the lint will still catch them
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.51.0"]
|
||||
pub MISSING_PANICS_DOC,
|
||||
pedantic,
|
||||
|
|
@ -657,20 +664,16 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
|
|||
self.check_private_items,
|
||||
);
|
||||
match item.kind {
|
||||
ItemKind::Fn { sig, body: body_id, .. } => {
|
||||
ItemKind::Fn { sig, body, .. } => {
|
||||
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
|
||||
|| item.span.in_external_macro(cx.tcx.sess.source_map()))
|
||||
{
|
||||
let body = cx.tcx.hir_body(body_id);
|
||||
|
||||
let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
|
||||
missing_headers::check(
|
||||
cx,
|
||||
item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
panic_info,
|
||||
Some(body),
|
||||
self.check_private_items,
|
||||
);
|
||||
}
|
||||
|
|
@ -697,15 +700,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
|
|||
if let TraitItemKind::Fn(sig, ..) = trait_item.kind
|
||||
&& !trait_item.span.in_external_macro(cx.tcx.sess.source_map())
|
||||
{
|
||||
missing_headers::check(
|
||||
cx,
|
||||
trait_item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
None,
|
||||
None,
|
||||
self.check_private_items,
|
||||
);
|
||||
missing_headers::check(cx, trait_item.owner_id, sig, headers, None, self.check_private_items);
|
||||
}
|
||||
},
|
||||
Node::ImplItem(impl_item) => {
|
||||
|
|
@ -713,16 +708,12 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
|
|||
&& !impl_item.span.in_external_macro(cx.tcx.sess.source_map())
|
||||
&& !is_trait_impl_item(cx, impl_item.hir_id())
|
||||
{
|
||||
let body = cx.tcx.hir_body(body_id);
|
||||
|
||||
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value);
|
||||
missing_headers::check(
|
||||
cx,
|
||||
impl_item.owner_id,
|
||||
sig,
|
||||
headers,
|
||||
Some(body_id),
|
||||
panic_span,
|
||||
self.check_private_items,
|
||||
);
|
||||
}
|
||||
|
|
@ -880,19 +871,18 @@ fn check_for_code_clusters<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a
|
|||
if let Some(start) = code_starts_at
|
||||
&& let Some(end) = code_ends_at
|
||||
&& code_includes_link
|
||||
&& let Some(span) = fragments.span(cx, start..end)
|
||||
{
|
||||
if let Some(span) = fragments.span(cx, start..end) {
|
||||
span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| {
|
||||
let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"wrap the entire group in `<code>` tags",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("separate code snippets will be shown with a gap");
|
||||
});
|
||||
}
|
||||
span_lint_and_then(cx, DOC_LINK_CODE, span, "code link adjacent to code text", |diag| {
|
||||
let sugg = format!("<code>{}</code>", doc[start..end].replace('`', ""));
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"wrap the entire group in `<code>` tags",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("separate code snippets will be shown with a gap");
|
||||
});
|
||||
}
|
||||
code_includes_link = false;
|
||||
code_starts_at = None;
|
||||
|
|
@ -1169,72 +1159,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
headers
|
||||
}
|
||||
|
||||
struct FindPanicUnwrap<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
is_const: bool,
|
||||
panic_span: Option<Span>,
|
||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
|
||||
pub fn find_span(
|
||||
cx: &'a LateContext<'tcx>,
|
||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
body: impl Visitable<'tcx>,
|
||||
) -> Option<(Span, bool)> {
|
||||
let mut vis = Self {
|
||||
cx,
|
||||
is_const: false,
|
||||
panic_span: None,
|
||||
typeck_results,
|
||||
};
|
||||
body.visit(&mut vis);
|
||||
vis.panic_span.map(|el| (el, vis.is_const))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, '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"
|
||||
)
|
||||
{
|
||||
self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id);
|
||||
self.panic_span = Some(macro_call.span);
|
||||
}
|
||||
}
|
||||
|
||||
// check for `unwrap` and `expect` for both `Option` and `Result`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) {
|
||||
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 maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
|
||||
self.cx.tcx
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type
|
||||
fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> {
|
||||
if range.end < range.start {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,10 @@ pub fn check(
|
|||
match parser.parse_item(ForceCollect::No) {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
ItemKind::Fn(box Fn {
|
||||
ident, sig, body: Some(block), ..
|
||||
ident,
|
||||
sig,
|
||||
body: Some(block),
|
||||
..
|
||||
}) if ident.name == sym::main => {
|
||||
if !ignore {
|
||||
get_test_spans(&item, *ident, &mut test_attr_spans);
|
||||
|
|
|
|||
|
|
@ -144,10 +144,10 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
|||
// ..
|
||||
// }
|
||||
fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool {
|
||||
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
|
||||
if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) {
|
||||
return body.hir_id == drop_expr.hir_id;
|
||||
}
|
||||
if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
|
||||
&& let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id)
|
||||
{
|
||||
return body.hir_id == drop_expr.hir_id;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet_indent};
|
||||
use clippy_utils::tokenize_with_text;
|
||||
|
|
@ -89,7 +91,7 @@ declare_clippy_lint! {
|
|||
#[derive(Debug)]
|
||||
struct ItemInfo {
|
||||
kind: &'static str,
|
||||
name: Symbol,
|
||||
name: Option<Symbol>,
|
||||
span: Span,
|
||||
mod_items: Option<NodeId>,
|
||||
}
|
||||
|
|
@ -315,8 +317,12 @@ impl EmptyLineAfter {
|
|||
for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) {
|
||||
stop.comment_out(cx, &mut suggestions);
|
||||
}
|
||||
let name = match info.name {
|
||||
Some(name) => format!("{} `{name}`", info.kind).into(),
|
||||
None => Cow::from("the following item"),
|
||||
};
|
||||
diag.multipart_suggestion_verbose(
|
||||
format!("if the doc comment should not document `{}` comment it out", info.name),
|
||||
format!("if the doc comment should not document {name} then comment it out"),
|
||||
suggestions,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
|
@ -381,13 +387,10 @@ impl EmptyLineAfter {
|
|||
) {
|
||||
self.items.push(ItemInfo {
|
||||
kind: kind.descr(),
|
||||
// FIXME: this `sym::empty` can be leaked, see
|
||||
// https://github.com/rust-lang/rust/pull/138740#discussion_r2021979899
|
||||
name: if let Some(ident) = ident { ident.name } else { kw::Empty },
|
||||
span: if let Some(ident) = ident {
|
||||
span.with_hi(ident.span.hi())
|
||||
} else {
|
||||
span.with_hi(span.lo())
|
||||
name: ident.map(|ident| ident.name),
|
||||
span: match ident {
|
||||
Some(ident) => span.with_hi(ident.span.hi()),
|
||||
None => span.shrink_to_lo(),
|
||||
},
|
||||
mod_items: match kind {
|
||||
ItemKind::Mod(_, _, ModKind::Loaded(items, _, _, _)) => items
|
||||
|
|
@ -447,7 +450,7 @@ impl EarlyLintPass for EmptyLineAfter {
|
|||
fn check_crate(&mut self, _: &EarlyContext<'_>, krate: &Crate) {
|
||||
self.items.push(ItemInfo {
|
||||
kind: "crate",
|
||||
name: kw::Crate,
|
||||
name: Some(kw::Crate),
|
||||
span: krate.spans.inner_span.with_hi(krate.spans.inner_span.lo()),
|
||||
mod_items: krate
|
||||
.items
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_ast::ast::{Item, ItemKind, Variant, VariantData};
|
||||
use clippy_utils::attrs::span_contains_cfg;
|
||||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lexer::TokenKind;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_hir::def::CtorOf;
|
||||
use rustc_hir::def::DefKind::Ctor;
|
||||
use rustc_hir::def::Res::Def;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node, Path, QPath, Variant, VariantData};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -70,10 +75,23 @@ declare_clippy_lint! {
|
|||
"finds enum variants with empty brackets"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]);
|
||||
#[derive(Debug)]
|
||||
enum Usage {
|
||||
Unused { redundant_use_sites: Vec<Span> },
|
||||
Used,
|
||||
NoDefinition { redundant_use_sites: Vec<Span> },
|
||||
}
|
||||
|
||||
impl EarlyLintPass for EmptyWithBrackets {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
#[derive(Default)]
|
||||
pub struct EmptyWithBrackets {
|
||||
// Value holds `Usage::Used` if the empty tuple variant was used as a function
|
||||
empty_tuple_enum_variants: FxIndexMap<LocalDefId, Usage>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]);
|
||||
|
||||
impl LateLintPass<'_> for EmptyWithBrackets {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if let ItemKind::Struct(ident, var_data, _) = &item.kind
|
||||
&& has_brackets(var_data)
|
||||
&& let span_after_ident = item.span.with_lo(ident.span.hi())
|
||||
|
|
@ -96,70 +114,175 @@ impl EarlyLintPass for EmptyWithBrackets {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &Variant) {
|
||||
fn check_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>) {
|
||||
// the span of the parentheses/braces
|
||||
let span_after_ident = variant.span.with_lo(variant.ident.span.hi());
|
||||
|
||||
if has_brackets(&variant.data) && has_no_fields(cx, &variant.data, span_after_ident) {
|
||||
span_lint_and_then(
|
||||
if has_no_fields(cx, &variant.data, span_after_ident) {
|
||||
match variant.data {
|
||||
VariantData::Struct { .. } => {
|
||||
// Empty struct variants can be linted immediately
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
|
||||
span_after_ident,
|
||||
"enum variant has empty brackets",
|
||||
|diagnostic| {
|
||||
diagnostic.span_suggestion_hidden(
|
||||
span_after_ident,
|
||||
"remove the brackets",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
VariantData::Tuple(.., local_def_id) => {
|
||||
// Don't lint reachable tuple enums
|
||||
if cx.effective_visibilities.is_reachable(variant.def_id) {
|
||||
return;
|
||||
}
|
||||
if let Some(entry) = self.empty_tuple_enum_variants.get_mut(&local_def_id) {
|
||||
// empty_tuple_enum_variants contains Usage::NoDefinition if the variant was called before the
|
||||
// definition was encountered. Now that there's a definition, convert it
|
||||
// to Usage::Unused.
|
||||
if let Usage::NoDefinition { redundant_use_sites } = entry {
|
||||
*entry = Usage::Unused {
|
||||
redundant_use_sites: redundant_use_sites.clone(),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
self.empty_tuple_enum_variants.insert(
|
||||
local_def_id,
|
||||
Usage::Unused {
|
||||
redundant_use_sites: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
VariantData::Unit(..) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(def_id) = check_expr_for_enum_as_function(expr) {
|
||||
if let Some(parentheses_span) = call_parentheses_span(cx.tcx, expr) {
|
||||
// Do not count expressions from macro expansion as a redundant use site.
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
match self.empty_tuple_enum_variants.get_mut(&def_id) {
|
||||
Some(
|
||||
&mut (Usage::Unused {
|
||||
ref mut redundant_use_sites,
|
||||
}
|
||||
| Usage::NoDefinition {
|
||||
ref mut redundant_use_sites,
|
||||
}),
|
||||
) => {
|
||||
redundant_use_sites.push(parentheses_span);
|
||||
},
|
||||
None => {
|
||||
// The variant isn't in the IndexMap which means its definition wasn't encountered yet.
|
||||
self.empty_tuple_enum_variants.insert(
|
||||
def_id,
|
||||
Usage::NoDefinition {
|
||||
redundant_use_sites: vec![parentheses_span],
|
||||
},
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
// The parentheses are not redundant.
|
||||
self.empty_tuple_enum_variants.insert(def_id, Usage::Used);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'_>) {
|
||||
for (local_def_id, usage) in &self.empty_tuple_enum_variants {
|
||||
// Ignore all variants with Usage::Used or Usage::NoDefinition
|
||||
let Usage::Unused { redundant_use_sites } = usage else {
|
||||
continue;
|
||||
};
|
||||
// Attempt to fetch the Variant from LocalDefId.
|
||||
let Node::Variant(variant) = cx.tcx.hir_node(
|
||||
cx.tcx
|
||||
.local_def_id_to_hir_id(cx.tcx.parent(local_def_id.to_def_id()).expect_local()),
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
// Span of the parentheses in variant definition
|
||||
let span = variant.span.with_lo(variant.ident.span.hi());
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
|
||||
span_after_ident,
|
||||
variant.hir_id,
|
||||
span,
|
||||
"enum variant has empty brackets",
|
||||
|diagnostic| {
|
||||
diagnostic.span_suggestion_hidden(
|
||||
span_after_ident,
|
||||
"remove the brackets",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if redundant_use_sites.is_empty() {
|
||||
// If there's no redundant use sites, the definition is the only place to modify.
|
||||
diagnostic.span_suggestion_hidden(
|
||||
span,
|
||||
"remove the brackets",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
let mut parentheses_spans: Vec<_> =
|
||||
redundant_use_sites.iter().map(|span| (*span, String::new())).collect();
|
||||
parentheses_spans.push((span, String::new()));
|
||||
diagnostic.multipart_suggestion(
|
||||
"remove the brackets",
|
||||
parentheses_spans,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_no_ident_token(braces_span_str: &str) -> bool {
|
||||
!rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident)
|
||||
fn has_brackets(var_data: &VariantData<'_>) -> bool {
|
||||
!matches!(var_data, VariantData::Unit(..))
|
||||
}
|
||||
|
||||
fn has_brackets(var_data: &VariantData) -> bool {
|
||||
!matches!(var_data, VariantData::Unit(_))
|
||||
}
|
||||
|
||||
fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool {
|
||||
if !var_data.fields().is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn has_no_fields(cx: &LateContext<'_>, var_data: &VariantData<'_>, braces_span: Span) -> bool {
|
||||
var_data.fields().is_empty() &&
|
||||
// there might still be field declarations hidden from the AST
|
||||
// (conditionally compiled code using #[cfg(..)])
|
||||
|
||||
let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
has_no_ident_token(braces_span_str.as_ref())
|
||||
!span_contains_cfg(cx, braces_span)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod unit_test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_has_no_ident_token() {
|
||||
let input = "{ field: u8 }";
|
||||
assert!(!has_no_ident_token(input));
|
||||
|
||||
let input = "(u8, String);";
|
||||
assert!(!has_no_ident_token(input));
|
||||
|
||||
let input = " {
|
||||
// test = 5
|
||||
}
|
||||
";
|
||||
assert!(has_no_ident_token(input));
|
||||
|
||||
let input = " ();";
|
||||
assert!(has_no_ident_token(input));
|
||||
// If expression HIR ID and callee HIR ID are same, returns the span of the parentheses, else,
|
||||
// returns None.
|
||||
fn call_parentheses_span(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Span> {
|
||||
if let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id)
|
||||
&& let ExprKind::Call(callee, ..) = parent.kind
|
||||
&& callee.hir_id == expr.hir_id
|
||||
{
|
||||
Some(parent.span.with_lo(expr.span.hi()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the LocalDefId of the variant being called as a function if it exists.
|
||||
fn check_expr_for_enum_as_function(expr: &Expr<'_>) -> Option<LocalDefId> {
|
||||
if let ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
res: Def(Ctor(CtorOf::Variant, _), def_id),
|
||||
..
|
||||
},
|
||||
)) = expr.kind
|
||||
{
|
||||
def_id.as_local()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,14 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
return;
|
||||
};
|
||||
|
||||
if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy {
|
||||
span_lint(cx, MAP_ENTRY, expr.span, lint_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if then_search.edits.is_empty() && else_search.edits.is_empty() {
|
||||
// No insertions
|
||||
return;
|
||||
} else if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy {
|
||||
// If there are other uses of the key, and the key is not copy,
|
||||
// we cannot perform a fix automatically, but continue to emit a lint.
|
||||
None
|
||||
} else if then_search.edits.is_empty() || else_search.edits.is_empty() {
|
||||
// if .. { insert } else { .. } or if .. { .. } else { insert }
|
||||
let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
|
||||
|
|
@ -123,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
|
||||
),
|
||||
};
|
||||
format!(
|
||||
Some(format!(
|
||||
"if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}",
|
||||
map_ty.entry_path(),
|
||||
)
|
||||
))
|
||||
} else {
|
||||
// if .. { insert } else { insert }
|
||||
let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
|
||||
|
|
@ -142,13 +141,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
};
|
||||
let indent_str = snippet_indent(cx, expr.span);
|
||||
let indent_str = indent_str.as_deref().unwrap_or("");
|
||||
format!(
|
||||
Some(format!(
|
||||
"match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\
|
||||
{indent_str} {entry}::{else_entry} => {}\n{indent_str}}}",
|
||||
reindent_multiline(&then_str, true, Some(4 + indent_str.len())),
|
||||
reindent_multiline(&else_str, true, Some(4 + indent_str.len())),
|
||||
entry = map_ty.entry_path(),
|
||||
)
|
||||
))
|
||||
}
|
||||
} else {
|
||||
if then_search.edits.is_empty() {
|
||||
|
|
@ -163,17 +162,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
} else {
|
||||
then_search.snippet_occupied(cx, then_expr.span, &mut app)
|
||||
};
|
||||
format!(
|
||||
Some(format!(
|
||||
"if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}",
|
||||
map_ty.entry_path(),
|
||||
)
|
||||
))
|
||||
} else if let Some(insertion) = then_search.as_single_insertion() {
|
||||
let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
|
||||
if contains_expr.negated {
|
||||
if insertion.value.can_have_side_effects() {
|
||||
format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});")
|
||||
Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});"))
|
||||
} else {
|
||||
format!("{map_str}.entry({key_str}).or_insert({value_str});")
|
||||
Some(format!("{map_str}.entry({key_str}).or_insert({value_str});"))
|
||||
}
|
||||
} else {
|
||||
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
|
||||
|
|
@ -183,7 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
} else {
|
||||
let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
|
||||
if contains_expr.negated {
|
||||
format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});")
|
||||
Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});"))
|
||||
} else {
|
||||
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
|
||||
// This would need to be a different lint.
|
||||
|
|
@ -192,7 +191,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app);
|
||||
if let Some(sugg) = sugg {
|
||||
span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app);
|
||||
} else {
|
||||
span_lint(cx, MAP_ENTRY, expr.span, lint_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
|
|||
.ok()
|
||||
.map(|val| rustc_middle::mir::Const::from_value(val, ty));
|
||||
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
|
||||
if let ty::Adt(adt, _) = ty.kind() {
|
||||
if adt.is_enum() {
|
||||
ty = adt.repr().discr_type().to_ty(cx.tcx);
|
||||
}
|
||||
if let ty::Adt(adt, _) = ty.kind()
|
||||
&& adt.is_enum()
|
||||
{
|
||||
ty = adt.repr().discr_type().to_ty(cx.tcx);
|
||||
}
|
||||
match ty.kind() {
|
||||
ty::Int(IntTy::Isize) => {
|
||||
|
|
|
|||
|
|
@ -72,10 +72,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
|||
_: Span,
|
||||
fn_def_id: LocalDefId,
|
||||
) {
|
||||
if let Some(header) = fn_kind.header() {
|
||||
if header.abi != ExternAbi::Rust {
|
||||
return;
|
||||
}
|
||||
if let Some(header) = fn_kind.header()
|
||||
&& header.abi != ExternAbi::Rust
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let parent_id = cx
|
||||
|
|
@ -93,12 +93,11 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
|||
// find `self` ty for this trait if relevant
|
||||
if let ItemKind::Trait(_, _, _, _, _, items) = item.kind {
|
||||
for trait_item in items {
|
||||
if trait_item.id.owner_id.def_id == fn_def_id {
|
||||
if trait_item.id.owner_id.def_id == fn_def_id
|
||||
// be sure we have `self` parameter in this function
|
||||
if trait_item.kind == (AssocItemKind::Fn { has_self: true }) {
|
||||
trait_self_ty =
|
||||
Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty());
|
||||
}
|
||||
&& trait_item.kind == (AssocItemKind::Fn { has_self: true })
|
||||
{
|
||||
trait_self_ty = Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,22 +141,22 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
|||
|
||||
impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> {
|
||||
fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
|
||||
if cmt.place.projections.is_empty() {
|
||||
if let PlaceBase::Local(lid) = cmt.place.base {
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
if cmt.place.projections.is_empty()
|
||||
&& let PlaceBase::Local(lid) = cmt.place.base
|
||||
{
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
}
|
||||
|
||||
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
|
||||
|
||||
fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
|
||||
if cmt.place.projections.is_empty() {
|
||||
if let PlaceBase::Local(lid) = cmt.place.base {
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
if cmt.place.projections.is_empty()
|
||||
&& let PlaceBase::Local(lid) = cmt.place.base
|
||||
{
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
self.set.swap_remove(&lid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,10 +170,11 @@ impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> {
|
|||
|
||||
// skip if there is a `self` parameter binding to a type
|
||||
// that contains `Self` (i.e.: `self: Box<Self>`), see #4804
|
||||
if let Some(trait_self_ty) = self.trait_self_ty {
|
||||
if self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
|
||||
return;
|
||||
}
|
||||
if let Some(trait_self_ty) = self.trait_self_ty
|
||||
&& self.cx.tcx.hir_name(cmt.hir_id) == kw::SelfLower
|
||||
&& cmt.place.ty().contains(trait_self_ty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
|
||||
|
|
|
|||
|
|
@ -75,10 +75,10 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
|
|||
|
||||
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
|
||||
if is_panic(self.lcx, macro_call.def_id) {
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr)
|
||||
&& is_panic(self.lcx, macro_call.def_id)
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
|
||||
// check for `unwrap`
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
|
|||
};
|
||||
}
|
||||
|
||||
suggestion.maybe_par()
|
||||
suggestion.maybe_paren()
|
||||
}
|
||||
|
||||
fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
|
|
@ -165,7 +165,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, ar
|
|||
expr.span,
|
||||
"logarithm for bases 2, 10 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_par()),
|
||||
format!("{}.{method}()", Sugg::hir(cx, receiver, "..").maybe_paren()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
@ -228,24 +228,24 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
|
|||
|
||||
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
// Check receiver
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) {
|
||||
if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver)
|
||||
&& let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
Some("exp")
|
||||
} else if F32(2.0) == value || F64(2.0) == value {
|
||||
Some("exp2")
|
||||
} else {
|
||||
None
|
||||
} {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{method}()", prepare_receiver_sugg(cx, &args[0])),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
// Check argument
|
||||
|
|
@ -254,13 +254,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
(
|
||||
SUBOPTIMAL_FLOPS,
|
||||
"square-root of a number can be computed more efficiently and accurately",
|
||||
format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
|
||||
format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_paren()),
|
||||
)
|
||||
} else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
|
||||
(
|
||||
IMPRECISE_FLOPS,
|
||||
"cube-root of a number can be computed more accurately",
|
||||
format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
|
||||
format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_paren()),
|
||||
)
|
||||
} else if let Some(exponent) = get_integer_from_float_constant(&value) {
|
||||
(
|
||||
|
|
@ -268,7 +268,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
"exponentiation with integer powers can be computed more efficiently",
|
||||
format!(
|
||||
"{}.powi({})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_par(),
|
||||
Sugg::hir(cx, receiver, "..").maybe_paren(),
|
||||
numeric_literal::format(&exponent.to_string(), None, false)
|
||||
),
|
||||
)
|
||||
|
|
@ -289,55 +289,53 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
|
|||
}
|
||||
|
||||
fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
|
||||
if value == Int(2) {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent) {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind
|
||||
{
|
||||
if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0])
|
||||
&& value == Int(2)
|
||||
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if let Some(grandparent) = get_parent_expr(cx, parent)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind
|
||||
&& method_name.as_str() == "sqrt"
|
||||
&& detect_hypot(cx, receiver).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = parent.kind
|
||||
{
|
||||
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
|
||||
|
||||
// Negate expr if original code has subtraction and expr is on the right side
|
||||
let maybe_neg_sugg = |expr, hir_id| {
|
||||
let sugg = Sugg::hir(cx, expr, "..");
|
||||
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
|
||||
-sugg
|
||||
} else {
|
||||
sugg
|
||||
}
|
||||
};
|
||||
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: op @ (BinOpKind::Add | BinOpKind::Sub),
|
||||
..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = parent.kind
|
||||
{
|
||||
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
|
||||
|
||||
// Negate expr if original code has subtraction and expr is on the right side
|
||||
let maybe_neg_sugg = |expr, hir_id| {
|
||||
let sugg = Sugg::hir(cx, expr, "..");
|
||||
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
|
||||
-sugg
|
||||
} else {
|
||||
sugg
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
parent.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_par(),
|
||||
maybe_neg_sugg(receiver, expr.hir_id),
|
||||
maybe_neg_sugg(other_addend, other_addend.hir_id),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
parent.span,
|
||||
"multiply and add expressions can be calculated more efficiently and accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, receiver, "..").maybe_paren(),
|
||||
maybe_neg_sugg(receiver, expr.hir_id),
|
||||
maybe_neg_sugg(other_addend, other_addend.hir_id),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -371,7 +369,7 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
|
|||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir(cx, lmul_lhs, "..").maybe_par(),
|
||||
Sugg::hir(cx, lmul_lhs, "..").maybe_paren(),
|
||||
Sugg::hir(cx, rmul_lhs, "..")
|
||||
));
|
||||
}
|
||||
|
|
@ -403,7 +401,7 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
|
|||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir(cx, largs_0, "..").maybe_par(),
|
||||
Sugg::hir(cx, largs_0, "..").maybe_paren(),
|
||||
Sugg::hir(cx, rargs_0, "..")
|
||||
));
|
||||
}
|
||||
|
|
@ -449,7 +447,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
expr.span,
|
||||
"(e.pow(x) - 1) can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()),
|
||||
format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_paren()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
@ -483,12 +481,12 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
rhs,
|
||||
) = &expr.kind
|
||||
{
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind {
|
||||
if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind
|
||||
&& method_name.as_str() == "sqrt"
|
||||
&& detect_hypot(cx, receiver).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let maybe_neg_sugg = |expr| {
|
||||
|
|
@ -566,15 +564,15 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
/// If the two expressions are not negations of each other, then it
|
||||
/// returns None.
|
||||
fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
|
||||
if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind {
|
||||
if eq_expr_value(cx, expr1_negated, expr2) {
|
||||
return Some((false, expr2));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind
|
||||
&& eq_expr_value(cx, expr1_negated, expr2)
|
||||
{
|
||||
return Some((false, expr2));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind {
|
||||
if eq_expr_value(cx, expr1, expr2_negated) {
|
||||
return Some((true, expr1));
|
||||
}
|
||||
if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind
|
||||
&& eq_expr_value(cx, expr1, expr2_negated)
|
||||
{
|
||||
return Some((true, expr1));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -591,11 +589,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
{
|
||||
let positive_abs_sugg = (
|
||||
"manual implementation of `abs` method",
|
||||
format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
|
||||
format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()),
|
||||
);
|
||||
let negative_abs_sugg = (
|
||||
"manual implementation of negation of `abs` method",
|
||||
format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
|
||||
format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_paren()),
|
||||
);
|
||||
let sugg = if is_testing_positive(cx, cond, body) {
|
||||
if if_expr_positive {
|
||||
|
|
@ -672,7 +670,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
"consider using",
|
||||
format!(
|
||||
"{}.log({})",
|
||||
Sugg::hir(cx, largs_self, "..").maybe_par(),
|
||||
Sugg::hir(cx, largs_self, "..").maybe_paren(),
|
||||
Sugg::hir(cx, rargs_self, ".."),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
|
|
@ -703,7 +701,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)
|
||||
&& (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_paren());
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
|
|
@ -726,7 +724,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
} else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue)
|
||||
&& (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_paren());
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
|||
.into_owned()
|
||||
} else {
|
||||
let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
|
||||
format!("{}.to_string()", sugg.maybe_par())
|
||||
format!("{}.to_string()", sugg.maybe_paren())
|
||||
};
|
||||
span_useless_format(cx, call_site, sugg, applicability);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ declare_clippy_lint! {
|
|||
/// format!("{var:.prec$}");
|
||||
/// ```
|
||||
///
|
||||
/// If allow-mixed-uninlined-format-args is set to false in clippy.toml,
|
||||
/// If `allow-mixed-uninlined-format-args` is set to `false` in clippy.toml,
|
||||
/// the following code will also trigger the lint:
|
||||
/// ```no_run
|
||||
/// # let var = 42;
|
||||
|
|
@ -159,7 +159,7 @@ declare_clippy_lint! {
|
|||
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub UNINLINED_FORMAT_ARGS,
|
||||
pedantic,
|
||||
style,
|
||||
"using non-inlined variables in `format!` calls"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node};
|
||||
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
|
||||
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators, sym};
|
||||
use rustc_ast::{FormatArgsPiece, FormatTrait};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -185,13 +185,13 @@ impl FormatImplExpr<'_, '_> {
|
|||
&& let trait_name = match placeholder.format_trait {
|
||||
FormatTrait::Display => sym::Display,
|
||||
FormatTrait::Debug => sym::Debug,
|
||||
FormatTrait::LowerExp => sym!(LowerExp),
|
||||
FormatTrait::UpperExp => sym!(UpperExp),
|
||||
FormatTrait::Octal => sym!(Octal),
|
||||
FormatTrait::LowerExp => sym::LowerExp,
|
||||
FormatTrait::UpperExp => sym::UpperExp,
|
||||
FormatTrait::Octal => sym::Octal,
|
||||
FormatTrait::Pointer => sym::Pointer,
|
||||
FormatTrait::Binary => sym!(Binary),
|
||||
FormatTrait::LowerHex => sym!(LowerHex),
|
||||
FormatTrait::UpperHex => sym!(UpperHex),
|
||||
FormatTrait::Binary => sym::Binary,
|
||||
FormatTrait::LowerHex => sym::LowerHex,
|
||||
FormatTrait::UpperHex => sym::UpperHex,
|
||||
}
|
||||
&& trait_name == self.format_trait_impl.name
|
||||
&& let Ok(index) = placeholder.argument.index
|
||||
|
|
|
|||
|
|
@ -138,27 +138,28 @@ impl EarlyLintPass for Formatting {
|
|||
|
||||
/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
|
||||
fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
|
||||
if !lhs.span.from_expansion() && !rhs.span.from_expansion() {
|
||||
let eq_span = lhs.span.between(rhs.span);
|
||||
if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
|
||||
if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
|
||||
let op = op.as_str();
|
||||
let eqop_span = lhs.span.between(sub_rhs.span);
|
||||
if eq_snippet.ends_with('=') {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
eqop_span,
|
||||
format!(
|
||||
"this looks like you are trying to use `.. {op}= ..`, but you \
|
||||
if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind
|
||||
&& !lhs.span.from_expansion()
|
||||
&& !rhs.span.from_expansion()
|
||||
{
|
||||
let eq_span = lhs.span.between(rhs.span);
|
||||
if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind
|
||||
&& let Some(eq_snippet) = snippet_opt(cx, eq_span)
|
||||
{
|
||||
let op = op.as_str();
|
||||
let eqop_span = lhs.span.between(sub_rhs.span);
|
||||
if eq_snippet.ends_with('=') {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
eqop_span,
|
||||
format!(
|
||||
"this looks like you are trying to use `.. {op}= ..`, but you \
|
||||
really are doing `.. = ({op} ..)`"
|
||||
),
|
||||
None,
|
||||
format!("to remove this lint, use either `{op}=` or `= {op}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
None,
|
||||
format!("to remove this lint, use either `{op}=` or `= {op}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
|||
};
|
||||
|
||||
let sugg =
|
||||
Sugg::hir_with_applicability(cx, expr, "<string>", &mut Applicability::MachineApplicable).maybe_par();
|
||||
Sugg::hir_with_applicability(cx, expr, "<string>", &mut Applicability::MachineApplicable).maybe_paren();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind};
|
||||
use rustc_hir::{BlockCheckMode, Body, ExprKind, FnDecl, ImplicitSelfKind, UnsafeSource};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -40,14 +40,25 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
|
|||
name
|
||||
};
|
||||
|
||||
// Body must be &(mut) <self_data>.name
|
||||
// Body must be `&(mut) <self_data>.name`, potentially in an `unsafe` block
|
||||
// self_data is not necessarily self, to also lint sub-getters, etc…
|
||||
|
||||
let block_expr = if let ExprKind::Block(block, _) = body.value.kind
|
||||
&& block.stmts.is_empty()
|
||||
&& let Some(block_expr) = block.expr
|
||||
{
|
||||
block_expr
|
||||
if let ExprKind::Block(unsafe_block, _) = block_expr.kind
|
||||
&& unsafe_block.stmts.is_empty()
|
||||
&& matches!(
|
||||
unsafe_block.rules,
|
||||
BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
|
||||
)
|
||||
&& let Some(unsafe_block_expr) = unsafe_block.expr
|
||||
{
|
||||
unsafe_block_expr
|
||||
} else {
|
||||
block_expr
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,9 +59,7 @@ impl RenamedFnArgs {
|
|||
let mut renamed: Vec<(Span, String)> = vec![];
|
||||
|
||||
debug_assert!(default_idents.size_hint() == current_idents.size_hint());
|
||||
while let (Some(default_ident), Some(current_ident)) =
|
||||
(default_idents.next(), current_idents.next())
|
||||
{
|
||||
while let (Some(default_ident), Some(current_ident)) = (default_idents.next(), current_idents.next()) {
|
||||
let has_name_to_check = |ident: Option<Ident>| {
|
||||
if let Some(ident) = ident
|
||||
&& ident.name != kw::Underscore
|
||||
|
|
|
|||
|
|
@ -47,16 +47,16 @@ pub(super) fn check_fn(
|
|||
}
|
||||
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) {
|
||||
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
|
||||
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind
|
||||
// don't lint extern functions decls, it's not their fault
|
||||
if sig.header.abi == ExternAbi::Rust {
|
||||
check_arg_number(
|
||||
cx,
|
||||
sig.decl,
|
||||
item.span.with_hi(sig.decl.output.span().hi()),
|
||||
too_many_arguments_threshold,
|
||||
);
|
||||
}
|
||||
&& sig.header.abi == ExternAbi::Rust
|
||||
{
|
||||
check_arg_number(
|
||||
cx,
|
||||
sig.decl,
|
||||
item.span.with_hi(sig.decl.output.span().hi()),
|
||||
too_many_arguments_threshold,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
|
||||
.maybe_par()
|
||||
.maybe_paren()
|
||||
.to_string();
|
||||
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
|
||||
let method_body = if let Some(first_stmt) = then_block.stmts.first() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
|
||||
use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
|
|
@ -134,6 +134,10 @@ fn lint_implicit_returns(
|
|||
},
|
||||
|
||||
ExprKind::Match(_, arms, _) => {
|
||||
if let Some(await_expr) = desugar_await(expr) {
|
||||
lint_return(cx, await_expr.hir_id, await_expr.span);
|
||||
return LintLocation::Inner;
|
||||
}
|
||||
for arm in arms {
|
||||
let res = lint_implicit_returns(
|
||||
cx,
|
||||
|
|
@ -153,18 +157,18 @@ fn lint_implicit_returns(
|
|||
ExprKind::Loop(block, ..) => {
|
||||
let mut add_return = false;
|
||||
let _: Option<!> = for_each_expr_without_closures(block, |e| {
|
||||
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
||||
if dest.target_id.ok() == Some(expr.hir_id) {
|
||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||
// At this point sub_expr can be `None` in async functions which either diverge, or return
|
||||
// the unit type.
|
||||
if let Some(sub_expr) = sub_expr {
|
||||
lint_break(cx, e.hir_id, e.span, sub_expr.span);
|
||||
}
|
||||
} else {
|
||||
// the break expression is from a macro call, add a return to the loop
|
||||
add_return = true;
|
||||
if let ExprKind::Break(dest, sub_expr) = e.kind
|
||||
&& dest.target_id.ok() == Some(expr.hir_id)
|
||||
{
|
||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||
// At this point sub_expr can be `None` in async functions which either diverge, or return
|
||||
// the unit type.
|
||||
if let Some(sub_expr) = sub_expr {
|
||||
lint_break(cx, e.hir_id, e.span, sub_expr.span);
|
||||
}
|
||||
} else {
|
||||
// the break expression is from a macro call, add a return to the loop
|
||||
add_return = true;
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
|
|
@ -241,6 +245,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
|
|||
Some(e) => e,
|
||||
None => return,
|
||||
}
|
||||
} else if let Some(expr) = get_async_closure_expr(cx.tcx, body.value) {
|
||||
expr
|
||||
} else {
|
||||
body.value
|
||||
};
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ fn check_subtraction(
|
|||
// This part of the condition is voluntarily split from the one before to ensure that
|
||||
// if `snippet_opt` fails, it won't try the next conditions.
|
||||
if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST))
|
||||
&& let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_par)
|
||||
&& let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren)
|
||||
&& let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr)
|
||||
{
|
||||
let sugg = format!(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_hir_analysis::lower_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, AssocItem, ClauseKind, Generics, Ty, TyCtxt};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -315,7 +315,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) {
|
|||
assocs
|
||||
.filter_by_name_unhygienic(constraint.ident.name)
|
||||
.next()
|
||||
.is_some_and(|assoc| assoc.is_type())
|
||||
.is_some_and(AssocItem::is_type)
|
||||
})
|
||||
{
|
||||
emit_lint(cx, poly_trait, bounds, index, implied_constraints, bound);
|
||||
|
|
|
|||
|
|
@ -65,13 +65,13 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct InconsistentStructConstructor {
|
||||
lint_inconsistent_struct_field_initializers: bool,
|
||||
check_inconsistent_struct_field_initializers: bool,
|
||||
}
|
||||
|
||||
impl InconsistentStructConstructor {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers,
|
||||
check_inconsistent_struct_field_initializers: conf.check_inconsistent_struct_field_initializers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
|||
let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand);
|
||||
let applicability = if all_fields_are_shorthand {
|
||||
Applicability::MachineApplicable
|
||||
} else if self.lint_inconsistent_struct_field_initializers {
|
||||
} else if self.check_inconsistent_struct_field_initializers {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ use clippy_config::Conf;
|
|||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
|
||||
use clippy_utils::{higher, is_from_proc_macro, is_in_test};
|
||||
use clippy_utils::{higher, is_from_proc_macro, is_in_test, sym};
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -136,28 +135,28 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
|||
|
||||
let const_range = to_const_range(cx, range, size);
|
||||
|
||||
if let (Some(start), _) = const_range {
|
||||
if start > size {
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.start.map_or(expr.span, |start| start.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if let (Some(start), _) = const_range
|
||||
&& start > size
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.start.map_or(expr.span, |start| start.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if let (_, Some(end)) = const_range {
|
||||
if end > size {
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.end.map_or(expr.span, |end| end.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
if let (_, Some(end)) = const_range
|
||||
&& end > size
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
OUT_OF_BOUNDS_INDEXING,
|
||||
range.end.map_or(expr.span, |end| end.span),
|
||||
"range is out of bounds",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if let (Some(_), Some(_)) = const_range {
|
||||
|
|
@ -268,7 +267,7 @@ fn ty_has_applicable_get_function<'tcx>(
|
|||
index_expr: &Expr<'_>,
|
||||
) -> bool {
|
||||
if let ty::Adt(_, _) = array_ty.kind()
|
||||
&& let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym!(get)).map(|m| {
|
||||
&& let Some(get_output_ty) = get_adt_inherent_method(cx, ty, sym::get).map(|m| {
|
||||
cx.tcx
|
||||
.fn_sig(m.def_id)
|
||||
.skip_binder()
|
||||
|
|
|
|||
|
|
@ -156,11 +156,12 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
|||
.and(cap);
|
||||
}
|
||||
}
|
||||
if method.ident.name.as_str() == "flat_map" && args.len() == 1 {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
|
||||
let body = cx.tcx.hir_body(body);
|
||||
return is_infinite(cx, body.value);
|
||||
}
|
||||
if method.ident.name.as_str() == "flat_map"
|
||||
&& args.len() == 1
|
||||
&& let ExprKind::Closure(&Closure { body, .. }) = args[0].kind
|
||||
{
|
||||
let body = cx.tcx.hir_body(body);
|
||||
return is_infinite(cx, body.value);
|
||||
}
|
||||
Finite
|
||||
},
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg
|
|||
expr.span,
|
||||
"manual implementation of `Instant::elapsed`",
|
||||
"try",
|
||||
format!("{}.elapsed()", sugg.maybe_par()),
|
||||
format!("{}.elapsed()", sugg.maybe_paren()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,14 +130,14 @@ impl IntPlusOne {
|
|||
BinOpKind::Le => "<",
|
||||
_ => return None,
|
||||
};
|
||||
if let Some(snippet) = node.span.get_source_text(cx) {
|
||||
if let Some(other_side_snippet) = other_side.span.get_source_text(cx) {
|
||||
let rec = match side {
|
||||
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
|
||||
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
|
||||
};
|
||||
return rec;
|
||||
}
|
||||
if let Some(snippet) = node.span.get_source_text(cx)
|
||||
&& let Some(other_side_snippet) = other_side.span.get_source_text(cx)
|
||||
{
|
||||
let rec = match side {
|
||||
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
|
||||
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
|
||||
};
|
||||
return rec;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -157,10 +157,10 @@ impl IntPlusOne {
|
|||
|
||||
impl EarlyLintPass for IntPlusOne {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
|
||||
if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
|
||||
if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
|
||||
Self::emit_warning(cx, item, rec);
|
||||
}
|
||||
if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind
|
||||
&& let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs)
|
||||
{
|
||||
Self::emit_warning(cx, item, rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue