Merge remote-tracking branch 'upstream/master' into doc-markdown
This commit is contained in:
commit
fb943fbe86
35
.github/ISSUE_TEMPLATE/false_negative.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/false_negative.md
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug Report (False Negative)
|
||||
about: Create a bug report about missing warnings from a lint
|
||||
labels: L-bug, L-false-negative
|
||||
---
|
||||
<!--
|
||||
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
|
||||
along with any information you feel relevant to replicating the bug.
|
||||
-->
|
||||
Lint name:
|
||||
|
||||
|
||||
I tried this code:
|
||||
|
||||
```rust
|
||||
<code>
|
||||
```
|
||||
|
||||
I expected to see this happen: *explanation*
|
||||
|
||||
Instead, this happened: *explanation*
|
||||
|
||||
### Meta
|
||||
|
||||
- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
|
||||
- `rustc -Vv`:
|
||||
```
|
||||
rustc 1.46.0-nightly (f455e46ea 2020-06-20)
|
||||
binary: rustc
|
||||
commit-hash: f455e46eae1a227d735091091144601b467e1565
|
||||
commit-date: 2020-06-20
|
||||
host: x86_64-unknown-linux-gnu
|
||||
release: 1.46.0-nightly
|
||||
LLVM version: 10.0
|
||||
```
|
35
.github/ISSUE_TEMPLATE/false_positive.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/false_positive.md
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug Report (False Positive)
|
||||
about: Create a bug report about a wrongly emitted lint warning
|
||||
labels: L-bug, L-false-positive
|
||||
---
|
||||
<!--
|
||||
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
|
||||
along with any information you feel relevant to replicating the bug.
|
||||
-->
|
||||
Lint name:
|
||||
|
||||
|
||||
I tried this code:
|
||||
|
||||
```rust
|
||||
<code>
|
||||
```
|
||||
|
||||
I expected to see this happen: *explanation*
|
||||
|
||||
Instead, this happened: *explanation*
|
||||
|
||||
### Meta
|
||||
|
||||
- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
|
||||
- `rustc -Vv`:
|
||||
```
|
||||
rustc 1.46.0-nightly (f455e46ea 2020-06-20)
|
||||
binary: rustc
|
||||
commit-hash: f455e46eae1a227d735091091144601b467e1565
|
||||
commit-date: 2020-06-20
|
||||
host: x86_64-unknown-linux-gnu
|
||||
release: 1.46.0-nightly
|
||||
LLVM version: 10.0
|
||||
```
|
37
.github/workflows/clippy.yml
vendored
37
.github/workflows/clippy.yml
vendored
@ -35,29 +35,11 @@ jobs:
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Run
|
||||
- name: Set LD_LIBRARY_PATH (Linux)
|
||||
@ -66,13 +48,16 @@ jobs:
|
||||
echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: cargo build --features deny-warnings
|
||||
run: cargo build --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test "--fix -Zunstable-options"
|
||||
run: cargo run --features deny-warnings,internal-lints --bin cargo-clippy -- clippy --fix -Zunstable-options
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features deny-warnings
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features deny-warnings
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test rustc_tools_util
|
||||
@ -98,9 +83,3 @@ jobs:
|
||||
cargo dev new_lint --name new_late_pass --pass late
|
||||
cargo check
|
||||
git reset --hard HEAD
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
|
97
.github/workflows/clippy_bors.yml
vendored
97
.github/workflows/clippy_bors.yml
vendored
@ -23,6 +23,7 @@ jobs:
|
||||
- uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
with:
|
||||
@ -84,31 +85,11 @@ jobs:
|
||||
sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
|
||||
if: matrix.host == 'i686-unknown-linux-gnu'
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: ${{ matrix.host }}
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.host }}
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
env:
|
||||
HOST_TOOLCHAIN: ${{ matrix.host }}
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Run
|
||||
- name: Set LD_LIBRARY_PATH (Linux)
|
||||
@ -129,13 +110,13 @@ jobs:
|
||||
echo "$SYSROOT/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build
|
||||
run: cargo build --features deny-warnings
|
||||
run: cargo build --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features deny-warnings
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features deny-warnings
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test rustc_tools_util
|
||||
@ -155,12 +136,6 @@ jobs:
|
||||
env:
|
||||
OS: ${{ runner.os }}
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
|
||||
integration_build:
|
||||
needs: changelog
|
||||
runs-on: ubuntu-latest
|
||||
@ -171,29 +146,11 @@ jobs:
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Run
|
||||
- name: Build Integration Test
|
||||
@ -214,11 +171,6 @@ jobs:
|
||||
name: target
|
||||
path: target
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
integration:
|
||||
needs: integration_build
|
||||
strategy:
|
||||
@ -252,29 +204,11 @@ jobs:
|
||||
with:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Run cargo update
|
||||
run: cargo update
|
||||
|
||||
- name: Cache cargo dir
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Master Toolchain Setup
|
||||
run: bash setup-toolchain.sh
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
||||
# Download
|
||||
- name: Download target dir
|
||||
@ -288,16 +222,11 @@ jobs:
|
||||
|
||||
# Run
|
||||
- name: Test ${{ matrix.integration }}
|
||||
run: $CARGO_TARGET_DIR/debug/integration
|
||||
run: |
|
||||
RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
|
||||
$CARGO_TARGET_DIR/debug/integration
|
||||
env:
|
||||
INTEGRATION: ${{ matrix.integration }}
|
||||
RUSTUP_TOOLCHAIN: master
|
||||
|
||||
# Cleanup
|
||||
- name: Run cargo-cache --autoclean
|
||||
run: |
|
||||
cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache
|
||||
cargo cache
|
||||
|
||||
# These jobs doesn't actually test anything, but they're only used to tell
|
||||
# bors the build completed, as there is no practical way to detect when a
|
||||
|
10
.github/workflows/clippy_dev.yml
vendored
10
.github/workflows/clippy_dev.yml
vendored
@ -22,6 +22,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: remove toolchain file
|
||||
run: rm rust-toolchain
|
||||
|
||||
- name: rust-toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
@ -29,9 +35,7 @@ jobs:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
profile: minimal
|
||||
components: rustfmt
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
default: true
|
||||
|
||||
# Run
|
||||
- name: Build
|
||||
|
146
CHANGELOG.md
146
CHANGELOG.md
@ -6,11 +6,138 @@ document.
|
||||
|
||||
## Unreleased / In Rust Nightly
|
||||
|
||||
[b20d4c1...master](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...master)
|
||||
[4911ab1...master](https://github.com/rust-lang/rust-clippy/compare/4911ab1...master)
|
||||
|
||||
## Rust 1.50
|
||||
|
||||
Current beta, release 2021-02-11
|
||||
|
||||
[b20d4c1...4911ab1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4911ab1)
|
||||
|
||||
### New Lints
|
||||
|
||||
* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
|
||||
* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
|
||||
* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
|
||||
* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
|
||||
* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
|
||||
* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
|
||||
* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
|
||||
* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
|
||||
* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
|
||||
as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
|
||||
* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt`
|
||||
[#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
|
||||
* Move [`map_err_ignore`] to `restriction`
|
||||
[#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
|
||||
* Move [`await_holding_refcell_ref`] to `pedantic`
|
||||
[#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
|
||||
* Move [`await_holding_lock`] to `pedantic`
|
||||
[#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Add the `unreadable-literal-lint-fractions` configuration to disable
|
||||
the `unreadable_literal` lint for fractions
|
||||
[#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
|
||||
* [`clone_on_copy`]: Now shows the type in the lint message
|
||||
[#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
|
||||
* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
|
||||
[#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
|
||||
* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
|
||||
[#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
|
||||
* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
|
||||
[#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
|
||||
* [`clone_double_ref`]: Now prints the reference type in the lint message
|
||||
[#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
|
||||
* [`modulo_one`]: Now also lints on -1.
|
||||
[#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
|
||||
* [`empty_loop`]: Now lints no_std crates, too
|
||||
[#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
|
||||
* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
|
||||
[#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
|
||||
* [`wrong_self_convention`]: Now also lints in trait definitions
|
||||
[#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
|
||||
* [`needless_borrow`]: Print the type in the lint message
|
||||
[#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
|
||||
|
||||
[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`manual_range_contains`]: No longer lints in `const fn`
|
||||
[#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
|
||||
* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
|
||||
[#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
|
||||
* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
|
||||
[#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
|
||||
* [`match_like_matches_macro`]: No longer lints on arms with attributes
|
||||
[#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
|
||||
* [`map_clone`]: No longer lints with deref and clone
|
||||
[#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
|
||||
* [`map_clone`]: No longer lints in the case of &mut
|
||||
[#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
|
||||
* [`needless_update`]: Now ignores `non_exhaustive` structs
|
||||
[#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
|
||||
* [`needless_collect`]: No longer lints when a collect is needed multiple times
|
||||
[#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
|
||||
* [`unnecessary_cast`] No longer lints cfg-dependent types
|
||||
[#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
|
||||
* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
|
||||
Both now ignore enums with frozen variants
|
||||
[#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
|
||||
|
||||
|
||||
### Suggestion Fixes/Improvements
|
||||
|
||||
* [`vec_box`]: Provide correct type scope suggestion
|
||||
[#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
|
||||
* [`manual_range_contains`]: Give correct suggestion when using floats
|
||||
[#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
|
||||
* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
|
||||
[#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
|
||||
* [`manual_async_fn`]: Improve suggestion formatting
|
||||
[#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
|
||||
* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
|
||||
[#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* Fix a crash in [`from_iter_instead_of_collect`]
|
||||
[#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
|
||||
* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
|
||||
[#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
|
||||
* Searching for lints with dashes and spaces is possible now. For example
|
||||
`missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
|
||||
* Improved fuzzy search in lint descriptions
|
||||
* Various README improvements
|
||||
[#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
|
||||
* Add known problems to [`comparison_chain`] documentation
|
||||
[#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
|
||||
* Fix example used in [`cargo_common_metadata`]
|
||||
[#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
|
||||
* Improve [`map_clone`] documentation
|
||||
[#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
|
||||
|
||||
### Others
|
||||
|
||||
* You can now tell Clippy about the MSRV your project supports. Please refer to
|
||||
the specific README section to learn more about MSRV support [here][msrv_readme]
|
||||
[#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
|
||||
* Add `--no-deps` option to avoid running on path dependencies in workspaces
|
||||
[#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
|
||||
|
||||
## Rust 1.49
|
||||
|
||||
Current beta, release 2020-12-31
|
||||
Current stable, released 2020-12-31
|
||||
|
||||
[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
|
||||
|
||||
@ -116,7 +243,7 @@ Current beta, release 2020-12-31
|
||||
|
||||
## Rust 1.48
|
||||
|
||||
Current stable, released 2020-11-19
|
||||
Released 2020-11-19
|
||||
|
||||
[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
|
||||
|
||||
@ -1751,6 +1878,7 @@ Released 2018-09-13
|
||||
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
|
||||
[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
|
||||
[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
|
||||
[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
|
||||
[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
|
||||
[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
|
||||
[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
|
||||
@ -1769,7 +1897,9 @@ Released 2018-09-13
|
||||
[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
|
||||
[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
|
||||
[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
|
||||
[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
|
||||
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
||||
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
|
||||
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
||||
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
|
||||
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
|
||||
@ -1840,6 +1970,7 @@ Released 2018-09-13
|
||||
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
|
||||
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
|
||||
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
|
||||
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
|
||||
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
|
||||
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
|
||||
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
|
||||
@ -1971,6 +2102,7 @@ Released 2018-09-13
|
||||
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
||||
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
|
||||
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
|
||||
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
|
||||
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
|
||||
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
|
||||
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
|
||||
@ -2005,10 +2137,12 @@ Released 2018-09-13
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
|
||||
[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
|
||||
[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
|
||||
[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
|
||||
[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
|
||||
[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
|
||||
[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
|
||||
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
|
||||
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
|
||||
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
|
||||
@ -2023,10 +2157,12 @@ Released 2018-09-13
|
||||
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
|
||||
[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
|
||||
[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
|
||||
[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
|
||||
[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
|
||||
[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
|
||||
[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
|
||||
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
|
||||
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
|
||||
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
|
||||
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
|
||||
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
|
||||
@ -2056,6 +2192,7 @@ Released 2018-09-13
|
||||
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
|
||||
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
|
||||
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
|
||||
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
|
||||
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
||||
@ -2073,6 +2210,7 @@ Released 2018-09-13
|
||||
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
|
||||
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
|
||||
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
|
||||
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
|
||||
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
|
||||
[`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
|
||||
@ -2146,6 +2284,7 @@ Released 2018-09-13
|
||||
[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
|
||||
[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
|
||||
[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
|
||||
[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
|
||||
[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
|
||||
[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
|
||||
[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
|
||||
@ -2166,5 +2305,6 @@ Released 2018-09-13
|
||||
[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
|
||||
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
|
||||
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
|
||||
[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
|
||||
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
|
||||
<!-- end autogenerated links to lint list -->
|
||||
|
@ -14,11 +14,16 @@ All contributors are expected to follow the [Rust Code of Conduct].
|
||||
|
||||
- [Contributing to Clippy](#contributing-to-clippy)
|
||||
- [Getting started](#getting-started)
|
||||
- [High level approach](#high-level-approach)
|
||||
- [Finding something to fix/improve](#finding-something-to-fiximprove)
|
||||
- [Writing code](#writing-code)
|
||||
- [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
|
||||
- [How Clippy works](#how-clippy-works)
|
||||
- [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust)
|
||||
- [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
|
||||
- [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
|
||||
- [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
|
||||
- [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
|
||||
- [Defining remotes](#defining-remotes)
|
||||
- [Issue and PR triage](#issue-and-pr-triage)
|
||||
- [Bors and Homu](#bors-and-homu)
|
||||
- [Contributions](#contributions)
|
||||
@ -44,7 +49,7 @@ first read the [Basics docs](doc/basics.md).**
|
||||
All issues on Clippy are mentored, if you want help with a bug just ask
|
||||
@Manishearth, @flip1995, @phansch or @yaahc.
|
||||
|
||||
Some issues are easier than others. The [`good first issue`] label can be used to find the easy issues.
|
||||
Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues.
|
||||
If you want to work on an issue, please leave a comment so that we can assign it to you!
|
||||
|
||||
There are also some abandoned PRs, marked with [`S-inactive-closed`].
|
||||
@ -63,16 +68,16 @@ To figure out how this syntax structure is encoded in the AST, it is recommended
|
||||
Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
|
||||
But we can make it nest-less by using [if_chain] macro, [like this][nest-less].
|
||||
|
||||
[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good first issue`]
|
||||
[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`]
|
||||
first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
|
||||
Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
|
||||
debugging to find the actual problem behind the issue.
|
||||
Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
|
||||
debugging to find the actual problem behind the issue.
|
||||
|
||||
[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
|
||||
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
|
||||
an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
|
||||
|
||||
[`good first issue`]: https://github.com/rust-lang/rust-clippy/labels/good%20first%20issue
|
||||
[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
|
||||
[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
|
||||
[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
|
||||
[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
|
||||
@ -106,7 +111,7 @@ To work around this, you need to have a copy of the [rustc-repo][rustc_repo] ava
|
||||
`git clone https://github.com/rust-lang/rust/`.
|
||||
Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
|
||||
which rust-analyzer will be able to understand.
|
||||
Run `cargo dev ra-setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
|
||||
Run `cargo dev ra_setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
|
||||
you just cloned.
|
||||
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
|
||||
Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
|
||||
@ -177,18 +182,26 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun
|
||||
[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
|
||||
[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
|
||||
|
||||
## Fixing build failures caused by Rust
|
||||
## Syncing changes between Clippy and [`rust-lang/rust`]
|
||||
|
||||
Clippy currently gets built with `rustc` of the `rust-lang/rust` `master`
|
||||
branch. Most of the times we have to adapt to the changes and only very rarely
|
||||
there's an actual bug in Rust.
|
||||
Clippy currently gets built with a pinned nightly version.
|
||||
|
||||
If you decide to make Clippy work again with a Rust commit that breaks it, you
|
||||
have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of
|
||||
Clippy in the `rust-lang/rust` repository.
|
||||
In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy
|
||||
that compiler hackers modify from time to time to adapt to changes in the unstable
|
||||
API of the compiler.
|
||||
|
||||
For general information about `subtree`s in the Rust repository see [Rust's
|
||||
`CONTRIBUTING.md`][subtree].
|
||||
We need to sync these changes back to this repository periodically, and the changes
|
||||
made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository.
|
||||
|
||||
To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done
|
||||
in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
|
||||
the Rust stable release and then every other week. That way we guarantee that we keep
|
||||
this repo up to date with the latest compiler API, and every feature in Clippy is available
|
||||
for 2 weeks in nightly, before it can get to beta. For reference, the first sync
|
||||
following this cadence was performed the 2020-08-27.
|
||||
|
||||
This process is described in detail in the following sections. For general information
|
||||
about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree].
|
||||
|
||||
### Patching git-subtree to work with big repos
|
||||
|
||||
@ -217,13 +230,14 @@ This shell has a hardcoded recursion limit set to 1000. In order to make this pr
|
||||
you need to force the script to run `bash` instead. You can do this by editing the first
|
||||
line of the `git-subtree` script and changing `sh` to `bash`.
|
||||
|
||||
### Performing the sync
|
||||
### Performing the sync from [`rust-lang/rust`] to Clippy
|
||||
|
||||
Here is a TL;DR version of the sync process (all of the following commands have
|
||||
to be run inside the `rust` directory):
|
||||
|
||||
1. Clone the [`rust-lang/rust`] repository
|
||||
2. Sync the changes to the rust-copy of Clippy to your Clippy fork:
|
||||
1. Clone the [`rust-lang/rust`] repository or make sure it is up to date.
|
||||
2. Checkout the commit from the latest available nightly. You can get it using `rustup check`.
|
||||
3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
|
||||
```bash
|
||||
# Make sure to change `your-github-name` to your github name in the following command
|
||||
git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
|
||||
@ -241,17 +255,11 @@ to be run inside the `rust` directory):
|
||||
git checkout sync-from-rust
|
||||
git merge upstream/master
|
||||
```
|
||||
3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
|
||||
4. 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
|
||||
~~annoy~~ ask them in the [Zulip] stream.)
|
||||
|
||||
### Syncing back changes in Clippy to [`rust-lang/rust`]
|
||||
|
||||
To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back
|
||||
in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
|
||||
the Rust stable release and then every other week. That way we guarantee that
|
||||
every feature in Clippy is available for 2 weeks in nightly, before it can get to beta.
|
||||
For reference, the first sync following this cadence was performed the 2020-08-27.
|
||||
### Performing the sync from Clippy to [`rust-lang/rust`]
|
||||
|
||||
All of the following commands have to be run inside the `rust` directory.
|
||||
|
||||
@ -302,10 +310,19 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and
|
||||
responding to issues there may not always be enough time to stay on top of it
|
||||
all.
|
||||
|
||||
Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug]. We don't
|
||||
Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
|
||||
an ICE in a popular crate that many other crates depend on. We don't
|
||||
want Clippy to crash on your code and we want it to be as reliable as the
|
||||
suggestions from Rust compiler errors.
|
||||
|
||||
We have prioritization labels and a sync-blocker label, which are described below.
|
||||
- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent.
|
||||
- [P-medium][p-medium]: Should be addressed by a team member until the next sync.
|
||||
- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport.
|
||||
- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync.
|
||||
Or rather: before the sync this should be addressed,
|
||||
e.g. by removing a lint again, so it doesn't hit beta/stable.
|
||||
|
||||
## Bors and Homu
|
||||
|
||||
We use a bot powered by [Homu][homu] to help automate testing and landing of pull
|
||||
@ -319,9 +336,13 @@ commands [here][homu_instructions].
|
||||
[triage]: https://forge.rust-lang.org/release/triage-procedure.html
|
||||
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
|
||||
[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
|
||||
[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
|
||||
[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
|
||||
[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
|
||||
[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker
|
||||
[homu]: https://github.com/rust-lang/homu
|
||||
[homu_instructions]: https://buildbot2.rust-lang.org/homu/
|
||||
[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy
|
||||
[homu_instructions]: https://bors.rust-lang.org/
|
||||
[homu_queue]: https://bors.rust-lang.org/queue/clippy
|
||||
|
||||
## Contributions
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy"
|
||||
version = "0.0.212"
|
||||
version = "0.1.51"
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
"Andre Bogus <bogusandre@gmail.com>",
|
||||
@ -29,10 +29,10 @@ path = "src/driver.rs"
|
||||
|
||||
[dependencies]
|
||||
# begin automatic update
|
||||
clippy_lints = { version = "0.0.212", path = "clippy_lints" }
|
||||
clippy_lints = { version = "0.1.50", path = "clippy_lints" }
|
||||
# end automatic update
|
||||
semver = "0.11"
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
|
||||
tempfile = { version = "3.1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
@ -49,8 +49,9 @@ derive-new = "0.5"
|
||||
rustc-workspace-hack = "1.0.0"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
|
||||
|
||||
[features]
|
||||
deny-warnings = []
|
||||
integration = ["tempfile"]
|
||||
internal-lints = ["clippy_lints/internal-lints"]
|
||||
|
52
README.md
52
README.md
@ -10,16 +10,16 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g
|
||||
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
|
||||
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
|
||||
|
||||
Category | Description | Default level
|
||||
-- | -- | --
|
||||
`clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny**
|
||||
`clippy::correctness` | code that is outright wrong or very useless | **deny**
|
||||
`clippy::style` | code that should be written in a more idiomatic way | **warn**
|
||||
`clippy::complexity` | code that does something simple but in a complex way | **warn**
|
||||
`clippy::perf` | code that can be written to run faster | **warn**
|
||||
`clippy::pedantic` | lints which are rather strict or might have false positives | allow
|
||||
`clippy::nursery` | new lints that are still under development | allow
|
||||
`clippy::cargo` | lints for the cargo manifest | allow
|
||||
| Category | Description | Default level |
|
||||
| --------------------- | ----------------------------------------------------------------------- | ------------- |
|
||||
| `clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** |
|
||||
| `clippy::correctness` | code that is outright wrong or very useless | **deny** |
|
||||
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
|
||||
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
|
||||
| `clippy::perf` | code that can be written to run faster | **warn** |
|
||||
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
|
||||
| `clippy::nursery` | new lints that are still under development | allow |
|
||||
| `clippy::cargo` | lints for the cargo manifest | allow |
|
||||
|
||||
More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
|
||||
|
||||
@ -82,16 +82,21 @@ Note that this is still experimental and only supported on the nightly channel:
|
||||
cargo clippy --fix -Z unstable-options
|
||||
```
|
||||
|
||||
### Running Clippy from the command line without installing it
|
||||
#### Workspaces
|
||||
|
||||
To have cargo compile your crate with Clippy without Clippy installation
|
||||
in your code, you can use:
|
||||
All the usual workspace options should work with Clippy. For example the following command
|
||||
will run Clippy on the `example` crate:
|
||||
|
||||
```terminal
|
||||
cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
|
||||
cargo clippy -p example
|
||||
```
|
||||
|
||||
*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
|
||||
As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies.
|
||||
If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this:
|
||||
|
||||
```terminal
|
||||
cargo clippy -p example -- --no-deps
|
||||
```
|
||||
|
||||
### Travis CI
|
||||
|
||||
@ -114,18 +119,6 @@ script:
|
||||
# etc.
|
||||
```
|
||||
|
||||
If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
|
||||
In this case you can try to conditionally install Clippy from the Git repo.
|
||||
|
||||
```yaml
|
||||
language: rust
|
||||
rust:
|
||||
- nightly
|
||||
before_script:
|
||||
- rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
|
||||
# etc.
|
||||
```
|
||||
|
||||
Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
|
||||
That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
|
||||
an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
|
||||
@ -182,7 +175,7 @@ cargo clippy -- -W clippy::lint_name
|
||||
```
|
||||
|
||||
This also works with lint groups. For example you
|
||||
can run Clippy with warnings for all lints enabled:
|
||||
can run Clippy with warnings for all lints enabled:
|
||||
```terminal
|
||||
cargo clippy -- -W clippy::pedantic
|
||||
```
|
||||
@ -214,7 +207,8 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Tilde/Caret version requirements (like `^1.0` or `~1.2`) can be specified as well.
|
||||
You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
|
||||
is equivalent to `msrv = 1.30.0`.
|
||||
|
||||
Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
|
||||
|
||||
|
99
clippy_dev/src/bless.rs
Normal file
99
clippy_dev/src/bless.rs
Normal file
@ -0,0 +1,99 @@
|
||||
//! `bless` updates the reference files in the repo with changed output files
|
||||
//! from the last test run.
|
||||
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::lazy::SyncLazy;
|
||||
use std::path::{Path, PathBuf};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::clippy_project_root;
|
||||
|
||||
// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
|
||||
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
|
||||
Some(v) => v.into(),
|
||||
None => env::current_dir().unwrap().join("target"),
|
||||
});
|
||||
|
||||
static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
|
||||
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
|
||||
let mut path = PathBuf::from(&**CARGO_TARGET_DIR);
|
||||
path.push(profile);
|
||||
path.push("cargo-clippy");
|
||||
fs::metadata(path).ok()?.modified().ok()
|
||||
});
|
||||
|
||||
pub fn bless(ignore_timestamp: bool) {
|
||||
let test_suite_dirs = [
|
||||
clippy_project_root().join("tests").join("ui"),
|
||||
clippy_project_root().join("tests").join("ui-internal"),
|
||||
clippy_project_root().join("tests").join("ui-toml"),
|
||||
clippy_project_root().join("tests").join("ui-cargo"),
|
||||
];
|
||||
for test_suite_dir in &test_suite_dirs {
|
||||
WalkDir::new(test_suite_dir)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
|
||||
.for_each(|f| {
|
||||
let test_name = f.path().strip_prefix(test_suite_dir).unwrap();
|
||||
for &ext in &["stdout", "stderr", "fixed"] {
|
||||
update_reference_file(
|
||||
f.path().with_extension(ext),
|
||||
test_name.with_extension(ext),
|
||||
ignore_timestamp,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn update_reference_file(reference_file_path: PathBuf, test_name: PathBuf, ignore_timestamp: bool) {
|
||||
let test_output_path = build_dir().join(test_name);
|
||||
let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();
|
||||
|
||||
// If compiletest did not write any changes during the test run,
|
||||
// we don't have to update anything
|
||||
if !test_output_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the test output was not updated since the last clippy build, it may be outdated
|
||||
if !ignore_timestamp && !updated_since_clippy_build(&test_output_path).unwrap_or(true) {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
|
||||
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
|
||||
|
||||
if test_output_file != reference_file {
|
||||
// If a test run caused an output file to change, update the reference file
|
||||
println!("updating {}", &relative_reference_file_path.display());
|
||||
fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
|
||||
|
||||
// We need to re-read the file now because it was potentially updated from copying
|
||||
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
|
||||
|
||||
if reference_file.is_empty() {
|
||||
// If we copied over an empty output file, we remove the now empty reference file
|
||||
println!("removing {}", &relative_reference_file_path.display());
|
||||
fs::remove_file(reference_file_path).expect("Could not remove reference file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn updated_since_clippy_build(path: &Path) -> Option<bool> {
|
||||
let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
|
||||
let modified = fs::metadata(path).ok()?.modified().ok()?;
|
||||
Some(modified >= clippy_build_time)
|
||||
}
|
||||
|
||||
fn build_dir() -> PathBuf {
|
||||
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
|
||||
let mut path = PathBuf::new();
|
||||
path.push(CARGO_TARGET_DIR.clone());
|
||||
path.push(profile);
|
||||
path.push("test_build_base");
|
||||
path
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use crate::clippy_project_root;
|
||||
use shell_escape::escape;
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::process::{self, Command};
|
||||
use std::{fs, io};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -12,6 +12,7 @@ pub enum CliError {
|
||||
IoError(io::Error),
|
||||
RustfmtNotInstalled,
|
||||
WalkDirError(walkdir::Error),
|
||||
RaSetupActive,
|
||||
}
|
||||
|
||||
impl From<io::Error> for CliError {
|
||||
@ -31,12 +32,23 @@ struct FmtContext {
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
// the "main" function of cargo dev fmt
|
||||
pub fn run(check: bool, verbose: bool) {
|
||||
fn try_run(context: &FmtContext) -> Result<bool, CliError> {
|
||||
let mut success = true;
|
||||
|
||||
let project_root = clippy_project_root();
|
||||
|
||||
// if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
|
||||
// format because rustfmt would also format the entire rustc repo as it is a local
|
||||
// dependency
|
||||
if fs::read_to_string(project_root.join("Cargo.toml"))
|
||||
.expect("Failed to read clippy Cargo.toml")
|
||||
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||
{
|
||||
return Err(CliError::RaSetupActive);
|
||||
}
|
||||
|
||||
rustfmt_test(context)?;
|
||||
|
||||
success &= cargo_fmt(context, project_root.as_path())?;
|
||||
@ -75,6 +87,13 @@ pub fn run(check: bool, verbose: bool) {
|
||||
CliError::WalkDirError(err) => {
|
||||
eprintln!("error: {}", err);
|
||||
},
|
||||
CliError::RaSetupActive => {
|
||||
eprintln!(
|
||||
"error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`.
|
||||
Not formatting because that would format the local repo as well!
|
||||
Please revert the changes to Cargo.tomls first."
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use std::lazy::SyncLazy;
|
||||
use std::path::{Path, PathBuf};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub mod bless;
|
||||
pub mod fmt;
|
||||
pub mod new_lint;
|
||||
pub mod ra_setup;
|
||||
@ -146,16 +147,30 @@ pub fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String>
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn gen_register_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
|
||||
let pre = " store.register_lints(&[".to_string();
|
||||
let post = " ]);".to_string();
|
||||
let mut inner = lints
|
||||
pub fn gen_register_lint_list<'a>(
|
||||
internal_lints: impl Iterator<Item = &'a Lint>,
|
||||
usable_lints: impl Iterator<Item = &'a Lint>,
|
||||
) -> Vec<String> {
|
||||
let header = " store.register_lints(&[".to_string();
|
||||
let footer = " ]);".to_string();
|
||||
let internal_lints = internal_lints
|
||||
.sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
|
||||
.map(|l| {
|
||||
format!(
|
||||
" #[cfg(feature = \"internal-lints\")]\n &{}::{},",
|
||||
l.module,
|
||||
l.name.to_uppercase()
|
||||
)
|
||||
});
|
||||
let other_lints = usable_lints
|
||||
.sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
|
||||
.map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
|
||||
.sorted()
|
||||
.collect::<Vec<String>>();
|
||||
inner.insert(0, pre);
|
||||
inner.push(post);
|
||||
inner
|
||||
.sorted();
|
||||
let mut lint_list = vec![header];
|
||||
lint_list.extend(internal_lints);
|
||||
lint_list.extend(other_lints);
|
||||
lint_list.push(footer);
|
||||
lint_list
|
||||
}
|
||||
|
||||
/// Gathers all files in `src/clippy_lints` and gathers all lints inside
|
||||
|
@ -1,10 +1,61 @@
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
|
||||
|
||||
fn main() {
|
||||
let matches = App::new("Clippy developer tooling")
|
||||
let matches = get_clap_config();
|
||||
|
||||
match matches.subcommand() {
|
||||
("bless", Some(matches)) => {
|
||||
bless::bless(matches.is_present("ignore-timestamp"));
|
||||
},
|
||||
("fmt", Some(matches)) => {
|
||||
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
|
||||
},
|
||||
("update_lints", Some(matches)) => {
|
||||
if matches.is_present("print-only") {
|
||||
update_lints::print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
update_lints::run(update_lints::UpdateMode::Check);
|
||||
} else {
|
||||
update_lints::run(update_lints::UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
("new_lint", Some(matches)) => {
|
||||
match new_lint::create(
|
||||
matches.value_of("pass"),
|
||||
matches.value_of("name"),
|
||||
matches.value_of("category"),
|
||||
) {
|
||||
Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {}", e),
|
||||
}
|
||||
},
|
||||
("limit_stderr_length", _) => {
|
||||
stderr_length_check::check();
|
||||
},
|
||||
("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
|
||||
("serve", Some(matches)) => {
|
||||
let port = matches.value_of("port").unwrap().parse().unwrap();
|
||||
let lint = matches.value_of("lint");
|
||||
serve::run(port, lint);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
App::new("Clippy developer tooling")
|
||||
.subcommand(
|
||||
SubCommand::with_name("bless")
|
||||
.about("bless the test output changes")
|
||||
.arg(
|
||||
Arg::with_name("ignore-timestamp")
|
||||
.long("ignore-timestamp")
|
||||
.help("Include files updated before clippy was built"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("fmt")
|
||||
.about("Run rustfmt on all projects and tests")
|
||||
@ -25,16 +76,16 @@ fn main() {
|
||||
.about("Updates lint registration and information from the source code")
|
||||
.long_about(
|
||||
"Makes sure that:\n \
|
||||
* the lint count in README.md is correct\n \
|
||||
* the changelog contains markdown link references at the bottom\n \
|
||||
* all lint groups include the correct lints\n \
|
||||
* lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
|
||||
* all lints are registered in the lint store",
|
||||
* the lint count in README.md is correct\n \
|
||||
* the changelog contains markdown link references at the bottom\n \
|
||||
* all lint groups include the correct lints\n \
|
||||
* lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \
|
||||
* all lints are registered in the lint store",
|
||||
)
|
||||
.arg(Arg::with_name("print-only").long("print-only").help(
|
||||
"Print a table of lints to STDOUT. \
|
||||
This does not include deprecated and internal lints. \
|
||||
(Does not modify any files)",
|
||||
This does not include deprecated and internal lints. \
|
||||
(Does not modify any files)",
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name("check")
|
||||
@ -88,7 +139,7 @@ fn main() {
|
||||
.about("Ensures that stderr files do not grow longer than a certain amount of lines."),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("ra-setup")
|
||||
SubCommand::with_name("ra_setup")
|
||||
.about("Alter dependencies so rust-analyzer can find rustc internals")
|
||||
.arg(
|
||||
Arg::with_name("rustc-repo-path")
|
||||
@ -113,40 +164,5 @@ fn main() {
|
||||
)
|
||||
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
match matches.subcommand() {
|
||||
("fmt", Some(matches)) => {
|
||||
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
|
||||
},
|
||||
("update_lints", Some(matches)) => {
|
||||
if matches.is_present("print-only") {
|
||||
update_lints::print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
update_lints::run(update_lints::UpdateMode::Check);
|
||||
} else {
|
||||
update_lints::run(update_lints::UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
("new_lint", Some(matches)) => {
|
||||
match new_lint::create(
|
||||
matches.value_of("pass"),
|
||||
matches.value_of("name"),
|
||||
matches.value_of("category"),
|
||||
) {
|
||||
Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {}", e),
|
||||
}
|
||||
},
|
||||
("limit_stderr_length", _) => {
|
||||
stderr_length_check::check();
|
||||
},
|
||||
("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
|
||||
("serve", Some(matches)) => {
|
||||
let port = matches.value_of("port").unwrap().parse().unwrap();
|
||||
let lint = matches.value_of("lint");
|
||||
serve::run(port, lint);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
.get_matches()
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
|
||||
// the respective rustc subcrates instead of using extern crate xyz.
|
||||
@ -44,7 +44,7 @@ pub fn run(rustc_path: Option<&str>) {
|
||||
}
|
||||
|
||||
fn inject_deps_into_manifest(
|
||||
rustc_source_dir: &PathBuf,
|
||||
rustc_source_dir: &Path,
|
||||
manifest_path: &str,
|
||||
cargo_toml: &str,
|
||||
lib_rs: &str,
|
||||
@ -52,7 +52,7 @@ fn inject_deps_into_manifest(
|
||||
// do not inject deps if we have aleady done so
|
||||
if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
|
||||
eprintln!(
|
||||
"cargo dev ra-setup: warning: deps already found inside {}, doing nothing.",
|
||||
"cargo dev ra_setup: warning: deps already found inside {}, doing nothing.",
|
||||
manifest_path
|
||||
);
|
||||
return Ok(());
|
||||
|
@ -22,20 +22,7 @@ pub fn run(update_mode: UpdateMode) {
|
||||
|
||||
let usable_lint_count = round_to_fifty(usable_lints.len());
|
||||
|
||||
let mut file_change = replace_region_in_file(
|
||||
Path::new("src/lintlist/mod.rs"),
|
||||
"begin lint list",
|
||||
"end lint list",
|
||||
false,
|
||||
update_mode == UpdateMode::Change,
|
||||
|| {
|
||||
format!("vec!{:#?}", sorted_usable_lints)
|
||||
.lines()
|
||||
.map(ToString::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
)
|
||||
.changed;
|
||||
let mut file_change = false;
|
||||
|
||||
file_change |= replace_region_in_file(
|
||||
Path::new("README.md"),
|
||||
@ -81,7 +68,7 @@ pub fn run(update_mode: UpdateMode) {
|
||||
"end register lints",
|
||||
false,
|
||||
update_mode == UpdateMode::Change,
|
||||
|| gen_register_lint_list(usable_lints.iter().chain(internal_lints.iter())),
|
||||
|| gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
|
||||
)
|
||||
.changed;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "clippy_lints"
|
||||
# begin automatic update
|
||||
version = "0.0.212"
|
||||
version = "0.1.51"
|
||||
# end automatic update
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
@ -28,11 +28,16 @@ smallvec = { version = "1", features = ["union"] }
|
||||
toml = "0.5.3"
|
||||
unicode-normalization = "0.1"
|
||||
semver = "0.11"
|
||||
rustc-semver="1.1.0"
|
||||
# NOTE: cargo requires serde feat in its url dep
|
||||
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
|
||||
url = { version = "2.1.0", features = ["serde"] }
|
||||
quote = "1"
|
||||
syn = { version = "1", features = ["full"] }
|
||||
regex = "1.4"
|
||||
lazy_static = "1.4"
|
||||
|
||||
[features]
|
||||
deny-warnings = []
|
||||
# build clippy with internal lints enabled, off by default
|
||||
internal-lints = []
|
||||
|
@ -1,8 +1,7 @@
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, snippet_opt, span_lint_and_help};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, UnOp};
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
@ -102,31 +101,22 @@ enum AssertKind {
|
||||
/// Check if the expression matches
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// match { let _t = !c; _t } {
|
||||
/// true => {
|
||||
/// {
|
||||
/// ::std::rt::begin_panic(message, _)
|
||||
/// }
|
||||
/// }
|
||||
/// _ => { }
|
||||
/// };
|
||||
/// if !c {
|
||||
/// {
|
||||
/// ::std::rt::begin_panic(message, _)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// where `message` is any expression and `c` is a constant bool.
|
||||
fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
|
||||
if_chain! {
|
||||
if let ExprKind::Match(ref expr, ref arms, _) = expr.kind;
|
||||
// matches { let _t = expr; _t }
|
||||
if let ExprKind::DropTemps(ref expr) = expr.kind;
|
||||
if let ExprKind::Unary(UnOp::UnNot, ref expr) = expr.kind;
|
||||
if let ExprKind::If(ref cond, ref then, _) = expr.kind;
|
||||
if let ExprKind::Unary(UnOp::UnNot, ref expr) = cond.kind;
|
||||
// bind the first argument of the `assert!` macro
|
||||
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
|
||||
// arm 1 pattern
|
||||
if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind;
|
||||
if let ExprKind::Lit(ref lit) = lit_expr.kind;
|
||||
if let LitKind::Bool(true) = lit.node;
|
||||
// arm 1 block
|
||||
if let ExprKind::Block(ref block, _) = arms[0].body.kind;
|
||||
// block
|
||||
if let ExprKind::Block(ref block, _) = then.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(block_expr) = &block.expr;
|
||||
// inner block is optional. unwrap it if it exists, or use the expression as is otherwise.
|
||||
|
@ -5,7 +5,6 @@ use crate::utils::{
|
||||
span_lint_and_sugg, span_lint_and_then, without_block_comments,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::util::lev_distance::find_best_match_for_name;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
@ -15,6 +14,7 @@ use rustc_lint::{CheckLintNameResult, EarlyContext, EarlyLintPass, LateContext,
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::{Symbol, SymbolStr};
|
||||
@ -399,7 +399,7 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<SymbolStr> {
|
||||
if let Some(meta_item) = lint.meta_item();
|
||||
if meta_item.path.segments.len() > 1;
|
||||
if let tool_name = meta_item.path.segments[0].ident;
|
||||
if tool_name.as_str() == "clippy";
|
||||
if tool_name.name == sym::clippy;
|
||||
let lint_name = meta_item.path.segments.last().unwrap().ident.name;
|
||||
then {
|
||||
return Some(lint_name.as_str());
|
||||
@ -427,7 +427,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMet
|
||||
.map(|l| Symbol::intern(&l.name_lower()))
|
||||
.collect::<Vec<_>>();
|
||||
let sugg = find_best_match_for_name(
|
||||
symbols.iter(),
|
||||
&symbols,
|
||||
Symbol::intern(&format!("clippy::{}", name_lower)),
|
||||
None,
|
||||
);
|
||||
|
@ -18,7 +18,7 @@ declare_clippy_lint! {
|
||||
/// other solution is to ensure the mutex is unlocked before calling await,
|
||||
/// either by introducing a scope or an explicit call to Drop::drop.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
@ -57,7 +57,7 @@ declare_clippy_lint! {
|
||||
/// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
|
||||
/// risks panics from a mutable ref shared while other refs are outstanding.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
@ -99,7 +99,11 @@ impl LateLintPass<'_> for AwaitHolding {
|
||||
};
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body_id);
|
||||
let typeck_results = cx.tcx.typeck(def_id);
|
||||
check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span);
|
||||
check_interior_types(
|
||||
cx,
|
||||
&typeck_results.generator_interior_types.as_ref().skip_binder(),
|
||||
body.value.span,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg};
|
||||
use crate::utils::{differing_macro_contexts, snippet_block_with_applicability, span_lint, span_lint_and_sugg};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind};
|
||||
@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
if let Some((cond, _, _)) = higher::if_block(&expr) {
|
||||
if let ExprKind::If(cond, _, _) = &expr.kind {
|
||||
if let ExprKind::Block(block, _) = &cond.kind {
|
||||
if block.rules == BlockCheckMode::DefaultBlock {
|
||||
if block.stmts.is_empty() {
|
||||
|
@ -0,0 +1,88 @@
|
||||
use crate::utils::paths::STRING;
|
||||
use crate::utils::{match_def_path, span_lint_and_help};
|
||||
use if_chain::if_chain;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{source_map::Spanned, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Checks for calls to `ends_with` with possible file extensions
|
||||
/// and suggests to use a case-insensitive approach instead.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// `ends_with` is case-sensitive and may not detect files with a valid extension.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// filename.ends_with(".rs")
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
|
||||
/// }
|
||||
/// ```
|
||||
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
pedantic,
|
||||
"Checks for calls to ends_with with case-sensitive file extensions"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
|
||||
|
||||
fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(r"^\.([a-z0-9]{1,5}|[A-Z0-9]{1,5})$").unwrap();
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(PathSegment { ident, .. }, _, [obj, extension, ..], span) = expr.kind;
|
||||
if ident.as_str() == "ends_with";
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
|
||||
if RE.is_match(&ext_literal.as_str());
|
||||
then {
|
||||
let mut ty = ctx.typeck_results().expr_ty(obj);
|
||||
ty = match ty.kind() {
|
||||
ty::Ref(_, ty, ..) => ty,
|
||||
_ => ty
|
||||
};
|
||||
|
||||
match ty.kind() {
|
||||
ty::Str => {
|
||||
return Some(span);
|
||||
},
|
||||
ty::Adt(&ty::AdtDef { did, .. }, _) => {
|
||||
if match_def_path(ctx, did, &STRING) {
|
||||
return Some(span);
|
||||
}
|
||||
},
|
||||
_ => { return None; }
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
|
||||
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
|
||||
span_lint_and_help(
|
||||
ctx,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
span,
|
||||
"case-sensitive file extension comparison",
|
||||
None,
|
||||
"consider using a case-insensitive comparison instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,9 +6,12 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
|
||||
use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
|
||||
|
||||
const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for explicit bounds checking when casting.
|
||||
@ -39,10 +42,25 @@ declare_clippy_lint! {
|
||||
"`try_from` could replace manual bounds checking when casting"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
pub struct CheckedConversions {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl CheckedConversions {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = if_chain! {
|
||||
if !in_external_macro(cx.sess(), item.span);
|
||||
if let ExprKind::Binary(op, ref left, ref right) = &item.kind;
|
||||
@ -74,6 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Searches for a single check from unsigned to _ is done
|
||||
|
@ -147,6 +147,9 @@ impl<'tcx> Visitor<'tcx> for CCHelper {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
walk_expr(self, e);
|
||||
match e.kind {
|
||||
ExprKind::If(_, _, _) => {
|
||||
self.cc += 1;
|
||||
},
|
||||
ExprKind::Match(_, ref arms, _) => {
|
||||
if arms.len() > 1 {
|
||||
self.cc += 1;
|
||||
|
@ -23,9 +23,7 @@ use rustc_errors::Applicability;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for nested `if` statements which can be collapsed
|
||||
/// by `&&`-combining their conditions and for `else { if ... }` expressions
|
||||
/// that
|
||||
/// can be collapsed to `else if ...`.
|
||||
/// by `&&`-combining their conditions.
|
||||
///
|
||||
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
@ -40,7 +38,31 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
/// ```
|
||||
///
|
||||
/// Should be written:
|
||||
///
|
||||
/// ```rust.ignore
|
||||
/// if x && y {
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
pub COLLAPSIBLE_IF,
|
||||
style,
|
||||
"nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for collapsible `else { if ... }` expressions
|
||||
/// that can be collapsed to `else if ...`.
|
||||
///
|
||||
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
///
|
||||
/// if x {
|
||||
/// …
|
||||
@ -54,24 +76,18 @@ declare_clippy_lint! {
|
||||
/// Should be written:
|
||||
///
|
||||
/// ```rust.ignore
|
||||
/// if x && y {
|
||||
/// …
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// if x {
|
||||
/// …
|
||||
/// } else if y {
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
pub COLLAPSIBLE_IF,
|
||||
pub COLLAPSIBLE_ELSE_IF,
|
||||
style,
|
||||
"`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)"
|
||||
"nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]);
|
||||
declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
|
||||
|
||||
impl EarlyLintPass for CollapsibleIf {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
@ -112,7 +128,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_IF,
|
||||
COLLAPSIBLE_ELSE_IF,
|
||||
block.span,
|
||||
"this `else { if .. }` block can be collapsed",
|
||||
"collapse nested if block",
|
||||
|
172
clippy_lints/src/collapsible_match.rs
Normal file
172
clippy_lints/src/collapsible_match.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use crate::utils::visitors::LocalUsedVisitor;
|
||||
use crate::utils::{span_lint_and_then, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{DefIdTree, TyCtxt};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
|
||||
/// without adding any branches.
|
||||
///
|
||||
/// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
|
||||
/// cases where merging would most likely make the code more readable.
|
||||
///
|
||||
/// **Why is this bad?** It is unnecessarily verbose and complex.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(n) => match n {
|
||||
/// Ok(n) => n,
|
||||
/// _ => return,
|
||||
/// }
|
||||
/// None => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(Ok(n)) => n,
|
||||
/// _ => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
pub COLLAPSIBLE_MATCH,
|
||||
style,
|
||||
"Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
|
||||
}
|
||||
|
||||
declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::Match(_expr, arms, _source) = expr.kind {
|
||||
if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) {
|
||||
for arm in arms {
|
||||
check_arm(arm, wild_arm, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
|
||||
if_chain! {
|
||||
let expr = strip_singleton_blocks(arm.body);
|
||||
if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
|
||||
// the outer arm pattern and the inner match
|
||||
if expr_in.span.ctxt() == arm.pat.span.ctxt();
|
||||
// there must be no more than two arms in the inner match for this lint
|
||||
if arms_inner.len() == 2;
|
||||
// no if guards on the inner match
|
||||
if arms_inner.iter().all(|arm| arm.guard.is_none());
|
||||
// match expression must be a local binding
|
||||
// match <local> { .. }
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind;
|
||||
if let Res::Local(binding_id) = path.res;
|
||||
// one of the branches must be "wild-like"
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
|
||||
let (wild_inner_arm, non_wild_inner_arm) =
|
||||
(&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
|
||||
if !pat_contains_or(non_wild_inner_arm.pat);
|
||||
// the binding must come from the pattern of the containing match arm
|
||||
// ..<local>.. => match <local> { .. }
|
||||
if let Some(binding_span) = find_pat_binding(arm.pat, binding_id);
|
||||
// the "wild-like" branches must be equal
|
||||
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
|
||||
// the binding must not be used in the if guard
|
||||
if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard));
|
||||
// ...or anywhere in the inner match
|
||||
if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm));
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
COLLAPSIBLE_MATCH,
|
||||
expr.span,
|
||||
"Unnecessary nested match",
|
||||
|diag| {
|
||||
let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]);
|
||||
help_span.push_span_label(binding_span, "Replace this binding".into());
|
||||
help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into());
|
||||
diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern.");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
|
||||
while let ExprKind::Block(block, _) = expr.kind {
|
||||
match (block.stmts, block.expr) {
|
||||
([stmt], None) => match stmt.kind {
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e,
|
||||
_ => break,
|
||||
},
|
||||
([], Some(e)) => expr = e,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
/// A "wild-like" pattern is wild ("_") or `None`.
|
||||
/// For this lint to apply, both the outer and inner match expressions
|
||||
/// must have "wild-like" branches that can be combined.
|
||||
fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool {
|
||||
if arm.guard.is_some() {
|
||||
return false;
|
||||
}
|
||||
match arm.pat.kind {
|
||||
PatKind::Binding(..) | PatKind::Wild => true,
|
||||
PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
|
||||
let mut span = None;
|
||||
pat.walk_short(|p| match &p.kind {
|
||||
// ignore OR patterns
|
||||
PatKind::Or(_) => false,
|
||||
PatKind::Binding(_bm, _, _ident, _) => {
|
||||
let found = p.hir_id == hir_id;
|
||||
if found {
|
||||
span = Some(p.span);
|
||||
}
|
||||
!found
|
||||
},
|
||||
_ => true,
|
||||
});
|
||||
span
|
||||
}
|
||||
|
||||
fn pat_contains_or(pat: &Pat<'_>) -> bool {
|
||||
let mut result = false;
|
||||
pat.walk(|p| {
|
||||
let is_or = matches!(p.kind, PatKind::Or(_));
|
||||
result |= is_or;
|
||||
!is_or
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
|
||||
if let Some(none_id) = tcx.lang_items().option_none_variant() {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res {
|
||||
if let Some(variant_id) = tcx.parent(id) {
|
||||
return variant_id == none_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
@ -12,7 +12,8 @@ declare_clippy_lint! {
|
||||
/// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
|
||||
/// repetitive
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** The match statement may be slower due to the compiler
|
||||
/// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![allow(clippy::float_cmp)]
|
||||
|
||||
use crate::utils::{clip, higher, sext, unsext};
|
||||
use crate::utils::{clip, sext, unsext};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
@ -228,9 +228,6 @@ pub struct ConstEvalLateContext<'a, 'tcx> {
|
||||
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
/// Simple constant folding: Insert an expression, get a constant or none.
|
||||
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
|
||||
if let Some((ref cond, ref then, otherwise)) = higher::if_block(&e) {
|
||||
return self.ifthenelse(cond, then, otherwise);
|
||||
}
|
||||
match e.kind {
|
||||
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
|
||||
ExprKind::Block(ref block, _) => self.block(block),
|
||||
@ -249,6 +246,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||
UnOp::UnNeg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
|
||||
UnOp::UnDeref => Some(if let Constant::Ref(r) = o { *r } else { o }),
|
||||
}),
|
||||
ExprKind::If(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
|
||||
ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
|
||||
ExprKind::Call(ref callee, ref args) => {
|
||||
// We only handle a few const functions for now.
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
|
||||
use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note};
|
||||
use rustc_hir::{Block, Expr};
|
||||
use crate::utils::{get_parent_expr, if_sequence, span_lint_and_note};
|
||||
use rustc_hir::{Block, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
@ -109,11 +109,13 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion() {
|
||||
// skip ifs directly in else, it will be checked in the parent if
|
||||
if let Some(expr) = get_parent_expr(cx, expr) {
|
||||
if let Some((_, _, Some(ref else_expr))) = higher::if_block(&expr) {
|
||||
if else_expr.hir_id == expr.hir_id {
|
||||
return;
|
||||
}
|
||||
if let Some(&Expr {
|
||||
kind: ExprKind::If(_, _, Some(ref else_expr)),
|
||||
..
|
||||
}) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if else_expr.hir_id == expr.hir_id {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::utils::{is_copy, match_path, paths, span_lint_and_note};
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_hir::{Impl, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
@ -33,10 +33,10 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyIterator {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Impl {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(ref trait_ref),
|
||||
..
|
||||
} = item.kind
|
||||
}) = item.kind
|
||||
{
|
||||
let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id));
|
||||
|
||||
|
@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.
|
||||
///
|
||||
/// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`.
|
||||
/// **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet};
|
||||
use crate::utils::{
|
||||
any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite,
|
||||
};
|
||||
use crate::utils::{span_lint_and_note, span_lint_and_sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
@ -6,7 +8,8 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Adt, Ty};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
@ -103,18 +106,43 @@ impl LateLintPass<'_> for Default {
|
||||
}
|
||||
|
||||
fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
|
||||
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
|
||||
// `default` method of the `Default` trait, and store statement index in current block being
|
||||
// checked and the name of the bound variable
|
||||
let binding_statements_using_default = enumerate_bindings_using_default(cx, block);
|
||||
|
||||
// start from the `let mut _ = _::default();` and look at all the following
|
||||
// statements, see if they re-assign the fields of the binding
|
||||
for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default {
|
||||
// the last statement of a block cannot trigger the lint
|
||||
if stmt_idx == block.stmts.len() - 1 {
|
||||
break;
|
||||
}
|
||||
let stmts_head = match block.stmts {
|
||||
// Skip the last statement since there cannot possibly be any following statements that re-assign fields.
|
||||
[head @ .., _] if !head.is_empty() => head,
|
||||
_ => return,
|
||||
};
|
||||
for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
|
||||
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
|
||||
// `default` method of the `Default` trait, and store statement index in current block being
|
||||
// checked and the name of the bound variable
|
||||
let (local, variant, binding_name, binding_type, span) = if_chain! {
|
||||
// only take `let ...` statements
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let Some(expr) = local.init;
|
||||
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
|
||||
if !in_external_macro(cx.tcx.sess, expr.span);
|
||||
// only take bindings to identifiers
|
||||
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
|
||||
// only when assigning `... = Default::default()`
|
||||
if is_expr_default(expr, cx);
|
||||
let binding_type = cx.typeck_results().node_type(binding_id);
|
||||
if let Some(adt) = binding_type.ty_adt_def();
|
||||
if adt.is_struct();
|
||||
let variant = adt.non_enum_variant();
|
||||
if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
|
||||
let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
|
||||
if variant
|
||||
.fields
|
||||
.iter()
|
||||
.all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
|
||||
then {
|
||||
(local, variant, ident.name, binding_type, expr.span)
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// find all "later statement"'s where the fields of the binding set as
|
||||
// Default::default() get reassigned, unless the reassignment refers to the original binding
|
||||
@ -122,15 +150,8 @@ impl LateLintPass<'_> for Default {
|
||||
let mut assigned_fields = Vec::new();
|
||||
let mut cancel_lint = false;
|
||||
for consecutive_statement in &block.stmts[stmt_idx + 1..] {
|
||||
// interrupt if the statement is a let binding (`Local`) that shadows the original
|
||||
// binding
|
||||
if stmt_shadows_binding(consecutive_statement, binding_name) {
|
||||
break;
|
||||
}
|
||||
// find out if and which field was set by this `consecutive_statement`
|
||||
else if let Some((field_ident, assign_rhs)) =
|
||||
field_reassigned_by_stmt(consecutive_statement, binding_name)
|
||||
{
|
||||
if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
|
||||
// interrupt and cancel lint if assign_rhs references the original binding
|
||||
if contains_name(binding_name, assign_rhs) {
|
||||
cancel_lint = true;
|
||||
@ -152,7 +173,7 @@ impl LateLintPass<'_> for Default {
|
||||
first_assign = Some(consecutive_statement);
|
||||
}
|
||||
}
|
||||
// interrupt also if no field was assigned, since we only want to look at consecutive statements
|
||||
// interrupt if no field was assigned, since we only want to look at consecutive statements
|
||||
else {
|
||||
break;
|
||||
}
|
||||
@ -161,55 +182,45 @@ impl LateLintPass<'_> for Default {
|
||||
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
|
||||
// construction using `Ty { fields, ..Default::default() }`
|
||||
if !assigned_fields.is_empty() && !cancel_lint {
|
||||
// take the original assignment as span
|
||||
let stmt = &block.stmts[stmt_idx];
|
||||
// if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
|
||||
let ext_with_default = !variant
|
||||
.fields
|
||||
.iter()
|
||||
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
|
||||
|
||||
if let StmtKind::Local(preceding_local) = &stmt.kind {
|
||||
// filter out fields like `= Default::default()`, because the FRU already covers them
|
||||
let assigned_fields = assigned_fields
|
||||
.into_iter()
|
||||
.filter(|(_, rhs)| !is_expr_default(rhs, cx))
|
||||
.collect::<Vec<(Symbol, &Expr<'_>)>>();
|
||||
let field_list = assigned_fields
|
||||
.into_iter()
|
||||
.map(|(field, rhs)| {
|
||||
// extract and store the assigned value for help message
|
||||
let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
|
||||
format!("{}: {}", field, value_snippet)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
// if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
|
||||
let ext_with_default = !fields_of_type(binding_type)
|
||||
.iter()
|
||||
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
|
||||
|
||||
let field_list = assigned_fields
|
||||
.into_iter()
|
||||
.map(|(field, rhs)| {
|
||||
// extract and store the assigned value for help message
|
||||
let value_snippet = snippet(cx, rhs.span, "..");
|
||||
format!("{}: {}", field, value_snippet)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
let sugg = if ext_with_default {
|
||||
if field_list.is_empty() {
|
||||
format!("{}::default()", binding_type)
|
||||
} else {
|
||||
format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
|
||||
}
|
||||
let sugg = if ext_with_default {
|
||||
if field_list.is_empty() {
|
||||
format!("{}::default()", binding_type)
|
||||
} else {
|
||||
format!("{} {{ {} }}", binding_type, field_list)
|
||||
};
|
||||
format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
|
||||
}
|
||||
} else {
|
||||
format!("{} {{ {} }}", binding_type, field_list)
|
||||
};
|
||||
|
||||
// span lint once per statement that binds default
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
FIELD_REASSIGN_WITH_DEFAULT,
|
||||
first_assign.unwrap().span,
|
||||
"field assignment outside of initializer for an instance created with Default::default()",
|
||||
Some(preceding_local.span),
|
||||
&format!(
|
||||
"consider initializing the variable with `{}` and removing relevant reassignments",
|
||||
sugg
|
||||
),
|
||||
);
|
||||
self.reassigned_linted.insert(span);
|
||||
}
|
||||
// span lint once per statement that binds default
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
FIELD_REASSIGN_WITH_DEFAULT,
|
||||
first_assign.unwrap().span,
|
||||
"field assignment outside of initializer for an instance created with Default::default()",
|
||||
Some(local.span),
|
||||
&format!(
|
||||
"consider initializing the variable with `{}` and removing relevant reassignments",
|
||||
sugg
|
||||
),
|
||||
);
|
||||
self.reassigned_linted.insert(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,47 +241,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
|
||||
/// for when the pattern type is a tuple.
|
||||
fn enumerate_bindings_using_default<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
block: &Block<'tcx>,
|
||||
) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> {
|
||||
block
|
||||
.stmts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, stmt)| {
|
||||
if_chain! {
|
||||
// only take `let ...` statements
|
||||
if let StmtKind::Local(ref local) = stmt.kind;
|
||||
// only take bindings to identifiers
|
||||
if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
|
||||
// that are not tuples
|
||||
let ty = cx.typeck_results().pat_ty(local.pat);
|
||||
if !matches!(ty.kind(), ty::Tuple(_));
|
||||
// only when assigning `... = Default::default()`
|
||||
if let Some(ref expr) = local.init;
|
||||
if is_expr_default(expr, cx);
|
||||
then {
|
||||
Some((idx, ident.name, ty, expr.span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool {
|
||||
if let StmtKind::Local(local) = &this.kind {
|
||||
if let PatKind::Binding(_, _, ident, _) = local.pat.kind {
|
||||
return ident.name == shadowed;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns the reassigned field and the assigning expression (right-hand side of assign).
|
||||
fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
|
||||
if_chain! {
|
||||
@ -280,8 +250,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
|
||||
// only take assignments to fields where the left-hand side field is a field of
|
||||
// the same binding as the previous statement
|
||||
if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind;
|
||||
if let ExprKind::Path(ref qpath) = binding.kind;
|
||||
if let QPath::Resolved(_, path) = qpath;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
|
||||
if let Some(second_binding_name) = path.segments.last();
|
||||
if second_binding_name.ident.name == binding_name;
|
||||
then {
|
||||
@ -291,14 +260,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
|
||||
fn fields_of_type(ty: Ty<'_>) -> Vec<Ident> {
|
||||
if let Adt(adt, _) = ty.kind() {
|
||||
if adt.is_struct() {
|
||||
let variant = &adt.non_enum_variant();
|
||||
return variant.fields.iter().map(|f| f.ident).collect();
|
||||
}
|
||||
}
|
||||
vec![]
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use if_chain::if_chain;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{
|
||||
BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
|
||||
BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
@ -164,10 +164,10 @@ declare_lint_pass!(Derive => [
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Derive {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Impl {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(ref trait_ref),
|
||||
..
|
||||
} = item.kind
|
||||
}) = item.kind
|
||||
{
|
||||
let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id));
|
||||
let is_automatically_derived = is_automatically_derived(&*item.attrs);
|
||||
|
@ -14,6 +14,7 @@ use rustc_middle::ty;
|
||||
use rustc_parse::maybe_new_parser_from_source_str;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
|
||||
use rustc_span::{sym, FileName, Pos};
|
||||
use std::io;
|
||||
@ -181,11 +182,8 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
||||
lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id));
|
||||
}
|
||||
},
|
||||
hir::ItemKind::Impl {
|
||||
of_trait: ref trait_ref,
|
||||
..
|
||||
} => {
|
||||
self.in_trait_impl = trait_ref.is_some();
|
||||
hir::ItemKind::Impl(ref impl_) => {
|
||||
self.in_trait_impl = impl_.of_trait.is_some();
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -377,7 +375,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
|
||||
check_doc(cx, valid_idents, events, &spans)
|
||||
}
|
||||
|
||||
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"];
|
||||
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
|
||||
|
||||
fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
|
||||
cx: &LateContext<'_>,
|
||||
@ -400,13 +398,24 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
let mut in_link = None;
|
||||
let mut in_heading = false;
|
||||
let mut is_rust = false;
|
||||
let mut edition = None;
|
||||
for (event, range) in events {
|
||||
match event {
|
||||
Start(CodeBlock(ref kind)) => {
|
||||
in_code = true;
|
||||
if let CodeBlockKind::Fenced(lang) = kind {
|
||||
is_rust =
|
||||
lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i));
|
||||
for item in lang.split(',') {
|
||||
if item == "ignore" {
|
||||
is_rust = false;
|
||||
break;
|
||||
}
|
||||
if let Some(stripped) = item.strip_prefix("edition") {
|
||||
is_rust = true;
|
||||
edition = stripped.parse::<Edition>().ok();
|
||||
} else if item.is_empty() || RUST_CODE.contains(&item) {
|
||||
is_rust = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
End(CodeBlock(_)) => {
|
||||
@ -436,7 +445,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
let (begin, span) = spans[index];
|
||||
if in_code {
|
||||
if is_rust {
|
||||
check_code(cx, &text, span);
|
||||
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
|
||||
check_code(cx, &text, edition, span);
|
||||
}
|
||||
} else {
|
||||
// Adjust for the beginning of the current `Event`
|
||||
@ -450,67 +460,73 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||
headers
|
||||
}
|
||||
|
||||
fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
|
||||
fn has_needless_main(code: &str) -> bool {
|
||||
let filename = FileName::anon_source_code(code);
|
||||
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
|
||||
fn has_needless_main(code: &str, edition: Edition) -> bool {
|
||||
rustc_driver::catch_fatal_errors(|| {
|
||||
rustc_span::with_session_globals(edition, || {
|
||||
let filename = FileName::anon_source_code(code);
|
||||
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
|
||||
let handler = Handler::with_emitter(false, None, box emitter);
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
|
||||
let handler = Handler::with_emitter(false, None, box emitter);
|
||||
let sess = ParseSess::with_span_handler(handler, sm);
|
||||
|
||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
for mut err in errs {
|
||||
err.cancel();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
let mut relevant_main_found = false;
|
||||
loop {
|
||||
match parser.parse_item() {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..) => return false,
|
||||
// We found a main function ...
|
||||
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
|
||||
let is_async = matches!(sig.header.asyncness, Async::Yes{..});
|
||||
let returns_nothing = match &sig.decl.output {
|
||||
FnRetTy::Default(..) => true,
|
||||
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if returns_nothing && !is_async && !block.stmts.is_empty() {
|
||||
// This main function should be linted, but only if there are no other functions
|
||||
relevant_main_found = true;
|
||||
} else {
|
||||
// This main function should not be linted, we're done
|
||||
return false;
|
||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
|
||||
Ok(p) => p,
|
||||
Err(errs) => {
|
||||
for mut err in errs {
|
||||
err.cancel();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// Another function was found; this case is ignored too
|
||||
ItemKind::Fn(..) => return false,
|
||||
_ => {},
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(mut e) => {
|
||||
e.cancel();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relevant_main_found
|
||||
let mut relevant_main_found = false;
|
||||
loop {
|
||||
match parser.parse_item() {
|
||||
Ok(Some(item)) => match &item.kind {
|
||||
// Tests with one of these items are ignored
|
||||
ItemKind::Static(..)
|
||||
| ItemKind::Const(..)
|
||||
| ItemKind::ExternCrate(..)
|
||||
| ItemKind::ForeignMod(..) => return false,
|
||||
// We found a main function ...
|
||||
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
|
||||
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
|
||||
let returns_nothing = match &sig.decl.output {
|
||||
FnRetTy::Default(..) => true,
|
||||
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if returns_nothing && !is_async && !block.stmts.is_empty() {
|
||||
// This main function should be linted, but only if there are no other functions
|
||||
relevant_main_found = true;
|
||||
} else {
|
||||
// This main function should not be linted, we're done
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// Another function was found; this case is ignored too
|
||||
ItemKind::Fn(..) => return false,
|
||||
_ => {},
|
||||
},
|
||||
Ok(None) => break,
|
||||
Err(mut e) => {
|
||||
e.cancel();
|
||||
return false;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
relevant_main_found
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
if has_needless_main(text) {
|
||||
if has_needless_main(text, edition) {
|
||||
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,12 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `enum`s with no variants.
|
||||
///
|
||||
/// As of this writing, the `never_type` is still a
|
||||
/// nightly-only experimental API. Therefore, this lint is only triggered
|
||||
/// if the `never_type` is enabled.
|
||||
///
|
||||
/// **Why is this bad?** If you want to introduce a type which
|
||||
/// can't be instantiated, you should use `!` (the never type),
|
||||
/// can't be instantiated, you should use `!` (the primitive type "never"),
|
||||
/// or a wrapper around it, because `!` has more extensive
|
||||
/// compiler support (type inference, etc...) and wrappers
|
||||
/// around it are the conventional way to define an uninhabited type.
|
||||
@ -40,6 +44,11 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for EmptyEnum {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
// Only suggest the `never_type` if the feature is enabled
|
||||
if !cx.tcx.features().never_type {
|
||||
return;
|
||||
}
|
||||
|
||||
let did = cx.tcx.hir().local_def_id(item.hir_id);
|
||||
if let ItemKind::Enum(..) = item.kind {
|
||||
let ty = cx.tcx.type_of(did);
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::utils::SpanlessEq;
|
||||
use crate::utils::{get_item_name, higher, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
|
||||
use crate::utils::{get_item_name, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
|
||||
use crate::utils::{snippet_with_applicability, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -54,7 +54,7 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some((ref check, ref then_block, ref else_block)) = higher::if_block(&expr) {
|
||||
if let ExprKind::If(ref check, ref then_block, ref else_block) = expr.kind {
|
||||
if let ExprKind::Unary(UnOp::UnNot, ref check) = check.kind {
|
||||
if let Some((ty, map, key)) = check_cond(cx, check) {
|
||||
// in case of `if !m.contains_key(&k) { m.insert(k, v); }`
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::utils::{
|
||||
eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint,
|
||||
span_lint_and_then,
|
||||
ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of,
|
||||
multispan_sugg, snippet, span_lint, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
|
||||
if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
|
||||
return;
|
||||
}
|
||||
if is_valid_operator(op) && eq_expr_value(cx, left, right) {
|
||||
if is_useless_with_eq_exprs(higher::binop(op.node)) && eq_expr_value(cx, left, right) {
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
@ -245,22 +245,3 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_operator(op: BinOp) -> bool {
|
||||
matches!(
|
||||
op.node,
|
||||
BinOpKind::Sub
|
||||
| BinOpKind::Div
|
||||
| BinOpKind::Eq
|
||||
| BinOpKind::Lt
|
||||
| BinOpKind::Le
|
||||
| BinOpKind::Gt
|
||||
| BinOpKind::Ge
|
||||
| BinOpKind::Ne
|
||||
| BinOpKind::And
|
||||
| BinOpKind::Or
|
||||
| BinOpKind::BitXor
|
||||
| BinOpKind::BitAnd
|
||||
| BinOpKind::BitOr
|
||||
)
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node};
|
||||
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, TraitRef, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_target::abi::LayoutOf;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
|
||||
use crate::utils::span_lint;
|
||||
use crate::utils::{contains_ty, span_lint};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BoxedLocal {
|
||||
@ -51,6 +52,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool {
|
||||
struct EscapeDelegate<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
set: HirIdSet,
|
||||
trait_self_ty: Option<Ty<'a>>,
|
||||
too_large_for_stack: u64,
|
||||
}
|
||||
|
||||
@ -72,19 +74,34 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
||||
}
|
||||
}
|
||||
|
||||
// If the method is an impl for a trait, don't warn.
|
||||
let parent_id = cx.tcx.hir().get_parent_item(hir_id);
|
||||
let parent_node = cx.tcx.hir().find(parent_id);
|
||||
|
||||
let mut trait_self_ty = None;
|
||||
if let Some(Node::Item(item)) = parent_node {
|
||||
if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
|
||||
// If the method is an impl for a trait, don't warn.
|
||||
if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
// find `self` ty for this trait if relevant
|
||||
if let ItemKind::Trait(_, _, _, _, items) = item.kind {
|
||||
for trait_item in items {
|
||||
if trait_item.id.hir_id == hir_id {
|
||||
// be sure we have `self` parameter in this function
|
||||
if let AssocItemKind::Fn { has_self: true } = trait_item.kind {
|
||||
trait_self_ty =
|
||||
Some(TraitRef::identity(cx.tcx, trait_item.id.hir_id.owner.to_def_id()).self_ty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut v = EscapeDelegate {
|
||||
cx,
|
||||
set: HirIdSet::default(),
|
||||
trait_self_ty,
|
||||
too_large_for_stack: self.too_large_for_stack,
|
||||
};
|
||||
|
||||
@ -153,10 +170,17 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
|
||||
self.set.insert(cmt.hir_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ declare_clippy_lint! {
|
||||
/// **Known problems:** If creating the closure inside the closure has a side-
|
||||
/// effect then moving the closure creation out will change when that side-
|
||||
/// effect runs.
|
||||
/// See rust-lang/rust-clippy#1439 for more details.
|
||||
/// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
@ -45,8 +45,9 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Why is this bad?** It's unnecessary to create the closure.
|
||||
///
|
||||
/// **Known problems:** rust-lang/rust-clippy#3071, rust-lang/rust-clippy#4002,
|
||||
/// rust-lang/rust-clippy#3942
|
||||
/// **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),
|
||||
/// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),
|
||||
/// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)
|
||||
///
|
||||
///
|
||||
/// **Example:**
|
||||
|
@ -57,11 +57,11 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
|
||||
// check for `impl From<???> for ..`
|
||||
let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
|
||||
if_chain! {
|
||||
if let hir::ItemKind::Impl{ items: impl_items, .. } = item.kind;
|
||||
if let hir::ItemKind::Impl(impl_) = &item.kind;
|
||||
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
|
||||
if match_def_path(cx, impl_trait_ref.def_id, &FROM_TRAIT);
|
||||
then {
|
||||
lint_impl_body(cx, item.span, impl_items);
|
||||
lint_impl_body(cx, item.span, impl_.items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::consts::{
|
||||
constant, constant_simple, Constant,
|
||||
Constant::{Int, F32, F64},
|
||||
};
|
||||
use crate::utils::{eq_expr_value, get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg};
|
||||
use crate::utils::{eq_expr_value, get_parent_expr, numeric_literal, span_lint_and_sugg, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
||||
@ -556,11 +556,11 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a
|
||||
|
||||
fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some((cond, body, Some(else_body))) = higher::if_block(&expr);
|
||||
if let ExprKind::If(cond, body, else_body) = expr.kind;
|
||||
if let ExprKind::Block(block, _) = body.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(if_body_expr) = block.expr;
|
||||
if let ExprKind::Block(else_block, _) = else_body.kind;
|
||||
if let Some(ExprKind::Block(else_block, _)) = else_body.map(|el| &el.kind);
|
||||
if else_block.stmts.is_empty();
|
||||
if let Some(else_body_expr) = else_block.expr;
|
||||
if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
|
||||
|
83
clippy_lints/src/from_over_into.rs
Normal file
83
clippy_lints/src/from_over_into.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use crate::utils::paths::INTO;
|
||||
use crate::utils::{match_def_path, meets_msrv, span_lint_and_help};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
|
||||
///
|
||||
/// **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct StringWrapper(String);
|
||||
///
|
||||
/// impl Into<StringWrapper> for String {
|
||||
/// fn into(self) -> StringWrapper {
|
||||
/// StringWrapper(self)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct StringWrapper(String);
|
||||
///
|
||||
/// impl From<String> for StringWrapper {
|
||||
/// fn from(s: String) -> StringWrapper {
|
||||
/// StringWrapper(s)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub FROM_OVER_INTO,
|
||||
style,
|
||||
"Warns on implementations of `Into<..>` to use `From<..>`"
|
||||
}
|
||||
|
||||
pub struct FromOverInto {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl FromOverInto {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
FromOverInto { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
||||
|
||||
impl LateLintPass<'_> for FromOverInto {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
|
||||
if_chain! {
|
||||
if let hir::ItemKind::Impl{ .. } = &item.kind;
|
||||
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
|
||||
if match_def_path(cx, impl_trait_ref.def_id, &INTO);
|
||||
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FROM_OVER_INTO,
|
||||
cx.tcx.sess.source_map().guess_head_span(item.span),
|
||||
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
|
||||
None,
|
||||
"consider to implement `From` instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
@ -405,13 +405,10 @@ impl<'tcx> Functions {
|
||||
break;
|
||||
}
|
||||
if in_comment {
|
||||
match line.find("*/") {
|
||||
Some(i) => {
|
||||
line = &line[i + 2..];
|
||||
in_comment = false;
|
||||
continue;
|
||||
},
|
||||
None => break,
|
||||
if let Some(i) = line.find("*/") {
|
||||
line = &line[i + 2..];
|
||||
in_comment = false;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
|
||||
@ -423,8 +420,8 @@ impl<'tcx> Functions {
|
||||
in_comment = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if code_in_line {
|
||||
line_count += 1;
|
||||
|
@ -145,7 +145,7 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
|
||||
fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind;
|
||||
if path.ident.to_string() == "lock";
|
||||
if path.ident.as_str() == "lock";
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(mutex_type));
|
||||
then {
|
||||
|
@ -41,8 +41,7 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
|
||||
impl<'tcx> LateLintPass<'tcx> for OkIfLet {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! { //begin checking variables
|
||||
if let ExprKind::Match(ref op, ref body, source) = expr.kind; //test if expr is a match
|
||||
if let MatchSource::IfLetDesugar { .. } = source; //test if it is an If Let
|
||||
if let ExprKind::Match(ref op, ref body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let
|
||||
if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation
|
||||
if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
|
||||
|
@ -68,8 +68,7 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(expr, ..) = &stmt.kind;
|
||||
// make sure it's a break, otherwise we want to skip
|
||||
if let ExprKind::Break(.., break_expr) = &expr.kind;
|
||||
if let Some(break_expr) = break_expr;
|
||||
if let ExprKind::Break(.., Some(break_expr)) = &expr.kind;
|
||||
then {
|
||||
lint(cx, expr.span, break_expr.span, LINT_BREAK);
|
||||
}
|
||||
@ -82,6 +81,13 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
lint(cx, expr.span, break_expr.span, LINT_BREAK);
|
||||
}
|
||||
},
|
||||
ExprKind::If(.., if_expr, else_expr) => {
|
||||
expr_match(cx, if_expr);
|
||||
|
||||
if let Some(else_expr) = else_expr {
|
||||
expr_match(cx, else_expr);
|
||||
}
|
||||
},
|
||||
ExprKind::Match(.., arms, source) => {
|
||||
let check_all_arms = match source {
|
||||
MatchSource::IfLetDesugar {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::utils::{higher, in_macro, match_qpath, span_lint_and_sugg, SpanlessEq};
|
||||
use crate::utils::{in_macro, match_qpath, span_lint_and_sugg, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
||||
return;
|
||||
}
|
||||
if_chain! {
|
||||
if let Some((ref cond, ref then, None)) = higher::if_block(&expr);
|
||||
if let ExprKind::If(cond, then, None) = &expr.kind;
|
||||
|
||||
// Check if the conditional expression is a binary operation
|
||||
if let ExprKind::Binary(ref cond_op, ref cond_left, ref cond_right) = cond.kind;
|
||||
@ -59,8 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
||||
if let Some(target) = subtracts_one(cx, e);
|
||||
|
||||
// Extracting out the variable name
|
||||
if let ExprKind::Path(ref assign_path) = target.kind;
|
||||
if let QPath::Resolved(_, ref ares_path) = assign_path;
|
||||
if let ExprKind::Path(QPath::Resolved(_, ref ares_path)) = target.kind;
|
||||
|
||||
then {
|
||||
// Handle symmetric conditions in the if statement
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use crate::utils::{in_macro, span_lint_and_then};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{def_id, Crate, Item, ItemKind};
|
||||
use rustc_hir::{def_id, Crate, Impl, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
@ -49,11 +49,11 @@ impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
|
||||
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Impl {
|
||||
if let ItemKind::Impl(Impl {
|
||||
ref generics,
|
||||
of_trait: None,
|
||||
..
|
||||
} = item.kind
|
||||
}) = item.kind
|
||||
{
|
||||
// Remember for each inherent implementation encountered its span and generics
|
||||
// but filter out implementations that have generic params (type or lifetime)
|
||||
|
@ -58,12 +58,12 @@ impl EarlyLintPass for ItemsAfterStatements {
|
||||
return;
|
||||
}
|
||||
|
||||
// skip initial items
|
||||
// skip initial items and trailing semicolons
|
||||
let stmts = item
|
||||
.stmts
|
||||
.iter()
|
||||
.map(|stmt| &stmt.kind)
|
||||
.skip_while(|s| matches!(**s, StmtKind::Item(..)));
|
||||
.skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty));
|
||||
|
||||
// lint on all further items
|
||||
for stmt in stmts {
|
||||
|
@ -52,8 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
||||
if let ItemKind::Const(hir_ty, _) = &item.kind;
|
||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||
if let ty::Array(element_type, cst) = ty.kind();
|
||||
if let ConstKind::Value(val) = cst.val;
|
||||
if let ConstValue::Scalar(element_count) = val;
|
||||
if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val;
|
||||
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
|
||||
if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
|
||||
if self.maximum_allowed_size < element_count * element_size;
|
||||
|
@ -4,6 +4,7 @@ use crate::utils::{snippet_opt, span_lint_and_then};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind, VariantData};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_target::abi::LayoutOf;
|
||||
|
||||
@ -58,6 +59,9 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if in_external_macro(cx.tcx.sess, item.span) {
|
||||
return;
|
||||
}
|
||||
let did = cx.tcx.hir().local_def_id(item.hir_id);
|
||||
if let ItemKind::Enum(ref def, _) = item.kind {
|
||||
let ty = cx.tcx.type_of(did);
|
||||
|
@ -43,8 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
||||
if_chain! {
|
||||
if let ExprKind::Repeat(_, _) = expr.kind;
|
||||
if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind();
|
||||
if let ConstKind::Value(val) = cst.val;
|
||||
if let ConstValue::Scalar(element_count) = val;
|
||||
if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val;
|
||||
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
|
||||
if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
|
||||
if self.maximum_allowed_size < element_count * element_size;
|
||||
|
@ -3,7 +3,7 @@ use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, ImplItemRef, Item, ItemKind, TraitItemRef};
|
||||
use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, Impl, ImplItemRef, Item, ItemKind, TraitItemRef};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
@ -115,11 +115,11 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
|
||||
|
||||
match item.kind {
|
||||
ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items),
|
||||
ItemKind::Impl {
|
||||
ItemKind::Impl(Impl {
|
||||
of_trait: None,
|
||||
items: ref impl_items,
|
||||
..
|
||||
} => check_impl_items(cx, item, impl_items),
|
||||
}) => check_impl_items(cx, item, impl_items),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@ -222,9 +222,8 @@ fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplIte
|
||||
let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
|
||||
if cx.access_levels.is_exported(is_empty.id.hir_id) {
|
||||
return;
|
||||
} else {
|
||||
"a private"
|
||||
}
|
||||
"a private"
|
||||
} else {
|
||||
"no corresponding"
|
||||
};
|
||||
|
@ -1,12 +1,10 @@
|
||||
use crate::utils::{higher, qpath_res, snippet, span_lint_and_then};
|
||||
use crate::utils::{qpath_res, snippet, span_lint_and_then, visitors::LocalUsedVisitor};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::BindingAnnotation;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -65,11 +63,11 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
if let hir::StmtKind::Local(ref local) = stmt.kind;
|
||||
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
|
||||
if let hir::StmtKind::Expr(ref if_) = expr.kind;
|
||||
if let Some((ref cond, ref then, ref else_)) = higher::if_block(&if_);
|
||||
if !used_in_expr(cx, canonical_id, cond);
|
||||
if let hir::ExprKind::If(ref cond, ref then, ref else_) = if_.kind;
|
||||
if !LocalUsedVisitor::new(canonical_id).check_expr(cond);
|
||||
if let hir::ExprKind::Block(ref then, _) = then.kind;
|
||||
if let Some(value) = check_assign(cx, canonical_id, &*then);
|
||||
if !used_in_expr(cx, canonical_id, value);
|
||||
if !LocalUsedVisitor::new(canonical_id).check_expr(value);
|
||||
then {
|
||||
let span = stmt.span.to(if_.span);
|
||||
|
||||
@ -136,32 +134,6 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
}
|
||||
}
|
||||
|
||||
struct UsedVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
id: hir::HirId,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Path(ref qpath) = expr.kind;
|
||||
if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id);
|
||||
if self.id == local_id;
|
||||
then {
|
||||
self.used = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
||||
intravisit::NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_assign<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
decl: hir::HirId,
|
||||
@ -176,18 +148,10 @@ fn check_assign<'tcx>(
|
||||
if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id);
|
||||
if decl == local_id;
|
||||
then {
|
||||
let mut v = UsedVisitor {
|
||||
cx,
|
||||
id: decl,
|
||||
used: false,
|
||||
};
|
||||
let mut v = LocalUsedVisitor::new(decl);
|
||||
|
||||
for s in block.stmts.iter().take(block.stmts.len()-1) {
|
||||
intravisit::walk_stmt(&mut v, s);
|
||||
|
||||
if v.used {
|
||||
return None;
|
||||
}
|
||||
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(value);
|
||||
@ -196,9 +160,3 @@ fn check_assign<'tcx>(
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn used_in_expr<'tcx>(cx: &LateContext<'tcx>, id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> bool {
|
||||
let mut v = UsedVisitor { cx, id, used: false };
|
||||
intravisit::walk_expr(&mut v, expr);
|
||||
v.used
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ extern crate rustc_ast;
|
||||
extern crate rustc_ast_pretty;
|
||||
extern crate rustc_attr;
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_hir_pretty;
|
||||
@ -169,9 +170,11 @@ mod blocks_in_if_conditions;
|
||||
mod booleans;
|
||||
mod bytecount;
|
||||
mod cargo_common_metadata;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
mod checked_conversions;
|
||||
mod cognitive_complexity;
|
||||
mod collapsible_if;
|
||||
mod collapsible_match;
|
||||
mod comparison_chain;
|
||||
mod copies;
|
||||
mod copy_iterator;
|
||||
@ -205,6 +208,7 @@ mod float_literal;
|
||||
mod floating_point_arithmetic;
|
||||
mod format;
|
||||
mod formatting;
|
||||
mod from_over_into;
|
||||
mod functions;
|
||||
mod future_not_send;
|
||||
mod get_last_with_len;
|
||||
@ -268,6 +272,7 @@ mod needless_borrow;
|
||||
mod needless_borrowed_ref;
|
||||
mod needless_continue;
|
||||
mod needless_pass_by_value;
|
||||
mod needless_question_mark;
|
||||
mod needless_update;
|
||||
mod neg_cmp_op_on_partial_ord;
|
||||
mod neg_multiply;
|
||||
@ -293,8 +298,10 @@ mod question_mark;
|
||||
mod ranges;
|
||||
mod redundant_clone;
|
||||
mod redundant_closure_call;
|
||||
mod redundant_else;
|
||||
mod redundant_field_names;
|
||||
mod redundant_pub_crate;
|
||||
mod redundant_slicing;
|
||||
mod redundant_static_lifetimes;
|
||||
mod ref_option_ref;
|
||||
mod reference;
|
||||
@ -305,9 +312,11 @@ mod self_assignment;
|
||||
mod serde_api;
|
||||
mod shadow;
|
||||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
mod slow_vector_initialization;
|
||||
mod stable_sort_primitive;
|
||||
mod strings;
|
||||
mod suspicious_operation_groupings;
|
||||
mod suspicious_trait_impl;
|
||||
mod swap;
|
||||
mod tabs_in_doc_comments;
|
||||
@ -335,12 +344,14 @@ mod unwrap_in_result;
|
||||
mod use_self;
|
||||
mod useless_conversion;
|
||||
mod vec;
|
||||
mod vec_init_then_push;
|
||||
mod vec_resize_to_zero;
|
||||
mod verbose_file_reads;
|
||||
mod wildcard_dependencies;
|
||||
mod wildcard_imports;
|
||||
mod write;
|
||||
mod zero_div_zero;
|
||||
mod zero_sized_map_values;
|
||||
// end lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
pub use crate::utils::conf::Conf;
|
||||
@ -497,6 +508,28 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
|
||||
// begin register lints, do not remove this comment, it’s used in `update_lints`
|
||||
store.register_lints(&[
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::CLIPPY_LINTS_INTERNAL,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::DEFAULT_LINT,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::INTERNING_DEFINED_SYMBOL,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::INVALID_PATHS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::PRODUCE_ICE,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
&approx_const::APPROX_CONSTANT,
|
||||
&arithmetic::FLOAT_ARITHMETIC,
|
||||
&arithmetic::INTEGER_ARITHMETIC,
|
||||
@ -527,9 +560,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&booleans::NONMINIMAL_BOOL,
|
||||
&bytecount::NAIVE_BYTECOUNT,
|
||||
&cargo_common_metadata::CARGO_COMMON_METADATA,
|
||||
&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
&checked_conversions::CHECKED_CONVERSIONS,
|
||||
&cognitive_complexity::COGNITIVE_COMPLEXITY,
|
||||
&collapsible_if::COLLAPSIBLE_ELSE_IF,
|
||||
&collapsible_if::COLLAPSIBLE_IF,
|
||||
&collapsible_match::COLLAPSIBLE_MATCH,
|
||||
&comparison_chain::COMPARISON_CHAIN,
|
||||
&copies::IFS_SAME_COND,
|
||||
&copies::IF_SAME_THEN_ELSE,
|
||||
@ -587,6 +623,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
&formatting::SUSPICIOUS_ELSE_FORMATTING,
|
||||
&formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
&from_over_into::FROM_OVER_INTO,
|
||||
&functions::DOUBLE_MUST_USE,
|
||||
&functions::MUST_USE_CANDIDATE,
|
||||
&functions::MUST_USE_UNIT,
|
||||
@ -770,6 +807,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
|
||||
&needless_continue::NEEDLESS_CONTINUE,
|
||||
&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
|
||||
&needless_question_mark::NEEDLESS_QUESTION_MARK,
|
||||
&needless_update::NEEDLESS_UPDATE,
|
||||
&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
|
||||
&neg_multiply::NEG_MULTIPLY,
|
||||
@ -809,8 +847,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&ranges::REVERSED_EMPTY_RANGES,
|
||||
&redundant_clone::REDUNDANT_CLONE,
|
||||
&redundant_closure_call::REDUNDANT_CLOSURE_CALL,
|
||||
&redundant_else::REDUNDANT_ELSE,
|
||||
&redundant_field_names::REDUNDANT_FIELD_NAMES,
|
||||
&redundant_pub_crate::REDUNDANT_PUB_CRATE,
|
||||
&redundant_slicing::REDUNDANT_SLICING,
|
||||
&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
|
||||
&ref_option_ref::REF_OPTION_REF,
|
||||
&reference::DEREF_ADDROF,
|
||||
@ -826,6 +866,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&shadow::SHADOW_SAME,
|
||||
&shadow::SHADOW_UNRELATED,
|
||||
&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||
&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
|
||||
&stable_sort_primitive::STABLE_SORT_PRIMITIVE,
|
||||
&strings::STRING_ADD,
|
||||
@ -834,6 +875,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&strings::STRING_LIT_AS_BYTES,
|
||||
&strings::STRING_TO_STRING,
|
||||
&strings::STR_TO_STRING,
|
||||
&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
|
||||
&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
|
||||
&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
|
||||
&swap::ALMOST_SWAPPED,
|
||||
@ -876,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&types::LET_UNIT_VALUE,
|
||||
&types::LINKEDLIST,
|
||||
&types::OPTION_OPTION,
|
||||
&types::PTR_AS_PTR,
|
||||
&types::RC_BUFFER,
|
||||
&types::REDUNDANT_ALLOCATION,
|
||||
&types::TYPE_COMPLEXITY,
|
||||
@ -902,16 +945,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&unwrap_in_result::UNWRAP_IN_RESULT,
|
||||
&use_self::USE_SELF,
|
||||
&useless_conversion::USELESS_CONVERSION,
|
||||
&utils::internal_lints::CLIPPY_LINTS_INTERNAL,
|
||||
&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
&utils::internal_lints::DEFAULT_LINT,
|
||||
&utils::internal_lints::INVALID_PATHS,
|
||||
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
&utils::internal_lints::PRODUCE_ICE,
|
||||
&vec::USELESS_VEC,
|
||||
&vec_init_then_push::VEC_INIT_THEN_PUSH,
|
||||
&vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
|
||||
&verbose_file_reads::VERBOSE_FILE_READS,
|
||||
&wildcard_dependencies::WILDCARD_DEPENDENCIES,
|
||||
@ -919,6 +954,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&wildcard_imports::WILDCARD_IMPORTS,
|
||||
&write::PRINTLN_EMPTY_STRING,
|
||||
&write::PRINT_LITERAL,
|
||||
&write::PRINT_STDERR,
|
||||
&write::PRINT_STDOUT,
|
||||
&write::PRINT_WITH_NEWLINE,
|
||||
&write::USE_DEBUG,
|
||||
@ -926,16 +962,27 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&write::WRITE_LITERAL,
|
||||
&write::WRITE_WITH_NEWLINE,
|
||||
&zero_div_zero::ZERO_DIVIDED_BY_ZERO,
|
||||
&zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
|
||||
]);
|
||||
// end register lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
// all the internal lints
|
||||
#[cfg(feature = "internal-lints")]
|
||||
{
|
||||
store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
|
||||
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
|
||||
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
|
||||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
||||
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
||||
store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
|
||||
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
|
||||
}
|
||||
store.register_late_pass(|| box utils::author::Author);
|
||||
store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
|
||||
store.register_late_pass(|| box serde_api::SerdeAPI);
|
||||
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
||||
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
|
||||
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
||||
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
|
||||
store.register_late_pass(|| box utils::author::Author);
|
||||
let vec_box_size_threshold = conf.vec_box_size_threshold;
|
||||
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
|
||||
store.register_late_pass(|| box booleans::NonminimalBool);
|
||||
@ -958,28 +1005,35 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box len_zero::LenZero);
|
||||
store.register_late_pass(|| box attrs::Attributes);
|
||||
store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions);
|
||||
store.register_late_pass(|| box collapsible_match::CollapsibleMatch);
|
||||
store.register_late_pass(|| box unicode::Unicode);
|
||||
store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd);
|
||||
store.register_late_pass(|| box strings::StringAdd);
|
||||
store.register_late_pass(|| box implicit_return::ImplicitReturn);
|
||||
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
|
||||
|
||||
let parsed_msrv = conf.msrv.as_ref().and_then(|s| {
|
||||
let msrv = conf.msrv.as_ref().and_then(|s| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
|
||||
None
|
||||
})
|
||||
});
|
||||
|
||||
let msrv = parsed_msrv.clone();
|
||||
store.register_late_pass(move || box methods::Methods::new(msrv.clone()));
|
||||
let msrv = parsed_msrv.clone();
|
||||
store.register_late_pass(move || box matches::Matches::new(msrv.clone()));
|
||||
let msrv = parsed_msrv.clone();
|
||||
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone()));
|
||||
let msrv = parsed_msrv;
|
||||
store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone()));
|
||||
store.register_late_pass(move || box methods::Methods::new(msrv));
|
||||
store.register_late_pass(move || box matches::Matches::new(msrv));
|
||||
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv));
|
||||
store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv));
|
||||
store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv));
|
||||
store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv));
|
||||
store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv));
|
||||
store.register_late_pass(move || box mem_replace::MemReplace::new(msrv));
|
||||
store.register_late_pass(move || box ranges::Ranges::new(msrv));
|
||||
store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
|
||||
store.register_late_pass(move || box use_self::UseSelf::new(msrv));
|
||||
store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
|
||||
store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv));
|
||||
|
||||
store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount);
|
||||
store.register_late_pass(|| box map_clone::MapClone);
|
||||
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
|
||||
store.register_late_pass(|| box shadow::Shadow);
|
||||
@ -989,7 +1043,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box main_recursion::MainRecursion::default());
|
||||
store.register_late_pass(|| box lifetimes::Lifetimes);
|
||||
store.register_late_pass(|| box entry::HashMapPass);
|
||||
store.register_late_pass(|| box ranges::Ranges);
|
||||
store.register_late_pass(|| box types::Casts);
|
||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||
store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
|
||||
@ -1034,7 +1087,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box neg_multiply::NegMultiply);
|
||||
store.register_late_pass(|| box mem_discriminant::MemDiscriminant);
|
||||
store.register_late_pass(|| box mem_forget::MemForget);
|
||||
store.register_late_pass(|| box mem_replace::MemReplace);
|
||||
store.register_late_pass(|| box arithmetic::Arithmetic::default());
|
||||
store.register_late_pass(|| box assign_ops::AssignOps);
|
||||
store.register_late_pass(|| box let_if_seq::LetIfSeq);
|
||||
@ -1056,7 +1108,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || box pass_by_ref_or_value);
|
||||
store.register_late_pass(|| box ref_option_ref::RefOptionRef);
|
||||
store.register_late_pass(|| box try_err::TryErr);
|
||||
store.register_late_pass(|| box use_self::UseSelf);
|
||||
store.register_late_pass(|| box bytecount::ByteCount);
|
||||
store.register_late_pass(|| box infinite_iter::InfiniteIter);
|
||||
store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
|
||||
@ -1066,6 +1117,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box types::UnitArg);
|
||||
store.register_late_pass(|| box double_comparison::DoubleComparisons);
|
||||
store.register_late_pass(|| box question_mark::QuestionMark);
|
||||
store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings);
|
||||
store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl);
|
||||
store.register_late_pass(|| box map_unit_fn::MapUnit);
|
||||
store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default());
|
||||
@ -1081,10 +1133,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps);
|
||||
store.register_late_pass(|| box types::RefToMut);
|
||||
store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants);
|
||||
store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn);
|
||||
store.register_late_pass(|| box transmuting_null::TransmutingNull);
|
||||
store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite);
|
||||
store.register_late_pass(|| box checked_conversions::CheckedConversions);
|
||||
store.register_late_pass(|| box integer_division::IntegerDivision);
|
||||
store.register_late_pass(|| box inherent_to_string::InherentToString);
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
@ -1110,16 +1160,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
|
||||
store.register_early_pass(|| box precedence::Precedence);
|
||||
store.register_early_pass(|| box needless_continue::NeedlessContinue);
|
||||
store.register_early_pass(|| box redundant_else::RedundantElse);
|
||||
store.register_late_pass(|| box create_dir::CreateDir);
|
||||
store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
|
||||
store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes);
|
||||
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
|
||||
store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
|
||||
store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
|
||||
store.register_early_pass(|| box literal_representation::LiteralDigitGrouping);
|
||||
let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
|
||||
store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability));
|
||||
let literal_representation_threshold = conf.literal_representation_threshold;
|
||||
store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold));
|
||||
store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
|
||||
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
|
||||
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
|
||||
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
|
||||
@ -1133,7 +1183,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
|
||||
store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic);
|
||||
store.register_early_pass(|| box as_conversions::AsConversions);
|
||||
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
|
||||
store.register_late_pass(|| box let_underscore::LetUnderscore);
|
||||
store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
|
||||
store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports);
|
||||
@ -1149,15 +1198,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box dereference::Dereferencing);
|
||||
store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
|
||||
store.register_late_pass(|| box future_not_send::FutureNotSend);
|
||||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
|
||||
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
|
||||
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
|
||||
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
|
||||
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
|
||||
store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
|
||||
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
|
||||
single_char_binding_names_threshold,
|
||||
@ -1174,7 +1220,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box manual_ok_or::ManualOkOr);
|
||||
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
|
||||
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
|
||||
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
|
||||
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
|
||||
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
|
||||
store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
|
||||
@ -1182,7 +1227,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops);
|
||||
store.register_late_pass(|| box strings::StrToString);
|
||||
store.register_late_pass(|| box strings::StringToString);
|
||||
|
||||
store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues);
|
||||
store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default());
|
||||
store.register_late_pass(move || box types::PtrAsPtr::new(msrv));
|
||||
store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons);
|
||||
store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
@ -1201,6 +1250,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&integer_division::INTEGER_DIVISION),
|
||||
LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE),
|
||||
LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
|
||||
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
|
||||
LintId::of(&mem_forget::MEM_FORGET),
|
||||
@ -1229,6 +1279,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::RC_BUFFER),
|
||||
LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT),
|
||||
LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
|
||||
LintId::of(&write::PRINT_STDERR),
|
||||
LintId::of(&write::PRINT_STDOUT),
|
||||
LintId::of(&write::USE_DEBUG),
|
||||
]);
|
||||
@ -1238,6 +1289,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
|
||||
LintId::of(&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
|
||||
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
|
||||
LintId::of(©_iterator::COPY_ITERATOR),
|
||||
@ -1267,7 +1319,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&loops::EXPLICIT_ITER_LOOP),
|
||||
LintId::of(¯o_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(&manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(&matches::MATCH_BOOL),
|
||||
LintId::of(&matches::MATCH_SAME_ARMS),
|
||||
@ -1291,6 +1342,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
|
||||
LintId::of(&ranges::RANGE_MINUS_ONE),
|
||||
LintId::of(&ranges::RANGE_PLUS_ONE),
|
||||
LintId::of(&redundant_else::REDUNDANT_ELSE),
|
||||
LintId::of(&ref_option_ref::REF_OPTION_REF),
|
||||
LintId::of(&shadow::SHADOW_UNRELATED),
|
||||
LintId::of(&strings::STRING_ADD_ASSIGN),
|
||||
@ -1307,24 +1359,29 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::LET_UNIT_VALUE),
|
||||
LintId::of(&types::LINKEDLIST),
|
||||
LintId::of(&types::OPTION_OPTION),
|
||||
LintId::of(&types::PTR_AS_PTR),
|
||||
LintId::of(&unicode::NON_ASCII_LITERAL),
|
||||
LintId::of(&unicode::UNICODE_NOT_NFC),
|
||||
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
|
||||
LintId::of(&unused_self::UNUSED_SELF),
|
||||
LintId::of(&wildcard_imports::ENUM_GLOB_USE),
|
||||
LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
|
||||
LintId::of(&zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
|
||||
]);
|
||||
|
||||
#[cfg(feature = "internal-lints")]
|
||||
store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
|
||||
LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL),
|
||||
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
|
||||
LintId::of(&utils::internal_lints::DEFAULT_LINT),
|
||||
LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL),
|
||||
LintId::of(&utils::internal_lints::INVALID_PATHS),
|
||||
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
|
||||
LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
|
||||
LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
|
||||
LintId::of(&utils::internal_lints::PRODUCE_ICE),
|
||||
LintId::of(&utils::internal_lints::UNNECESSARY_SYMBOL_STR),
|
||||
]);
|
||||
|
||||
store.register_group(true, "clippy::all", Some("clippy"), vec![
|
||||
@ -1347,7 +1404,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&booleans::LOGIC_BUG),
|
||||
LintId::of(&booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(&bytecount::NAIVE_BYTECOUNT),
|
||||
LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF),
|
||||
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
|
||||
LintId::of(&collapsible_match::COLLAPSIBLE_MATCH),
|
||||
LintId::of(&comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(&copies::IFS_SAME_COND),
|
||||
LintId::of(&copies::IF_SAME_THEN_ELSE),
|
||||
@ -1382,6 +1441,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
|
||||
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
|
||||
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
|
||||
LintId::of(&from_over_into::FROM_OVER_INTO),
|
||||
LintId::of(&functions::DOUBLE_MUST_USE),
|
||||
LintId::of(&functions::MUST_USE_UNIT),
|
||||
LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
|
||||
@ -1506,6 +1566,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&needless_bool::BOOL_COMPARISON),
|
||||
LintId::of(&needless_bool::NEEDLESS_BOOL),
|
||||
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
|
||||
LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK),
|
||||
LintId::of(&needless_update::NEEDLESS_UPDATE),
|
||||
LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
|
||||
LintId::of(&neg_multiply::NEG_MULTIPLY),
|
||||
@ -1533,6 +1594,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL),
|
||||
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
|
||||
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
|
||||
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(&reference::DEREF_ADDROF),
|
||||
LintId::of(&reference::REF_IN_DEREF),
|
||||
@ -1544,9 +1606,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(&serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES),
|
||||
LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(&swap::ALMOST_SWAPPED),
|
||||
@ -1595,6 +1659,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(&useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(&vec::USELESS_VEC),
|
||||
LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH),
|
||||
LintId::of(&vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(&write::PRINTLN_EMPTY_STRING),
|
||||
LintId::of(&write::PRINT_LITERAL),
|
||||
@ -1612,7 +1677,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
|
||||
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(&collapsible_if::COLLAPSIBLE_ELSE_IF),
|
||||
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
|
||||
LintId::of(&collapsible_match::COLLAPSIBLE_MATCH),
|
||||
LintId::of(&comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
|
||||
LintId::of(&doc::MISSING_SAFETY_DOC),
|
||||
@ -1625,6 +1692,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
|
||||
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
|
||||
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
|
||||
LintId::of(&from_over_into::FROM_OVER_INTO),
|
||||
LintId::of(&functions::DOUBLE_MUST_USE),
|
||||
LintId::of(&functions::MUST_USE_UNIT),
|
||||
LintId::of(&functions::RESULT_UNIT_ERR),
|
||||
@ -1698,6 +1766,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&returns::LET_AND_RETURN),
|
||||
LintId::of(&returns::NEEDLESS_RETURN),
|
||||
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
|
||||
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
|
||||
LintId::of(&try_err::TRY_ERR),
|
||||
@ -1759,6 +1828,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&needless_bool::BOOL_COMPARISON),
|
||||
LintId::of(&needless_bool::NEEDLESS_BOOL),
|
||||
LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
|
||||
LintId::of(&needless_question_mark::NEEDLESS_QUESTION_MARK),
|
||||
LintId::of(&needless_update::NEEDLESS_UPDATE),
|
||||
LintId::of(&neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
|
||||
LintId::of(&no_effect::NO_EFFECT),
|
||||
@ -1769,6 +1839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL),
|
||||
LintId::of(&redundant_slicing::REDUNDANT_SLICING),
|
||||
LintId::of(&reference::DEREF_ADDROF),
|
||||
LintId::of(&reference::REF_IN_DEREF),
|
||||
LintId::of(&repeat_once::REPEAT_ONCE),
|
||||
@ -1850,6 +1921,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(®ex::INVALID_REGEX),
|
||||
LintId::of(&self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(&serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(&swap::ALMOST_SWAPPED),
|
||||
@ -1890,6 +1962,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&types::BOX_VEC),
|
||||
LintId::of(&types::REDUNDANT_ALLOCATION),
|
||||
LintId::of(&vec::USELESS_VEC),
|
||||
LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH),
|
||||
]);
|
||||
|
||||
store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![
|
||||
|
@ -501,7 +501,7 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
|
||||
|
||||
// for lifetimes as parameters of generics
|
||||
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
|
||||
if lifetime.name.ident().name != kw::Invalid && lifetime.name.ident().name != kw::StaticLifetime {
|
||||
if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime {
|
||||
self.lifetimes_used_in_body = true;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if a long integral or floating-point constant does
|
||||
@ -32,7 +32,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
pub UNREADABLE_LITERAL,
|
||||
pedantic,
|
||||
"long integer literal without underscores"
|
||||
"long literal without underscores"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -208,7 +208,13 @@ impl WarningType {
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint_pass!(LiteralDigitGrouping => [
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LiteralDigitGrouping {
|
||||
lint_fraction_readability: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(LiteralDigitGrouping => [
|
||||
UNREADABLE_LITERAL,
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
@ -223,7 +229,7 @@ impl EarlyLintPass for LiteralDigitGrouping {
|
||||
}
|
||||
|
||||
if let ExprKind::Lit(ref lit) = expr.kind {
|
||||
Self::check_lit(cx, lit)
|
||||
self.check_lit(cx, lit)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,7 +238,13 @@ impl EarlyLintPass for LiteralDigitGrouping {
|
||||
const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
|
||||
|
||||
impl LiteralDigitGrouping {
|
||||
fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
|
||||
pub fn new(lint_fraction_readability: bool) -> Self {
|
||||
Self {
|
||||
lint_fraction_readability,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
|
||||
if_chain! {
|
||||
if let Some(src) = snippet_opt(cx, lit.span);
|
||||
if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit);
|
||||
@ -247,9 +259,12 @@ impl LiteralDigitGrouping {
|
||||
|
||||
let result = (|| {
|
||||
|
||||
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?;
|
||||
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?;
|
||||
if let Some(fraction) = num_lit.fraction {
|
||||
let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?;
|
||||
let fractional_group_size = Self::get_group_size(
|
||||
fraction.rsplit('_'),
|
||||
num_lit.radix,
|
||||
self.lint_fraction_readability)?;
|
||||
|
||||
let consistent = Self::parts_consistent(integral_group_size,
|
||||
fractional_group_size,
|
||||
@ -363,7 +378,11 @@ impl LiteralDigitGrouping {
|
||||
|
||||
/// Returns the size of the digit groups (or None if ungrouped) if successful,
|
||||
/// otherwise returns a `WarningType` for linting.
|
||||
fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>, radix: Radix) -> Result<Option<usize>, WarningType> {
|
||||
fn get_group_size<'a>(
|
||||
groups: impl Iterator<Item = &'a str>,
|
||||
radix: Radix,
|
||||
lint_unreadable: bool,
|
||||
) -> Result<Option<usize>, WarningType> {
|
||||
let mut groups = groups.map(str::len);
|
||||
|
||||
let first = groups.next().expect("At least one group");
|
||||
@ -380,7 +399,7 @@ impl LiteralDigitGrouping {
|
||||
} else {
|
||||
Ok(Some(second))
|
||||
}
|
||||
} else if first > 5 {
|
||||
} else if first > 5 && lint_unreadable {
|
||||
Err(WarningType::UnreadableLiteral)
|
||||
} else {
|
||||
Ok(None)
|
||||
|
@ -2,6 +2,7 @@ use crate::consts::constant;
|
||||
use crate::utils::paths;
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::{is_unused, mutated_variables};
|
||||
use crate::utils::visitors::LocalUsedVisitor;
|
||||
use crate::utils::{
|
||||
contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
|
||||
indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item,
|
||||
@ -217,7 +218,7 @@ declare_clippy_lint! {
|
||||
/// **Why is this bad?** The `while let` loop is usually shorter and more
|
||||
/// readable.
|
||||
///
|
||||
/// **Known problems:** Sometimes the wrong binding is displayed (#383).
|
||||
/// **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,no_run
|
||||
@ -741,6 +742,14 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
// Break can come from the inner loop so remove them.
|
||||
absorb_break(&never_loop_block(b, main_loop_id))
|
||||
},
|
||||
ExprKind::If(ref e, ref e2, ref e3) => {
|
||||
let e1 = never_loop_expr(e, main_loop_id);
|
||||
let e2 = never_loop_expr(e2, main_loop_id);
|
||||
let e3 = e3
|
||||
.as_ref()
|
||||
.map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
|
||||
combine_seq(e1, combine_branches(e2, e3))
|
||||
},
|
||||
ExprKind::Match(ref e, ref arms, _) => {
|
||||
let e = never_loop_expr(e, main_loop_id);
|
||||
if arms.is_empty() {
|
||||
@ -767,7 +776,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
ExprKind::InlineAsm(ref asm) => asm
|
||||
.operands
|
||||
.iter()
|
||||
.map(|o| match o {
|
||||
.map(|(o, _)| match o {
|
||||
InlineAsmOperand::In { expr, .. }
|
||||
| InlineAsmOperand::InOut { expr, .. }
|
||||
| InlineAsmOperand::Const { expr }
|
||||
@ -1919,8 +1928,7 @@ fn check_for_single_element_loop<'tcx>(
|
||||
if_chain! {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind;
|
||||
if let PatKind::Binding(.., target, _) = pat.kind;
|
||||
if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind;
|
||||
if let [arg_expression] = arg_expr_list;
|
||||
if let ExprKind::Array([arg_expression]) = arg_expr.kind;
|
||||
if let ExprKind::Path(ref list_item) = arg_expression.kind;
|
||||
if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name);
|
||||
if let ExprKind::Block(ref block, _) = body.kind;
|
||||
@ -2025,8 +2033,7 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId>
|
||||
let node_str = cx.tcx.hir().get(hir_id);
|
||||
if_chain! {
|
||||
if let Node::Binding(pat) = node_str;
|
||||
if let PatKind::Binding(bind_ann, ..) = pat.kind;
|
||||
if let BindingAnnotation::Mutable = bind_ann;
|
||||
if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
|
||||
then {
|
||||
return Some(hir_id);
|
||||
}
|
||||
@ -2071,28 +2078,6 @@ fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalUsedVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
local: HirId,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if same_var(self.cx, expr, self.local) {
|
||||
self.used = true;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
struct VarVisitor<'a, 'tcx> {
|
||||
/// context reference
|
||||
cx: &'a LateContext<'tcx>,
|
||||
@ -2126,11 +2111,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
then {
|
||||
let index_used_directly = same_var(self.cx, idx, self.var);
|
||||
let indexed_indirectly = {
|
||||
let mut used_visitor = LocalUsedVisitor {
|
||||
cx: self.cx,
|
||||
local: self.var,
|
||||
used: false,
|
||||
};
|
||||
let mut used_visitor = LocalUsedVisitor::new(self.var);
|
||||
walk_expr(&mut used_visitor, idx);
|
||||
used_visitor.used
|
||||
};
|
||||
@ -2621,7 +2602,7 @@ fn is_loop(expr: &Expr<'_>) -> bool {
|
||||
}
|
||||
|
||||
fn is_conditional(expr: &Expr<'_>) -> bool {
|
||||
matches!(expr.kind, ExprKind::Match(..))
|
||||
matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..))
|
||||
}
|
||||
|
||||
fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
|
||||
|
@ -105,7 +105,7 @@ impl MacroUseImports {
|
||||
impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if_chain! {
|
||||
if cx.sess().opts.edition == Edition::Edition2018;
|
||||
if cx.sess().opts.edition >= Edition::Edition2018;
|
||||
if let hir::ItemKind::Use(path, _kind) = &item.kind;
|
||||
if let Some(mac_attr) = item
|
||||
.attrs
|
||||
|
@ -9,7 +9,7 @@ use rustc_hir::{
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** It checks for manual implementations of `async` functions.
|
||||
@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if let Some(header_snip) = snippet_opt(cx, header_span);
|
||||
if let Some(ret_pos) = position_before_rarrow(header_snip.clone());
|
||||
if let Some(ret_pos) = position_before_rarrow(&header_snip);
|
||||
if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
|
||||
then {
|
||||
let help = format!("make the function `async` and {}", ret_sugg);
|
||||
@ -137,7 +137,7 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
|
||||
if let Some(args) = segment.args;
|
||||
if args.bindings.len() == 1;
|
||||
let binding = &args.bindings[0];
|
||||
if binding.ident.as_str() == "Output";
|
||||
if binding.ident.name == sym::Output;
|
||||
if let TypeBindingKind::Equality{ty: output} = binding.kind;
|
||||
then {
|
||||
return Some(output)
|
||||
|
@ -4,17 +4,11 @@ use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantDat
|
||||
use rustc_attr as attr;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{sym, Span};
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 40,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
|
||||
@ -66,12 +60,12 @@ declare_clippy_lint! {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ManualNonExhaustive {
|
||||
msrv: Option<VersionReq>,
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualNonExhaustive {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
@ -22,9 +23,6 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.map_or(Err("error"), |v| Ok(v));
|
||||
///
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.map_or(Err("error"), |v| Ok(v));
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
@ -51,7 +49,7 @@ impl LateLintPass<'_> for ManualOkOr {
|
||||
if args.len() == 3;
|
||||
let method_receiver = &args[0];
|
||||
let ty = cx.typeck_results().expr_ty(method_receiver);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(option_type));
|
||||
if is_type_diagnostic_item(cx, ty, sym::option_type);
|
||||
let or_expr = &args[1];
|
||||
if is_ok_wrapping(cx, &args[2]);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
|
||||
|
@ -13,18 +13,12 @@ use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::Span;
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
const MANUAL_STRIP_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 45,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
@ -61,12 +55,12 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
pub struct ManualStrip {
|
||||
msrv: Option<VersionReq>,
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
@ -86,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some((cond, then, _)) = higher::if_block(&expr);
|
||||
if let ExprKind::If(cond, then, _) = &expr.kind;
|
||||
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
|
||||
if let ExprKind::Path(target_path) = &target_arg.kind;
|
||||
@ -225,8 +219,7 @@ fn find_stripping<'tcx>(
|
||||
if is_ref_str(self.cx, ex);
|
||||
let unref = peel_ref(ex);
|
||||
if let ExprKind::Index(indexed, index) = &unref.kind;
|
||||
if let Some(range) = higher::range(index);
|
||||
if let higher::Range { start, end, .. } = range;
|
||||
if let Some(higher::Range { start, end, .. }) = higher::range(index);
|
||||
if let ExprKind::Path(path) = &indexed.kind;
|
||||
if qpath_res(self.cx, path, ex.hir_id) == self.target;
|
||||
then {
|
||||
|
@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind;
|
||||
if args.len() == 2;
|
||||
if method.ident.as_str() == "map";
|
||||
if method.ident.name == sym::map;
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR);
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
|
||||
|
@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for instances of `map_err(|_| Some::Enum)`
|
||||
///
|
||||
/// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error
|
||||
/// **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
@ -99,7 +99,7 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// ```
|
||||
pub MAP_ERR_IGNORE,
|
||||
pedantic,
|
||||
restriction,
|
||||
"`map_err` should not ignore the original error"
|
||||
}
|
||||
|
||||
@ -133,9 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
|
||||
cx,
|
||||
MAP_ERR_IGNORE,
|
||||
body_span,
|
||||
"`map_err(|_|...` ignores the original error",
|
||||
"`map_err(|_|...` wildcard pattern discards the original error",
|
||||
None,
|
||||
"Consider wrapping the error in an enum variant",
|
||||
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for MapIdentity {
|
||||
fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
|
||||
if args.len() == 2 && method.ident.as_str() == "map";
|
||||
if args.len() == 2 && method.ident.name == sym::map;
|
||||
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR)
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|
||||
|
@ -131,7 +131,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) ->
|
||||
Some(expr.span)
|
||||
},
|
||||
hir::ExprKind::Block(ref block, _) => {
|
||||
match (&block.stmts[..], block.expr.as_ref()) {
|
||||
match (block.stmts, block.expr.as_ref()) {
|
||||
(&[], Some(inner_expr)) => {
|
||||
// If block only contains an expression,
|
||||
// reduce `{ X }` to `X`
|
||||
|
@ -2,10 +2,10 @@ use crate::consts::{constant, miri_to_const, Constant};
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::is_unused;
|
||||
use crate::utils::{
|
||||
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
|
||||
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks,
|
||||
snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
||||
span_lint_and_then,
|
||||
expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of,
|
||||
is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
|
||||
peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt,
|
||||
snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
use if_chain::if_chain;
|
||||
@ -20,10 +20,10 @@ use rustc_hir::{
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::{sym, Symbol};
|
||||
use semver::{Version, VersionReq};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::Bound;
|
||||
@ -412,8 +412,8 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Lint for redundant pattern matching over `Result`, `Option` or
|
||||
/// `std::task::Poll`
|
||||
/// **What it does:** Lint for redundant pattern matching over `Result`, `Option`,
|
||||
/// `std::task::Poll` or `std::net::IpAddr`
|
||||
///
|
||||
/// **Why is this bad?** It's more concise and clear to just use the proper
|
||||
/// utility function
|
||||
@ -424,12 +424,15 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::task::Poll;
|
||||
/// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
/// if let Ok(_) = Ok::<i32, i32>(42) {}
|
||||
/// if let Err(_) = Err::<i32, i32>(42) {}
|
||||
/// if let None = None::<()> {}
|
||||
/// if let Some(_) = Some(42) {}
|
||||
/// if let Poll::Pending = Poll::Pending::<()> {}
|
||||
/// if let Poll::Ready(_) = Poll::Ready(42) {}
|
||||
/// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
|
||||
/// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
|
||||
/// match Ok::<i32, i32>(42) {
|
||||
/// Ok(_) => true,
|
||||
/// Err(_) => false,
|
||||
@ -440,12 +443,15 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::task::Poll;
|
||||
/// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
/// if Ok::<i32, i32>(42).is_ok() {}
|
||||
/// if Err::<i32, i32>(42).is_err() {}
|
||||
/// if None::<()>.is_none() {}
|
||||
/// if Some(42).is_some() {}
|
||||
/// if Poll::Pending::<()>.is_pending() {}
|
||||
/// if Poll::Ready(42).is_ready() {}
|
||||
/// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
|
||||
/// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
|
||||
/// Ok::<i32, i32>(42).is_ok();
|
||||
/// ```
|
||||
pub REDUNDANT_PATTERN_MATCHING,
|
||||
@ -459,7 +465,8 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Why is this bad?** Readability and needless complexity.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
/// **Known problems:** This lint falsely triggers, if there are arms with
|
||||
/// `cfg` attributes that remove an arm evaluating to `false`.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
@ -528,13 +535,13 @@ declare_clippy_lint! {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Matches {
|
||||
msrv: Option<VersionReq>,
|
||||
msrv: Option<RustcVersion>,
|
||||
infallible_destructuring_match_linted: bool,
|
||||
}
|
||||
|
||||
impl Matches {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Matches::default()
|
||||
@ -561,13 +568,7 @@ impl_lint_pass!(Matches => [
|
||||
MATCH_SAME_ARMS,
|
||||
]);
|
||||
|
||||
const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 42,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
@ -645,8 +646,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), pat.span);
|
||||
if !in_macro(pat.span);
|
||||
if let PatKind::Struct(ref qpath, fields, true) = pat.kind;
|
||||
if let QPath::Resolved(_, ref path) = qpath;
|
||||
if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind;
|
||||
if let Some(def_id) = path.res.opt_def_id();
|
||||
let ty = cx.tcx.type_of(def_id);
|
||||
if let ty::Adt(def, _) = ty.kind();
|
||||
@ -689,10 +689,9 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
|
||||
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
|
||||
// single statement/expr "else" block, don't lint
|
||||
return;
|
||||
} else {
|
||||
// block with 2+ statements or 1 expr and 1+ statement
|
||||
Some(els)
|
||||
}
|
||||
// block with 2+ statements or 1 expr and 1+ statement
|
||||
Some(els)
|
||||
} else {
|
||||
// not a block, don't lint
|
||||
return;
|
||||
@ -729,20 +728,60 @@ fn report_single_match_single_pattern(
|
||||
let els_str = els.map_or(String::new(), |els| {
|
||||
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
|
||||
});
|
||||
|
||||
let (msg, sugg) = if_chain! {
|
||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
|
||||
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
|
||||
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
|
||||
if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
|
||||
if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
|
||||
then {
|
||||
// scrutinee derives PartialEq and the pattern is a constant.
|
||||
let pat_ref_count = match pat.kind {
|
||||
// string literals are already a reference.
|
||||
PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
|
||||
_ => pat_ref_count,
|
||||
};
|
||||
// References are only implicitly added to the pattern, so no overflow here.
|
||||
// e.g. will work: match &Some(_) { Some(_) => () }
|
||||
// will not: match Some(_) { &Some(_) => () }
|
||||
let ref_count_diff = ty_ref_count - pat_ref_count;
|
||||
|
||||
// Try to remove address of expressions first.
|
||||
let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
|
||||
let ref_count_diff = ref_count_diff - removed;
|
||||
|
||||
let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
|
||||
let sugg = format!(
|
||||
"if {} == {}{} {}{}",
|
||||
snippet(cx, ex.span, ".."),
|
||||
// PartialEq for different reference counts may not exist.
|
||||
"&".repeat(ref_count_diff),
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
|
||||
els_str,
|
||||
);
|
||||
(msg, sugg)
|
||||
} else {
|
||||
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
|
||||
let sugg = format!(
|
||||
"if let {} = {} {}{}",
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
snippet(cx, ex.span, ".."),
|
||||
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
|
||||
els_str,
|
||||
);
|
||||
(msg, sugg)
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
|
||||
let`",
|
||||
msg,
|
||||
"try this",
|
||||
format!(
|
||||
"if let {} = {} {}{}",
|
||||
snippet(cx, arms[0].pat.span, ".."),
|
||||
snippet(cx, ex.span, ".."),
|
||||
expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
|
||||
els_str,
|
||||
),
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
@ -955,16 +994,14 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
||||
if let QPath::Resolved(_, p) = path {
|
||||
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
|
||||
}
|
||||
} else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind {
|
||||
if let QPath::Resolved(_, p) = path {
|
||||
// Some simple checks for exhaustive patterns.
|
||||
// There is a room for improvements to detect more cases,
|
||||
// but it can be more expensive to do so.
|
||||
let is_pattern_exhaustive =
|
||||
|pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
|
||||
if patterns.iter().all(is_pattern_exhaustive) {
|
||||
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
|
||||
}
|
||||
} else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind {
|
||||
// Some simple checks for exhaustive patterns.
|
||||
// There is a room for improvements to detect more cases,
|
||||
// but it can be more expensive to do so.
|
||||
let is_pattern_exhaustive =
|
||||
|pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
|
||||
if patterns.iter().all(is_pattern_exhaustive) {
|
||||
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1167,13 +1204,16 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
|
||||
if b0 != b1;
|
||||
let if_guard = &b0_arms[0].guard;
|
||||
if if_guard.is_none() || b0_arms.len() == 1;
|
||||
if b0_arms[0].attrs.is_empty();
|
||||
if b0_arms[1..].iter()
|
||||
.all(|arm| {
|
||||
find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
|
||||
arm.guard.is_none()
|
||||
arm.guard.is_none() && arm.attrs.is_empty()
|
||||
});
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// The suggestion may be incorrect, because some arms can have `cfg` attributes
|
||||
// evaluated into `false` and so such arms will be stripped before.
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let pat = {
|
||||
use itertools::Itertools as _;
|
||||
b0_arms.iter()
|
||||
@ -1237,6 +1277,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
|
||||
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK:
|
||||
// This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
|
||||
// to prevent false positives as there is currently no better way to detect if code was excluded by
|
||||
// a macro. See PR #6435
|
||||
if_chain! {
|
||||
if let Some(match_snippet) = snippet_opt(cx, expr.span);
|
||||
if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
|
||||
if let Some(ex_snippet) = snippet_opt(cx, ex.span);
|
||||
let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
|
||||
if rest_snippet.contains("=>");
|
||||
then {
|
||||
// The code it self contains another thick arrow "=>"
|
||||
// -> Either another arm or a comment
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let matched_vars = ex.span;
|
||||
let bind_names = arms[0].pat.span;
|
||||
let match_body = remove_blocks(&arms[0].body);
|
||||
@ -1436,8 +1494,7 @@ fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
|
||||
if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
|
||||
if let ExprKind::Path(ref some_path) = e.kind;
|
||||
if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
|
||||
if let ExprKind::Path(ref qpath) = args[0].kind;
|
||||
if let &QPath::Resolved(_, ref path2) = qpath;
|
||||
if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind;
|
||||
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
|
||||
then {
|
||||
return Some(rb)
|
||||
@ -1573,6 +1630,10 @@ mod redundant_pattern_match {
|
||||
"is_some()"
|
||||
} else if match_qpath(path, &paths::POLL_READY) {
|
||||
"is_ready()"
|
||||
} else if match_qpath(path, &paths::IPADDR_V4) {
|
||||
"is_ipv4()"
|
||||
} else if match_qpath(path, &paths::IPADDR_V6) {
|
||||
"is_ipv6()"
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -1653,6 +1714,17 @@ mod redundant_pattern_match {
|
||||
"is_ok()",
|
||||
"is_err()",
|
||||
)
|
||||
.or_else(|| {
|
||||
find_good_method_for_match(
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
&paths::IPADDR_V4,
|
||||
&paths::IPADDR_V6,
|
||||
"is_ipv4()",
|
||||
"is_ipv6()",
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::utils::{
|
||||
in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help,
|
||||
in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help,
|
||||
span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
@ -94,7 +95,7 @@ declare_clippy_lint! {
|
||||
"replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MemReplace =>
|
||||
impl_lint_pass!(MemReplace =>
|
||||
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
|
||||
|
||||
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
||||
}
|
||||
}
|
||||
|
||||
const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
||||
|
||||
pub struct MemReplace {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl MemReplace {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
@ -236,8 +250,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
then {
|
||||
check_replace_option_with_none(cx, src, dest, expr.span);
|
||||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
|
||||
check_replace_with_default(cx, src, dest, expr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
@ -90,8 +90,7 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<M
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Call(func, args) = &expr.kind;
|
||||
if args.is_empty();
|
||||
if let hir::ExprKind::Path(path) = &func.kind;
|
||||
if let hir::QPath::TypeRelative(_, segment) = path;
|
||||
if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
|
||||
then {
|
||||
match &*segment.ident.as_str() {
|
||||
"max_value" => return Some(MinMax::Max),
|
||||
|
@ -14,27 +14,27 @@ use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{TraitItem, TraitItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, SymbolStr};
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::eager_or_lazy::is_lazyness_candidate;
|
||||
use crate::utils::usage::mutated_variables;
|
||||
use crate::utils::{
|
||||
contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
|
||||
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
|
||||
match_trait_method, match_type, match_var, meets_msrv, method_calls, method_chain_args, paths, remove_blocks,
|
||||
return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
|
||||
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
|
||||
contains_return, contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher,
|
||||
implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
|
||||
match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls,
|
||||
method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
|
||||
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
|
||||
walk_ptrs_ty_depth, SpanlessEq,
|
||||
};
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
|
||||
@ -1406,12 +1406,12 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
msrv: Option<VersionReq>,
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Methods {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<VersionReq>) -> Self {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
@ -1488,7 +1488,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
|
||||
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
|
||||
["unwrap_or_else", "map"] => {
|
||||
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
|
||||
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) {
|
||||
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or");
|
||||
}
|
||||
},
|
||||
@ -1510,7 +1510,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]),
|
||||
["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]),
|
||||
["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()),
|
||||
["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
@ -1569,7 +1569,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
|
||||
|
||||
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||
if args.len() == 1 && method_call.ident.name == sym!(clone) {
|
||||
if args.len() == 1 && method_call.ident.name == sym::clone {
|
||||
lint_clone_on_copy(cx, expr, &args[0], self_ty);
|
||||
lint_clone_on_ref_ptr(cx, expr, &args[0]);
|
||||
}
|
||||
@ -1593,7 +1593,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
}
|
||||
}
|
||||
},
|
||||
ty::Ref(..) if method_call.ident.name == sym!(into_iter) => {
|
||||
ty::Ref(..) if method_call.ident.name == sym::into_iter => {
|
||||
lint_into_iter(cx, expr, self_ty, *method_span);
|
||||
},
|
||||
_ => (),
|
||||
@ -1624,10 +1624,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
let item = cx.tcx.hir().expect_item(parent);
|
||||
let def_id = cx.tcx.hir().local_def_id(item.hir_id);
|
||||
let self_ty = cx.tcx.type_of(def_id);
|
||||
|
||||
// if this impl block implements a trait, lint in trait definition instead
|
||||
if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
|
||||
if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next();
|
||||
if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind;
|
||||
|
||||
let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
|
||||
let method_sig = cx.tcx.fn_sig(method_def_id);
|
||||
@ -1669,40 +1674,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((ref conv, self_kinds)) = &CONVENTIONS
|
||||
.iter()
|
||||
.find(|(ref conv, _)| conv.check(&name))
|
||||
{
|
||||
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
|
||||
let lint = if item.vis.node.is_pub() {
|
||||
WRONG_PUB_SELF_CONVENTION
|
||||
} else {
|
||||
WRONG_SELF_CONVENTION
|
||||
};
|
||||
|
||||
span_lint(
|
||||
cx,
|
||||
lint,
|
||||
first_arg.pat.span,
|
||||
&format!("methods called `{}` usually take {}; consider choosing a less ambiguous name",
|
||||
conv,
|
||||
&self_kinds
|
||||
.iter()
|
||||
.map(|k| k.description())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" or ")
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
lint_wrong_self_convention(
|
||||
cx,
|
||||
&name,
|
||||
item.vis.node.is_pub(),
|
||||
self_ty,
|
||||
first_arg_ty,
|
||||
first_arg.pat.span
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if this impl block implements a trait, lint in trait definition instead
|
||||
if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
|
||||
let ret_ty = return_ty(cx, impl_item.hir_id);
|
||||
|
||||
@ -1736,8 +1718,23 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
|
||||
if in_external_macro(cx.tcx.sess, item.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let TraitItemKind::Fn(ref sig, _) = item.kind;
|
||||
if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
|
||||
let first_arg_span = first_arg_ty.span;
|
||||
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
|
||||
|
||||
then {
|
||||
lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span);
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, item.span);
|
||||
if item.ident.name == sym::new;
|
||||
if let TraitItemKind::Fn(_, _) = item.kind;
|
||||
let ret_ty = return_ty(cx, item.hir_id);
|
||||
@ -1758,6 +1755,39 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn lint_wrong_self_convention<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item_name: &str,
|
||||
is_pub: bool,
|
||||
self_ty: &'tcx TyS<'tcx>,
|
||||
first_arg_ty: &'tcx TyS<'tcx>,
|
||||
first_arg_span: Span,
|
||||
) {
|
||||
let lint = if is_pub {
|
||||
WRONG_PUB_SELF_CONVENTION
|
||||
} else {
|
||||
WRONG_SELF_CONVENTION
|
||||
};
|
||||
if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) {
|
||||
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
|
||||
span_lint(
|
||||
cx,
|
||||
lint,
|
||||
first_arg_span,
|
||||
&format!(
|
||||
"methods called `{}` usually take {}; consider choosing a less ambiguous name",
|
||||
conv,
|
||||
&self_kinds
|
||||
.iter()
|
||||
.map(|k| k.description())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" or ")
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for the `OR_FUN_CALL` lint.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn lint_or_fun_call<'tcx>(
|
||||
@ -2018,6 +2048,7 @@ fn lint_expect_fun_call(
|
||||
hir::ExprKind::Call(..)
|
||||
| hir::ExprKind::MethodCall(..)
|
||||
// These variants are debatable or require further examination
|
||||
| hir::ExprKind::If(..)
|
||||
| hir::ExprKind::Match(..)
|
||||
| hir::ExprKind::Block{ .. } => true,
|
||||
_ => false,
|
||||
@ -2101,8 +2132,11 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
|
||||
cx,
|
||||
CLONE_DOUBLE_REF,
|
||||
expr.span,
|
||||
"using `clone` on a double-reference; \
|
||||
this will copy the reference instead of cloning the inner type",
|
||||
&format!(
|
||||
"using `clone` on a double-reference; \
|
||||
this will copy the reference of type `{}` instead of cloning the inner type",
|
||||
ty
|
||||
),
|
||||
|diag| {
|
||||
if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
let mut ty = innermost;
|
||||
@ -2175,11 +2209,17 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp
|
||||
} else {
|
||||
snip = None;
|
||||
}
|
||||
span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| {
|
||||
if let Some((text, snip)) = snip {
|
||||
diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
|
||||
}
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CLONE_ON_COPY,
|
||||
expr.span,
|
||||
&format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
|
||||
|diag| {
|
||||
if let Some((text, snip)) = snip {
|
||||
diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2639,9 +2679,9 @@ fn lint_unwrap(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::E
|
||||
fn lint_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) {
|
||||
Some((EXPECT_USED, "an Option", "None"))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) {
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::result_type) {
|
||||
Some((EXPECT_USED, "a Result", "Err"))
|
||||
} else {
|
||||
None
|
||||
@ -2734,6 +2774,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
||||
}
|
||||
}
|
||||
|
||||
const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
|
||||
|
||||
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
|
||||
/// Return true if lint triggered
|
||||
fn lint_map_unwrap_or_else<'tcx>(
|
||||
@ -2741,7 +2783,11 @@ fn lint_map_unwrap_or_else<'tcx>(
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
map_args: &'tcx [hir::Expr<'_>],
|
||||
unwrap_args: &'tcx [hir::Expr<'_>],
|
||||
msrv: Option<&RustcVersion>,
|
||||
) -> bool {
|
||||
if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
|
||||
return false;
|
||||
}
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
|
||||
@ -2924,9 +2970,20 @@ fn lint_filter_map<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
|
||||
|
||||
/// lint use of `filter_map().next()` for `Iterators`
|
||||
fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
|
||||
fn lint_filter_map_next<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
filter_args: &'tcx [hir::Expr<'_>],
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find_map(..)` instead.";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
@ -3039,7 +3096,7 @@ fn lint_flat_map_identity<'tcx>(
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
|
||||
|
||||
if path.segments.len() == 1;
|
||||
if path.segments[0].ident.as_str() == binding_ident.as_str();
|
||||
if path.segments[0].ident.name == binding_ident.name;
|
||||
|
||||
then {
|
||||
apply_lint("called `flat_map(|x| x)` on an `Iterator`");
|
||||
@ -3117,7 +3174,7 @@ fn lint_search_is_some<'tcx>(
|
||||
else if search_method == "find" {
|
||||
let is_string_or_str_slice = |e| {
|
||||
let self_ty = cx.typeck_results().expr_ty(e).peel_refs();
|
||||
if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
|
||||
if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
|
||||
true
|
||||
} else {
|
||||
*self_ty.kind() == ty::Str
|
||||
@ -3471,13 +3528,7 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||
);
|
||||
}
|
||||
|
||||
const OPTION_AS_REF_DEREF_MSRV: Version = Version {
|
||||
major: 1,
|
||||
minor: 40,
|
||||
patch: 0,
|
||||
pre: Vec::new(),
|
||||
build: Vec::new(),
|
||||
};
|
||||
const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
||||
|
||||
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
|
||||
fn lint_option_as_ref_deref<'tcx>(
|
||||
@ -3486,7 +3537,7 @@ fn lint_option_as_ref_deref<'tcx>(
|
||||
as_ref_args: &[hir::Expr<'_>],
|
||||
map_args: &[hir::Expr<'_>],
|
||||
is_mut: bool,
|
||||
msrv: Option<&VersionReq>,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
|
||||
return;
|
||||
@ -3877,36 +3928,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns `true` if `expr` contains a return expression
|
||||
fn contains_return(expr: &hir::Expr<'_>) -> bool {
|
||||
struct RetCallFinder {
|
||||
found: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.found {
|
||||
return;
|
||||
}
|
||||
if let hir::ExprKind::Ret(..) = &expr.kind {
|
||||
self.found = true;
|
||||
} else {
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
||||
intravisit::NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = RetCallFinder { found: false };
|
||||
visitor.visit_expr(expr);
|
||||
visitor.found
|
||||
}
|
||||
|
||||
fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
if_chain! {
|
||||
if args.len() == 2;
|
||||
|
@ -69,10 +69,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
||||
}
|
||||
}
|
||||
return (true, false);
|
||||
} else {
|
||||
// We don't know. It might do anything.
|
||||
return (true, true);
|
||||
}
|
||||
// We don't know. It might do anything.
|
||||
return (true, true);
|
||||
}
|
||||
}
|
||||
(true, true)
|
||||
@ -91,6 +90,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
||||
}
|
||||
(found_mapping, found_filtering)
|
||||
},
|
||||
// There must be an else_arm or there will be a type error
|
||||
hir::ExprKind::If(_, ref if_arm, Some(ref else_arm)) => {
|
||||
let if_check = check_expression(cx, arg_id, if_arm);
|
||||
let else_check = check_expression(cx, arg_id, else_arm);
|
||||
(if_check.0 | else_check.0, if_check.1 | else_check.1)
|
||||
},
|
||||
hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
|
||||
_ => (true, true),
|
||||
}
|
||||
|
@ -89,9 +89,9 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons
|
||||
if let [obj, _] = args;
|
||||
if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
|
||||
then {
|
||||
if path.ident.as_str() == sym!(max).as_str() {
|
||||
if path.ident.name == sym!(max) {
|
||||
fetch_const(cx, args, MinMax::Max)
|
||||
} else if path.ident.as_str() == sym!(min).as_str() {
|
||||
} else if path.ident.name == sym!(min) {
|
||||
fetch_const(cx, args, MinMax::Min)
|
||||
} else {
|
||||
None
|
||||
|
@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
|
||||
last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
|
||||
span_lint_and_then, span_lint_hir_and_then, SpanlessEq,
|
||||
span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -139,12 +139,14 @@ declare_clippy_lint! {
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for getting the remainder of a division by one.
|
||||
/// **What it does:** Checks for getting the remainder of a division by one or minus
|
||||
/// one.
|
||||
///
|
||||
/// **Why is this bad?** The result can only ever be zero. No one will write
|
||||
/// such code deliberately, unless trying to win an Underhanded Rust
|
||||
/// Contest. Even for that contest, it's probably a bad idea. Use something more
|
||||
/// underhanded.
|
||||
/// **Why is this bad?** The result for a divisor of one can only ever be zero; for
|
||||
/// minus one it can cause panic/overflow (if the left operand is the minimal value of
|
||||
/// the respective integer type) or results in zero. No one will write such code
|
||||
/// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
|
||||
/// contest, it's probably a bad idea. Use something more underhanded.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
@ -152,10 +154,11 @@ declare_clippy_lint! {
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// let a = x % 1;
|
||||
/// let a = x % -1;
|
||||
/// ```
|
||||
pub MODULO_ONE,
|
||||
correctness,
|
||||
"taking a number modulo 1, which always returns 0"
|
||||
"taking a number modulo +/-1, which can either panic/overflow or always returns 0"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -378,60 +381,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
return;
|
||||
},
|
||||
ExprKind::Binary(ref cmp, ref left, ref right) => {
|
||||
let op = cmp.node;
|
||||
if op.is_comparison() {
|
||||
check_nan(cx, left, expr);
|
||||
check_nan(cx, right, expr);
|
||||
check_to_owned(cx, left, right, true);
|
||||
check_to_owned(cx, right, left, false);
|
||||
}
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name) = get_item_name(cx, expr) {
|
||||
let name = name.as_str();
|
||||
if name == "eq"
|
||||
|| name == "ne"
|
||||
|| name == "is_nan"
|
||||
|| name.starts_with("eq_")
|
||||
|| name.ends_with("_eq")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
|
||||
let (lint, msg) = get_lint_and_message(
|
||||
is_named_constant(cx, left) || is_named_constant(cx, right),
|
||||
is_comparing_arrays,
|
||||
);
|
||||
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
|
||||
let lhs = Sugg::hir(cx, left, "..");
|
||||
let rhs = Sugg::hir(cx, right, "..");
|
||||
|
||||
if !is_comparing_arrays {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider comparing them within some margin of error",
|
||||
format!(
|
||||
"({}).abs() {} error_margin",
|
||||
lhs - rhs,
|
||||
if op == BinOpKind::Eq { '<' } else { '>' }
|
||||
),
|
||||
Applicability::HasPlaceholders, // snippet
|
||||
);
|
||||
}
|
||||
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
|
||||
});
|
||||
} else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
check_binary(cx, expr, cmp, left, right);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -744,3 +695,74 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_binary(
|
||||
cx: &LateContext<'a>,
|
||||
expr: &Expr<'_>,
|
||||
cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
|
||||
left: &'a Expr<'_>,
|
||||
right: &'a Expr<'_>,
|
||||
) {
|
||||
let op = cmp.node;
|
||||
if op.is_comparison() {
|
||||
check_nan(cx, left, expr);
|
||||
check_nan(cx, right, expr);
|
||||
check_to_owned(cx, left, right, true);
|
||||
check_to_owned(cx, right, left, false);
|
||||
}
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name) = get_item_name(cx, expr) {
|
||||
let name = name.as_str();
|
||||
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
|
||||
let (lint, msg) = get_lint_and_message(
|
||||
is_named_constant(cx, left) || is_named_constant(cx, right),
|
||||
is_comparing_arrays,
|
||||
);
|
||||
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
|
||||
let lhs = Sugg::hir(cx, left, "..");
|
||||
let rhs = Sugg::hir(cx, right, "..");
|
||||
|
||||
if !is_comparing_arrays {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider comparing them within some margin of error",
|
||||
format!(
|
||||
"({}).abs() {} error_margin",
|
||||
lhs - rhs,
|
||||
if op == BinOpKind::Eq { '<' } else { '>' }
|
||||
),
|
||||
Applicability::HasPlaceholders, // snippet
|
||||
);
|
||||
}
|
||||
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
|
||||
});
|
||||
} else if op == BinOpKind::Rem {
|
||||
if is_integer_const(cx, right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
|
||||
if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
|
||||
if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
|
||||
span_lint(
|
||||
cx,
|
||||
MODULO_ONE,
|
||||
expr.span,
|
||||
"any number modulo -1 will panic/overflow or result in 0",
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,19 @@
|
||||
use crate::utils::qualify_min_const_fn::is_min_const_fn;
|
||||
use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
|
||||
use crate::utils::{
|
||||
fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
///
|
||||
@ -69,7 +74,18 @@ declare_clippy_lint! {
|
||||
"Lint functions definitions that could be made `const fn`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||
impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
|
||||
|
||||
pub struct MissingConstForFn {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl MissingConstForFn {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
fn check_fn(
|
||||
@ -81,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let def_id = cx.tcx.hir().local_def_id(hir_id);
|
||||
|
||||
if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
|
||||
@ -99,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
let has_const_generic_params = generics
|
||||
.params
|
||||
.iter()
|
||||
.any(|param| matches!(param.kind, GenericParamKind::Const{ .. }));
|
||||
.any(|param| matches!(param.kind, GenericParamKind::Const { .. }));
|
||||
|
||||
if already_const(header) || has_const_generic_params {
|
||||
return;
|
||||
@ -126,6 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
|
||||
|
@ -2,7 +2,7 @@
|
||||
// *rustc*'s
|
||||
// [`missing_doc`].
|
||||
//
|
||||
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246
|
||||
// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415
|
||||
//
|
||||
|
||||
use crate::utils::span_lint;
|
||||
@ -63,14 +63,21 @@ impl MissingDoc {
|
||||
if let Some(meta) = list.get(0);
|
||||
if let Some(name) = meta.ident();
|
||||
then {
|
||||
name.as_str() == "include"
|
||||
name.name == sym::include
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_missing_docs_attrs(&self, cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
|
||||
fn check_missing_docs_attrs(
|
||||
&self,
|
||||
cx: &LateContext<'_>,
|
||||
attrs: &[ast::Attribute],
|
||||
sp: Span,
|
||||
article: &'static str,
|
||||
desc: &'static str,
|
||||
) {
|
||||
// If we're building a test harness, then warning about
|
||||
// documentation is probably not really relevant right now.
|
||||
if cx.sess().opts.test {
|
||||
@ -94,7 +101,7 @@ impl MissingDoc {
|
||||
cx,
|
||||
MISSING_DOCS_IN_PRIVATE_ITEMS,
|
||||
sp,
|
||||
&format!("missing documentation for {}", desc),
|
||||
&format!("missing documentation for {} {}", article, desc),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -120,13 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
}
|
||||
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) {
|
||||
self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate");
|
||||
self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate");
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
|
||||
let desc = match it.kind {
|
||||
hir::ItemKind::Const(..) => "a constant",
|
||||
hir::ItemKind::Enum(..) => "an enum",
|
||||
match it.kind {
|
||||
hir::ItemKind::Fn(..) => {
|
||||
// ignore main()
|
||||
if it.ident.name == sym::main {
|
||||
@ -136,34 +141,35 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
return;
|
||||
}
|
||||
}
|
||||
"a function"
|
||||
},
|
||||
hir::ItemKind::Mod(..) => "a module",
|
||||
hir::ItemKind::Static(..) => "a static",
|
||||
hir::ItemKind::Struct(..) => "a struct",
|
||||
hir::ItemKind::Trait(..) => "a trait",
|
||||
hir::ItemKind::TraitAlias(..) => "a trait alias",
|
||||
hir::ItemKind::TyAlias(..) => "a type alias",
|
||||
hir::ItemKind::Union(..) => "a union",
|
||||
hir::ItemKind::OpaqueTy(..) => "an existential type",
|
||||
hir::ItemKind::Const(..)
|
||||
| hir::ItemKind::Enum(..)
|
||||
| hir::ItemKind::Mod(..)
|
||||
| hir::ItemKind::Static(..)
|
||||
| hir::ItemKind::Struct(..)
|
||||
| hir::ItemKind::Trait(..)
|
||||
| hir::ItemKind::TraitAlias(..)
|
||||
| hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Union(..)
|
||||
| hir::ItemKind::OpaqueTy(..) => {},
|
||||
hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod(..)
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::GlobalAsm(..)
|
||||
| hir::ItemKind::Impl { .. }
|
||||
| hir::ItemKind::Use(..) => return,
|
||||
};
|
||||
|
||||
self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
|
||||
let def_id = cx.tcx.hir().local_def_id(it.hir_id);
|
||||
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
||||
|
||||
self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
|
||||
let desc = match trait_item.kind {
|
||||
hir::TraitItemKind::Const(..) => "an associated constant",
|
||||
hir::TraitItemKind::Fn(..) => "a trait method",
|
||||
hir::TraitItemKind::Type(..) => "an associated type",
|
||||
};
|
||||
let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id);
|
||||
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
||||
|
||||
self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
|
||||
self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
@ -178,21 +184,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
},
|
||||
}
|
||||
|
||||
let desc = match impl_item.kind {
|
||||
hir::ImplItemKind::Const(..) => "an associated constant",
|
||||
hir::ImplItemKind::Fn(..) => "a method",
|
||||
hir::ImplItemKind::TyAlias(_) => "an associated type",
|
||||
};
|
||||
self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
|
||||
let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id());
|
||||
self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc);
|
||||
}
|
||||
|
||||
fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) {
|
||||
if !sf.is_positional() {
|
||||
self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
|
||||
self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
|
||||
self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant");
|
||||
self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant");
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
||||
| hir::ItemKind::Union(..)
|
||||
| hir::ItemKind::OpaqueTy(..)
|
||||
| hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod(..)
|
||||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::Impl { .. }
|
||||
| hir::ItemKind::Use(..) => {},
|
||||
};
|
||||
|
@ -3,10 +3,7 @@
|
||||
//! This lint is **warn** by default
|
||||
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
|
||||
@ -72,7 +69,7 @@ declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
use self::Expression::{Bool, RetBool};
|
||||
if let Some((ref pred, ref then_block, Some(ref else_expr))) = higher::if_block(&e) {
|
||||
if let ExprKind::If(ref pred, ref then_block, Some(ref else_expr)) = e.kind {
|
||||
let reduce = |ret, not| {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
|
||||
@ -198,13 +195,9 @@ struct ExpressionInfoWithSpan {
|
||||
}
|
||||
|
||||
fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
|
||||
if_chain! {
|
||||
if let ExprKind::Unary(unop, operand) = e.kind;
|
||||
if let UnOp::UnNot = unop;
|
||||
then {
|
||||
return (true, operand.span);
|
||||
}
|
||||
};
|
||||
if let ExprKind::Unary(UnOp::UnNot, operand) = e.kind {
|
||||
return (true, operand.span);
|
||||
}
|
||||
(false, e.span)
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! This lint is **warn** by default
|
||||
|
||||
use crate::utils::{snippet_opt, span_lint_and_then};
|
||||
use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, HirId, Item, Mutability, Pat, PatKind};
|
||||
@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for address of operations (`&`) that are going to
|
||||
@ -47,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind {
|
||||
if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
|
||||
if let [Adjustment {
|
||||
kind: Adjust::Deref(_), ..
|
||||
@ -62,8 +61,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
|
||||
cx,
|
||||
NEEDLESS_BORROW,
|
||||
e.span,
|
||||
"this expression borrows a reference that is immediately dereferenced \
|
||||
&format!(
|
||||
"this expression borrows a reference (`&{}`) that is immediately dereferenced \
|
||||
by the compiler",
|
||||
ty
|
||||
),
|
||||
|diag| {
|
||||
if let Some(snippet) = snippet_opt(cx, inner.span) {
|
||||
diag.span_suggestion(
|
||||
@ -113,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
|
||||
}
|
||||
|
||||
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if item.attrs.iter().any(|a| a.has_name(sym::automatically_derived)) {
|
||||
if is_automatically_derived(item.attrs) {
|
||||
debug_assert!(self.derived_item.is_none());
|
||||
self.derived_item = Some(item.hir_id);
|
||||
}
|
||||
|
@ -8,11 +8,12 @@ use rustc_ast::ast::Attribute;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Node, PatKind, QPath, TyKind};
|
||||
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, TypeFoldable};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_trait_selection::traits;
|
||||
@ -90,9 +91,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
|
||||
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
|
||||
ItemKind::Trait(..))
|
||||
{
|
||||
if matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -152,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
||||
// Ignore `self`s.
|
||||
if idx == 0 {
|
||||
if let PatKind::Binding(.., ident, _) = arg.pat.kind {
|
||||
if ident.as_str() == "self" {
|
||||
if ident.name == kw::SelfLower {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
232
clippy_lints/src/needless_question_mark.rs
Normal file
232
clippy_lints/src/needless_question_mark.rs
Normal file
@ -0,0 +1,232 @@
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
use crate::utils;
|
||||
use if_chain::if_chain;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Suggests alternatives for useless applications of `?` in terminating expressions
|
||||
///
|
||||
/// **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct TO {
|
||||
/// magic: Option<usize>,
|
||||
/// }
|
||||
///
|
||||
/// fn f(to: TO) -> Option<usize> {
|
||||
/// Some(to.magic?)
|
||||
/// }
|
||||
///
|
||||
/// struct TR {
|
||||
/// magic: Result<usize, bool>,
|
||||
/// }
|
||||
///
|
||||
/// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
|
||||
/// tr.and_then(|t| Ok(t.magic?))
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct TO {
|
||||
/// magic: Option<usize>,
|
||||
/// }
|
||||
///
|
||||
/// fn f(to: TO) -> Option<usize> {
|
||||
/// to.magic
|
||||
/// }
|
||||
///
|
||||
/// struct TR {
|
||||
/// magic: Result<usize, bool>,
|
||||
/// }
|
||||
///
|
||||
/// fn g(tr: Result<TR, bool>) -> Result<usize, bool> {
|
||||
/// tr.and_then(|t| t.magic)
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_QUESTION_MARK,
|
||||
complexity,
|
||||
"Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
|
||||
}
|
||||
|
||||
const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
|
||||
const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);
|
||||
|
||||
pub struct NeedlessQuestionMark {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl NeedlessQuestionMark {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SomeOkCall<'a> {
|
||||
SomeCall(&'a Expr<'a>, &'a Expr<'a>),
|
||||
OkCall(&'a Expr<'a>, &'a Expr<'a>),
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for NeedlessQuestionMark {
|
||||
/*
|
||||
* The question mark operator is compatible with both Result<T, E> and Option<T>,
|
||||
* from Rust 1.13 and 1.22 respectively.
|
||||
*/
|
||||
|
||||
/*
|
||||
* What do we match:
|
||||
* Expressions that look like this:
|
||||
* Some(option?), Ok(result?)
|
||||
*
|
||||
* Where do we match:
|
||||
* Last expression of a body
|
||||
* Return statement
|
||||
* A body's value (single line closure)
|
||||
*
|
||||
* What do we not match:
|
||||
* Implicit calls to `from(..)` on the error value
|
||||
*/
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
let e = match &expr.kind {
|
||||
ExprKind::Ret(Some(e)) => e,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
|
||||
emit_lint(cx, &ok_some_call);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
|
||||
// Function / Closure block
|
||||
let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind {
|
||||
block.expr
|
||||
} else {
|
||||
// Single line closure
|
||||
Some(&body.value)
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let Some(expr) = expr_opt;
|
||||
if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
|
||||
then {
|
||||
emit_lint(cx, &ok_some_call);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
|
||||
let (entire_expr, inner_expr) = match expr {
|
||||
SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner),
|
||||
};
|
||||
|
||||
utils::span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_QUESTION_MARK,
|
||||
entire_expr.span,
|
||||
"Question mark operator is useless here",
|
||||
"try",
|
||||
format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn is_some_or_ok_call<'a>(
|
||||
nqml: &NeedlessQuestionMark,
|
||||
cx: &'a LateContext<'_>,
|
||||
expr: &'a Expr<'_>,
|
||||
) -> Option<SomeOkCall<'a>> {
|
||||
if_chain! {
|
||||
// Check outer expression matches CALL_IDENT(ARGUMENT) format
|
||||
if let ExprKind::Call(path, args) = &expr.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
|
||||
if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
|
||||
|
||||
// Extract inner expression from ARGUMENT
|
||||
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
|
||||
if let ExprKind::Call(called, args) = &inner_expr_with_q.kind;
|
||||
if args.len() == 1;
|
||||
|
||||
if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind;
|
||||
then {
|
||||
// Extract inner expr type from match argument generated by
|
||||
// question mark operator
|
||||
let inner_expr = &args[0];
|
||||
|
||||
let inner_ty = cx.typeck_results().expr_ty(inner_expr);
|
||||
let outer_ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
// Check if outer and inner type are Option
|
||||
let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type);
|
||||
let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type);
|
||||
|
||||
// Check for Option MSRV
|
||||
let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
|
||||
if outer_is_some && inner_is_some && meets_option_msrv {
|
||||
return Some(SomeOkCall::SomeCall(expr, inner_expr));
|
||||
}
|
||||
|
||||
// Check if outer and inner type are Result
|
||||
let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type);
|
||||
let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type);
|
||||
|
||||
// Additional check: if the error type of the Result can be converted
|
||||
// via the From trait, then don't match
|
||||
let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
|
||||
|
||||
// Must meet Result MSRV
|
||||
let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
|
||||
if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
|
||||
return Some(SomeOkCall::OkCall(expr, inner_expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool {
|
||||
return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr);
|
||||
}
|
||||
|
||||
fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
|
||||
if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
|
||||
if let Some(variant_id) = cx.tcx.parent(id) {
|
||||
return variant_id == ok_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
|
||||
if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
|
||||
if let Some(variant_id) = cx.tcx.parent(id) {
|
||||
return variant_id == some_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
@ -8,6 +8,9 @@ declare_clippy_lint! {
|
||||
/// **What it does:** Checks for needlessly including a base struct on update
|
||||
/// when all fields are changed anyway.
|
||||
///
|
||||
/// This lint is not applied to structs marked with
|
||||
/// [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).
|
||||
///
|
||||
/// **Why is this bad?** This will cost resources (because the base has to be
|
||||
/// somewhere), and make the code less readable.
|
||||
///
|
||||
@ -49,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
|
||||
if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(def, _) = ty.kind() {
|
||||
if fields.len() == def.non_enum_variant().fields.len() {
|
||||
if fields.len() == def.non_enum_variant().fields.len()
|
||||
&& !def.variants[0_usize.into()].is_field_list_non_exhaustive()
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_UPDATE,
|
||||
|
@ -60,9 +60,9 @@ impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
|
||||
impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if let hir::ItemKind::Impl {
|
||||
if let hir::ItemKind::Impl(hir::Impl {
|
||||
of_trait: None, items, ..
|
||||
} = item.kind
|
||||
}) = item.kind
|
||||
{
|
||||
for assoc_item in items {
|
||||
if let hir::AssocItemKind::Fn { has_self: false } = assoc_item.kind {
|
||||
|
@ -7,7 +7,7 @@ use std::ptr;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{
|
||||
BodyId, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
|
||||
BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
|
||||
};
|
||||
use rustc_infer::traits::specialization_graph;
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
@ -275,10 +275,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
||||
let item = cx.tcx.hir().expect_item(item_hir_id);
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Impl {
|
||||
ItemKind::Impl(Impl {
|
||||
of_trait: Some(of_trait_ref),
|
||||
..
|
||||
} => {
|
||||
}) => {
|
||||
if_chain! {
|
||||
// Lint a trait impl item only when the definition is a generic type,
|
||||
// assuming a assoc const is not meant to be a interior mutable type.
|
||||
@ -317,7 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
||||
}
|
||||
}
|
||||
},
|
||||
ItemKind::Impl { of_trait: None, .. } => {
|
||||
ItemKind::Impl(Impl { of_trait: None, .. }) => {
|
||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||
// Normalize assoc types originated from generic params.
|
||||
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||
|
@ -409,11 +409,10 @@ fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
|
||||
if let Some(b2) = b_chars.next() {
|
||||
// check if there's just one character inserted
|
||||
return a != b2 || a_chars.ne(b_chars);
|
||||
} else {
|
||||
// tuple
|
||||
// ntuple
|
||||
return true;
|
||||
}
|
||||
// tuple
|
||||
// ntuple
|
||||
return true;
|
||||
}
|
||||
// for item in items
|
||||
true
|
||||
|
@ -66,7 +66,7 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
|
||||
/// Returns true iff the given expression is the result of calling `Result::ok`
|
||||
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
|
||||
if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind {
|
||||
path.ident.name.to_ident_string() == "ok"
|
||||
path.ident.name.as_str() == "ok"
|
||||
&& is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type)
|
||||
} else {
|
||||
false
|
||||
@ -109,25 +109,30 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
|
||||
/// it in curly braces. Otherwise, we don't.
|
||||
fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
|
||||
let mut should_wrap = false;
|
||||
|
||||
if let Some(Expr {
|
||||
kind:
|
||||
ExprKind::Match(
|
||||
_,
|
||||
arms,
|
||||
MatchSource::IfDesugar {
|
||||
contains_else_clause: true,
|
||||
}
|
||||
| MatchSource::IfLetDesugar {
|
||||
MatchSource::IfLetDesugar {
|
||||
contains_else_clause: true,
|
||||
},
|
||||
),
|
||||
..
|
||||
}) = parent.expr
|
||||
{
|
||||
expr.hir_id == arms[1].body.hir_id
|
||||
} else {
|
||||
false
|
||||
should_wrap = expr.hir_id == arms[1].body.hir_id;
|
||||
} else if let Some(Expr {
|
||||
kind: ExprKind::If(_, _, Some(else_clause)),
|
||||
..
|
||||
}) = parent.expr
|
||||
{
|
||||
should_wrap = expr.hir_id == else_clause.hir_id;
|
||||
}
|
||||
|
||||
should_wrap
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,16 @@
|
||||
use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then};
|
||||
use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
|
||||
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
|
||||
///
|
||||
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
|
||||
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
@ -22,9 +20,15 @@ declare_clippy_lint! {
|
||||
/// panic!("error");
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn result_without_panic() -> Result<bool, String> {
|
||||
/// Err(String::from("error"))
|
||||
/// }
|
||||
/// ```
|
||||
pub PANIC_IN_RESULT_FN,
|
||||
restriction,
|
||||
"functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
|
||||
"functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]);
|
||||
@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
|
||||
}
|
||||
}
|
||||
|
||||
struct FindPanicUnimplementedUnreachable {
|
||||
result: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if ["unimplemented", "unreachable", "panic", "todo"]
|
||||
.iter()
|
||||
.any(|fun| is_expn_of(expr.span, fun).is_some())
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
// and check sub-expressions
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
||||
let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() };
|
||||
panics.visit_expr(&body.value);
|
||||
if !panics.result.is_empty() {
|
||||
let panics = find_macro_calls(
|
||||
&[
|
||||
"unimplemented",
|
||||
"unreachable",
|
||||
"panic",
|
||||
"todo",
|
||||
"assert",
|
||||
"assert_eq",
|
||||
"assert_ne",
|
||||
"debug_assert",
|
||||
"debug_assert_eq",
|
||||
"debug_assert_ne",
|
||||
],
|
||||
body,
|
||||
);
|
||||
if !panics.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PANIC_IN_RESULT_FN,
|
||||
impl_span,
|
||||
"used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`",
|
||||
"used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`",
|
||||
move |diag| {
|
||||
diag.help(
|
||||
"`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
|
||||
"`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
|
||||
);
|
||||
diag.span_note(panics.result, "return Err() instead of panicking");
|
||||
diag.span_note(panics, "return Err() instead of panicking");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
pub UNREACHABLE,
|
||||
restriction,
|
||||
"`unreachable!` should not be present in production code"
|
||||
"usage of the `unreachable!` macro"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
|
||||
@ -85,12 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
||||
} else if is_expn_of(expr.span, "todo").is_some() {
|
||||
span_lint(cx, TODO, span, "`todo` should not be present in production code");
|
||||
} else if is_expn_of(expr.span, "unreachable").is_some() {
|
||||
span_lint(
|
||||
cx,
|
||||
UNREACHABLE,
|
||||
span,
|
||||
"`unreachable` should not be present in production code",
|
||||
);
|
||||
span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro");
|
||||
} else if is_expn_of(expr.span, "panic").is_some() {
|
||||
span_lint(cx, PANIC, span, "`panic` should not be present in production code");
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::utils::{is_automatically_derived, span_lint_hir};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_hir::{Impl, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
@ -34,7 +34,7 @@ declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]);
|
||||
impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if_chain! {
|
||||
if let ItemKind::Impl{ of_trait: Some(ref trait_ref), items: impl_items, .. } = item.kind;
|
||||
if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind;
|
||||
if !is_automatically_derived(&*item.attrs);
|
||||
if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
|
||||
if trait_ref.path.res.def_id() == eq_trait;
|
||||
|
@ -6,7 +6,7 @@ use rustc_ast::attr;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind};
|
||||
use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
@ -63,7 +63,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// **Why is this bad?** Arguments passed by value might result in an unnecessary
|
||||
/// shallow copy, taking up more space in the stack and requiring a call to
|
||||
/// `memcpy`, which which can be expensive.
|
||||
/// `memcpy`, which can be expensive.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
@ -244,9 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
|
||||
if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
|
||||
ItemKind::Trait(..))
|
||||
{
|
||||
if matches!(
|
||||
item.kind,
|
||||
ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ use crate::utils::{
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind,
|
||||
Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
|
||||
BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, Impl, ImplItem, ImplItemKind, Item,
|
||||
ItemKind, Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
@ -132,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
|
||||
let parent_item = cx.tcx.hir().get_parent_item(item.hir_id);
|
||||
if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
|
||||
if let ItemKind::Impl { of_trait: Some(_), .. } = it.kind {
|
||||
if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = it.kind {
|
||||
return; // ignore trait impls
|
||||
}
|
||||
}
|
||||
@ -182,20 +182,6 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
||||
|
||||
if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
|
||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) {
|
||||
let mut ty_snippet = None;
|
||||
if_chain! {
|
||||
if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
|
||||
if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
|
||||
then {
|
||||
let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
|
||||
GenericArg::Type(ty) => Some(ty),
|
||||
_ => None,
|
||||
}).collect();
|
||||
if types.len() == 1 {
|
||||
ty_snippet = snippet_opt(cx, types[0].span);
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
@ -204,7 +190,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
||||
"writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
|
||||
with non-Vec-based slices.",
|
||||
|diag| {
|
||||
if let Some(ref snippet) = ty_snippet {
|
||||
if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) {
|
||||
diag.span_suggestion(
|
||||
arg.span,
|
||||
"change this to",
|
||||
@ -247,6 +233,33 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if match_type(cx, ty, &paths::PATH_BUF) {
|
||||
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
PTR_ARG,
|
||||
arg.span,
|
||||
"writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
arg.span,
|
||||
"change this to",
|
||||
"&Path".into(),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
for (clonespan, suggestion) in spans {
|
||||
diag.span_suggestion_short(
|
||||
clonespan,
|
||||
&snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
|
||||
Cow::Owned(format!("change `{}` to", x))
|
||||
}),
|
||||
suggestion.into(),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if match_type(cx, ty, &paths::COW) {
|
||||
if_chain! {
|
||||
if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
|
||||
@ -309,6 +322,23 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
||||
}
|
||||
}
|
||||
|
||||
fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> {
|
||||
if_chain! {
|
||||
if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
|
||||
if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
|
||||
let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
|
||||
GenericArg::Type(ty) => Some(ty),
|
||||
_ => None,
|
||||
}).collect();
|
||||
if types.len() == 1;
|
||||
then {
|
||||
snippet_opt(cx, types[0].span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
|
||||
if let TyKind::Rptr(ref lt, ref m) = ty.kind {
|
||||
Some((lt, m.mutbl, ty.span))
|
||||
|
@ -8,7 +8,7 @@ use rustc_span::sym;
|
||||
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
eq_expr_value, higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
|
||||
eq_expr_value, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
|
||||
span_lint_and_sugg,
|
||||
};
|
||||
|
||||
@ -50,7 +50,7 @@ impl QuestionMark {
|
||||
/// If it matches, it will suggest to use the question mark operator instead
|
||||
fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some((if_expr, body, else_)) = higher::if_block(&expr);
|
||||
if let ExprKind::If(if_expr, body, else_) = &expr.kind;
|
||||
if let ExprKind::MethodCall(segment, _, args, _) = &if_expr.kind;
|
||||
if segment.ident.name == sym!(is_none);
|
||||
if Self::expression_returns_none(cx, body);
|
||||
@ -176,8 +176,7 @@ impl QuestionMark {
|
||||
if block.stmts.len() == 1;
|
||||
if let Some(expr) = block.stmts.iter().last();
|
||||
if let StmtKind::Semi(ref expr) = expr.kind;
|
||||
if let ExprKind::Ret(ret_expr) = expr.kind;
|
||||
if let Some(ret_expr) = ret_expr;
|
||||
if let ExprKind::Ret(Some(ret_expr)) = expr.kind;
|
||||
|
||||
then {
|
||||
return Some(ret_expr);
|
||||
|
@ -3,9 +3,10 @@ use if_chain::if_chain;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
@ -13,8 +14,8 @@ use std::cmp::Ordering;
|
||||
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability,
|
||||
span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt,
|
||||
snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{higher, SpanlessEq};
|
||||
|
||||
@ -160,7 +161,20 @@ declare_clippy_lint! {
|
||||
"manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Ranges => [
|
||||
const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
|
||||
|
||||
pub struct Ranges {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Ranges {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Ranges => [
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
RANGE_PLUS_ONE,
|
||||
RANGE_MINUS_ONE,
|
||||
@ -175,7 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
check_range_zip_with_len(cx, path, args, expr.span);
|
||||
},
|
||||
ExprKind::Binary(ref op, ref l, ref r) => {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr.span);
|
||||
if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -184,9 +200,15 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
check_inclusive_range_minus_one(cx, expr);
|
||||
check_reversed_empty_range(cx, expr);
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) {
|
||||
fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) {
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let span = expr.span;
|
||||
let combine_and = match op {
|
||||
BinOpKind::And | BinOpKind::BitAnd => true,
|
||||
BinOpKind::Or | BinOpKind::BitOr => false,
|
||||
|
@ -390,7 +390,10 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
|
||||
let local = place.local;
|
||||
|
||||
if local == self.used.0
|
||||
&& !matches!(ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_))
|
||||
&& !matches!(
|
||||
ctx,
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
|
||||
)
|
||||
{
|
||||
self.used.1 = true;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
path: &'tcx hir::Path<'tcx>,
|
||||
count: usize,
|
||||
};
|
||||
}
|
||||
impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
||||
fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
|
||||
hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
||||
}
|
||||
};
|
||||
}
|
||||
let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 };
|
||||
closure_usage_count.visit_block(block);
|
||||
closure_usage_count.count
|
||||
|
135
clippy_lints/src/redundant_else.rs
Normal file
135
clippy_lints/src/redundant_else.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use crate::utils::span_lint_and_help;
|
||||
use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_ast::visit::{walk_expr, Visitor};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `else` blocks that can be removed without changing semantics.
|
||||
///
|
||||
/// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.
|
||||
///
|
||||
/// **Known problems:** Some may prefer to keep the `else` block for clarity.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn my_func(count: u32) {
|
||||
/// if count == 0 {
|
||||
/// print!("Nothing to do");
|
||||
/// return;
|
||||
/// } else {
|
||||
/// print!("Moving on...");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn my_func(count: u32) {
|
||||
/// if count == 0 {
|
||||
/// print!("Nothing to do");
|
||||
/// return;
|
||||
/// }
|
||||
/// print!("Moving on...");
|
||||
/// }
|
||||
/// ```
|
||||
pub REDUNDANT_ELSE,
|
||||
pedantic,
|
||||
"`else` branch that can be removed without changing semantics"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]);
|
||||
|
||||
impl EarlyLintPass for RedundantElse {
|
||||
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) {
|
||||
if in_external_macro(cx.sess, stmt.span) {
|
||||
return;
|
||||
}
|
||||
// Only look at expressions that are a whole statement
|
||||
let expr: &Expr = match &stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
|
||||
_ => return,
|
||||
};
|
||||
// if else
|
||||
let (mut then, mut els): (&Block, &Expr) = match &expr.kind {
|
||||
ExprKind::If(_, then, Some(els)) => (then, els),
|
||||
_ => return,
|
||||
};
|
||||
loop {
|
||||
if !BreakVisitor::default().check_block(then) {
|
||||
// then block does not always break
|
||||
return;
|
||||
}
|
||||
match &els.kind {
|
||||
// else if else
|
||||
ExprKind::If(_, next_then, Some(next_els)) => {
|
||||
then = next_then;
|
||||
els = next_els;
|
||||
continue;
|
||||
},
|
||||
// else if without else
|
||||
ExprKind::If(..) => return,
|
||||
// done
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
REDUNDANT_ELSE,
|
||||
els.span,
|
||||
"redundant else block",
|
||||
None,
|
||||
"remove the `else` block and move the contents out",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Call `check` functions to check if an expression always breaks control flow
|
||||
#[derive(Default)]
|
||||
struct BreakVisitor {
|
||||
is_break: bool,
|
||||
}
|
||||
|
||||
impl<'ast> Visitor<'ast> for BreakVisitor {
|
||||
fn visit_block(&mut self, block: &'ast Block) {
|
||||
self.is_break = match block.stmts.as_slice() {
|
||||
[.., last] => self.check_stmt(last),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'ast Expr) {
|
||||
self.is_break = match expr.kind {
|
||||
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true,
|
||||
ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)),
|
||||
ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els),
|
||||
ExprKind::If(_, _, None)
|
||||
// ignore loops for simplicity
|
||||
| ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false,
|
||||
_ => {
|
||||
walk_expr(self, expr);
|
||||
return;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl BreakVisitor {
|
||||
fn check<T>(&mut self, item: T, visit: fn(&mut Self, T)) -> bool {
|
||||
visit(self, item);
|
||||
std::mem::replace(&mut self.is_break, false)
|
||||
}
|
||||
|
||||
fn check_block(&mut self, block: &Block) -> bool {
|
||||
self.check(block, Self::visit_block)
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, expr: &Expr) -> bool {
|
||||
self.check(expr, Self::visit_expr)
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, stmt: &Stmt) -> bool {
|
||||
self.check(stmt, Self::visit_stmt)
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
use crate::utils::span_lint_and_sugg;
|
||||
use crate::utils::{meets_msrv, span_lint_and_sugg};
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for fields in struct literals where shorthands
|
||||
@ -33,10 +36,25 @@ declare_clippy_lint! {
|
||||
"checks for fields in struct literals where shorthands could be used"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
||||
pub struct RedundantFieldNames {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl RedundantFieldNames {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
|
||||
|
||||
impl EarlyLintPass for RedundantFieldNames {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if in_external_macro(cx.sess, expr.span) {
|
||||
return;
|
||||
}
|
||||
@ -64,4 +82,5 @@ impl EarlyLintPass for RedundantFieldNames {
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
67
clippy_lints/src/redundant_slicing.rs
Normal file
67
clippy_lints/src/redundant_slicing.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::{lint::in_external_macro, ty::TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for redundant slicing expressions which use the full range, and
|
||||
/// do not change the type.
|
||||
///
|
||||
/// **Why is this bad?** It unnecessarily adds complexity to the expression.
|
||||
///
|
||||
/// **Known problems:** If the type being sliced has an implementation of `Index<RangeFull>`
|
||||
/// that actually changes anything then it can't be removed. However, this would be surprising
|
||||
/// to people reading the code and should have a note with it.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn get_slice(x: &[u32]) -> &[u32] {
|
||||
/// &x[..]
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// fn get_slice(x: &[u32]) -> &[u32] {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
pub REDUNDANT_SLICING,
|
||||
complexity,
|
||||
"redundant slicing of the whole range of a type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
|
||||
|
||||
impl LateLintPass<'_> for RedundantSlicing {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::AddrOf(_, _, addressee) = expr.kind;
|
||||
if let ExprKind::Index(indexed, range) = addressee.kind;
|
||||
if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
|
||||
if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed));
|
||||
then {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_SLICING,
|
||||
expr.span,
|
||||
"redundant slicing of the whole range",
|
||||
"use the original slice instead",
|
||||
hint,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
use crate::utils::{snippet, span_lint_and_then};
|
||||
use crate::utils::{meets_msrv, snippet, span_lint_and_then};
|
||||
use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
|
||||
@ -29,7 +32,18 @@ declare_clippy_lint! {
|
||||
"Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
|
||||
}
|
||||
|
||||
declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
|
||||
pub struct RedundantStaticLifetimes {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl RedundantStaticLifetimes {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
|
||||
|
||||
impl RedundantStaticLifetimes {
|
||||
// Recursively visit types
|
||||
@ -84,6 +98,10 @@ impl RedundantStaticLifetimes {
|
||||
|
||||
impl EarlyLintPass for RedundantStaticLifetimes {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !item.span.from_expansion() {
|
||||
if let ItemKind::Const(_, ref var_type, _) = item.kind {
|
||||
self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
|
||||
@ -96,4 +114,6 @@ impl EarlyLintPass for RedundantStaticLifetimes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
|
||||
use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -12,7 +13,7 @@ declare_clippy_lint! {
|
||||
/// **Why is this bad?** Since `&` is Copy, it's useless to have a
|
||||
/// reference on `Option<&T>`.
|
||||
///
|
||||
/// **Known problems:** It may be irrevelent to use this lint on
|
||||
/// **Known problems:** It may be irrelevant to use this lint on
|
||||
/// public API code as it will make a breaking change to apply it.
|
||||
///
|
||||
/// **Example:**
|
||||
@ -41,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
|
||||
if let Some(res) = last.res;
|
||||
if let Some(def_id) = res.opt_def_id();
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym!(option_type), def_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::option_type, def_id);
|
||||
if let Some(ref params) = last_path_segment(qpath).args ;
|
||||
if !params.parenthesized;
|
||||
if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg {
|
||||
|
@ -131,7 +131,16 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
||||
_: HirId,
|
||||
) {
|
||||
match kind {
|
||||
FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty),
|
||||
FnKind::Closure(_) => {
|
||||
// when returning without value in closure, replace this `return`
|
||||
// with an empty block to prevent invalid suggestion (see #6501)
|
||||
let replacement = if let ExprKind::Ret(None) = &body.value.kind {
|
||||
RetReplacement::Block
|
||||
} else {
|
||||
RetReplacement::Empty
|
||||
};
|
||||
check_final_expr(cx, &body.value, Some(body.value.span), replacement)
|
||||
},
|
||||
FnKind::ItemFn(..) | FnKind::Method(..) => {
|
||||
if let ExprKind::Block(ref block, _) = body.value.kind {
|
||||
check_block_return(cx, block);
|
||||
@ -184,6 +193,14 @@ fn check_final_expr<'tcx>(
|
||||
ExprKind::Block(ref block, _) => {
|
||||
check_block_return(cx, block);
|
||||
},
|
||||
ExprKind::If(_, then, else_clause_opt) => {
|
||||
if let ExprKind::Block(ref ifblock, _) = then.kind {
|
||||
check_block_return(cx, ifblock);
|
||||
}
|
||||
if let Some(else_clause) = else_clause_opt {
|
||||
check_final_expr(cx, else_clause, None, RetReplacement::Empty);
|
||||
}
|
||||
},
|
||||
// a match expr, check all arms
|
||||
// an if/if let expr, check both exprs
|
||||
// note, if without else is going to be a type checking error anyways
|
||||
@ -194,10 +211,7 @@ fn check_final_expr<'tcx>(
|
||||
check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
|
||||
}
|
||||
},
|
||||
MatchSource::IfDesugar {
|
||||
contains_else_clause: true,
|
||||
}
|
||||
| MatchSource::IfLetDesugar {
|
||||
MatchSource::IfLetDesugar {
|
||||
contains_else_clause: true,
|
||||
} => {
|
||||
if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind {
|
||||
@ -212,6 +226,9 @@ fn check_final_expr<'tcx>(
|
||||
}
|
||||
|
||||
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
|
||||
if ret_span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
match inner_span {
|
||||
Some(inner_span) => {
|
||||
if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user