mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 15:11:30 +00:00
Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
8010c3462d
901 changed files with 20724 additions and 5605 deletions
|
@ -1,5 +1,7 @@
|
|||
[alias]
|
||||
uitest = "test --test compile-test"
|
||||
uibless = "test --test compile-test -- -- --bless"
|
||||
bless = "test -- -- --bless"
|
||||
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
|
||||
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
|
||||
collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored"
|
||||
|
|
1
.github/workflows/clippy.yml
vendored
1
.github/workflows/clippy.yml
vendored
|
@ -25,7 +25,6 @@ env:
|
|||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
NO_FMT_TEST: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_UNSTABLE_SPARSE_REGISTRY: true
|
||||
|
||||
jobs:
|
||||
base:
|
||||
|
|
1
.github/workflows/clippy_bors.yml
vendored
1
.github/workflows/clippy_bors.yml
vendored
|
@ -11,7 +11,6 @@ env:
|
|||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
NO_FMT_TEST: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_UNSTABLE_SPARSE_REGISTRY: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
|
1
.github/workflows/clippy_dev.yml
vendored
1
.github/workflows/clippy_dev.yml
vendored
|
@ -16,7 +16,6 @@ on:
|
|||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_UNSTABLE_SPARSE_REGISTRY: true
|
||||
|
||||
jobs:
|
||||
clippy_dev:
|
||||
|
|
197
CHANGELOG.md
197
CHANGELOG.md
|
@ -10,9 +10,9 @@ document.
|
|||
|
||||
## Rust 1.70
|
||||
|
||||
Current beta, released 2023-06-01
|
||||
Current stable, released 2023-06-01
|
||||
|
||||
[149392b0...83e42a23](https://github.com/rust-lang/rust-clippy/compare/149392b0...83e42a23)
|
||||
[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -95,7 +95,7 @@ Current beta, released 2023-06-01
|
|||
[#10375](https://github.com/rust-lang/rust-clippy/pull/10375)
|
||||
* [`mem_replace_option_with_none`]: No longer lints on field expressions
|
||||
[#10594](https://github.com/rust-lang/rust-clippy/pull/10594)
|
||||
* [`items_after_statements`]: No longer lints on times from macros
|
||||
* [`items_after_statements`]: No longer lints on items from macros
|
||||
[#10542](https://github.com/rust-lang/rust-clippy/pull/10542)
|
||||
* [`print_literal`], [`write_literal`]: No longer lint strings coming from the `file!()` macro
|
||||
[#10573](https://github.com/rust-lang/rust-clippy/pull/10573)
|
||||
|
@ -135,9 +135,9 @@ Current beta, released 2023-06-01
|
|||
|
||||
## Rust 1.69
|
||||
|
||||
Current stable, released 2023-04-20
|
||||
Released 2023-04-20
|
||||
|
||||
[7f27e2e7...149392b0](https://github.com/rust-lang/rust-clippy/compare/7f27e2e7...149392b0)
|
||||
[**View 86 PRs merged since 1.68**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-03-09..2023-04-20+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -252,7 +252,7 @@ Current stable, released 2023-04-20
|
|||
|
||||
Released 2023-03-09
|
||||
|
||||
[d822110d...7f27e2e7](https://github.com/rust-lang/rust-clippy/compare/d822110d...7f27e2e7)
|
||||
[**View 85 PRs merged since 1.67**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-01-26..2023-03-09+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -399,7 +399,7 @@ Released 2023-03-09
|
|||
|
||||
Released 2023-01-26
|
||||
|
||||
[4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
|
||||
[**View 68 PRs merged since 1.66**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-12-15..2023-01-26+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -590,7 +590,8 @@ Released 2023-01-26
|
|||
|
||||
Released 2022-12-15
|
||||
|
||||
[b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1)
|
||||
[**View 93 PRs merged since 1.65**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-11-03..2022-12-15+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -761,7 +762,8 @@ Released 2022-12-15
|
|||
|
||||
Released 2022-11-03
|
||||
|
||||
[3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523)
|
||||
[**View 129 PRs merged since 1.64**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-09-22..2022-11-03+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### Important Changes
|
||||
|
||||
|
@ -905,7 +907,8 @@ Released 2022-11-03
|
|||
|
||||
Released 2022-09-22
|
||||
|
||||
[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc)
|
||||
[**View 92 PRs merged since 1.63**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-08-11..2022-09-22+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -1055,7 +1058,8 @@ Released 2022-09-22
|
|||
|
||||
Released 2022-08-11
|
||||
|
||||
[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
|
||||
[**View 100 PRs merged since 1.62**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-06-30..2022-08-11+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -1201,7 +1205,8 @@ Released 2022-08-11
|
|||
|
||||
Released 2022-06-30
|
||||
|
||||
[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
|
||||
[**View 104 PRs merged since 1.61**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-05-19..2022-06-30+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -1358,7 +1363,8 @@ Released 2022-06-30
|
|||
|
||||
Released 2022-05-19
|
||||
|
||||
[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
|
||||
[**View 93 PRs merged since 1.60**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-04-07..2022-05-19+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -1459,7 +1465,8 @@ Released 2022-05-19
|
|||
|
||||
Released 2022-04-07
|
||||
|
||||
[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
|
||||
[**View 75 PRs merged since 1.59**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-02-24..2022-04-07+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -1591,7 +1598,8 @@ Released 2022-04-07
|
|||
|
||||
Released 2022-02-24
|
||||
|
||||
[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
|
||||
[**View 63 PRs merged since 1.58**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-01-13..2022-02-24+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -1755,7 +1763,8 @@ Released 2022-02-24
|
|||
|
||||
Released 2022-01-13
|
||||
|
||||
[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
|
||||
[**View 73 PRs merged since 1.57**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-12-02..2022-01-13+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### Rust 1.58.1
|
||||
|
||||
|
@ -1876,7 +1885,8 @@ Released 2022-01-13
|
|||
|
||||
Released 2021-12-02
|
||||
|
||||
[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
|
||||
[**View 92 PRs merged since 1.56**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-10-21..2021-12-02+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -2027,7 +2037,7 @@ Released 2021-12-02
|
|||
|
||||
Released 2021-10-21
|
||||
|
||||
[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
|
||||
[**View 92 PRs merged since 1.55**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-09-09..2021-10-21+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -2093,7 +2103,7 @@ Released 2021-10-21
|
|||
|
||||
Released 2021-09-09
|
||||
|
||||
[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
|
||||
[**View 61 PRs merged since 1.54**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-07-29..2021-09-09+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### Important Changes
|
||||
|
||||
|
@ -2211,7 +2221,8 @@ Released 2021-09-09
|
|||
|
||||
Released 2021-07-29
|
||||
|
||||
[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
|
||||
[**View 68 PRs merged since 1.53**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-06-17..2021-07-29+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -2339,7 +2350,7 @@ Released 2021-07-29
|
|||
|
||||
Released 2021-06-17
|
||||
|
||||
[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
|
||||
[**View 80 PRs merged since 1.52**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-05-06..2021-06-17+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -2523,7 +2534,8 @@ Released 2021-06-17
|
|||
|
||||
Released 2021-05-06
|
||||
|
||||
[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
|
||||
[**View 113 PRs merged since 1.51**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-03-25..2021-05-06+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -2658,7 +2670,8 @@ Released 2021-05-06
|
|||
|
||||
Released 2021-03-25
|
||||
|
||||
[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
|
||||
[**View 117 PRs merged since 1.50**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2021-02-11..2021-03-25+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -2773,7 +2786,8 @@ Released 2021-03-25
|
|||
|
||||
Released 2021-02-11
|
||||
|
||||
[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
|
||||
[**View 90 PRs merged since 1.49**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-12-31..2021-02-11+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -2902,7 +2916,8 @@ Released 2021-02-11
|
|||
|
||||
Released 2020-12-31
|
||||
|
||||
[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
|
||||
[**View 85 PRs merged since 1.48**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-11-19..2020-12-31+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New Lints
|
||||
|
||||
|
@ -3008,7 +3023,7 @@ Released 2020-12-31
|
|||
|
||||
Released 2020-11-19
|
||||
|
||||
[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
|
||||
[**View 112 PRs merged since 1.47**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-10-08..2020-11-19+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -3126,7 +3141,8 @@ Released 2020-11-19
|
|||
|
||||
Released 2020-10-08
|
||||
|
||||
[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
|
||||
[**View 80 PRs merged since 1.46**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-08-27..2020-10-08+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -3228,7 +3244,8 @@ Released 2020-10-08
|
|||
|
||||
Released 2020-08-27
|
||||
|
||||
[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
|
||||
[**View 93 PRs merged since 1.45**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-07-16..2020-08-27+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -3290,7 +3307,8 @@ Released 2020-08-27
|
|||
|
||||
Released 2020-07-16
|
||||
|
||||
[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
|
||||
[**View 65 PRs merged since 1.44**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-06-04..2020-07-16+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -3367,7 +3385,8 @@ and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/565
|
|||
|
||||
Released 2020-06-04
|
||||
|
||||
[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
|
||||
[**View 88 PRs merged since 1.43**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-04-23..2020-06-04+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -3450,7 +3469,8 @@ Released 2020-06-04
|
|||
|
||||
Released 2020-04-23
|
||||
|
||||
[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
|
||||
[**View 121 PRs merged since 1.42**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-03-12..2020-04-23+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -3508,7 +3528,7 @@ Released 2020-04-23
|
|||
|
||||
Released 2020-03-12
|
||||
|
||||
[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
|
||||
[**View 106 PRs merged since 1.41**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2020-01-30..2020-03-12+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
### New lints
|
||||
|
||||
|
@ -3575,7 +3595,7 @@ Released 2020-03-12
|
|||
|
||||
Released 2020-01-30
|
||||
|
||||
[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
|
||||
[**View 107 PRs merged since 1.40**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-12-19..2020-01-30+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
* New Lints:
|
||||
* [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
|
||||
|
@ -3620,7 +3640,8 @@ Released 2020-01-30
|
|||
|
||||
Released 2019-12-19
|
||||
|
||||
[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
|
||||
[**View 69 😺 PRs merged since 1.39**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-11-07..2019-12-19+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
* New Lints:
|
||||
* [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
|
||||
|
@ -3662,7 +3683,7 @@ Released 2019-12-19
|
|||
|
||||
Released 2019-11-07
|
||||
|
||||
[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
|
||||
[**View 84 PRs merged since 1.38**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-09-26..2019-11-07+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
* New Lints:
|
||||
* [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
|
||||
|
@ -3706,7 +3727,7 @@ Released 2019-11-07
|
|||
|
||||
Released 2019-09-26
|
||||
|
||||
[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
|
||||
[**View 102 PRs merged since 1.37**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-08-15..2019-09-26+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
* New Lints:
|
||||
* [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
|
||||
|
@ -3736,7 +3757,7 @@ Released 2019-09-26
|
|||
|
||||
Released 2019-08-15
|
||||
|
||||
[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
|
||||
[**View 83 PRs merged since 1.36**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-07-04..2019-08-15+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
* New Lints:
|
||||
* [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
|
||||
|
@ -3760,7 +3781,8 @@ Released 2019-08-15
|
|||
|
||||
Released 2019-07-04
|
||||
|
||||
[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
|
||||
[**View 75 PRs merged since 1.35**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-05-20..2019-07-04+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
|
||||
* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
|
||||
|
@ -3791,7 +3813,8 @@ Released 2019-07-04
|
|||
|
||||
Released 2019-05-20
|
||||
|
||||
[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
|
||||
[**View 90 PRs merged since 1.34**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-04-10..2019-05-20+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
* New lint: `drop_bounds` to detect `T: Drop` bounds
|
||||
* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
|
||||
|
@ -3819,7 +3842,8 @@ Released 2019-05-20
|
|||
|
||||
Released 2019-04-10
|
||||
|
||||
[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
|
||||
[**View 66 PRs merged since 1.33**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-02-26..2019-04-10+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
|
||||
* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
|
||||
|
@ -3849,7 +3873,7 @@ Released 2019-04-10
|
|||
|
||||
Released 2019-02-26
|
||||
|
||||
[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
|
||||
[**View 83 PRs merged since 1.32**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2019-01-17..2019-02-26+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
|
||||
* The `rust-clippy` repository is now part of the `rust-lang` org.
|
||||
|
@ -3882,7 +3906,7 @@ Released 2019-02-26
|
|||
|
||||
Released 2019-01-17
|
||||
|
||||
[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
|
||||
[**View 106 PRs merged since 1.31**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-12-06..2019-01-17+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
|
||||
[`redundant_clone`], [`wildcard_dependencies`],
|
||||
|
@ -3912,7 +3936,8 @@ Released 2019-01-17
|
|||
|
||||
Released 2018-12-06
|
||||
|
||||
[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
|
||||
[**View 85 PRs merged since 1.30**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-10-25..2018-12-06+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
* Clippy has been relicensed under a dual MIT / Apache license.
|
||||
See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
|
||||
|
@ -3952,7 +3977,8 @@ Released 2018-12-06
|
|||
|
||||
Released 2018-10-25
|
||||
|
||||
[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
|
||||
[**View 106 PRs merged since 1.29**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2018-09-13..2018-10-25+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
|
||||
* Deprecate `assign_ops` lint
|
||||
* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
|
||||
|
@ -4628,6 +4654,7 @@ Released 2018-09-13
|
|||
[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range
|
||||
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
|
||||
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
|
||||
[`arc_with_non_send_sync`]: https://rust-lang.github.io/rust-clippy/master/index.html#arc_with_non_send_sync
|
||||
[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
|
||||
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
|
||||
[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut
|
||||
|
@ -4641,6 +4668,7 @@ Released 2018-09-13
|
|||
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
|
||||
[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
|
||||
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
|
||||
[`big_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#big_endian_bytes
|
||||
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
|
||||
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
|
||||
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
|
||||
|
@ -4735,6 +4763,7 @@ Released 2018-09-13
|
|||
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
|
||||
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
|
||||
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
|
||||
[`drain_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#drain_collect
|
||||
[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
|
||||
[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
|
||||
[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
|
||||
|
@ -4757,6 +4786,7 @@ Released 2018-09-13
|
|||
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
|
||||
[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
|
||||
[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
|
||||
[`excessive_nesting`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting
|
||||
[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
|
||||
[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
|
||||
[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
|
||||
|
@ -4811,6 +4841,7 @@ Released 2018-09-13
|
|||
[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
|
||||
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
|
||||
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
|
||||
[`host_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#host_endian_bytes
|
||||
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
|
||||
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
|
||||
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
|
||||
|
@ -4829,6 +4860,7 @@ Released 2018-09-13
|
|||
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
|
||||
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
|
||||
[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
|
||||
[`incorrect_clone_impl_on_copy_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#incorrect_clone_impl_on_copy_type
|
||||
[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
|
||||
[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
|
||||
[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
|
||||
|
@ -4879,6 +4911,7 @@ Released 2018-09-13
|
|||
[`large_futures`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_futures
|
||||
[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
|
||||
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
|
||||
[`large_stack_frames`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames
|
||||
[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
|
||||
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
|
||||
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
|
||||
|
@ -4892,6 +4925,7 @@ Released 2018-09-13
|
|||
[`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
|
||||
[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
|
||||
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
|
||||
[`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
|
||||
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
|
||||
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
|
||||
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
|
||||
|
@ -4915,6 +4949,7 @@ Released 2018-09-13
|
|||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
||||
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
|
||||
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
|
||||
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
|
||||
[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
|
||||
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
|
||||
|
@ -4947,11 +4982,13 @@ Released 2018-09-13
|
|||
[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
|
||||
[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
|
||||
[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
|
||||
[`maybe_misused_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_misused_cfg
|
||||
[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
|
||||
[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
|
||||
[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
|
||||
[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
|
||||
[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
|
||||
[`min_ident_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars
|
||||
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
|
||||
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
|
||||
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
|
||||
|
@ -5002,6 +5039,7 @@ Released 2018-09-13
|
|||
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
|
||||
[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else
|
||||
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
|
||||
[`needless_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_if
|
||||
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
|
||||
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
|
||||
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
|
||||
|
@ -5009,8 +5047,11 @@ Released 2018-09-13
|
|||
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
|
||||
[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
|
||||
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
|
||||
[`needless_pub_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pub_self
|
||||
[`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_raw_string_hashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes
|
||||
[`needless_raw_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_strings
|
||||
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
|
||||
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
|
||||
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
|
||||
|
@ -5081,6 +5122,8 @@ Released 2018-09-13
|
|||
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
|
||||
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
|
||||
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
|
||||
[`pub_with_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_with_shorthand
|
||||
[`pub_without_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_without_shorthand
|
||||
[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
|
||||
[`question_mark_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark_used
|
||||
[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
|
||||
|
@ -5106,6 +5149,7 @@ Released 2018-09-13
|
|||
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
|
||||
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
|
||||
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
|
||||
[`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations
|
||||
[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
|
||||
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
|
||||
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
|
||||
|
@ -5146,6 +5190,7 @@ Released 2018-09-13
|
|||
[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
|
||||
[`significant_drop_tightening`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_tightening
|
||||
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
|
||||
[`single_call_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn
|
||||
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
|
||||
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
|
||||
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
|
||||
|
@ -5154,6 +5199,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
|
||||
[`single_range_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_range_in_vec_init
|
||||
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
|
||||
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
|
@ -5238,6 +5284,7 @@ Released 2018-09-13
|
|||
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
|
||||
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
|
||||
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
|
||||
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
|
||||
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
||||
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
||||
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
|
||||
|
@ -5311,3 +5358,65 @@ Released 2018-09-13
|
|||
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
|
||||
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
|
||||
<!-- end autogenerated links to lint list -->
|
||||
<!-- begin autogenerated links to configuration documentation -->
|
||||
[`arithmetic-side-effects-allowed`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed
|
||||
[`arithmetic-side-effects-allowed-binary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-binary
|
||||
[`arithmetic-side-effects-allowed-unary`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed-unary
|
||||
[`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api
|
||||
[`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv
|
||||
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
|
||||
[`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold
|
||||
[`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names
|
||||
[`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline
|
||||
[`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline
|
||||
[`doc-valid-idents`]: https://doc.rust-lang.org/clippy/lint_configuration.html#doc-valid-idents
|
||||
[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold
|
||||
[`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold
|
||||
[`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold
|
||||
[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack
|
||||
[`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold
|
||||
[`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold
|
||||
[`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
|
||||
[`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold
|
||||
[`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit
|
||||
[`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit
|
||||
[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold
|
||||
[`array-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#array-size-threshold
|
||||
[`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold
|
||||
[`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold
|
||||
[`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds
|
||||
[`max-struct-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-struct-bools
|
||||
[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools
|
||||
[`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
|
||||
[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros
|
||||
[`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods
|
||||
[`disallowed-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-types
|
||||
[`unreadable-literal-lint-fractions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unreadable-literal-lint-fractions
|
||||
[`upper-case-acronyms-aggressive`]: https://doc.rust-lang.org/clippy/lint_configuration.html#upper-case-acronyms-aggressive
|
||||
[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else
|
||||
[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
|
||||
[`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces
|
||||
[`enforced-import-renames`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforced-import-renames
|
||||
[`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts
|
||||
[`enable-raw-pointer-heuristic-for-send`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enable-raw-pointer-heuristic-for-send
|
||||
[`max-suggested-slice-pattern-length`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-suggested-slice-pattern-length
|
||||
[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
|
||||
[`max-include-file-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-include-file-size
|
||||
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
|
||||
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
|
||||
[`allow-dbg-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-dbg-in-tests
|
||||
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
|
||||
[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold
|
||||
[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
|
||||
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
|
||||
[`suppress-restriction-lint-in-const`]: https://doc.rust-lang.org/clippy/lint_configuration.html#suppress-restriction-lint-in-const
|
||||
[`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items
|
||||
[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
|
||||
[`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size
|
||||
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
|
||||
[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars
|
||||
[`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold
|
||||
[`accept-comment-above-statement`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-statement
|
||||
[`accept-comment-above-attributes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#accept-comment-above-attributes
|
||||
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
|
||||
<!-- end autogenerated links to configuration documentation -->
|
||||
|
|
19
Cargo.toml
19
Cargo.toml
|
@ -27,27 +27,14 @@ tempfile = { version = "3.2", optional = true }
|
|||
termize = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
compiletest_rs = { version = "0.10", features = ["tmp"] }
|
||||
ui_test = "0.11.5"
|
||||
tester = "0.9"
|
||||
regex = "1.5"
|
||||
toml = "0.7.3"
|
||||
walkdir = "2.3"
|
||||
# This is used by the `collect-metadata` alias.
|
||||
filetime = "0.2"
|
||||
|
||||
# UI test dependencies
|
||||
clap = { version = "4.1.4", features = ["derive"] }
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
derive-new = "0.5"
|
||||
if_chain = "1.0"
|
||||
itertools = "0.10.1"
|
||||
quote = "1.0"
|
||||
serde = { version = "1.0.125", features = ["derive"] }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
futures = "0.3"
|
||||
parking_lot = "0.12"
|
||||
tokio = { version = "1", features = ["io-util"] }
|
||||
rustc-semver = "1.1"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = "0.3.0"
|
||||
|
@ -60,3 +47,7 @@ internal = ["clippy_lints/internal", "tempfile"]
|
|||
[package.metadata.rust-analyzer]
|
||||
# This package uses #[feature(rustc_private)]
|
||||
rustc_private = true
|
||||
|
||||
[[test]]
|
||||
name = "compile-test"
|
||||
harness = false
|
||||
|
|
|
@ -122,20 +122,17 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently
|
||||
Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently
|
||||
this test is meaningless though.
|
||||
|
||||
While we are working on implementing our lint, we can keep running the UI test.
|
||||
That allows us to check if the output is turning into what we want.
|
||||
That allows us to check if the output is turning into what we want by checking the
|
||||
`.stderr` file that gets updated on every test run.
|
||||
|
||||
Once we are satisfied with the output, we need to run `cargo dev bless` to
|
||||
update the `.stderr` file for our lint. Please note that, we should run
|
||||
`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev
|
||||
bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we
|
||||
Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we
|
||||
commit our lint, we need to commit the generated `.stderr` files, too. In
|
||||
general, you should only commit files changed by `cargo dev bless` for the
|
||||
specific lint you are creating/editing. Note that if the generated files are
|
||||
empty, they should be removed.
|
||||
general, you should only commit files changed by `cargo bless` for the
|
||||
specific lint you are creating/editing.
|
||||
|
||||
> _Note:_ you can run multiple test files by specifying a comma separated list:
|
||||
> `TESTNAME=foo_functions,test2,test3`.
|
||||
|
@ -169,7 +166,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions
|
|||
from the lint to the code of the test file and compare that to the contents of a
|
||||
`.fixed` file.
|
||||
|
||||
Use `cargo dev bless` to automatically generate the `.fixed` file after running
|
||||
Use `cargo bless` to automatically generate the `.fixed` file while running
|
||||
the tests.
|
||||
|
||||
[rustfix]: https://github.com/rust-lang/rustfix
|
||||
|
@ -417,7 +414,7 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
|
|||
|
||||
Now we should also run the full test suite with `cargo test`. At this point
|
||||
running `cargo test` should produce the expected output. Remember to run `cargo
|
||||
dev bless` to update the `.stderr` file.
|
||||
bless` to update the `.stderr` file.
|
||||
|
||||
`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
|
||||
implementation is not violating any Clippy lints itself.
|
||||
|
|
|
@ -66,7 +66,7 @@ If the output of a [UI test] differs from the expected output, you can update
|
|||
the reference file with:
|
||||
|
||||
```bash
|
||||
cargo dev bless
|
||||
cargo bless
|
||||
```
|
||||
|
||||
For example, this is necessary if you fix a typo in an error message of a lint,
|
||||
|
|
|
@ -56,6 +56,28 @@ and open that file in your editor of choice.
|
|||
When updating the changelog it's also a good idea to make sure that `commit1` is
|
||||
already correct in the current changelog.
|
||||
|
||||
#### PR ranges
|
||||
|
||||
We developed the concept of PR ranges to help the user understand the size of a new update. To create a PR range,
|
||||
get the current release date and the date that the last version was released (YYYY-MM-DD) and use the following link:
|
||||
|
||||
```
|
||||
[**View <NUMBER OF PRs> PRs merged since 1.<LAST VERSION NUM>**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A<LAST VERSION DATE>..<CURRENT VERSION DATE>+base%3Amaster+sort%3Amerged-desc+)
|
||||
```
|
||||
|
||||
> Note: Be sure to check click the link and check how many PRs got merged between
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+)
|
||||
```
|
||||
|
||||
Which renders to:
|
||||
[**View 85 PRs merged since 1.69**](https://github.com/rust-lang/rust-clippy/pulls?q=is%3Apr+is%3Aclosed+merged%3A2023-04-20..2023-06-01+base%3Amaster+sort%3Amerged-desc+)
|
||||
|
||||
Note that **commit ranges should not be included**, only PR ranges.
|
||||
|
||||
### 3. Authoring the final changelog
|
||||
|
||||
The above script should have dumped all the relevant PRs to the file you
|
||||
|
|
|
@ -3,63 +3,14 @@ This file is generated by `cargo collect-metadata`.
|
|||
Please use that command to update the file and do not edit it by hand.
|
||||
-->
|
||||
|
||||
## Lint Configuration Options
|
||||
| <div style="width:290px">Option</div> | Default Value |
|
||||
|--|--|
|
||||
| [arithmetic-side-effects-allowed](#arithmetic-side-effects-allowed) | `{}` |
|
||||
| [arithmetic-side-effects-allowed-binary](#arithmetic-side-effects-allowed-binary) | `[]` |
|
||||
| [arithmetic-side-effects-allowed-unary](#arithmetic-side-effects-allowed-unary) | `{}` |
|
||||
| [avoid-breaking-exported-api](#avoid-breaking-exported-api) | `true` |
|
||||
| [msrv](#msrv) | `None` |
|
||||
| [cognitive-complexity-threshold](#cognitive-complexity-threshold) | `25` |
|
||||
| [disallowed-names](#disallowed-names) | `["foo", "baz", "quux"]` |
|
||||
| [semicolon-inside-block-ignore-singleline](#semicolon-inside-block-ignore-singleline) | `false` |
|
||||
| [semicolon-outside-block-ignore-multiline](#semicolon-outside-block-ignore-multiline) | `false` |
|
||||
| [doc-valid-idents](#doc-valid-idents) | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` |
|
||||
| [too-many-arguments-threshold](#too-many-arguments-threshold) | `7` |
|
||||
| [type-complexity-threshold](#type-complexity-threshold) | `250` |
|
||||
| [single-char-binding-names-threshold](#single-char-binding-names-threshold) | `4` |
|
||||
| [too-large-for-stack](#too-large-for-stack) | `200` |
|
||||
| [enum-variant-name-threshold](#enum-variant-name-threshold) | `3` |
|
||||
| [enum-variant-size-threshold](#enum-variant-size-threshold) | `200` |
|
||||
| [verbose-bit-mask-threshold](#verbose-bit-mask-threshold) | `1` |
|
||||
| [literal-representation-threshold](#literal-representation-threshold) | `16384` |
|
||||
| [trivial-copy-size-limit](#trivial-copy-size-limit) | `None` |
|
||||
| [pass-by-value-size-limit](#pass-by-value-size-limit) | `256` |
|
||||
| [too-many-lines-threshold](#too-many-lines-threshold) | `100` |
|
||||
| [array-size-threshold](#array-size-threshold) | `512000` |
|
||||
| [vec-box-size-threshold](#vec-box-size-threshold) | `4096` |
|
||||
| [max-trait-bounds](#max-trait-bounds) | `3` |
|
||||
| [max-struct-bools](#max-struct-bools) | `3` |
|
||||
| [max-fn-params-bools](#max-fn-params-bools) | `3` |
|
||||
| [warn-on-all-wildcard-imports](#warn-on-all-wildcard-imports) | `false` |
|
||||
| [disallowed-macros](#disallowed-macros) | `[]` |
|
||||
| [disallowed-methods](#disallowed-methods) | `[]` |
|
||||
| [disallowed-types](#disallowed-types) | `[]` |
|
||||
| [unreadable-literal-lint-fractions](#unreadable-literal-lint-fractions) | `true` |
|
||||
| [upper-case-acronyms-aggressive](#upper-case-acronyms-aggressive) | `false` |
|
||||
| [matches-for-let-else](#matches-for-let-else) | `WellKnownTypes` |
|
||||
| [cargo-ignore-publish](#cargo-ignore-publish) | `false` |
|
||||
| [standard-macro-braces](#standard-macro-braces) | `[]` |
|
||||
| [enforced-import-renames](#enforced-import-renames) | `[]` |
|
||||
| [allowed-scripts](#allowed-scripts) | `["Latin"]` |
|
||||
| [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` |
|
||||
| [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` |
|
||||
| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` |
|
||||
| [max-include-file-size](#max-include-file-size) | `1000000` |
|
||||
| [allow-expect-in-tests](#allow-expect-in-tests) | `false` |
|
||||
| [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` |
|
||||
| [allow-dbg-in-tests](#allow-dbg-in-tests) | `false` |
|
||||
| [allow-print-in-tests](#allow-print-in-tests) | `false` |
|
||||
| [large-error-threshold](#large-error-threshold) | `128` |
|
||||
| [ignore-interior-mutability](#ignore-interior-mutability) | `["bytes::Bytes"]` |
|
||||
| [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` |
|
||||
| [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` |
|
||||
| [missing-docs-in-crate-items](#missing-docs-in-crate-items) | `false` |
|
||||
| [future-size-threshold](#future-size-threshold) | `16384` |
|
||||
| [unnecessary-box-size](#unnecessary-box-size) | `128` |
|
||||
# Lint Configuration Options
|
||||
|
||||
### arithmetic-side-effects-allowed
|
||||
The following list shows each configuration option, along with a description, its default value, an example
|
||||
and lints affected.
|
||||
|
||||
---
|
||||
|
||||
## `arithmetic-side-effects-allowed`
|
||||
Suppress checking of the passed type names in all types of operations.
|
||||
|
||||
If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
|
||||
|
@ -77,10 +28,12 @@ A type, say `SomeType`, listed in this configuration has the same behavior of
|
|||
|
||||
**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
|
||||
|
||||
* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
|
||||
|
||||
|
||||
### arithmetic-side-effects-allowed-binary
|
||||
## `arithmetic-side-effects-allowed-binary`
|
||||
Suppress checking of the passed type pair names in binary operations like addition or
|
||||
multiplication.
|
||||
|
||||
|
@ -98,10 +51,12 @@ arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType",
|
|||
|
||||
**Default Value:** `[]` (`Vec<[String; 2]>`)
|
||||
|
||||
* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
|
||||
|
||||
|
||||
### arithmetic-side-effects-allowed-unary
|
||||
## `arithmetic-side-effects-allowed-unary`
|
||||
Suppress checking of the passed type names in unary operations like "negation" (`-`).
|
||||
|
||||
#### Example
|
||||
|
@ -112,116 +67,143 @@ arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
|
|||
|
||||
**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
|
||||
|
||||
* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`arithmetic_side_effects`](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
|
||||
|
||||
|
||||
### avoid-breaking-exported-api
|
||||
## `avoid-breaking-exported-api`
|
||||
Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
|
||||
**Default Value:** `true` (`bool`)
|
||||
|
||||
* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
|
||||
* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
|
||||
* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
|
||||
* [unnecessary_wraps](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps)
|
||||
* [unused_self](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self)
|
||||
* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
|
||||
* [wrong_self_convention](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention)
|
||||
* [box_collection](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection)
|
||||
* [redundant_allocation](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation)
|
||||
* [rc_buffer](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
|
||||
* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
|
||||
* [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
|
||||
* [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
|
||||
* [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
|
||||
* [unnecessary_box_returns](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
|
||||
* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
|
||||
* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
|
||||
* [`unnecessary_wraps`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps)
|
||||
* [`unused_self`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self)
|
||||
* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
|
||||
* [`wrong_self_convention`](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention)
|
||||
* [`box_collection`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection)
|
||||
* [`redundant_allocation`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation)
|
||||
* [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
|
||||
* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
|
||||
* [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
|
||||
* [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
|
||||
* [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
|
||||
* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
|
||||
* [`single_call_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#single_call_fn)
|
||||
|
||||
|
||||
### msrv
|
||||
## `msrv`
|
||||
The minimum rust version that the project supports
|
||||
|
||||
**Default Value:** `None` (`Option<String>`)
|
||||
|
||||
* [manual_split_once](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
|
||||
* [manual_str_repeat](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
|
||||
* [cloned_instead_of_copied](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied)
|
||||
* [redundant_field_names](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
|
||||
* [redundant_static_lifetimes](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
|
||||
* [filter_map_next](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next)
|
||||
* [checked_conversions](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions)
|
||||
* [manual_range_contains](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
|
||||
* [use_self](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
|
||||
* [mem_replace_with_default](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
|
||||
* [manual_non_exhaustive](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
|
||||
* [option_as_ref_deref](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
|
||||
* [map_unwrap_or](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
|
||||
* [match_like_matches_macro](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro)
|
||||
* [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip)
|
||||
* [missing_const_for_fn](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
|
||||
* [unnested_or_patterns](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
|
||||
* [from_over_into](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
|
||||
* [ptr_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
|
||||
* [if_then_some_else_none](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
|
||||
* [approx_constant](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
|
||||
* [deprecated_cfg_attr](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr)
|
||||
* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
|
||||
* [map_clone](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone)
|
||||
* [borrow_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr)
|
||||
* [manual_bits](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
|
||||
* [err_expect](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect)
|
||||
* [cast_abs_to_unsigned](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned)
|
||||
* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
|
||||
* [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
|
||||
* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
|
||||
* [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction)
|
||||
* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace)
|
||||
* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
|
||||
* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
|
||||
* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
|
||||
* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
|
||||
* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
|
||||
* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
|
||||
* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls)
|
||||
* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check)
|
||||
* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
|
||||
* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
|
||||
* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
|
||||
* [`cloned_instead_of_copied`](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied)
|
||||
* [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
|
||||
* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or)
|
||||
* [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
|
||||
* [`filter_map_next`](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next)
|
||||
* [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions)
|
||||
* [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
|
||||
* [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
|
||||
* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
|
||||
* [`manual_non_exhaustive`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
|
||||
* [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
|
||||
* [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
|
||||
* [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro)
|
||||
* [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip)
|
||||
* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
|
||||
* [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
|
||||
* [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
|
||||
* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
|
||||
* [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
|
||||
* [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
|
||||
* [`deprecated_cfg_attr`](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr)
|
||||
* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
|
||||
* [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone)
|
||||
* [`borrow_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr)
|
||||
* [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
|
||||
* [`err_expect`](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect)
|
||||
* [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned)
|
||||
* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
|
||||
* [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
|
||||
* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
|
||||
* [`unchecked_duration_subtraction`](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction)
|
||||
* [`collapsible_str_replace`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace)
|
||||
* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
|
||||
* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
|
||||
* [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
|
||||
* [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
|
||||
* [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
|
||||
* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
|
||||
* [`derivable_impls`](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls)
|
||||
* [`manual_is_ascii_check`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check)
|
||||
* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
|
||||
* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
|
||||
* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
|
||||
|
||||
|
||||
### cognitive-complexity-threshold
|
||||
## `cognitive-complexity-threshold`
|
||||
The maximum cognitive complexity a function can have
|
||||
|
||||
**Default Value:** `25` (`u64`)
|
||||
|
||||
* [cognitive_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
|
||||
|
||||
|
||||
### disallowed-names
|
||||
## `excessive-nesting-threshold`
|
||||
The maximum amount of nesting a block can reside in
|
||||
|
||||
**Default Value:** `0` (`u64`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting)
|
||||
|
||||
|
||||
## `disallowed-names`
|
||||
The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
|
||||
`".."` can be used as part of the list to indicate, that the configured values should be appended to the
|
||||
default configuration of Clippy. By default, any configuration will replace the default value.
|
||||
|
||||
**Default Value:** `["foo", "baz", "quux"]` (`Vec<String>`)
|
||||
|
||||
* [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`disallowed_names`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names)
|
||||
|
||||
|
||||
### semicolon-inside-block-ignore-singleline
|
||||
## `semicolon-inside-block-ignore-singleline`
|
||||
Whether to lint only if it's multiline.
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [semicolon_inside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`semicolon_inside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block)
|
||||
|
||||
|
||||
### semicolon-outside-block-ignore-multiline
|
||||
## `semicolon-outside-block-ignore-multiline`
|
||||
Whether to lint only if it's singleline.
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [semicolon_outside_block](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`semicolon_outside_block`](https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block)
|
||||
|
||||
|
||||
### doc-valid-idents
|
||||
## `doc-valid-idents`
|
||||
The list of words this lint should not consider as identifiers needing ticks. The value
|
||||
`".."` can be used as part of the list to indicate, that the configured values should be appended to the
|
||||
default configuration of Clippy. By default, any configuration will replace the default value. For example:
|
||||
|
@ -230,207 +212,267 @@ default configuration of Clippy. By default, any configuration will replace the
|
|||
|
||||
Default list:
|
||||
|
||||
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` (`Vec<String>`)
|
||||
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` (`Vec<String>`)
|
||||
|
||||
* [doc_markdown](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`doc_markdown`](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown)
|
||||
|
||||
|
||||
### too-many-arguments-threshold
|
||||
## `too-many-arguments-threshold`
|
||||
The maximum number of argument a function or method can have
|
||||
|
||||
**Default Value:** `7` (`u64`)
|
||||
|
||||
* [too_many_arguments](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`too_many_arguments`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments)
|
||||
|
||||
|
||||
### type-complexity-threshold
|
||||
## `type-complexity-threshold`
|
||||
The maximum complexity a type can have
|
||||
|
||||
**Default Value:** `250` (`u64`)
|
||||
|
||||
* [type_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`type_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity)
|
||||
|
||||
|
||||
### single-char-binding-names-threshold
|
||||
## `single-char-binding-names-threshold`
|
||||
The maximum number of single char bindings a scope may have
|
||||
|
||||
**Default Value:** `4` (`u64`)
|
||||
|
||||
* [many_single_char_names](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`many_single_char_names`](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names)
|
||||
|
||||
|
||||
### too-large-for-stack
|
||||
## `too-large-for-stack`
|
||||
The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
|
||||
|
||||
**Default Value:** `200` (`u64`)
|
||||
|
||||
* [boxed_local](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local)
|
||||
* [useless_vec](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`boxed_local`](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local)
|
||||
* [`useless_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec)
|
||||
|
||||
|
||||
### enum-variant-name-threshold
|
||||
## `enum-variant-name-threshold`
|
||||
The minimum number of enum variants for the lints about variant names to trigger
|
||||
|
||||
**Default Value:** `3` (`u64`)
|
||||
|
||||
* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
|
||||
|
||||
|
||||
### enum-variant-size-threshold
|
||||
## `enum-variant-size-threshold`
|
||||
The maximum size of an enum's variant to avoid box suggestion
|
||||
|
||||
**Default Value:** `200` (`u64`)
|
||||
|
||||
* [large_enum_variant](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`large_enum_variant`](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant)
|
||||
|
||||
|
||||
### verbose-bit-mask-threshold
|
||||
## `verbose-bit-mask-threshold`
|
||||
The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
|
||||
|
||||
**Default Value:** `1` (`u64`)
|
||||
|
||||
* [verbose_bit_mask](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`verbose_bit_mask`](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask)
|
||||
|
||||
|
||||
### literal-representation-threshold
|
||||
## `literal-representation-threshold`
|
||||
The lower bound for linting decimal literals
|
||||
|
||||
**Default Value:** `16384` (`u64`)
|
||||
|
||||
* [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`decimal_literal_representation`](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation)
|
||||
|
||||
|
||||
### trivial-copy-size-limit
|
||||
## `trivial-copy-size-limit`
|
||||
The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
|
||||
|
||||
**Default Value:** `None` (`Option<u64>`)
|
||||
|
||||
* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
|
||||
|
||||
|
||||
### pass-by-value-size-limit
|
||||
## `pass-by-value-size-limit`
|
||||
The minimum size (in bytes) to consider a type for passing by reference instead of by value.
|
||||
|
||||
**Default Value:** `256` (`u64`)
|
||||
|
||||
* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
|
||||
|
||||
|
||||
### too-many-lines-threshold
|
||||
## `too-many-lines-threshold`
|
||||
The maximum number of lines a function or method can have
|
||||
|
||||
**Default Value:** `100` (`u64`)
|
||||
|
||||
* [too_many_lines](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines)
|
||||
|
||||
|
||||
### array-size-threshold
|
||||
## `array-size-threshold`
|
||||
The maximum allowed size for arrays on the stack
|
||||
|
||||
**Default Value:** `512000` (`u64`)
|
||||
|
||||
* [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays)
|
||||
* [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`large_stack_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays)
|
||||
* [`large_const_arrays`](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays)
|
||||
|
||||
|
||||
### vec-box-size-threshold
|
||||
## `stack-size-threshold`
|
||||
The maximum allowed stack size for functions in bytes
|
||||
|
||||
**Default Value:** `512000` (`u64`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`large_stack_frames`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames)
|
||||
|
||||
|
||||
## `vec-box-size-threshold`
|
||||
The size of the boxed type in bytes, where boxing in a `Vec` is allowed
|
||||
|
||||
**Default Value:** `4096` (`u64`)
|
||||
|
||||
* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`vec_box`](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
|
||||
|
||||
|
||||
### max-trait-bounds
|
||||
## `max-trait-bounds`
|
||||
The maximum number of bounds a trait can have to be linted
|
||||
|
||||
**Default Value:** `3` (`u64`)
|
||||
|
||||
* [type_repetition_in_bounds](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
|
||||
|
||||
|
||||
### max-struct-bools
|
||||
## `max-struct-bools`
|
||||
The maximum number of bool fields a struct can have
|
||||
|
||||
**Default Value:** `3` (`u64`)
|
||||
|
||||
* [struct_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`struct_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools)
|
||||
|
||||
|
||||
### max-fn-params-bools
|
||||
## `max-fn-params-bools`
|
||||
The maximum number of bool parameters a function can have
|
||||
|
||||
**Default Value:** `3` (`u64`)
|
||||
|
||||
* [fn_params_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`fn_params_excessive_bools`](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools)
|
||||
|
||||
|
||||
### warn-on-all-wildcard-imports
|
||||
## `warn-on-all-wildcard-imports`
|
||||
Whether to allow certain wildcard imports (prelude, super in tests).
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [wildcard_imports](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
|
||||
|
||||
|
||||
### disallowed-macros
|
||||
## `disallowed-macros`
|
||||
The list of disallowed macros, written as fully qualified paths.
|
||||
|
||||
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
|
||||
|
||||
* [disallowed_macros](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`disallowed_macros`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros)
|
||||
|
||||
|
||||
### disallowed-methods
|
||||
## `disallowed-methods`
|
||||
The list of disallowed methods, written as fully qualified paths.
|
||||
|
||||
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
|
||||
|
||||
* [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods)
|
||||
|
||||
|
||||
### disallowed-types
|
||||
## `disallowed-types`
|
||||
The list of disallowed types, written as fully qualified paths.
|
||||
|
||||
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
|
||||
|
||||
* [disallowed_types](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types)
|
||||
|
||||
|
||||
### unreadable-literal-lint-fractions
|
||||
## `unreadable-literal-lint-fractions`
|
||||
Should the fraction of a decimal be linted to include separators.
|
||||
|
||||
**Default Value:** `true` (`bool`)
|
||||
|
||||
* [unreadable_literal](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`unreadable_literal`](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal)
|
||||
|
||||
|
||||
### upper-case-acronyms-aggressive
|
||||
## `upper-case-acronyms-aggressive`
|
||||
Enables verbose mode. Triggers if there is more than one uppercase char next to each other
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`upper_case_acronyms`](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
|
||||
|
||||
|
||||
### matches-for-let-else
|
||||
## `matches-for-let-else`
|
||||
Whether the matches should be considered by the lint, and whether there should
|
||||
be filtering for common types.
|
||||
|
||||
**Default Value:** `WellKnownTypes` (`crate::manual_let_else::MatchLintBehaviour`)
|
||||
|
||||
* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`manual_let_else`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
|
||||
|
||||
|
||||
### cargo-ignore-publish
|
||||
## `cargo-ignore-publish`
|
||||
For internal testing only, ignores the current `publish` settings in the Cargo manifest.
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [_cargo_common_metadata](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`_cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata)
|
||||
|
||||
|
||||
### standard-macro-braces
|
||||
## `standard-macro-braces`
|
||||
Enforce the named macros always use the braces specified.
|
||||
|
||||
A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
|
||||
|
@ -439,119 +481,147 @@ could be used with a full path two `MacroMatcher`s have to be added one with the
|
|||
|
||||
**Default Value:** `[]` (`Vec<crate::nonstandard_macro_braces::MacroMatcher>`)
|
||||
|
||||
* [nonstandard_macro_braces](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`nonstandard_macro_braces`](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces)
|
||||
|
||||
|
||||
### enforced-import-renames
|
||||
## `enforced-import-renames`
|
||||
The list of imports to always rename, a fully qualified path followed by the rename.
|
||||
|
||||
**Default Value:** `[]` (`Vec<crate::utils::conf::Rename>`)
|
||||
|
||||
* [missing_enforced_import_renames](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`missing_enforced_import_renames`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames)
|
||||
|
||||
|
||||
### allowed-scripts
|
||||
## `allowed-scripts`
|
||||
The list of unicode scripts allowed to be used in the scope.
|
||||
|
||||
**Default Value:** `["Latin"]` (`Vec<String>`)
|
||||
|
||||
* [disallowed_script_idents](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`disallowed_script_idents`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents)
|
||||
|
||||
|
||||
### enable-raw-pointer-heuristic-for-send
|
||||
## `enable-raw-pointer-heuristic-for-send`
|
||||
Whether to apply the raw pointer heuristic to determine if a type is `Send`.
|
||||
|
||||
**Default Value:** `true` (`bool`)
|
||||
|
||||
* [non_send_fields_in_send_ty](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`non_send_fields_in_send_ty`](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty)
|
||||
|
||||
|
||||
### max-suggested-slice-pattern-length
|
||||
## `max-suggested-slice-pattern-length`
|
||||
When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
|
||||
the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
|
||||
For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
|
||||
|
||||
**Default Value:** `3` (`u64`)
|
||||
|
||||
* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
|
||||
|
||||
|
||||
### await-holding-invalid-types
|
||||
## `await-holding-invalid-types`
|
||||
|
||||
|
||||
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
|
||||
|
||||
* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`await_holding_invalid_type`](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type)
|
||||
|
||||
|
||||
### max-include-file-size
|
||||
## `max-include-file-size`
|
||||
The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
|
||||
|
||||
**Default Value:** `1000000` (`u64`)
|
||||
|
||||
* [large_include_file](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`large_include_file`](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file)
|
||||
|
||||
|
||||
### allow-expect-in-tests
|
||||
## `allow-expect-in-tests`
|
||||
Whether `expect` should be allowed in test functions or `#[cfg(test)]`
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [expect_used](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)
|
||||
|
||||
|
||||
### allow-unwrap-in-tests
|
||||
## `allow-unwrap-in-tests`
|
||||
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [unwrap_used](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
|
||||
|
||||
|
||||
### allow-dbg-in-tests
|
||||
## `allow-dbg-in-tests`
|
||||
Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [dbg_macro](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`dbg_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
|
||||
|
||||
|
||||
### allow-print-in-tests
|
||||
## `allow-print-in-tests`
|
||||
Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [print_stdout](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout)
|
||||
* [print_stderr](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`print_stdout`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout)
|
||||
* [`print_stderr`](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr)
|
||||
|
||||
|
||||
### large-error-threshold
|
||||
## `large-error-threshold`
|
||||
The maximum size of the `Err`-variant in a `Result` returned from a function
|
||||
|
||||
**Default Value:** `128` (`u64`)
|
||||
|
||||
* [result_large_err](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)
|
||||
|
||||
|
||||
### ignore-interior-mutability
|
||||
## `ignore-interior-mutability`
|
||||
A list of paths to types that should be treated like `Arc`, i.e. ignored but
|
||||
for the generic parameters for determining interior mutability
|
||||
|
||||
**Default Value:** `["bytes::Bytes"]` (`Vec<String>`)
|
||||
|
||||
* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
|
||||
* [ifs_same_cond](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
|
||||
* [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
|
||||
|
||||
|
||||
### allow-mixed-uninlined-format-args
|
||||
## `allow-mixed-uninlined-format-args`
|
||||
Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
|
||||
|
||||
**Default Value:** `true` (`bool`)
|
||||
|
||||
* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
|
||||
|
||||
|
||||
### suppress-restriction-lint-in-const
|
||||
## `suppress-restriction-lint-in-const`
|
||||
Whether to suppress a restriction lint in constant code. In same
|
||||
cases the restructured operation might not be unavoidable, as the
|
||||
suggested counterparts are unavailable in constant code. This
|
||||
|
@ -560,32 +630,101 @@ if no suggestion can be made.
|
|||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
|
||||
|
||||
|
||||
### missing-docs-in-crate-items
|
||||
## `missing-docs-in-crate-items`
|
||||
Whether to **only** check for missing documentation in items visible within the current
|
||||
crate. For example, `pub(crate)` items.
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
* [missing_docs_in_private_items](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)
|
||||
|
||||
|
||||
### future-size-threshold
|
||||
## `future-size-threshold`
|
||||
The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
|
||||
|
||||
**Default Value:** `16384` (`u64`)
|
||||
|
||||
* [large_futures](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`large_futures`](https://rust-lang.github.io/rust-clippy/master/index.html#large_futures)
|
||||
|
||||
|
||||
### unnecessary-box-size
|
||||
## `unnecessary-box-size`
|
||||
The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
|
||||
|
||||
**Default Value:** `128` (`u64`)
|
||||
|
||||
* [unnecessary_box_returns](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`unnecessary_box_returns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_box_returns)
|
||||
|
||||
|
||||
## `allow-private-module-inception`
|
||||
Whether to allow module inception if it's not public.
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception)
|
||||
|
||||
|
||||
## `allowed-idents-below-min-chars`
|
||||
Allowed names below the minimum allowed characters. The value `".."` can be used as part of
|
||||
the list to indicate, that the configured values should be appended to the default
|
||||
configuration of Clippy. By default, any configuration will replace the default value.
|
||||
|
||||
**Default Value:** `{"j", "z", "i", "y", "n", "x", "w"}` (`rustc_data_structures::fx::FxHashSet<String>`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars)
|
||||
|
||||
|
||||
## `min-ident-chars-threshold`
|
||||
Minimum chars an ident can have, anything below or equal to this will be linted.
|
||||
|
||||
**Default Value:** `1` (`u64`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars)
|
||||
|
||||
|
||||
## `accept-comment-above-statement`
|
||||
Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks)
|
||||
|
||||
|
||||
## `accept-comment-above-attributes`
|
||||
Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`undocumented_unsafe_blocks`](https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks)
|
||||
|
||||
|
||||
## `allow-one-hash-in-raw-strings`
|
||||
Whether to allow `r#""#` when `r""` can be used
|
||||
|
||||
**Default Value:** `false` (`bool`)
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes)
|
||||
|
||||
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
//! `bless` updates the reference files in the repo with changed output files
|
||||
//! from the last test run.
|
||||
|
||||
use crate::cargo_clippy_path;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::LazyLock;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
static CLIPPY_BUILD_TIME: LazyLock<Option<std::time::SystemTime>> =
|
||||
LazyLock::new(|| cargo_clippy_path().metadata().ok()?.modified().ok());
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the path to a test file is broken
|
||||
pub fn bless(ignore_timestamp: bool) {
|
||||
let extensions = ["stdout", "stderr", "fixed"].map(OsStr::new);
|
||||
|
||||
WalkDir::new(build_dir())
|
||||
.into_iter()
|
||||
.map(Result::unwrap)
|
||||
.filter(|entry| entry.path().extension().map_or(false, |ext| extensions.contains(&ext)))
|
||||
.for_each(|entry| update_reference_file(&entry, ignore_timestamp));
|
||||
}
|
||||
|
||||
fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
|
||||
let test_output_path = test_output_entry.path();
|
||||
|
||||
let reference_file_name = test_output_entry.file_name().to_str().unwrap().replace(".stage-id", "");
|
||||
let reference_file_path = Path::new("tests")
|
||||
.join(test_output_path.strip_prefix(build_dir()).unwrap())
|
||||
.with_file_name(reference_file_name);
|
||||
|
||||
// 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_entry).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 {}", reference_file_path.display());
|
||||
fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
|
||||
}
|
||||
}
|
||||
|
||||
fn updated_since_clippy_build(entry: &DirEntry) -> Option<bool> {
|
||||
let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
|
||||
let modified = entry.metadata().ok()?.modified().ok()?;
|
||||
Some(modified >= clippy_build_time)
|
||||
}
|
||||
|
||||
fn build_dir() -> PathBuf {
|
||||
let mut path = std::env::current_exe().unwrap();
|
||||
path.set_file_name("test");
|
||||
path
|
||||
}
|
|
@ -35,6 +35,7 @@ struct FmtContext {
|
|||
}
|
||||
|
||||
// the "main" function of cargo dev fmt
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn run(check: bool, verbose: bool) {
|
||||
fn try_run(context: &FmtContext) -> Result<bool, CliError> {
|
||||
let mut success = true;
|
||||
|
|
|
@ -14,7 +14,6 @@ use std::io;
|
|||
use std::path::PathBuf;
|
||||
use std::process::{self, ExitStatus};
|
||||
|
||||
pub mod bless;
|
||||
pub mod dogfood;
|
||||
pub mod fmt;
|
||||
pub mod lint;
|
||||
|
@ -29,6 +28,10 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
|
|||
static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
|
||||
|
||||
/// Returns the path to the `cargo-clippy` binary
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the path of current executable could not be retrieved.
|
||||
#[must_use]
|
||||
pub fn cargo_clippy_path() -> PathBuf {
|
||||
let mut path = std::env::current_exe().expect("failed to get current executable name");
|
||||
|
@ -61,6 +64,8 @@ pub fn clippy_project_root() -> PathBuf {
|
|||
panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// Panics if given command result was failed.
|
||||
pub fn exit_if_err(status: io::Result<ExitStatus>) {
|
||||
match status.expect("failed to run command").code() {
|
||||
Some(0) => {},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
|
||||
use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
|
||||
use indoc::indoc;
|
||||
use std::convert::Infallible;
|
||||
|
||||
|
@ -11,8 +11,8 @@ fn main() {
|
|||
let matches = get_clap_config();
|
||||
|
||||
match matches.subcommand() {
|
||||
Some(("bless", matches)) => {
|
||||
bless::bless(matches.get_flag("ignore-timestamp"));
|
||||
Some(("bless", _)) => {
|
||||
eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run");
|
||||
},
|
||||
Some(("dogfood", matches)) => {
|
||||
dogfood::dogfood(
|
||||
|
@ -35,7 +35,7 @@ fn main() {
|
|||
},
|
||||
Some(("new_lint", matches)) => {
|
||||
match new_lint::create(
|
||||
matches.get_one::<String>("pass"),
|
||||
matches.get_one::<String>("pass").unwrap(),
|
||||
matches.get_one::<String>("name"),
|
||||
matches.get_one::<String>("category").map(String::as_str),
|
||||
matches.get_one::<String>("type").map(String::as_str),
|
||||
|
@ -176,7 +176,7 @@ fn get_clap_config() -> ArgMatches {
|
|||
.help("Specify whether the lint runs during the early or late pass")
|
||||
.value_parser(["early", "late"])
|
||||
.conflicts_with("type")
|
||||
.required_unless_present("type"),
|
||||
.default_value("late"),
|
||||
Arg::new("name")
|
||||
.short('n')
|
||||
.long("name")
|
||||
|
|
|
@ -36,8 +36,9 @@ impl<T> Context for io::Result<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// This function errors out if the files couldn't be created or written to.
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn create(
|
||||
pass: Option<&String>,
|
||||
pass: &String,
|
||||
lint_name: Option<&String>,
|
||||
category: Option<&str>,
|
||||
mut ty: Option<&str>,
|
||||
|
@ -49,7 +50,7 @@ pub fn create(
|
|||
}
|
||||
|
||||
let lint = LintData {
|
||||
pass: pass.map_or("", String::as_str),
|
||||
pass,
|
||||
name: lint_name.expect("`name` argument is validated by clap"),
|
||||
category: category.expect("`category` argument is validated by clap"),
|
||||
ty,
|
||||
|
@ -63,6 +64,14 @@ pub fn create(
|
|||
add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
|
||||
}
|
||||
|
||||
if pass == "early" {
|
||||
println!(
|
||||
"\n\
|
||||
NOTE: Use a late pass unless you need something specific from\
|
||||
an early pass, as they lack many features and utilities"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -87,7 +96,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
|||
|
||||
path.push("src");
|
||||
fs::create_dir(&path)?;
|
||||
let header = format!("// compile-flags: --crate-name={lint_name}");
|
||||
let header = format!("//@compile-flags: --crate-name={lint_name}");
|
||||
write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -22,6 +22,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
serde_json = { version = "1.0", optional = true }
|
||||
tempfile = { version = "3.3.0", optional = true }
|
||||
toml = "0.7.3"
|
||||
regex = { version = "1.5", optional = true }
|
||||
unicode-normalization = "0.1"
|
||||
unicode-script = { version = "0.5", default-features = false }
|
||||
semver = "1.0"
|
||||
|
@ -31,7 +32,7 @@ url = "2.2"
|
|||
[features]
|
||||
deny-warnings = ["clippy_utils/deny-warnings"]
|
||||
# build clippy with internal lints enabled, off by default
|
||||
internal = ["clippy_utils/internal", "serde_json", "tempfile"]
|
||||
internal = ["clippy_utils/internal", "serde_json", "tempfile", "regex"]
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This crate uses #[feature(rustc_private)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ast::AttrStyle;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use ast::{AttrStyle, Attribute};
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro};
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -50,13 +50,14 @@ declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
|
|||
|
||||
impl LateLintPass<'_> for AllowAttribute {
|
||||
// Separate each crate's features.
|
||||
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
|
||||
fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), attr.span);
|
||||
if cx.tcx.features().lint_reasons;
|
||||
if let AttrStyle::Outer = attr.style;
|
||||
if let Some(ident) = attr.ident();
|
||||
if ident.name == rustc_span::symbol::sym::allow;
|
||||
if !is_from_proc_macro(cx, &attr);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
72
clippy_lints/src/arc_with_non_send_sync.rs
Normal file
72
clippy_lints/src/arc_with_non_send_sync.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::last_path_segment;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use if_chain::if_chain;
|
||||
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_lint::LateLintPass;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does.
|
||||
/// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Wrapping a type in Arc doesn't add thread safety to the underlying data, so data races
|
||||
/// could occur when touching the underlying data.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::cell::RefCell;
|
||||
/// # use std::sync::Arc;
|
||||
///
|
||||
/// fn main() {
|
||||
/// // This is safe, as `i32` implements `Send` and `Sync`.
|
||||
/// let a = Arc::new(42);
|
||||
///
|
||||
/// // This is not safe, as `RefCell` does not implement `Sync`.
|
||||
/// let b = Arc::new(RefCell::new(42));
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub ARC_WITH_NON_SEND_SYNC,
|
||||
correctness,
|
||||
"using `Arc` with a type that does not implement `Send` or `Sync`"
|
||||
}
|
||||
declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);
|
||||
|
||||
impl LateLintPass<'_> for ArcWithNonSendSync {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, ty, sym::Arc);
|
||||
if let ExprKind::Call(func, [arg]) = expr.kind;
|
||||
if let ExprKind::Path(func_path) = func.kind;
|
||||
if last_path_segment(&func_path).ident.name == sym::new;
|
||||
if let arg_ty = cx.typeck_results().expr_ty(arg);
|
||||
if !matches!(arg_ty.kind(), ty::Param(_));
|
||||
if !cx.tcx
|
||||
.lang_items()
|
||||
.sync_trait()
|
||||
.map_or(false, |id| implements_trait(cx, arg_ty, id, &[])) ||
|
||||
!cx.tcx
|
||||
.get_diagnostic_item(sym::Send)
|
||||
.map_or(false, |id| implements_trait(cx, arg_ty, id, &[]));
|
||||
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ARC_WITH_NON_SEND_SYNC,
|
||||
expr.span,
|
||||
"usage of `Arc<T>` where `T` is not `Send` or `Sync`",
|
||||
None,
|
||||
"consider using `Rc<T>` instead or wrapping `T` in a std::sync type like \
|
||||
`Mutex<T>`",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
|
@ -45,9 +46,9 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
|
||||
|
||||
impl EarlyLintPass for AsConversions {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
impl<'tcx> LateLintPass<'tcx> for AsConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), expr.span) || is_from_proc_macro(cx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
//! checks for attributes
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::{is_panic, macro_backtrace};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
|
||||
use clippy_utils::{
|
||||
diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then},
|
||||
is_from_proc_macro,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -362,6 +365,32 @@ declare_clippy_lint! {
|
|||
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[cfg(features = "...")]` and suggests to replace it with
|
||||
/// `#[cfg(feature = "...")]`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Misspelling `feature` as `features` can be sometimes hard to spot. It
|
||||
/// may cause conditional compilation not work quitely.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// #[cfg(features = "some-feature")]
|
||||
/// fn conditional() { }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #[cfg(feature = "some-feature")]
|
||||
/// fn conditional() { }
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub MAYBE_MISUSED_CFG,
|
||||
suspicious,
|
||||
"prevent from misusing the wrong attr name"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Attributes => [
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
INLINE_ALWAYS,
|
||||
|
@ -540,7 +569,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
|
|||
}
|
||||
}
|
||||
|
||||
fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
|
||||
fn check_lint_reason<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
|
||||
// Check for the feature
|
||||
if !cx.tcx.features().lint_reasons {
|
||||
return;
|
||||
|
@ -555,7 +584,7 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem
|
|||
}
|
||||
|
||||
// Check if the attribute is in an external macro and therefore out of the developer's control
|
||||
if in_external_macro(cx.sess(), attr.span) {
|
||||
if in_external_macro(cx.sess(), attr.span) || is_from_proc_macro(cx, &attr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -676,6 +705,7 @@ impl_lint_pass!(EarlyAttributes => [
|
|||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
NON_MINIMAL_CFG,
|
||||
MAYBE_MISUSED_CFG,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyAttributes {
|
||||
|
@ -687,6 +717,7 @@ impl EarlyLintPass for EarlyAttributes {
|
|||
check_deprecated_cfg_attr(cx, attr, &self.msrv);
|
||||
check_mismatched_target_os(cx, attr);
|
||||
check_minimal_cfg_condition(cx, attr);
|
||||
check_misused_cfg(cx, attr);
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
|
@ -777,7 +808,7 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
|
|||
}
|
||||
|
||||
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||
for item in items.iter() {
|
||||
for item in items {
|
||||
if let NestedMetaItem::MetaItem(meta) = item {
|
||||
if !meta.has_name(sym::any) && !meta.has_name(sym::all) {
|
||||
continue;
|
||||
|
@ -810,6 +841,27 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||
for item in items {
|
||||
if let NestedMetaItem::MetaItem(meta) = item {
|
||||
if meta.has_name(sym!(features)) && let Some(val) = meta.value_str() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAYBE_MISUSED_CFG,
|
||||
meta.span,
|
||||
"feature may misspelled as features",
|
||||
"use",
|
||||
format!("feature = \"{val}\""),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let MetaItemKind::List(list) = &meta.kind {
|
||||
check_nested_misused_cfg(cx, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if attr.has_name(sym::cfg) &&
|
||||
let Some(items) = attr.meta_item_list()
|
||||
|
@ -818,6 +870,14 @@ fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_misused_cfg(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if attr.has_name(sym::cfg) &&
|
||||
let Some(items) = attr.meta_item_list()
|
||||
{
|
||||
check_nested_misused_cfg(cx, &items);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
fn find_os(name: &str) -> Option<&'static str> {
|
||||
UNIX_SYSTEMS
|
||||
|
|
|
@ -85,8 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
let span =
|
||||
block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
|
||||
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
|
||||
if span.from_expansion() || expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -440,7 +440,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
|||
diag.span_suggestions(
|
||||
e.span,
|
||||
"try",
|
||||
suggestions.into_iter(),
|
||||
suggestions,
|
||||
// nonminimal_bool can produce minimal but
|
||||
// not human readable expressions (#3141)
|
||||
Applicability::Unspecified,
|
||||
|
|
|
@ -4,6 +4,7 @@ use clippy_utils::source::snippet_with_context;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
|
||||
use super::BORROW_AS_PTR;
|
||||
|
||||
|
@ -23,6 +24,15 @@ pub(super) fn check<'tcx>(
|
|||
};
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
|
||||
// Fix #9884
|
||||
if !e.is_place_expr(|base| {
|
||||
cx.typeck_results()
|
||||
.adjustments()
|
||||
.get(base.hir_id)
|
||||
.is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -1,41 +1,90 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::is_isize_or_usize;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::Ty;
|
||||
|
||||
use super::{utils, CAST_POSSIBLE_WRAP};
|
||||
|
||||
// this should be kept in sync with the allowed bit widths of `usize` and `isize`
|
||||
const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64];
|
||||
|
||||
// whether the lint should be emitted, and the required pointer size, if it matters
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum EmitState {
|
||||
NoLint,
|
||||
LintAlways,
|
||||
LintOnPtrSize(u64),
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
if !(cast_from.is_integral() && cast_to.is_integral()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let arch_64_suffix = " on targets with 64-bit wide pointers";
|
||||
let arch_32_suffix = " on targets with 32-bit wide pointers";
|
||||
let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed();
|
||||
// emit a lint if a cast is:
|
||||
// 1. unsigned to signed
|
||||
// and
|
||||
// 2. either:
|
||||
//
|
||||
// 2a. between two types of constant size that are always the same size
|
||||
// 2b. between one target-dependent size and one constant size integer,
|
||||
// and the constant integer is in the allowed set of target dependent sizes
|
||||
// (the ptr size could be chosen to be the same as the constant size)
|
||||
|
||||
if cast_from.is_signed() || !cast_to.is_signed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
|
||||
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
|
||||
|
||||
let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
|
||||
(true, true) | (false, false) => (to_nbits == from_nbits && cast_unsigned_to_signed, ""),
|
||||
(true, false) => (to_nbits <= 32 && cast_unsigned_to_signed, arch_32_suffix),
|
||||
(false, true) => (
|
||||
cast_unsigned_to_signed,
|
||||
if from_nbits == 64 {
|
||||
arch_64_suffix
|
||||
let should_lint = match (cast_from.is_ptr_sized_integral(), cast_to.is_ptr_sized_integral()) {
|
||||
(true, true) => {
|
||||
// casts between two ptr sized integers are trivially always the same size
|
||||
// so do not depend on any specific pointer size to be the same
|
||||
EmitState::LintAlways
|
||||
},
|
||||
(true, false) => {
|
||||
// the first type is `usize` and the second is a constant sized signed integer
|
||||
if ALLOWED_POINTER_SIZES.contains(&to_nbits) {
|
||||
EmitState::LintOnPtrSize(to_nbits)
|
||||
} else {
|
||||
arch_32_suffix
|
||||
},
|
||||
EmitState::NoLint
|
||||
}
|
||||
},
|
||||
(false, true) => {
|
||||
// the first type is a constant sized unsigned integer, and the second is `isize`
|
||||
if ALLOWED_POINTER_SIZES.contains(&from_nbits) {
|
||||
EmitState::LintOnPtrSize(from_nbits)
|
||||
} else {
|
||||
EmitState::NoLint
|
||||
}
|
||||
},
|
||||
(false, false) => {
|
||||
// the types are both a constant known size
|
||||
// and do not depend on any specific pointer size to be the same
|
||||
if from_nbits == to_nbits {
|
||||
EmitState::LintAlways
|
||||
} else {
|
||||
EmitState::NoLint
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let message = match should_lint {
|
||||
EmitState::NoLint => return,
|
||||
EmitState::LintAlways => format!("casting `{cast_from}` to `{cast_to}` may wrap around the value"),
|
||||
EmitState::LintOnPtrSize(ptr_size) => format!(
|
||||
"casting `{cast_from}` to `{cast_to}` may wrap around the value on targets with {ptr_size}-bit wide pointers",
|
||||
),
|
||||
};
|
||||
|
||||
if should_lint {
|
||||
span_lint(
|
||||
cx,
|
||||
CAST_POSSIBLE_WRAP,
|
||||
expr.span,
|
||||
&format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",),
|
||||
);
|
||||
}
|
||||
cx.struct_span_lint(CAST_POSSIBLE_WRAP, expr.span, message, |diag| {
|
||||
if let EmitState::LintOnPtrSize(16) = should_lint {
|
||||
diag
|
||||
.note("`usize` and `isize` may be as small as 16 bits on some platforms")
|
||||
.note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types")
|
||||
} else {
|
||||
diag
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -118,9 +118,10 @@ declare_clippy_lint! {
|
|||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an unsigned type to a signed type of
|
||||
/// the same size. Performing such a cast is a 'no-op' for the compiler,
|
||||
/// i.e., nothing is changed at the bit level, and the binary representation of
|
||||
/// the value is reinterpreted. This can cause wrapping if the value is too big
|
||||
/// the same size, or possibly smaller due to target dependent integers.
|
||||
/// Performing such a cast is a 'no-op' for the compiler, i.e., nothing is
|
||||
/// changed at the bit level, and the binary representation of the value is
|
||||
/// reinterpreted. This can cause wrapping if the value is too big
|
||||
/// for the target signed type. However, the cast works as defined, so this lint
|
||||
/// is `Allow` by default.
|
||||
///
|
||||
|
@ -174,8 +175,8 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts to the same type, casts of int literals to integer types
|
||||
/// and casts of float literals to float types.
|
||||
/// Checks for casts to the same type, casts of int literals to integer types, casts of float
|
||||
/// literals to float types and casts between raw pointers without changing type or constness.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's just unnecessary.
|
||||
|
@ -522,7 +523,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for the usage of `as _` conversion using inferred type.
|
||||
/// Checks for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The conversion might include lossy conversion and dangerous cast that might go
|
||||
|
|
|
@ -9,20 +9,21 @@ use rustc_middle::ty::{self, Ty, TypeAndMut};
|
|||
|
||||
use super::PTR_CAST_CONSTNESS;
|
||||
|
||||
pub(super) fn check(
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
cast_expr: &Expr<'_>,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
cast_from: Ty<'tcx>,
|
||||
cast_to: Ty<'tcx>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if_chain! {
|
||||
if msrv.meets(POINTER_CAST_CONSTNESS);
|
||||
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind();
|
||||
if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind();
|
||||
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, ty: from_ty }) = cast_from.kind();
|
||||
if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, ty: to_ty }) = cast_to.kind();
|
||||
if matches!((from_mutbl, to_mutbl),
|
||||
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not));
|
||||
if from_ty == to_ty;
|
||||
then {
|
||||
let sugg = Sugg::hir(cx, cast_expr, "_");
|
||||
let constness = match *to_mutbl {
|
||||
|
@ -34,7 +35,7 @@ pub(super) fn check(
|
|||
cx,
|
||||
PTR_CAST_CONSTNESS,
|
||||
expr.span,
|
||||
"`as` casting between raw pointers while changing its constness",
|
||||
"`as` casting between raw pointers while changing only its constness",
|
||||
&format!("try `pointer::cast_{constness}`, a safer alternative"),
|
||||
format!("{}.cast_{constness}()", sugg.maybe_par()),
|
||||
Applicability::MachineApplicable,
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::numeric_literal::NumericLiteral;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{get_parent_expr, path_to_local};
|
||||
use clippy_utils::visitors::{for_each_expr, Visitable};
|
||||
use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::UNNECESSARY_CAST;
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'tcx>,
|
||||
|
@ -20,19 +23,84 @@ pub(super) fn check<'tcx>(
|
|||
cast_from: Ty<'tcx>,
|
||||
cast_to: Ty<'tcx>,
|
||||
) -> bool {
|
||||
// skip non-primitive type cast
|
||||
let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
|
||||
|
||||
if_chain! {
|
||||
if let ty::RawPtr(..) = cast_from.kind();
|
||||
// check both mutability and type are the same
|
||||
if cast_from.kind() == cast_to.kind();
|
||||
if let ExprKind::Cast(_, cast_to_hir) = expr.kind;
|
||||
// Ignore casts to e.g. type aliases and infer types
|
||||
// - p as pointer_alias
|
||||
// - p as _
|
||||
if let TyKind::Ptr(to_pointee) = cast_to_hir.kind;
|
||||
then {
|
||||
match to_pointee.ty.kind {
|
||||
// Ignore casts to pointers that are aliases or cfg dependant, e.g.
|
||||
// - p as *const std::ffi::c_char (alias)
|
||||
// - p as *const std::os::raw::c_char (cfg dependant)
|
||||
TyKind::Path(qpath) => {
|
||||
if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// Ignore `p as *const _`
|
||||
TyKind::Infer => return false,
|
||||
_ => {},
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"),
|
||||
"try",
|
||||
cast_str.clone(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// skip cast of local that is a type alias
|
||||
if let ExprKind::Cast(inner, ..) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = inner.kind
|
||||
&& let QPath::Resolved(None, Path { res, .. }) = qpath
|
||||
&& let Res::Local(hir_id) = res
|
||||
&& let parent = cx.tcx.hir().get_parent(*hir_id)
|
||||
&& let Node::Local(local) = parent
|
||||
{
|
||||
if let Some(ty) = local.ty
|
||||
&& let TyKind::Path(qpath) = ty.kind
|
||||
&& is_ty_alias(&qpath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(expr) = local.init
|
||||
&& let ExprKind::Cast(.., cast_to) = expr.kind
|
||||
&& let TyKind::Path(qpath) = cast_to.kind
|
||||
&& is_ty_alias(&qpath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// skip cast of fn call that returns type alias
|
||||
if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip cast to non-primitive type
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(_, cast_to) = expr.kind;
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
|
||||
if let Res::PrimTy(_) = path.res;
|
||||
then {}
|
||||
else {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
|
||||
|
||||
if let Some(lit) = get_numeric_literal(cast_expr) {
|
||||
let literal_str = &cast_str;
|
||||
|
||||
|
@ -162,3 +230,61 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
|
|||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds whether an `Expr` returns a type alias.
|
||||
///
|
||||
/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
|
||||
/// dark path reimplementing this (or something similar).
|
||||
fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool {
|
||||
for_each_expr(expr, |expr| {
|
||||
// Calls are a `Path`, and usage of locals are a `Path`. So, this checks
|
||||
// - call() as i32
|
||||
// - local as i32
|
||||
if let ExprKind::Path(qpath) = expr.kind {
|
||||
let res = cx.qpath_res(&qpath, expr.hir_id);
|
||||
// Function call
|
||||
if let Res::Def(DefKind::Fn, def_id) = res {
|
||||
let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else {
|
||||
return ControlFlow::Continue(());
|
||||
};
|
||||
// This is the worst part of this entire function. This is the only way I know of to
|
||||
// check whether a function returns a type alias. Sure, you can get the return type
|
||||
// from a function in the current crate as an hir ty, but how do you get it for
|
||||
// external functions?? Simple: It's impossible. So, we check whether a part of the
|
||||
// function's declaration snippet is exactly equal to the `Ty`. That way, we can
|
||||
// see whether it's a type alias.
|
||||
//
|
||||
// Will this work for more complex types? Probably not!
|
||||
if !snippet
|
||||
.split("->")
|
||||
.skip(0)
|
||||
.map(|s| {
|
||||
s.trim() == cast_from.to_string()
|
||||
|| s.split("where").any(|ty| ty.trim() == cast_from.to_string())
|
||||
})
|
||||
.any(|a| a)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
// Local usage
|
||||
} else if let Res::Local(hir_id) = res
|
||||
&& let Some(parent) = get_parent_node(cx.tcx, hir_id)
|
||||
&& let Node::Local(l) = parent
|
||||
{
|
||||
if let Some(e) = l.init && is_cast_from_ty_alias(cx, e, cast_from) {
|
||||
return ControlFlow::Break::<()>(());
|
||||
}
|
||||
|
||||
if let Some(ty) = l.ty
|
||||
&& let TyKind::Path(qpath) = ty.kind
|
||||
&& is_ty_alias(&qpath)
|
||||
{
|
||||
return ControlFlow::Break::<()>(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// Manual edits will be overwritten.
|
||||
|
||||
pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
|
@ -38,6 +40,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::allow_attributes::ALLOW_ATTRIBUTES_INFO,
|
||||
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
|
||||
crate::approx_const::APPROX_CONSTANT_INFO,
|
||||
crate::arc_with_non_send_sync::ARC_WITH_NON_SEND_SYNC_INFO,
|
||||
crate::as_conversions::AS_CONVERSIONS_INFO,
|
||||
crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO,
|
||||
crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO,
|
||||
|
@ -51,6 +54,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
crate::attrs::INLINE_ALWAYS_INFO,
|
||||
crate::attrs::MAYBE_MISUSED_CFG_INFO,
|
||||
crate::attrs::MISMATCHED_TARGET_OS_INFO,
|
||||
crate::attrs::NON_MINIMAL_CFG_INFO,
|
||||
crate::attrs::USELESS_ATTRIBUTE_INFO,
|
||||
|
@ -136,11 +140,15 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::double_parens::DOUBLE_PARENS_INFO,
|
||||
crate::drop_forget_ref::DROP_NON_DROP_INFO,
|
||||
crate::drop_forget_ref::FORGET_NON_DROP_INFO,
|
||||
crate::drop_forget_ref::MEM_FORGET_INFO,
|
||||
crate::duplicate_mod::DUPLICATE_MOD_INFO,
|
||||
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
|
||||
crate::empty_drop::EMPTY_DROP_INFO,
|
||||
crate::empty_enum::EMPTY_ENUM_INFO,
|
||||
crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
|
||||
crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,
|
||||
crate::endian_bytes::HOST_ENDIAN_BYTES_INFO,
|
||||
crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO,
|
||||
crate::entry::MAP_ENTRY_INFO,
|
||||
crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO,
|
||||
crate::enum_variants::ENUM_VARIANT_NAMES_INFO,
|
||||
|
@ -152,6 +160,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO,
|
||||
crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO,
|
||||
crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO,
|
||||
crate::excessive_nesting::EXCESSIVE_NESTING_INFO,
|
||||
crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO,
|
||||
crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
|
||||
crate::exit::EXIT_INFO,
|
||||
|
@ -197,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
|
||||
crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
|
||||
crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
|
||||
crate::incorrect_impls::INCORRECT_CLONE_IMPL_ON_COPY_TYPE_INFO,
|
||||
crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
|
||||
crate::indexing_slicing::INDEXING_SLICING_INFO,
|
||||
crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
|
||||
|
@ -219,6 +229,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::large_futures::LARGE_FUTURES_INFO,
|
||||
crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
|
||||
crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
|
||||
crate::large_stack_frames::LARGE_STACK_FRAMES_INFO,
|
||||
crate::len_zero::COMPARISON_TO_EMPTY_INFO,
|
||||
crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
|
||||
crate::len_zero::LEN_ZERO_INFO,
|
||||
|
@ -266,6 +277,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
|
||||
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
|
||||
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
|
||||
crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO,
|
||||
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
|
||||
crate::manual_retain::MANUAL_RETAIN_INFO,
|
||||
crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
|
||||
|
@ -299,7 +311,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::matches::TRY_ERR_INFO,
|
||||
crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO,
|
||||
crate::matches::WILDCARD_IN_OR_PATTERNS_INFO,
|
||||
crate::mem_forget::MEM_FORGET_INFO,
|
||||
crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO,
|
||||
crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO,
|
||||
crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO,
|
||||
|
@ -314,6 +325,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::CLONE_ON_COPY_INFO,
|
||||
crate::methods::CLONE_ON_REF_PTR_INFO,
|
||||
crate::methods::COLLAPSIBLE_STR_REPLACE_INFO,
|
||||
crate::methods::DRAIN_COLLECT_INFO,
|
||||
crate::methods::ERR_EXPECT_INFO,
|
||||
crate::methods::EXPECT_FUN_CALL_INFO,
|
||||
crate::methods::EXPECT_USED_INFO,
|
||||
|
@ -398,6 +410,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::UNNECESSARY_FOLD_INFO,
|
||||
crate::methods::UNNECESSARY_JOIN_INFO,
|
||||
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
|
||||
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
|
||||
crate::methods::UNNECESSARY_SORT_BY_INFO,
|
||||
crate::methods::UNNECESSARY_TO_OWNED_INFO,
|
||||
crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO,
|
||||
|
@ -407,6 +420,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::VERBOSE_FILE_READS_INFO,
|
||||
crate::methods::WRONG_SELF_CONVENTION_INFO,
|
||||
crate::methods::ZST_OFFSET_INFO,
|
||||
crate::min_ident_chars::MIN_IDENT_CHARS_INFO,
|
||||
crate::minmax::MIN_MAX_INFO,
|
||||
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
|
||||
crate::misc::TOPLEVEL_REF_ARG_INFO,
|
||||
|
@ -450,6 +464,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
|
||||
crate::needless_else::NEEDLESS_ELSE_INFO,
|
||||
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
||||
crate::needless_if::NEEDLESS_IF_INFO,
|
||||
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
|
||||
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
|
||||
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
|
||||
|
@ -524,6 +539,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::ranges::RANGE_MINUS_ONE_INFO,
|
||||
crate::ranges::RANGE_PLUS_ONE_INFO,
|
||||
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
|
||||
crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO,
|
||||
crate::raw_strings::NEEDLESS_RAW_STRING_HASHES_INFO,
|
||||
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
|
||||
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
|
||||
crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
|
||||
|
@ -535,6 +552,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::redundant_slicing::DEREF_BY_SLICING_INFO,
|
||||
crate::redundant_slicing::REDUNDANT_SLICING_INFO,
|
||||
crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO,
|
||||
crate::redundant_type_annotations::REDUNDANT_TYPE_ANNOTATIONS_INFO,
|
||||
crate::ref_option_ref::REF_OPTION_REF_INFO,
|
||||
crate::ref_patterns::REF_PATTERNS_INFO,
|
||||
crate::reference::DEREF_ADDROF_INFO,
|
||||
|
@ -553,8 +571,10 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::shadow::SHADOW_SAME_INFO,
|
||||
crate::shadow::SHADOW_UNRELATED_INFO,
|
||||
crate::significant_drop_tightening::SIGNIFICANT_DROP_TIGHTENING_INFO,
|
||||
crate::single_call_fn::SINGLE_CALL_FN_INFO,
|
||||
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
|
||||
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
|
||||
crate::single_range_in_vec_init::SINGLE_RANGE_IN_VEC_INIT_INFO,
|
||||
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
|
||||
crate::size_of_ref::SIZE_OF_REF_INFO,
|
||||
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
|
||||
|
@ -644,6 +664,9 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::useless_conversion::USELESS_CONVERSION_INFO,
|
||||
crate::vec::USELESS_VEC_INFO,
|
||||
crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO,
|
||||
crate::visibility::NEEDLESS_PUB_SELF_INFO,
|
||||
crate::visibility::PUB_WITHOUT_SHORTHAND_INFO,
|
||||
crate::visibility::PUB_WITH_SHORTHAND_INFO,
|
||||
crate::wildcard_imports::ENUM_GLOB_USE_INFO,
|
||||
crate::wildcard_imports::WILDCARD_IMPORTS_INFO,
|
||||
crate::write::PRINTLN_EMPTY_STRING_INFO,
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for construction on unit struct using `default`.
|
||||
/// Checks for construction on unit struct using `default`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This adds code complexity and an unnecessary function call.
|
||||
|
|
|
@ -161,7 +161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
let fields_def = &variant.fields;
|
||||
|
||||
// Push field type then visit each field expr.
|
||||
for field in fields.iter() {
|
||||
for field in *fields {
|
||||
let bound =
|
||||
fields_def
|
||||
.iter()
|
||||
|
|
|
@ -56,9 +56,11 @@ declare_clippy_lint! {
|
|||
/// let b = &*a;
|
||||
/// ```
|
||||
///
|
||||
/// This lint excludes:
|
||||
/// This lint excludes all of:
|
||||
/// ```rust,ignore
|
||||
/// let _ = d.unwrap().deref();
|
||||
/// let _ = Foo::deref(&foo);
|
||||
/// let _ = <Foo as Deref>::deref(&foo);
|
||||
/// ```
|
||||
#[clippy::version = "1.44.0"]
|
||||
pub EXPLICIT_DEREF_METHODS,
|
||||
|
@ -355,15 +357,17 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
// start auto-deref.
|
||||
// 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
|
||||
// adjustments will not be inserted automatically, then leave one further reference to avoid
|
||||
// moving a mutable borrow.
|
||||
// e.g.
|
||||
// fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
|
||||
// let x = match x {
|
||||
// // Removing the borrow will cause `x` to be moved
|
||||
// Some(x) => &mut *x,
|
||||
// None => y
|
||||
// };
|
||||
// }
|
||||
// moving a mutable borrow. e.g.
|
||||
//
|
||||
// ```rust
|
||||
// fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
|
||||
// let x = match x {
|
||||
// // Removing the borrow will cause `x` to be moved
|
||||
// Some(x) => &mut *x,
|
||||
// None => y
|
||||
// };
|
||||
// }
|
||||
// ```
|
||||
let deref_msg =
|
||||
"this expression creates a reference which is immediately dereferenced by the compiler";
|
||||
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
|
||||
|
@ -1481,7 +1485,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
|||
target_mut,
|
||||
} => {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
|
||||
let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let (_, ref_count) = peel_mid_ty_refs(ty);
|
||||
let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
|
||||
|
@ -1504,11 +1508,20 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
|||
"&"
|
||||
};
|
||||
|
||||
let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
|
||||
format!("({expr_str})")
|
||||
// expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's
|
||||
// `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary.
|
||||
/*
|
||||
expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
|
||||
Cow::Owned(format!("({expr_str})"))
|
||||
} else {
|
||||
expr_str.into_owned()
|
||||
expr_str
|
||||
};
|
||||
*/
|
||||
|
||||
// Fix #10850, do not lint if it's `Foo::deref` instead of `foo.deref()`.
|
||||
if is_final_ufcs {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -4,11 +4,13 @@ use clippy_utils::source::indent_of;
|
|||
use clippy_utils::{is_default_equivalent, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
self as hir,
|
||||
def::{CtorKind, CtorOf, DefKind, Res},
|
||||
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
|
||||
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{Adt, AdtDef, SubstsRef};
|
||||
use rustc_middle::ty::adjustment::{Adjust, PointerCast};
|
||||
use rustc_middle::ty::{self, Adt, AdtDef, SubstsRef, Ty, TypeckResults};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
|
@ -75,13 +77,23 @@ fn is_path_self(e: &Expr<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn contains_trait_object(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Ref(_, ty, _) => contains_trait_object(*ty),
|
||||
ty::Adt(def, substs) => def.is_box() && substs[0].as_type().map_or(false, contains_trait_object),
|
||||
ty::Dynamic(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_struct<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item: &'tcx Item<'_>,
|
||||
self_ty: &Ty<'_>,
|
||||
self_ty: &hir::Ty<'_>,
|
||||
func_expr: &Expr<'_>,
|
||||
adt_def: AdtDef<'_>,
|
||||
substs: SubstsRef<'_>,
|
||||
typeck_results: &'tcx TypeckResults<'tcx>,
|
||||
) {
|
||||
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
|
||||
if let Some(PathSegment { args, .. }) = p.segments.last() {
|
||||
|
@ -96,10 +108,23 @@ fn check_struct<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the default() call might unsize coerce to a trait object (e.g. Box<T> to Box<dyn Trait>),
|
||||
// which would not be the same if derived (see #10158).
|
||||
// this closure checks both if the expr is equivalent to a `default()` call and does not
|
||||
// have such coercions.
|
||||
let is_default_without_adjusts = |expr| {
|
||||
is_default_equivalent(cx, expr)
|
||||
&& typeck_results.expr_adjustments(expr).iter().all(|adj| {
|
||||
!matches!(adj.kind, Adjust::Pointer(PointerCast::Unsize)
|
||||
if contains_trait_object(adj.target))
|
||||
})
|
||||
};
|
||||
|
||||
let should_emit = match peel_blocks(func_expr).kind {
|
||||
ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
|
||||
ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
|
||||
ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
|
||||
ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts),
|
||||
ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts),
|
||||
ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
@ -197,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
|
|||
|
||||
then {
|
||||
if adt_def.is_struct() {
|
||||
check_struct(cx, item, self_ty, func_expr, adt_def, substs);
|
||||
check_struct(cx, item, self_ty, func_expr, adt_def, substs, cx.tcx.typeck_body(*b));
|
||||
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
|
||||
check_enum(cx, item, func_expr, adt_def);
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::{
|
||||
self, BoundConstness, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv,
|
||||
ToPredicate, TraitPredicate, Ty, TyCtxt,
|
||||
self, BoundConstness, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate,
|
||||
TraitPredicate, Ty, TyCtxt,
|
||||
};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
@ -520,7 +520,8 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
|
|||
trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]),
|
||||
constness: BoundConstness::NotConst,
|
||||
polarity: ImplPolarity::Positive,
|
||||
}).to_predicate(tcx)
|
||||
})
|
||||
.to_predicate(tcx)
|
||||
}),
|
||||
)),
|
||||
Reveal::UserFacing,
|
||||
|
|
|
@ -571,6 +571,7 @@ 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 no_test = false;
|
||||
let mut edition = None;
|
||||
let mut ticks_unbalanced = false;
|
||||
let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
|
||||
|
@ -584,6 +585,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
if item == "ignore" {
|
||||
is_rust = false;
|
||||
break;
|
||||
} else if item == "no_test" {
|
||||
no_test = true;
|
||||
}
|
||||
if let Some(stripped) = item.strip_prefix("edition") {
|
||||
is_rust = true;
|
||||
|
@ -648,7 +651,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
headers.errors |= in_heading && trimmed_text == "Errors";
|
||||
headers.panics |= in_heading && trimmed_text == "Panics";
|
||||
if in_code {
|
||||
if is_rust {
|
||||
if is_rust && !no_test {
|
||||
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
|
||||
check_code(cx, &text, edition, span);
|
||||
}
|
||||
|
@ -906,15 +909,15 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
|
|||
if is_panic(self.cx, macro_call.def_id)
|
||||
|| matches!(
|
||||
self.cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"assert" | "assert_eq" | "assert_ne" | "todo"
|
||||
"assert" | "assert_eq" | "assert_ne"
|
||||
)
|
||||
{
|
||||
self.panic_span = Some(macro_call.span);
|
||||
}
|
||||
}
|
||||
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
|
||||
// check for `unwrap` and `expect` for both `Option` and `Result`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) {
|
||||
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
|
||||
|
|
|
@ -6,6 +6,7 @@ use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -47,6 +48,27 @@ declare_clippy_lint! {
|
|||
"call to `std::mem::forget` with a value which does not implement `Drop`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `std::mem::forget(t)` where `t` is
|
||||
/// `Drop` or has a field that implements `Drop`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `std::mem::forget(t)` prevents `t` from running its
|
||||
/// destructor, possibly causing leaks.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::mem;
|
||||
/// # use std::rc::Rc;
|
||||
/// mem::forget(Rc::new(55))
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MEM_FORGET,
|
||||
restriction,
|
||||
"`mem::forget` usage on `Drop` types, likely to cause memory leaks"
|
||||
}
|
||||
|
||||
const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
|
||||
Dropping such a type only extends its contained lifetimes";
|
||||
const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
|
||||
|
@ -55,6 +77,7 @@ const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value t
|
|||
declare_lint_pass!(DropForgetRef => [
|
||||
DROP_NON_DROP,
|
||||
FORGET_NON_DROP,
|
||||
MEM_FORGET,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
||||
|
@ -67,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
|||
let arg_ty = cx.typeck_results().expr_ty(arg);
|
||||
let is_copy = is_copy(cx, arg_ty);
|
||||
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
|
||||
let (lint, msg) = match fn_name {
|
||||
let (lint, msg, note_span) = match fn_name {
|
||||
// early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references, forgetting_copy_types
|
||||
sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return,
|
||||
sym::mem_forget if arg_ty.is_ref() => return,
|
||||
|
@ -81,19 +104,34 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
|||
|| drop_is_single_call_in_arm
|
||||
) =>
|
||||
{
|
||||
(DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
|
||||
},
|
||||
sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => {
|
||||
(FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY)
|
||||
(DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span))
|
||||
},
|
||||
sym::mem_forget => {
|
||||
if arg_ty.needs_drop(cx.tcx, cx.param_env) {
|
||||
(
|
||||
MEM_FORGET,
|
||||
Cow::Owned(format!(
|
||||
"usage of `mem::forget` on {}",
|
||||
if arg_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
|
||||
"`Drop` type"
|
||||
} else {
|
||||
"type with `Drop` fields"
|
||||
}
|
||||
)),
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
(FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY.into(), Some(arg.span))
|
||||
}
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
msg,
|
||||
Some(arg.span),
|
||||
&msg,
|
||||
note_span,
|
||||
&format!("argument has type `{arg_ty}`"),
|
||||
);
|
||||
}
|
||||
|
|
216
clippy_lints/src/endian_bytes.rs
Normal file
216
clippy_lints/src/endian_bytes.rs
Normal file
|
@ -0,0 +1,216 @@
|
|||
use crate::Lint;
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, is_lint_allowed};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::{lint::in_external_macro, ty::Ty};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Symbol;
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not, but some may prefer to specify the target endianness explicitly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _x = 2i32.to_ne_bytes();
|
||||
/// let _y = 2i64.to_ne_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub HOST_ENDIAN_BYTES,
|
||||
restriction,
|
||||
"disallows usage of the `to_ne_bytes` method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not, but some may wish to lint usage of this method, either to suggest using the host
|
||||
/// endianness or big endian.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _x = 2i32.to_le_bytes();
|
||||
/// let _y = 2i64.to_le_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub LITTLE_ENDIAN_BYTES,
|
||||
restriction,
|
||||
"disallows usage of the `to_le_bytes` method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not, but some may wish to lint usage of this method, either to suggest using the host
|
||||
/// endianness or little endian.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _x = 2i32.to_be_bytes();
|
||||
/// let _y = 2i64.to_be_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub BIG_ENDIAN_BYTES,
|
||||
restriction,
|
||||
"disallows usage of the `to_be_bytes` method"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
|
||||
|
||||
const HOST_NAMES: [&str; 2] = ["from_ne_bytes", "to_ne_bytes"];
|
||||
const LITTLE_NAMES: [&str; 2] = ["from_le_bytes", "to_le_bytes"];
|
||||
const BIG_NAMES: [&str; 2] = ["from_be_bytes", "to_be_bytes"];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum LintKind {
|
||||
Host,
|
||||
Little,
|
||||
Big,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Prefix {
|
||||
From,
|
||||
To,
|
||||
}
|
||||
|
||||
impl LintKind {
|
||||
fn allowed(&self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
is_lint_allowed(cx, self.as_lint(), expr.hir_id)
|
||||
}
|
||||
|
||||
fn as_lint(&self) -> &'static Lint {
|
||||
match self {
|
||||
LintKind::Host => HOST_ENDIAN_BYTES,
|
||||
LintKind::Little => LITTLE_ENDIAN_BYTES,
|
||||
LintKind::Big => BIG_ENDIAN_BYTES,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_name(&self, prefix: Prefix) -> &str {
|
||||
let index = usize::from(prefix == Prefix::To);
|
||||
|
||||
match self {
|
||||
LintKind::Host => HOST_NAMES[index],
|
||||
LintKind::Little => LITTLE_NAMES[index],
|
||||
LintKind::Big => BIG_NAMES[index],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for EndianBytes {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind;
|
||||
if args.is_empty();
|
||||
let ty = cx.typeck_results().expr_ty(receiver);
|
||||
if ty.is_primitive_ty();
|
||||
if maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty);
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(function, ..) = expr.kind;
|
||||
if let ExprKind::Path(qpath) = function.kind;
|
||||
if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id();
|
||||
if let Some(function_name) = cx.get_def_path(def_id).last();
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if ty.is_primitive_ty();
|
||||
then {
|
||||
maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool {
|
||||
let ne = LintKind::Host.as_name(prefix);
|
||||
let le = LintKind::Little.as_name(prefix);
|
||||
let be = LintKind::Big.as_name(prefix);
|
||||
|
||||
let (lint, other_lints) = match name.as_str() {
|
||||
name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]),
|
||||
name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]),
|
||||
name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]),
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let mut help = None;
|
||||
|
||||
'build_help: {
|
||||
// all lints disallowed, don't give help here
|
||||
if [&[lint], other_lints.as_slice()]
|
||||
.concat()
|
||||
.iter()
|
||||
.all(|lint| !lint.allowed(cx, expr))
|
||||
{
|
||||
break 'build_help;
|
||||
}
|
||||
|
||||
// ne_bytes and all other lints allowed
|
||||
if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
|
||||
help = Some(Cow::Borrowed("specify the desired endianness explicitly"));
|
||||
break 'build_help;
|
||||
}
|
||||
|
||||
// le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
|
||||
// le_bytes is not
|
||||
if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
|
||||
help = Some(Cow::Borrowed("use the native endianness instead"));
|
||||
break 'build_help;
|
||||
}
|
||||
|
||||
let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
|
||||
let len = allowed_lints.clone().count();
|
||||
|
||||
let mut help_str = "use ".to_owned();
|
||||
|
||||
for (i, lint) in allowed_lints.enumerate() {
|
||||
let only_one = len == 1;
|
||||
if !only_one {
|
||||
help_str.push_str("either of ");
|
||||
}
|
||||
|
||||
help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
|
||||
|
||||
if i != len && !only_one {
|
||||
help_str.push_str("or ");
|
||||
}
|
||||
}
|
||||
|
||||
help = Some(Cow::Owned(help_str + "instead"));
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint.as_lint(),
|
||||
expr.span,
|
||||
&format!(
|
||||
"usage of the {}`{ty}::{}`{}",
|
||||
if prefix == Prefix::From { "function " } else { "" },
|
||||
lint.as_name(prefix),
|
||||
if prefix == Prefix::To { " method" } else { "" },
|
||||
),
|
||||
move |diag| {
|
||||
if let Some(help) = help {
|
||||
diag.help(help);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
true
|
||||
}
|
|
@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
|
|||
.const_eval_poly(def_id.to_def_id())
|
||||
.ok()
|
||||
.map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty));
|
||||
if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx.tcx, c)) {
|
||||
if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c)) {
|
||||
if let ty::Adt(adt, _) = ty.kind() {
|
||||
if adt.is_enum() {
|
||||
ty = adt.repr().discr_type().to_ty(cx.tcx);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
|
||||
use clippy_utils::source::is_present_in_source;
|
||||
use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start};
|
||||
use rustc_hir::{EnumDef, Item, ItemKind, Variant};
|
||||
use rustc_hir::{EnumDef, Item, ItemKind, OwnerId, Variant};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
|
@ -105,18 +105,20 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct EnumVariantNames {
|
||||
modules: Vec<(Symbol, String)>,
|
||||
modules: Vec<(Symbol, String, OwnerId)>,
|
||||
threshold: u64,
|
||||
avoid_breaking_exported_api: bool,
|
||||
allow_private_module_inception: bool,
|
||||
}
|
||||
|
||||
impl EnumVariantNames {
|
||||
#[must_use]
|
||||
pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self {
|
||||
pub fn new(threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool) -> Self {
|
||||
Self {
|
||||
modules: Vec::new(),
|
||||
threshold,
|
||||
avoid_breaking_exported_api,
|
||||
allow_private_module_inception,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -252,18 +254,19 @@ impl LateLintPass<'_> for EnumVariantNames {
|
|||
let item_name = item.ident.name.as_str();
|
||||
let item_camel = to_camel_case(item_name);
|
||||
if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
|
||||
if let Some((mod_name, mod_camel)) = self.modules.last() {
|
||||
if let [.., (mod_name, mod_camel, owner_id)] = &*self.modules {
|
||||
// constants don't have surrounding modules
|
||||
if !mod_camel.is_empty() {
|
||||
if mod_name == &item.ident.name {
|
||||
if let ItemKind::Mod(..) = item.kind {
|
||||
span_lint(
|
||||
cx,
|
||||
MODULE_INCEPTION,
|
||||
item.span,
|
||||
"module has the same name as its containing module",
|
||||
);
|
||||
}
|
||||
if mod_name == &item.ident.name
|
||||
&& let ItemKind::Mod(..) = item.kind
|
||||
&& (!self.allow_private_module_inception || cx.tcx.visibility(owner_id.def_id).is_public())
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MODULE_INCEPTION,
|
||||
item.span,
|
||||
"module has the same name as its containing module",
|
||||
);
|
||||
}
|
||||
// The `module_name_repetitions` lint should only trigger if the item has the module in its
|
||||
// name. Having the same name is accepted.
|
||||
|
@ -302,6 +305,6 @@ impl LateLintPass<'_> for EnumVariantNames {
|
|||
check_variant(cx, self.threshold, def, item_name, item.span);
|
||||
}
|
||||
}
|
||||
self.modules.push((item.ident.name, item_camel));
|
||||
self.modules.push((item.ident.name, item_camel, item.owner_id));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,13 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
|
||||
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
|
||||
if let ty::Closure(_, substs) = *closure_ty.kind();
|
||||
// Don't lint if this is an inclusive range expression.
|
||||
// They desugar to a call to `RangeInclusiveNew` which would have odd suggestions. (#10684)
|
||||
if !matches!(higher::Range::hir(body.value), Some(higher::Range {
|
||||
start: Some(_),
|
||||
end: Some(_),
|
||||
limits: rustc_ast::RangeLimits::Closed
|
||||
}));
|
||||
then {
|
||||
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
|
||||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||
|
@ -136,6 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
// Mutable closure is used after current expr; we cannot consume it.
|
||||
snippet = format!("&mut {snippet}");
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"replace the closure with the function itself",
|
||||
|
|
181
clippy_lints/src/excessive_nesting.rs
Normal file
181
clippy_lints/src/excessive_nesting.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_help, source::snippet};
|
||||
use rustc_ast::{
|
||||
node_id::NodeSet,
|
||||
visit::{walk_block, walk_item, Visitor},
|
||||
Block, Crate, Inline, Item, ItemKind, ModKind, NodeId,
|
||||
};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for blocks which are nested beyond a certain threshold.
|
||||
///
|
||||
/// Note: Even though this lint is warn-by-default, it will only trigger if a maximum nesting level is defined in the clippy.toml file.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can severely hinder readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// An example clippy.toml configuration:
|
||||
/// ```toml
|
||||
/// # clippy.toml
|
||||
/// excessive-nesting-threshold = 3
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// // lib.rs
|
||||
/// pub mod a {
|
||||
/// pub struct X;
|
||||
/// impl X {
|
||||
/// pub fn run(&self) {
|
||||
/// if true {
|
||||
/// // etc...
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// // a.rs
|
||||
/// fn private_run(x: &X) {
|
||||
/// if true {
|
||||
/// // etc...
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// pub struct X;
|
||||
/// impl X {
|
||||
/// pub fn run(&self) {
|
||||
/// private_run(self);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// // lib.rs
|
||||
/// pub mod a;
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub EXCESSIVE_NESTING,
|
||||
complexity,
|
||||
"checks for blocks nested beyond a certain threshold"
|
||||
}
|
||||
impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExcessiveNesting {
|
||||
pub excessive_nesting_threshold: u64,
|
||||
pub nodes: NodeSet,
|
||||
}
|
||||
|
||||
impl ExcessiveNesting {
|
||||
pub fn check_node_id(&self, cx: &EarlyContext<'_>, span: Span, node_id: NodeId) {
|
||||
if self.nodes.contains(&node_id) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
EXCESSIVE_NESTING,
|
||||
span,
|
||||
"this block is too nested",
|
||||
None,
|
||||
"try refactoring your code to minimize nesting",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for ExcessiveNesting {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
|
||||
if self.excessive_nesting_threshold == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut visitor = NestingVisitor {
|
||||
conf: self,
|
||||
cx,
|
||||
nest_level: 0,
|
||||
};
|
||||
|
||||
for item in &krate.items {
|
||||
visitor.visit_item(item);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
|
||||
self.check_node_id(cx, block.span, block.id);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
self.check_node_id(cx, item.span, item.id);
|
||||
}
|
||||
}
|
||||
|
||||
struct NestingVisitor<'conf, 'cx> {
|
||||
conf: &'conf mut ExcessiveNesting,
|
||||
cx: &'cx EarlyContext<'cx>,
|
||||
nest_level: u64,
|
||||
}
|
||||
|
||||
impl NestingVisitor<'_, '_> {
|
||||
fn check_indent(&mut self, span: Span, id: NodeId) -> bool {
|
||||
if self.nest_level > self.conf.excessive_nesting_threshold && !in_external_macro(self.cx.sess(), span) {
|
||||
self.conf.nodes.insert(id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'conf, 'cx> Visitor<'_> for NestingVisitor<'conf, 'cx> {
|
||||
fn visit_block(&mut self, block: &Block) {
|
||||
if block.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This should be rewritten using `LateLintPass` so we can use `is_from_proc_macro` instead,
|
||||
// but for now, this is fine.
|
||||
let snippet = snippet(self.cx, block.span, "{}").trim().to_owned();
|
||||
if !snippet.starts_with('{') || !snippet.ends_with('}') {
|
||||
return;
|
||||
}
|
||||
|
||||
self.nest_level += 1;
|
||||
|
||||
if !self.check_indent(block.span, block.id) {
|
||||
walk_block(self, block);
|
||||
}
|
||||
|
||||
self.nest_level -= 1;
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, item: &Item) {
|
||||
if item.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _)) => {
|
||||
self.nest_level += 1;
|
||||
|
||||
if !self.check_indent(item.span, item.id) {
|
||||
walk_item(self, item);
|
||||
}
|
||||
|
||||
self.nest_level -= 1;
|
||||
},
|
||||
// Reset nesting level for non-inline modules (since these are in another file)
|
||||
ItemKind::Mod(..) => walk_item(
|
||||
&mut NestingVisitor {
|
||||
conf: self.conf,
|
||||
cx: self.cx,
|
||||
nest_level: 0,
|
||||
},
|
||||
item,
|
||||
),
|
||||
_ => walk_item(self, item),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -265,6 +266,7 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
|
|||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Fn(_, generics, body_id) = item.kind
|
||||
&& !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id)
|
||||
&& !is_from_proc_macro(cx, item)
|
||||
{
|
||||
let mut walker = TypeWalker::new(cx, generics);
|
||||
walk_item(&mut walker, item);
|
||||
|
|
|
@ -82,19 +82,24 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
|||
LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"),
|
||||
LitFloatType::Unsuffixed => None
|
||||
};
|
||||
let (is_whole, mut float_str) = match fty {
|
||||
let (is_whole, is_inf, mut float_str) = match fty {
|
||||
FloatTy::F32 => {
|
||||
let value = sym_str.parse::<f32>().unwrap();
|
||||
|
||||
(value.fract() == 0.0, formatter.format(value))
|
||||
(value.fract() == 0.0, value.is_infinite(), formatter.format(value))
|
||||
},
|
||||
FloatTy::F64 => {
|
||||
let value = sym_str.parse::<f64>().unwrap();
|
||||
|
||||
(value.fract() == 0.0, formatter.format(value))
|
||||
|
||||
(value.fract() == 0.0, value.is_infinite(), formatter.format(value))
|
||||
},
|
||||
};
|
||||
|
||||
if is_inf {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
|
||||
// Normalize the literal by stripping the fractional portion
|
||||
if sym_str.split('.').next().unwrap() != float_str {
|
||||
|
|
|
@ -215,7 +215,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
|
|||
// ranges [-16777215, 16777216) for type f32 as whole number floats outside
|
||||
// this range are lossy and ambiguous.
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
|
||||
fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
|
||||
match value {
|
||||
F32(num) if num.fract() == 0.0 => {
|
||||
if (-16_777_215.0..16_777_216.0).contains(num) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
|
||||
use clippy_utils::{higher, match_def_path, paths};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
@ -44,10 +44,24 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
|||
is_type_lang_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), LangItem::String)
|
||||
}
|
||||
fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
|
||||
let e = e.peel_blocks().peel_borrows();
|
||||
|
||||
if e.span.from_expansion()
|
||||
&& let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id
|
||||
{
|
||||
cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro)
|
||||
} else if let Some(higher::If { then, r#else, .. }) = higher::If::hir(e) {
|
||||
is_format(cx, then) || r#else.is_some_and(|e| is_format(cx, e))
|
||||
} else {
|
||||
false
|
||||
match higher::IfLetOrMatch::parse(cx, e) {
|
||||
Some(higher::IfLetOrMatch::Match(_, arms, MatchSource::Normal)) => {
|
||||
arms.iter().any(|arm| is_format(cx, arm.body))
|
||||
},
|
||||
Some(higher::IfLetOrMatch::IfLet(_, _, then, r#else)) => {
|
||||
is_format(cx, then) ||r#else.is_some_and(|e| is_format(cx, e))
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +82,6 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
|
|||
},
|
||||
_ => return,
|
||||
};
|
||||
let (arg, _) = peel_hir_expr_refs(arg);
|
||||
if is_format(cx, arg) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
|
|
@ -236,6 +236,12 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
}
|
||||
}
|
||||
|
||||
// Don't warn if the only thing inside post_else_post_eol is a comment block.
|
||||
let trimmed_post_else_post_eol = post_else_post_eol.trim();
|
||||
if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") {
|
||||
return
|
||||
}
|
||||
|
||||
let else_desc = if is_if(else_) { "if" } else { "{..}" };
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
|
|
|
@ -134,9 +134,10 @@ impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> {
|
|||
kw::SelfUpper => self.upper.push(segment.ident.span),
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
self.invalid |= segment.ident.span.from_expansion();
|
||||
}
|
||||
|
||||
self.invalid |= path.span.from_expansion();
|
||||
if !self.invalid {
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
@ -156,6 +157,11 @@ fn convert_to_from(
|
|||
self_ty: &Ty<'_>,
|
||||
impl_item_ref: &ImplItemRef,
|
||||
) -> Option<Vec<(Span, String)>> {
|
||||
if !target_ty.find_self_aliases().is_empty() {
|
||||
// It's tricky to expand self-aliases correctly, we'll ignore it to not cause a
|
||||
// bad suggestion/fix.
|
||||
return None;
|
||||
}
|
||||
let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
|
||||
let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None };
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
|
124
clippy_lints/src/incorrect_impls.rs
Normal file
124
clippy_lints/src/incorrect_impls.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_node, last_path_segment, ty::implements_trait};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{ExprKind, ImplItem, ImplItemKind, ItemKind, Node, UnOp};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::EarlyBinder;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for manual implementations of `Clone` when `Copy` is already implemented.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If both `Clone` and `Copy` are implemented, they must agree. This is done by dereferencing
|
||||
/// `self` in `Clone`'s implementation. Anything else is incorrect.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[derive(Eq, PartialEq)]
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl Clone for A {
|
||||
/// fn clone(&self) -> Self {
|
||||
/// Self(self.0)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Copy for A {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[derive(Eq, PartialEq)]
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl Clone for A {
|
||||
/// fn clone(&self) -> Self {
|
||||
/// *self
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Copy for A {}
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
|
||||
correctness,
|
||||
"manual implementation of `Clone` on a `Copy` type"
|
||||
}
|
||||
declare_lint_pass!(IncorrectImpls => [INCORRECT_CLONE_IMPL_ON_COPY_TYPE]);
|
||||
|
||||
impl LateLintPass<'_> for IncorrectImpls {
|
||||
#[expect(clippy::needless_return)]
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
|
||||
let node = get_parent_node(cx.tcx, impl_item.hir_id());
|
||||
let Some(Node::Item(item)) = node else {
|
||||
return;
|
||||
};
|
||||
let ItemKind::Impl(imp) = item.kind else {
|
||||
return;
|
||||
};
|
||||
let Some(trait_impl) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::skip_binder) else {
|
||||
return;
|
||||
};
|
||||
let trait_impl_def_id = trait_impl.def_id;
|
||||
if cx.tcx.is_automatically_derived(item.owner_id.to_def_id()) {
|
||||
return;
|
||||
}
|
||||
let ImplItemKind::Fn(_, impl_item_id) = cx.tcx.hir().impl_item(impl_item.impl_item_id()).kind else {
|
||||
return;
|
||||
};
|
||||
let body = cx.tcx.hir().body(impl_item_id);
|
||||
let ExprKind::Block(block, ..) = body.value.kind else {
|
||||
return;
|
||||
};
|
||||
// Above is duplicated from the `duplicate_manual_partial_ord_impl` branch.
|
||||
// Remove it while solving conflicts once that PR is merged.
|
||||
|
||||
// Actual implementation; remove this comment once aforementioned PR is merged
|
||||
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl_def_id)
|
||||
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
|
||||
&& implements_trait(
|
||||
cx,
|
||||
hir_ty_to_ty(cx.tcx, imp.self_ty),
|
||||
copy_def_id,
|
||||
trait_impl.substs,
|
||||
)
|
||||
{
|
||||
if impl_item.ident.name == sym::clone {
|
||||
if block.stmts.is_empty()
|
||||
&& let Some(expr) = block.expr
|
||||
&& let ExprKind::Unary(UnOp::Deref, inner) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = inner.kind
|
||||
&& last_path_segment(&qpath).ident.name == symbol::kw::SelfLower
|
||||
{} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
|
||||
block.span,
|
||||
"incorrect implementation of `clone` on a `Copy` type",
|
||||
"change this to",
|
||||
"{ *self }".to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if impl_item.ident.name == sym::clone_from {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
INCORRECT_CLONE_IMPL_ON_COPY_TYPE,
|
||||
impl_item.span,
|
||||
"incorrect implementation of `clone_from` on a `Copy` type",
|
||||
"remove this",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_help, is_in_cfg_test};
|
||||
use clippy_utils::{diagnostics::span_lint_and_help, is_from_proc_macro, is_in_cfg_test};
|
||||
use rustc_hir::{HirId, ItemId, ItemKind, Mod};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -59,6 +59,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
|
|||
if !matches!(item.kind, ItemKind::Mod(_));
|
||||
if !is_in_cfg_test(cx.tcx, itid.hir_id()); // The item isn't in the testing module itself
|
||||
if !in_external_macro(cx.sess(), item.span);
|
||||
if !is_from_proc_macro(cx, item);
|
||||
|
||||
then {
|
||||
span_lint_and_help(cx, ITEMS_AFTER_TEST_MODULE, test_mod_span.unwrap().with_hi(item.span.hi()), "items were found after the testing module", None, "move the items to before the testing module was defined");
|
||||
|
|
162
clippy_lints/src/large_stack_frames.rs
Normal file
162
clippy_lints/src/large_stack_frames.rs
Normal file
|
@ -0,0 +1,162 @@
|
|||
use std::ops::AddAssign;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_note;
|
||||
use clippy_utils::fn_has_unsatisfiable_preds;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::Body;
|
||||
use rustc_hir::FnDecl;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_tool_lint;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions that use a lot of stack space.
|
||||
///
|
||||
/// This often happens when constructing a large type, such as an array with a lot of elements,
|
||||
/// or constructing *many* smaller-but-still-large structs, or copying around a lot of large types.
|
||||
///
|
||||
/// This lint is a more general version of [`large_stack_arrays`](https://rust-lang.github.io/rust-clippy/master/#large_stack_arrays)
|
||||
/// that is intended to look at functions as a whole instead of only individual array expressions inside of a function.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The stack region of memory is very limited in size (usually *much* smaller than the heap) and attempting to
|
||||
/// use too much will result in a stack overflow and crash the program.
|
||||
/// To avoid this, you should consider allocating large types on the heap instead (e.g. by boxing them).
|
||||
///
|
||||
/// Keep in mind that the code path to construction of large types does not even need to be reachable;
|
||||
/// it purely needs to *exist* inside of the function to contribute to the stack size.
|
||||
/// For example, this causes a stack overflow even though the branch is unreachable:
|
||||
/// ```rust,ignore
|
||||
/// fn main() {
|
||||
/// if false {
|
||||
/// let x = [0u8; 10000000]; // 10 MB stack array
|
||||
/// black_box(&x);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Known issues
|
||||
/// False positives. The stack size that clippy sees is an estimated value and can be vastly different
|
||||
/// from the actual stack usage after optimizations passes have run (especially true in release mode).
|
||||
/// Modern compilers are very smart and are able to optimize away a lot of unnecessary stack allocations.
|
||||
/// In debug mode however, it is usually more accurate.
|
||||
///
|
||||
/// This lint works by summing up the size of all variables that the user typed, variables that were
|
||||
/// implicitly introduced by the compiler for temporaries, function arguments and the return value,
|
||||
/// and comparing them against a (configurable, but high-by-default).
|
||||
///
|
||||
/// ### Example
|
||||
/// This function creates four 500 KB arrays on the stack. Quite big but just small enough to not trigger `large_stack_arrays`.
|
||||
/// However, looking at the function as a whole, it's clear that this uses a lot of stack space.
|
||||
/// ```rust
|
||||
/// struct QuiteLargeType([u8; 500_000]);
|
||||
/// fn foo() {
|
||||
/// // ... some function that uses a lot of stack space ...
|
||||
/// let _x1 = QuiteLargeType([0; 500_000]);
|
||||
/// let _x2 = QuiteLargeType([0; 500_000]);
|
||||
/// let _x3 = QuiteLargeType([0; 500_000]);
|
||||
/// let _x4 = QuiteLargeType([0; 500_000]);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Instead of doing this, allocate the arrays on the heap.
|
||||
/// This currently requires going through a `Vec` first and then converting it to a `Box`:
|
||||
/// ```rust
|
||||
/// struct NotSoLargeType(Box<[u8]>);
|
||||
///
|
||||
/// fn foo() {
|
||||
/// let _x1 = NotSoLargeType(vec![0; 500_000].into_boxed_slice());
|
||||
/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Now heap allocated.
|
||||
/// // The size of `NotSoLargeType` is 16 bytes.
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub LARGE_STACK_FRAMES,
|
||||
nursery,
|
||||
"checks for functions that allocate a lot of stack space"
|
||||
}
|
||||
|
||||
pub struct LargeStackFrames {
|
||||
maximum_allowed_size: u64,
|
||||
}
|
||||
|
||||
impl LargeStackFrames {
|
||||
#[must_use]
|
||||
pub fn new(size: u64) -> Self {
|
||||
Self {
|
||||
maximum_allowed_size: size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeStackFrames => [LARGE_STACK_FRAMES]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Space {
|
||||
Used(u64),
|
||||
Overflow,
|
||||
}
|
||||
|
||||
impl Space {
|
||||
pub fn exceeds_limit(self, limit: u64) -> bool {
|
||||
match self {
|
||||
Self::Used(used) => used > limit,
|
||||
Self::Overflow => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<u64> for Space {
|
||||
fn add_assign(&mut self, rhs: u64) {
|
||||
if let Self::Used(lhs) = self {
|
||||
match lhs.checked_add(rhs) {
|
||||
Some(sum) => *self = Self::Used(sum),
|
||||
None => *self = Self::Overflow,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
_: FnKind<'tcx>,
|
||||
_: &'tcx FnDecl<'tcx>,
|
||||
_: &'tcx Body<'tcx>,
|
||||
span: Span,
|
||||
local_def_id: LocalDefId,
|
||||
) {
|
||||
let def_id = local_def_id.to_def_id();
|
||||
// Building MIR for `fn`s with unsatisfiable preds results in ICE.
|
||||
if fn_has_unsatisfiable_preds(cx, def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mir = cx.tcx.optimized_mir(def_id);
|
||||
let param_env = cx.tcx.param_env(def_id);
|
||||
|
||||
let mut frame_size = Space::Used(0);
|
||||
|
||||
for local in &mir.local_decls {
|
||||
if let Ok(layout) = cx.tcx.layout_of(param_env.and(local.ty)) {
|
||||
frame_size += layout.size.bytes();
|
||||
}
|
||||
}
|
||||
|
||||
if frame_size.exceeds_limit(self.maximum_allowed_size) {
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
LARGE_STACK_FRAMES,
|
||||
span,
|
||||
"this function allocates a large amount of stack space",
|
||||
None,
|
||||
"allocating large amounts of stack space can overflow the stack",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_hir::{Local, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -25,14 +26,21 @@ declare_clippy_lint! {
|
|||
declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
|
||||
|
||||
impl LateLintPass<'_> for UnderscoreTyped {
|
||||
fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, local.span);
|
||||
if let Some(ty) = local.ty; // Ensure that it has a type defined
|
||||
if let TyKind::Infer = &ty.kind; // that type is '_'
|
||||
if local.span.ctxt() == ty.span.ctxt();
|
||||
then {
|
||||
span_lint_and_help(cx,
|
||||
// NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
|
||||
// this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`
|
||||
if snippet(cx, ty.span, "_").trim() != "_" {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
LET_WITH_TYPE_UNDERSCORE,
|
||||
local.span,
|
||||
"variable declared with type underscore",
|
||||
|
|
|
@ -68,6 +68,7 @@ mod renamed_lints;
|
|||
mod allow_attributes;
|
||||
mod almost_complete_range;
|
||||
mod approx_const;
|
||||
mod arc_with_non_send_sync;
|
||||
mod as_conversions;
|
||||
mod asm_syntax;
|
||||
mod assertions_on_constants;
|
||||
|
@ -114,6 +115,7 @@ mod else_if_without_else;
|
|||
mod empty_drop;
|
||||
mod empty_enum;
|
||||
mod empty_structs_with_brackets;
|
||||
mod endian_bytes;
|
||||
mod entry;
|
||||
mod enum_clike;
|
||||
mod enum_variants;
|
||||
|
@ -121,6 +123,7 @@ mod equatable_if_let;
|
|||
mod escape;
|
||||
mod eta_reduction;
|
||||
mod excessive_bools;
|
||||
mod excessive_nesting;
|
||||
mod exhaustive_items;
|
||||
mod exit;
|
||||
mod explicit_write;
|
||||
|
@ -147,6 +150,7 @@ mod implicit_return;
|
|||
mod implicit_saturating_add;
|
||||
mod implicit_saturating_sub;
|
||||
mod inconsistent_struct_constructor;
|
||||
mod incorrect_impls;
|
||||
mod index_refutable_slice;
|
||||
mod indexing_slicing;
|
||||
mod infinite_iter;
|
||||
|
@ -165,6 +169,7 @@ mod large_enum_variant;
|
|||
mod large_futures;
|
||||
mod large_include_file;
|
||||
mod large_stack_arrays;
|
||||
mod large_stack_frames;
|
||||
mod len_zero;
|
||||
mod let_if_seq;
|
||||
mod let_underscore;
|
||||
|
@ -183,6 +188,7 @@ mod manual_is_ascii_check;
|
|||
mod manual_let_else;
|
||||
mod manual_main_separator_str;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_range_patterns;
|
||||
mod manual_rem_euclid;
|
||||
mod manual_retain;
|
||||
mod manual_slice_size_calculation;
|
||||
|
@ -191,9 +197,9 @@ mod manual_strip;
|
|||
mod map_unit_fn;
|
||||
mod match_result_ok;
|
||||
mod matches;
|
||||
mod mem_forget;
|
||||
mod mem_replace;
|
||||
mod methods;
|
||||
mod min_ident_chars;
|
||||
mod minmax;
|
||||
mod misc;
|
||||
mod misc_early;
|
||||
|
@ -220,6 +226,7 @@ mod needless_borrowed_ref;
|
|||
mod needless_continue;
|
||||
mod needless_else;
|
||||
mod needless_for_each;
|
||||
mod needless_if;
|
||||
mod needless_late_init;
|
||||
mod needless_parens_on_range_literals;
|
||||
mod needless_pass_by_value;
|
||||
|
@ -256,6 +263,7 @@ mod pub_use;
|
|||
mod question_mark;
|
||||
mod question_mark_used;
|
||||
mod ranges;
|
||||
mod raw_strings;
|
||||
mod rc_clone_in_vec_init;
|
||||
mod read_zero_byte_vec;
|
||||
mod redundant_async_block;
|
||||
|
@ -266,6 +274,7 @@ mod redundant_field_names;
|
|||
mod redundant_pub_crate;
|
||||
mod redundant_slicing;
|
||||
mod redundant_static_lifetimes;
|
||||
mod redundant_type_annotations;
|
||||
mod ref_option_ref;
|
||||
mod ref_patterns;
|
||||
mod reference;
|
||||
|
@ -279,8 +288,10 @@ mod semicolon_if_nothing_returned;
|
|||
mod serde_api;
|
||||
mod shadow;
|
||||
mod significant_drop_tightening;
|
||||
mod single_call_fn;
|
||||
mod single_char_lifetime_names;
|
||||
mod single_component_path_imports;
|
||||
mod single_range_in_vec_init;
|
||||
mod size_of_in_element_count;
|
||||
mod size_of_ref;
|
||||
mod slow_vector_initialization;
|
||||
|
@ -327,6 +338,7 @@ mod use_self;
|
|||
mod useless_conversion;
|
||||
mod vec;
|
||||
mod vec_init_then_push;
|
||||
mod visibility;
|
||||
mod wildcard_imports;
|
||||
mod write;
|
||||
mod zero_div_zero;
|
||||
|
@ -486,26 +498,27 @@ pub(crate) struct LintInfo {
|
|||
explanation: &'static str,
|
||||
}
|
||||
|
||||
pub fn explain(name: &str) {
|
||||
pub fn explain(name: &str) -> i32 {
|
||||
let target = format!("clippy::{}", name.to_ascii_uppercase());
|
||||
match declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
|
||||
Some(info) => {
|
||||
println!("{}", info.explanation);
|
||||
// Check if the lint has configuration
|
||||
let mdconf = get_configuration_metadata();
|
||||
if let Some(config_vec_positions) = mdconf
|
||||
.iter()
|
||||
.find_all(|cconf| cconf.lints.contains(&info.lint.name_lower()[8..].to_owned()))
|
||||
{
|
||||
// If it has, print it
|
||||
println!("### Configuration for {}:\n", info.lint.name_lower());
|
||||
for position in config_vec_positions {
|
||||
let conf = &mdconf[position];
|
||||
println!(" - {}: {} (default: {})", conf.name, conf.doc, conf.default);
|
||||
}
|
||||
if let Some(info) = declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
|
||||
println!("{}", info.explanation);
|
||||
// Check if the lint has configuration
|
||||
let mdconf = get_configuration_metadata();
|
||||
if let Some(config_vec_positions) = mdconf
|
||||
.iter()
|
||||
.find_all(|cconf| cconf.lints.contains(&info.lint.name_lower()[8..].to_owned()))
|
||||
{
|
||||
// If it has, print it
|
||||
println!("### Configuration for {}:\n", info.lint.name_lower());
|
||||
for position in config_vec_positions {
|
||||
let conf = &mdconf[position];
|
||||
println!(" - {}: {} (default: {})", conf.name, conf.doc, conf.default);
|
||||
}
|
||||
},
|
||||
None => println!("unknown lint: {name}"),
|
||||
}
|
||||
0
|
||||
} else {
|
||||
println!("unknown lint: {name}");
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,6 +577,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::<utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath>::default());
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl));
|
||||
store.register_late_pass(|_| {
|
||||
Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new())
|
||||
});
|
||||
}
|
||||
|
||||
let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
|
||||
|
@ -672,7 +688,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
});
|
||||
store.register_late_pass(|_| Box::<shadow::Shadow>::default());
|
||||
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
|
||||
store.register_late_pass(|_| Box::new(loops::Loops));
|
||||
store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv())));
|
||||
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
|
||||
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
|
||||
store.register_late_pass(|_| Box::new(entry::HashMapPass));
|
||||
|
@ -693,7 +709,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
});
|
||||
let too_large_for_stack = conf.too_large_for_stack;
|
||||
store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack }));
|
||||
store.register_late_pass(move |_| Box::new(vec::UselessVec { too_large_for_stack }));
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(vec::UselessVec {
|
||||
too_large_for_stack,
|
||||
msrv: msrv(),
|
||||
})
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
|
||||
store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
|
||||
store.register_late_pass(|_| Box::new(derive::Derive));
|
||||
|
@ -725,7 +746,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
let missing_docs_in_crate_items = conf.missing_docs_in_crate_items;
|
||||
store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
|
||||
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
|
||||
store.register_late_pass(|_| Box::new(mem_forget::MemForget));
|
||||
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
|
||||
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
|
||||
store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(missing_docs_in_crate_items)));
|
||||
|
@ -751,7 +771,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
|
||||
store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
|
||||
store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
|
||||
store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
|
||||
store.register_late_pass(|_| Box::<question_mark::QuestionMark>::default());
|
||||
store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed));
|
||||
store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
|
||||
store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
|
||||
|
@ -773,7 +793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
|
||||
store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
|
||||
store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv())));
|
||||
store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
|
||||
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
|
||||
store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
|
||||
|
@ -785,7 +805,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
|
||||
store.register_early_pass(|| Box::new(formatting::Formatting));
|
||||
store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
|
||||
store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
|
||||
store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
|
||||
store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
|
||||
store.register_late_pass(|_| Box::new(returns::Return));
|
||||
|
@ -810,10 +829,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
))
|
||||
});
|
||||
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
|
||||
let allow_private_module_inception = conf.allow_private_module_inception;
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(enum_variants::EnumVariantNames::new(
|
||||
enum_variant_name_threshold,
|
||||
avoid_breaking_exported_api,
|
||||
allow_private_module_inception,
|
||||
))
|
||||
});
|
||||
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
|
||||
|
@ -833,7 +854,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
|
||||
store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
|
||||
store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
|
||||
store.register_early_pass(|| Box::new(as_conversions::AsConversions));
|
||||
store.register_late_pass(|_| Box::new(as_conversions::AsConversions));
|
||||
store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
|
||||
store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default());
|
||||
let max_fn_params_bools = conf.max_fn_params_bools;
|
||||
|
@ -909,7 +930,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
enable_raw_pointer_heuristic_for_send,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
|
||||
let accept_comment_above_statement = conf.accept_comment_above_statement;
|
||||
let accept_comment_above_attributes = conf.accept_comment_above_attributes;
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(
|
||||
accept_comment_above_statement,
|
||||
accept_comment_above_attributes,
|
||||
))
|
||||
});
|
||||
let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args;
|
||||
store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined)));
|
||||
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
|
||||
|
@ -1002,11 +1030,48 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
|
||||
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
|
||||
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
|
||||
let excessive_nesting_threshold = conf.excessive_nesting_threshold;
|
||||
store.register_early_pass(move || {
|
||||
Box::new(excessive_nesting::ExcessiveNesting {
|
||||
excessive_nesting_threshold,
|
||||
nodes: rustc_ast::node_id::NodeSet::new(),
|
||||
})
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
|
||||
store.register_early_pass(|| Box::new(ref_patterns::RefPatterns));
|
||||
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
|
||||
store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
|
||||
store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
|
||||
store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
|
||||
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
|
||||
store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync));
|
||||
store.register_late_pass(|_| Box::new(needless_if::NeedlessIf));
|
||||
let allowed_idents_below_min_chars = conf.allowed_idents_below_min_chars.clone();
|
||||
let min_ident_chars_threshold = conf.min_ident_chars_threshold;
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(min_ident_chars::MinIdentChars {
|
||||
allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(),
|
||||
min_ident_chars_threshold,
|
||||
})
|
||||
});
|
||||
let stack_size_threshold = conf.stack_size_threshold;
|
||||
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
|
||||
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
|
||||
store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(single_call_fn::SingleCallFn {
|
||||
avoid_breaking_exported_api,
|
||||
def_id_to_usage: rustc_data_structures::fx::FxHashMap::default(),
|
||||
})
|
||||
});
|
||||
let needless_raw_string_hashes_allow_one = conf.allow_one_hash_in_raw_strings;
|
||||
store.register_early_pass(move || {
|
||||
Box::new(raw_strings::RawStrings {
|
||||
needless_raw_string_hashes_allow_one,
|
||||
})
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
|
||||
store.register_early_pass(|| Box::new(visibility::Visibility));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use itertools::Itertools;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
|
||||
|
@ -201,7 +202,19 @@ fn check_fn_inner<'tcx>(
|
|||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_LIFETIMES,
|
||||
span.with_hi(sig.decl.output.span().hi()),
|
||||
elidable_lts
|
||||
.iter()
|
||||
.map(|<| cx.tcx.def_span(lt))
|
||||
.chain(usages.iter().filter_map(|usage| {
|
||||
if let LifetimeName::Param(def_id) = usage.res
|
||||
&& elidable_lts.contains(&def_id)
|
||||
{
|
||||
return Some(usage.ident.span);
|
||||
}
|
||||
|
||||
None
|
||||
}))
|
||||
.collect_vec(),
|
||||
&format!("the following explicit lifetimes could be elided: {lts}"),
|
||||
|diag| {
|
||||
if sig.header.is_async() {
|
||||
|
@ -562,7 +575,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
|
|||
// if the bounds define new lifetimes, they are fine to occur
|
||||
let allowed_lts = allowed_lts_from(pred.bound_generic_params);
|
||||
// now walk the bounds
|
||||
for bound in pred.bounds.iter() {
|
||||
for bound in pred.bounds {
|
||||
walk_param_bound(&mut visitor, bound);
|
||||
}
|
||||
// and check that all lifetimes are allowed
|
||||
|
|
|
@ -5,15 +5,76 @@ use clippy_utils::source::snippet_with_applicability;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum AdjustKind {
|
||||
None,
|
||||
Borrow,
|
||||
BorrowMut,
|
||||
Reborrow,
|
||||
ReborrowMut,
|
||||
}
|
||||
impl AdjustKind {
|
||||
fn borrow(mutbl: AutoBorrowMutability) -> Self {
|
||||
match mutbl {
|
||||
AutoBorrowMutability::Not => Self::Borrow,
|
||||
AutoBorrowMutability::Mut { .. } => Self::BorrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow(mutbl: AutoBorrowMutability) -> Self {
|
||||
match mutbl {
|
||||
AutoBorrowMutability::Not => Self::Reborrow,
|
||||
AutoBorrowMutability::Mut { .. } => Self::ReborrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn display(self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "",
|
||||
Self::Borrow => "&",
|
||||
Self::BorrowMut => "&mut ",
|
||||
Self::Reborrow => "&*",
|
||||
Self::ReborrowMut => "&mut *",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) {
|
||||
let self_ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
||||
if !(self_ty == self_ty_adjusted && is_trait_method(cx, call_expr, sym::IntoIterator)) {
|
||||
if !is_trait_method(cx, call_expr, sym::IntoIterator) {
|
||||
return;
|
||||
}
|
||||
|
||||
let typeck = cx.typeck_results();
|
||||
let self_ty = typeck.expr_ty(self_arg);
|
||||
let adjust = match typeck.expr_adjustments(self_arg) {
|
||||
[] => AdjustKind::None,
|
||||
&[
|
||||
Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
||||
..
|
||||
},
|
||||
] => AdjustKind::borrow(mutbl),
|
||||
&[
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_), ..
|
||||
},
|
||||
Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
||||
target,
|
||||
},
|
||||
] => {
|
||||
if self_ty == target && matches!(mutbl, AutoBorrowMutability::Not) {
|
||||
AdjustKind::None
|
||||
} else {
|
||||
AdjustKind::reborrow(mutbl)
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
|
@ -23,7 +84,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<
|
|||
"it is more concise to loop over containers instead of using explicit \
|
||||
iteration methods",
|
||||
"to write this more concisely, try",
|
||||
object.to_string(),
|
||||
format!("{}{object}", adjust.display()),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,75 +1,235 @@
|
|||
use super::EXPLICIT_ITER_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::ty::{
|
||||
implements_trait, implements_trait_with_env, is_copy, make_normalized_projection,
|
||||
make_normalized_projection_with_regions, normalize_with_regions,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, EarlyBinder, Ty, TypeAndMut};
|
||||
use rustc_span::sym;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
|
||||
let should_lint = match method_name {
|
||||
"iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
|
||||
"into_iter" if is_trait_method(cx, arg, sym::IntoIterator) => {
|
||||
let receiver_ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
||||
let ref_receiver_ty = cx.tcx.mk_ref(
|
||||
cx.tcx.lifetimes.re_erased,
|
||||
ty::TypeAndMut {
|
||||
ty: receiver_ty,
|
||||
mutbl: Mutability::Not,
|
||||
},
|
||||
);
|
||||
receiver_ty_adjusted == ref_receiver_ty
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !should_lint {
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>, msrv: &Msrv) {
|
||||
let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr) else {
|
||||
return;
|
||||
};
|
||||
if let ty::Array(_, count) = *ty.peel_refs().kind() {
|
||||
if !ty.is_ref() {
|
||||
if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
|
||||
return;
|
||||
}
|
||||
} else if count
|
||||
.try_eval_target_usize(cx.tcx, cx.param_env)
|
||||
.map_or(true, |x| x > 32)
|
||||
&& !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
|
||||
let muta = if method_name == "iter_mut" { "mut " } else { "" };
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
EXPLICIT_ITER_LOOP,
|
||||
arg.span,
|
||||
call_expr.span,
|
||||
"it is more concise to loop over references to containers instead of using explicit \
|
||||
iteration methods",
|
||||
"to write this more concisely, try",
|
||||
format!("&{muta}{object}"),
|
||||
format!("{}{object}", adjust.display()),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns `true` if the type of expr is one that provides `IntoIterator` impls
|
||||
/// for `&T` and `&mut T`, such as `Vec`.
|
||||
#[rustfmt::skip]
|
||||
fn is_ref_iterable_type(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
// no walk_ptrs_ty: calling iter() on a reference can make sense because it
|
||||
// will allow further borrows afterwards
|
||||
let ty = cx.typeck_results().expr_ty(e);
|
||||
is_iterable_array(ty, cx) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::Vec) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::HashMap) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::HashSet) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
|
||||
is_type_diagnostic_item(cx, ty, sym::BTreeSet)
|
||||
#[derive(Clone, Copy)]
|
||||
enum AdjustKind {
|
||||
None,
|
||||
Borrow,
|
||||
BorrowMut,
|
||||
Deref,
|
||||
Reborrow,
|
||||
ReborrowMut,
|
||||
}
|
||||
impl AdjustKind {
|
||||
fn borrow(mutbl: Mutability) -> Self {
|
||||
match mutbl {
|
||||
Mutability::Not => Self::Borrow,
|
||||
Mutability::Mut => Self::BorrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
|
||||
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
||||
match ty.kind() {
|
||||
ty::Array(_, n) => n
|
||||
.try_eval_target_usize(cx.tcx, cx.param_env)
|
||||
.map_or(false, |val| (0..=32).contains(&val)),
|
||||
_ => false,
|
||||
fn auto_borrow(mutbl: AutoBorrowMutability) -> Self {
|
||||
match mutbl {
|
||||
AutoBorrowMutability::Not => Self::Borrow,
|
||||
AutoBorrowMutability::Mut { .. } => Self::BorrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn reborrow(mutbl: Mutability) -> Self {
|
||||
match mutbl {
|
||||
Mutability::Not => Self::Reborrow,
|
||||
Mutability::Mut => Self::ReborrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_reborrow(mutbl: AutoBorrowMutability) -> Self {
|
||||
match mutbl {
|
||||
AutoBorrowMutability::Not => Self::Reborrow,
|
||||
AutoBorrowMutability::Mut { .. } => Self::ReborrowMut,
|
||||
}
|
||||
}
|
||||
|
||||
fn display(self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "",
|
||||
Self::Borrow => "&",
|
||||
Self::BorrowMut => "&mut ",
|
||||
Self::Deref => "*",
|
||||
Self::Reborrow => "&*",
|
||||
Self::ReborrowMut => "&mut *",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an `iter` or `iter_mut` call returns `IntoIterator::IntoIter`. Returns how the
|
||||
/// argument needs to be adjusted.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn is_ref_iterable<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
self_arg: &Expr<'_>,
|
||||
call_expr: &Expr<'_>,
|
||||
) -> Option<(AdjustKind, Ty<'tcx>)> {
|
||||
let typeck = cx.typeck_results();
|
||||
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
||||
&& let Some(fn_id) = typeck.type_dependent_def_id(call_expr.hir_id)
|
||||
&& let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
|
||||
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
|
||||
&& let param_env = cx.tcx.param_env(fn_id)
|
||||
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, [])
|
||||
&& let Some(into_iter_ty) =
|
||||
make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
|
||||
&& let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)
|
||||
&& into_iter_ty == req_res_ty
|
||||
{
|
||||
let adjustments = typeck.expr_adjustments(self_arg);
|
||||
let self_ty = typeck.expr_ty(self_arg);
|
||||
let self_is_copy = is_copy(cx, self_ty);
|
||||
|
||||
if adjustments.is_empty() && self_is_copy {
|
||||
// Exact type match, already checked earlier
|
||||
return Some((AdjustKind::None, self_ty));
|
||||
}
|
||||
|
||||
let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty)
|
||||
.subst(cx.tcx, typeck.node_substs(call_expr.hir_id)));
|
||||
let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
|
||||
Some(mutbl)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if !adjustments.is_empty() {
|
||||
if self_is_copy {
|
||||
// Using by value won't consume anything
|
||||
if implements_trait(cx, self_ty, trait_id, &[])
|
||||
&& let Some(ty) =
|
||||
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
|
||||
&& ty == res_ty
|
||||
{
|
||||
return Some((AdjustKind::None, self_ty));
|
||||
}
|
||||
} else if let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind()
|
||||
&& let Some(mutbl) = mutbl
|
||||
{
|
||||
// Attempt to reborrow the mutable reference
|
||||
let self_ty = if mutbl.is_mut() {
|
||||
self_ty
|
||||
} else {
|
||||
cx.tcx.mk_ref(region, TypeAndMut { ty, mutbl })
|
||||
};
|
||||
if implements_trait(cx, self_ty, trait_id, &[])
|
||||
&& let Some(ty) =
|
||||
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
|
||||
&& ty == res_ty
|
||||
{
|
||||
return Some((AdjustKind::reborrow(mutbl), self_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(mutbl) = mutbl
|
||||
&& !self_ty.is_ref()
|
||||
{
|
||||
// Attempt to borrow
|
||||
let self_ty = cx.tcx.mk_ref(cx.tcx.lifetimes.re_erased, TypeAndMut {
|
||||
ty: self_ty,
|
||||
mutbl,
|
||||
});
|
||||
if implements_trait(cx, self_ty, trait_id, &[])
|
||||
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
|
||||
&& ty == res_ty
|
||||
{
|
||||
return Some((AdjustKind::borrow(mutbl), self_ty));
|
||||
}
|
||||
}
|
||||
|
||||
match adjustments {
|
||||
[] => Some((AdjustKind::None, self_ty)),
|
||||
&[
|
||||
Adjustment { kind: Adjust::Deref(_), ..},
|
||||
Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
||||
target,
|
||||
},
|
||||
..
|
||||
] => {
|
||||
if target != self_ty
|
||||
&& implements_trait(cx, target, trait_id, &[])
|
||||
&& let Some(ty) =
|
||||
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
|
||||
&& ty == res_ty
|
||||
{
|
||||
Some((AdjustKind::auto_reborrow(mutbl), target))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
&[Adjustment { kind: Adjust::Deref(_), target }, ..] => {
|
||||
if is_copy(cx, target)
|
||||
&& implements_trait(cx, target, trait_id, &[])
|
||||
&& let Some(ty) =
|
||||
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
|
||||
&& ty == res_ty
|
||||
{
|
||||
Some((AdjustKind::Deref, target))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
&[
|
||||
Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
||||
target,
|
||||
},
|
||||
..
|
||||
] => {
|
||||
if self_ty.is_ref()
|
||||
&& implements_trait(cx, target, trait_id, &[])
|
||||
&& let Some(ty) =
|
||||
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
|
||||
&& ty == res_ty
|
||||
{
|
||||
Some((AdjustKind::auto_borrow(mutbl), target))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ pub(super) fn check<'tcx>(
|
|||
iter_b = Some(get_assignment(body));
|
||||
}
|
||||
|
||||
let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter());
|
||||
let assignments = iter_a.into_iter().flatten().chain(iter_b);
|
||||
|
||||
let big_sugg = assignments
|
||||
// The only statements in the for loops can be indexed assignments from
|
||||
|
@ -402,7 +402,7 @@ fn get_assignments<'a, 'tcx>(
|
|||
StmtKind::Local(..) | StmtKind::Item(..) => None,
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
|
||||
})
|
||||
.chain((*expr).into_iter())
|
||||
.chain(*expr)
|
||||
.filter(move |e| {
|
||||
if let ExprKind::AssignOp(_, place, _) = e.kind {
|
||||
path_to_local(place).map_or(false, |id| {
|
||||
|
|
|
@ -20,9 +20,10 @@ mod while_let_loop;
|
|||
mod while_let_on_iterator;
|
||||
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
|
||||
|
||||
|
@ -479,7 +480,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for unnecessary `if let` usage in a for loop
|
||||
/// Checks for unnecessary `if let` usage in a for loop
|
||||
/// where only the `Some` or `Ok` variant of the iterator element is used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
|
@ -511,7 +512,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for empty spin loops
|
||||
/// Checks for empty spin loops
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The loop body should have something like `thread::park()` or at least
|
||||
|
@ -547,7 +548,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for manual implementations of Iterator::find
|
||||
/// Checks for manual implementations of Iterator::find
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It doesn't affect performance, but using `find` is shorter and easier to read.
|
||||
|
@ -606,7 +607,15 @@ declare_clippy_lint! {
|
|||
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Loops => [
|
||||
pub struct Loops {
|
||||
msrv: Msrv,
|
||||
}
|
||||
impl Loops {
|
||||
pub fn new(msrv: Msrv) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
impl_lint_pass!(Loops => [
|
||||
MANUAL_MEMCPY,
|
||||
MANUAL_FLATTEN,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
|
@ -645,7 +654,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
|||
if body.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
check_for_loop(cx, pat, arg, body, expr, span);
|
||||
self.check_for_loop(cx, pat, arg, body, expr, span);
|
||||
if let ExprKind::Block(block, _) = body.kind {
|
||||
never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
|
||||
}
|
||||
|
@ -678,46 +687,48 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
|||
manual_while_let_some::check(cx, condition, body, span);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn check_for_loop<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
|
||||
if !is_manual_memcpy_triggered {
|
||||
needless_range_loop::check(cx, pat, arg, body, expr);
|
||||
explicit_counter_loop::check(cx, pat, arg, body, expr);
|
||||
impl Loops {
|
||||
fn check_for_loop<'tcx>(
|
||||
&self,
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
span: Span,
|
||||
) {
|
||||
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
|
||||
if !is_manual_memcpy_triggered {
|
||||
needless_range_loop::check(cx, pat, arg, body, expr);
|
||||
explicit_counter_loop::check(cx, pat, arg, body, expr);
|
||||
}
|
||||
self.check_for_loop_arg(cx, pat, arg);
|
||||
for_kv_map::check(cx, pat, arg, body);
|
||||
mut_range_bound::check(cx, arg, body);
|
||||
single_element_loop::check(cx, pat, arg, body, expr);
|
||||
same_item_push::check(cx, pat, arg, body, expr);
|
||||
manual_flatten::check(cx, pat, arg, body, span);
|
||||
manual_find::check(cx, pat, arg, body, span, expr);
|
||||
}
|
||||
check_for_loop_arg(cx, pat, arg);
|
||||
for_kv_map::check(cx, pat, arg, body);
|
||||
mut_range_bound::check(cx, arg, body);
|
||||
single_element_loop::check(cx, pat, arg, body, expr);
|
||||
same_item_push::check(cx, pat, arg, body, expr);
|
||||
manual_flatten::check(cx, pat, arg, body, span);
|
||||
manual_find::check(cx, pat, arg, body, span, expr);
|
||||
}
|
||||
|
||||
fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
|
||||
let method_name = method.ident.as_str();
|
||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||
match method_name {
|
||||
"iter" | "iter_mut" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
||||
},
|
||||
"into_iter" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
||||
explicit_into_iter_loop::check(cx, self_arg, arg);
|
||||
},
|
||||
"next" => {
|
||||
iter_next_loop::check(cx, arg);
|
||||
},
|
||||
_ => {},
|
||||
fn check_for_loop_arg(&self, cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
|
||||
match method.ident.as_str() {
|
||||
"iter" | "iter_mut" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, &self.msrv);
|
||||
},
|
||||
"into_iter" => {
|
||||
explicit_into_iter_loop::check(cx, self_arg, arg);
|
||||
},
|
||||
"next" => {
|
||||
iter_next_loop::check(cx, arg);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
use super::utils::make_iterator_snippet;
|
||||
use super::NEVER_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::consts::constant;
|
||||
use clippy_utils::higher::ForLoop;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{consts::Constant, diagnostics::span_lint_and_then};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
use std::iter::{once, Iterator};
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
block: &Block<'_>,
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
block: &Block<'tcx>,
|
||||
loop_id: HirId,
|
||||
span: Span,
|
||||
for_loop: Option<&ForLoop<'_>>,
|
||||
) {
|
||||
match never_loop_block(block, &mut Vec::new(), loop_id) {
|
||||
match never_loop_block(cx, block, &mut Vec::new(), loop_id) {
|
||||
NeverLoopResult::AlwaysBreak => {
|
||||
span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
|
||||
if let Some(ForLoop {
|
||||
|
@ -95,7 +96,12 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirI
|
|||
}
|
||||
}
|
||||
|
||||
fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
fn never_loop_block<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
block: &Block<'tcx>,
|
||||
ignore_ids: &mut Vec<HirId>,
|
||||
main_loop_id: HirId,
|
||||
) -> NeverLoopResult {
|
||||
let iter = block
|
||||
.stmts
|
||||
.iter()
|
||||
|
@ -103,10 +109,10 @@ fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id
|
|||
.chain(block.expr.map(|expr| (expr, None)));
|
||||
|
||||
iter.map(|(e, els)| {
|
||||
let e = never_loop_expr(e, ignore_ids, main_loop_id);
|
||||
let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
|
||||
// els is an else block in a let...else binding
|
||||
els.map_or(e, |els| {
|
||||
combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id), ignore_ids)
|
||||
combine_branches(e, never_loop_block(cx, els, ignore_ids, main_loop_id), ignore_ids)
|
||||
})
|
||||
})
|
||||
.fold(NeverLoopResult::Otherwise, combine_seq)
|
||||
|
@ -122,7 +128,12 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
fn never_loop_expr<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'tcx>,
|
||||
ignore_ids: &mut Vec<HirId>,
|
||||
main_loop_id: HirId,
|
||||
) -> NeverLoopResult {
|
||||
match expr.kind {
|
||||
ExprKind::Unary(_, e)
|
||||
| ExprKind::Cast(e, _)
|
||||
|
@ -130,45 +141,51 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
|
|||
| ExprKind::Field(e, _)
|
||||
| ExprKind::AddrOf(_, _, e)
|
||||
| ExprKind::Repeat(e, _)
|
||||
| ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id),
|
||||
ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id),
|
||||
ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id),
|
||||
| ExprKind::DropTemps(e) => never_loop_expr(cx, e, ignore_ids, main_loop_id),
|
||||
ExprKind::Let(let_expr) => never_loop_expr(cx, let_expr.init, ignore_ids, main_loop_id),
|
||||
ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(cx, &mut es.iter(), ignore_ids, main_loop_id),
|
||||
ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
|
||||
cx,
|
||||
&mut std::iter::once(receiver).chain(es.iter()),
|
||||
ignore_ids,
|
||||
main_loop_id,
|
||||
),
|
||||
ExprKind::Struct(_, fields, base) => {
|
||||
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
|
||||
let fields = never_loop_expr_all(cx, &mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
|
||||
if let Some(base) = base {
|
||||
combine_seq(fields, never_loop_expr(base, ignore_ids, main_loop_id))
|
||||
combine_seq(fields, never_loop_expr(cx, base, ignore_ids, main_loop_id))
|
||||
} else {
|
||||
fields
|
||||
}
|
||||
},
|
||||
ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
|
||||
ExprKind::Call(e, es) => never_loop_expr_all(cx, &mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
|
||||
ExprKind::Binary(_, e1, e2)
|
||||
| ExprKind::Assign(e1, e2, _)
|
||||
| ExprKind::AssignOp(_, e1, e2)
|
||||
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
|
||||
| ExprKind::Index(e1, e2) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
|
||||
ExprKind::Loop(b, _, _, _) => {
|
||||
// Break can come from the inner loop so remove them.
|
||||
absorb_break(never_loop_block(b, ignore_ids, main_loop_id))
|
||||
absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id))
|
||||
},
|
||||
ExprKind::If(e, e2, e3) => {
|
||||
let e1 = never_loop_expr(e, ignore_ids, main_loop_id);
|
||||
let e2 = never_loop_expr(e2, ignore_ids, main_loop_id);
|
||||
let e1 = never_loop_expr(cx, e, ignore_ids, main_loop_id);
|
||||
let e2 = never_loop_expr(cx, e2, ignore_ids, main_loop_id);
|
||||
// If we know the `if` condition evaluates to `true`, don't check everything past it; it
|
||||
// should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable
|
||||
if let Some(Constant::Bool(true)) = constant(cx, cx.typeck_results(), e) {
|
||||
return combine_seq(e1, e2);
|
||||
}
|
||||
let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
|
||||
never_loop_expr(e, ignore_ids, main_loop_id)
|
||||
never_loop_expr(cx, e, ignore_ids, main_loop_id)
|
||||
});
|
||||
combine_seq(e1, combine_branches(e2, e3, ignore_ids))
|
||||
},
|
||||
ExprKind::Match(e, arms, _) => {
|
||||
let e = never_loop_expr(e, ignore_ids, main_loop_id);
|
||||
let e = never_loop_expr(cx, e, ignore_ids, main_loop_id);
|
||||
if arms.is_empty() {
|
||||
e
|
||||
} else {
|
||||
let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
|
||||
let arms = never_loop_expr_branch(cx, &mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
|
||||
combine_seq(e, arms)
|
||||
}
|
||||
},
|
||||
|
@ -176,7 +193,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
|
|||
if l.is_some() {
|
||||
ignore_ids.push(b.hir_id);
|
||||
}
|
||||
let ret = never_loop_block(b, ignore_ids, main_loop_id);
|
||||
let ret = never_loop_block(cx, b, ignore_ids, main_loop_id);
|
||||
if l.is_some() {
|
||||
ignore_ids.pop();
|
||||
}
|
||||
|
@ -198,31 +215,30 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
|
|||
// checks if break targets a block instead of a loop
|
||||
ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
|
||||
.map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| {
|
||||
never_loop_expr(e, ignore_ids, main_loop_id)
|
||||
never_loop_expr(cx, e, ignore_ids, main_loop_id)
|
||||
}),
|
||||
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
|
||||
combine_seq(
|
||||
never_loop_expr(e, ignore_ids, main_loop_id),
|
||||
never_loop_expr(cx, e, ignore_ids, main_loop_id),
|
||||
NeverLoopResult::AlwaysBreak,
|
||||
)
|
||||
}),
|
||||
ExprKind::Become(e) => {
|
||||
combine_seq(
|
||||
never_loop_expr(e, ignore_ids, main_loop_id),
|
||||
NeverLoopResult::AlwaysBreak,
|
||||
)
|
||||
}
|
||||
ExprKind::Become(e) => combine_seq(
|
||||
never_loop_expr(cx, e, ignore_ids, main_loop_id),
|
||||
NeverLoopResult::AlwaysBreak,
|
||||
),
|
||||
ExprKind::InlineAsm(asm) => asm
|
||||
.operands
|
||||
.iter()
|
||||
.map(|(o, _)| match o {
|
||||
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
|
||||
never_loop_expr(expr, ignore_ids, main_loop_id)
|
||||
never_loop_expr(cx, expr, ignore_ids, main_loop_id)
|
||||
},
|
||||
InlineAsmOperand::Out { expr, .. } => {
|
||||
never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id)
|
||||
never_loop_expr_all(cx, &mut expr.iter().copied(), ignore_ids, main_loop_id)
|
||||
},
|
||||
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
|
||||
cx,
|
||||
&mut once(*in_expr).chain(out_expr.iter().copied()),
|
||||
ignore_ids,
|
||||
main_loop_id,
|
||||
|
@ -242,22 +258,24 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
|
|||
}
|
||||
}
|
||||
|
||||
fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
|
||||
fn never_loop_expr_all<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
|
||||
cx: &LateContext<'tcx>,
|
||||
es: &mut T,
|
||||
ignore_ids: &mut Vec<HirId>,
|
||||
main_loop_id: HirId,
|
||||
) -> NeverLoopResult {
|
||||
es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
|
||||
es.map(|e| never_loop_expr(cx, e, ignore_ids, main_loop_id))
|
||||
.fold(NeverLoopResult::Otherwise, combine_seq)
|
||||
}
|
||||
|
||||
fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
|
||||
fn never_loop_expr_branch<'tcx, T: Iterator<Item = &'tcx Expr<'tcx>>>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &mut T,
|
||||
ignore_ids: &mut Vec<HirId>,
|
||||
main_loop_id: HirId,
|
||||
) -> NeverLoopResult {
|
||||
e.fold(NeverLoopResult::AlwaysBreak, |a, b| {
|
||||
combine_branches(a, never_loop_expr(b, ignore_ids, main_loop_id), ignore_ids)
|
||||
combine_branches(a, never_loop_expr(cx, b, ignore_ids, main_loop_id), ignore_ids)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_block(&mut self, b: &'tcx Block<'_>) {
|
||||
for stmt in b.stmts.iter() {
|
||||
for stmt in b.stmts {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ struct PathAndSpan {
|
|||
span: Span,
|
||||
}
|
||||
|
||||
/// `MacroRefData` includes the name of the macro.
|
||||
/// `MacroRefData` includes the name of the macro
|
||||
/// and the path from `SourceMap::span_to_filename`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MacroRefData {
|
||||
name: String,
|
||||
|
@ -100,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
|
|||
});
|
||||
if !id.is_local();
|
||||
then {
|
||||
for kid in cx.tcx.module_children(id).iter() {
|
||||
for kid in cx.tcx.module_children(id) {
|
||||
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
|
||||
let span = mac_attr.span;
|
||||
let def_path = cx.tcx.def_path_str(mac_id);
|
||||
|
|
|
@ -6,17 +6,18 @@ use clippy_utils::source::snippet_with_context;
|
|||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::{Descend, Visitable};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
use serde::Deserialize;
|
||||
use std::ops::ControlFlow;
|
||||
use std::slice;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -81,11 +82,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
|
|||
{
|
||||
match if_let_or_match {
|
||||
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! {
|
||||
if expr_is_simple_identity(let_pat, if_then);
|
||||
if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then);
|
||||
if let Some(if_else) = if_else;
|
||||
if expr_diverges(cx, if_else);
|
||||
then {
|
||||
emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else);
|
||||
emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else);
|
||||
}
|
||||
},
|
||||
IfLetOrMatch::Match(match_expr, arms, source) => {
|
||||
|
@ -118,11 +119,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
|
|||
return;
|
||||
}
|
||||
let pat_arm = &arms[1 - idx];
|
||||
if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) {
|
||||
return;
|
||||
}
|
||||
let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else {
|
||||
return
|
||||
};
|
||||
|
||||
emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body);
|
||||
emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -135,7 +136,7 @@ fn emit_manual_let_else(
|
|||
cx: &LateContext<'_>,
|
||||
span: Span,
|
||||
expr: &Expr<'_>,
|
||||
local: &Pat<'_>,
|
||||
ident_map: &FxHashMap<Symbol, &Pat<'_>>,
|
||||
pat: &Pat<'_>,
|
||||
else_body: &Expr<'_>,
|
||||
) {
|
||||
|
@ -146,8 +147,8 @@ fn emit_manual_let_else(
|
|||
"this could be rewritten as `let...else`",
|
||||
|diag| {
|
||||
// This is far from perfect, for example there needs to be:
|
||||
// * tracking for multi-binding cases: let (foo, bar) = if let (Some(foo), Ok(bar)) = ...
|
||||
// * renamings of the bindings for many `PatKind`s like structs, slices, etc.
|
||||
// * renamings of the bindings for many `PatKind`s like slices, etc.
|
||||
// * limitations in the existing replacement algorithms
|
||||
// * unused binding collision detection with existing ones
|
||||
// for this to be machine applicable.
|
||||
let mut app = Applicability::HasPlaceholders;
|
||||
|
@ -159,57 +160,126 @@ fn emit_manual_let_else(
|
|||
} else {
|
||||
format!("{{ {sn_else} }}")
|
||||
};
|
||||
let sn_bl = replace_in_pattern(cx, span, local, pat, &mut app);
|
||||
let sn_bl = replace_in_pattern(cx, span, ident_map, pat, &mut app, true);
|
||||
let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};");
|
||||
diag.span_suggestion(span, "consider writing", sugg, app);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// replaces the locals in the pattern
|
||||
/// Replaces the locals in the pattern
|
||||
///
|
||||
/// For this example:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let (a, FooBar { b, c }) = if let Bar { Some(a_i), b_i } = ex { (a_i, b_i) } else { return };
|
||||
/// ```
|
||||
///
|
||||
/// We have:
|
||||
///
|
||||
/// ```ignore
|
||||
/// pat: Bar { Some(a_i), b_i }
|
||||
/// ident_map: (a_i) -> (a), (b_i) -> (FooBar { b, c })
|
||||
/// ```
|
||||
///
|
||||
/// We return:
|
||||
///
|
||||
/// ```ignore
|
||||
/// Bar { Some(a), b_i: FooBar { b, c } }
|
||||
/// ```
|
||||
fn replace_in_pattern(
|
||||
cx: &LateContext<'_>,
|
||||
span: Span,
|
||||
local: &Pat<'_>,
|
||||
ident_map: &FxHashMap<Symbol, &Pat<'_>>,
|
||||
pat: &Pat<'_>,
|
||||
app: &mut Applicability,
|
||||
top_level: bool,
|
||||
) -> String {
|
||||
let mut bindings_count = 0;
|
||||
pat.each_binding_or_first(&mut |_, _, _, _| bindings_count += 1);
|
||||
// If the pattern creates multiple bindings, exit early,
|
||||
// as otherwise we might paste the pattern to the positions of multiple bindings.
|
||||
if bindings_count > 1 {
|
||||
let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app);
|
||||
return sn_pat.into_owned();
|
||||
}
|
||||
// We put a labeled block here so that we can implement the fallback in this function.
|
||||
// As the function has multiple call sites, implementing the fallback via an Option<T>
|
||||
// return type and unwrap_or_else would cause repetition. Similarly, the function also
|
||||
// invokes the fall back multiple times.
|
||||
'a: {
|
||||
// If the ident map is empty, there is no replacement to do.
|
||||
// The code following this if assumes a non-empty ident_map.
|
||||
if ident_map.is_empty() {
|
||||
break 'a;
|
||||
}
|
||||
|
||||
match pat.kind {
|
||||
PatKind::Binding(..) => {
|
||||
let (sn_bdg, _) = snippet_with_context(cx, local.span, span.ctxt(), "", app);
|
||||
return sn_bdg.to_string();
|
||||
},
|
||||
PatKind::Or(pats) => {
|
||||
let patterns = pats
|
||||
.iter()
|
||||
.map(|pat| replace_in_pattern(cx, span, local, pat, app))
|
||||
.collect::<Vec<_>>();
|
||||
let or_pat = patterns.join(" | ");
|
||||
return format!("({or_pat})");
|
||||
},
|
||||
// Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
|
||||
PatKind::TupleStruct(ref w, args, dot_dot_pos) => {
|
||||
let mut args = args
|
||||
.iter()
|
||||
.map(|pat| replace_in_pattern(cx, span, local, pat, app))
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(pos) = dot_dot_pos.as_opt_usize() {
|
||||
args.insert(pos, "..".to_owned());
|
||||
}
|
||||
let args = args.join(", ");
|
||||
let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default();
|
||||
return format!("{sn_wrapper}({args})");
|
||||
},
|
||||
_ => {},
|
||||
match pat.kind {
|
||||
PatKind::Binding(_ann, _id, binding_name, opt_subpt) => {
|
||||
let Some(pat_to_put) = ident_map.get(&binding_name.name) else { break 'a };
|
||||
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
|
||||
if let Some(subpt) = opt_subpt {
|
||||
let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false);
|
||||
return format!("{sn_ptp} @ {subpt}");
|
||||
}
|
||||
return sn_ptp.to_string();
|
||||
},
|
||||
PatKind::Or(pats) => {
|
||||
let patterns = pats
|
||||
.iter()
|
||||
.map(|pat| replace_in_pattern(cx, span, ident_map, pat, app, false))
|
||||
.collect::<Vec<_>>();
|
||||
let or_pat = patterns.join(" | ");
|
||||
if top_level {
|
||||
return format!("({or_pat})");
|
||||
}
|
||||
return or_pat;
|
||||
},
|
||||
PatKind::Struct(path, fields, has_dot_dot) => {
|
||||
let fields = fields
|
||||
.iter()
|
||||
.map(|fld| {
|
||||
if let PatKind::Binding(_, _, name, None) = fld.pat.kind &&
|
||||
let Some(pat_to_put) = ident_map.get(&name.name)
|
||||
{
|
||||
let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app);
|
||||
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
|
||||
// TODO: this is a bit of a hack, but it does its job. Ideally, we'd check if pat_to_put is
|
||||
// a PatKind::Binding but that is also hard to get right.
|
||||
if sn_fld_name == sn_ptp {
|
||||
// Field init shorthand
|
||||
return format!("{sn_fld_name}");
|
||||
}
|
||||
return format!("{sn_fld_name}: {sn_ptp}");
|
||||
}
|
||||
let (sn_fld, _) = snippet_with_context(cx, fld.span, span.ctxt(), "", app);
|
||||
sn_fld.into_owned()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let fields_string = fields.join(", ");
|
||||
|
||||
let dot_dot_str = if has_dot_dot { " .." } else { "" };
|
||||
let (sn_pth, _) = snippet_with_context(cx, path.span(), span.ctxt(), "", app);
|
||||
return format!("{sn_pth} {{ {fields_string}{dot_dot_str} }}");
|
||||
},
|
||||
// Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`.
|
||||
PatKind::TupleStruct(ref w, args, dot_dot_pos) => {
|
||||
let mut args = args
|
||||
.iter()
|
||||
.map(|pat| replace_in_pattern(cx, span, ident_map, pat, app, false))
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(pos) = dot_dot_pos.as_opt_usize() {
|
||||
args.insert(pos, "..".to_owned());
|
||||
}
|
||||
let args = args.join(", ");
|
||||
let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default();
|
||||
return format!("{sn_wrapper}({args})");
|
||||
},
|
||||
PatKind::Tuple(args, dot_dot_pos) => {
|
||||
let mut args = args
|
||||
.iter()
|
||||
.map(|pat| replace_in_pattern(cx, span, ident_map, pat, app, false))
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(pos) = dot_dot_pos.as_opt_usize() {
|
||||
args.insert(pos, "..".to_owned());
|
||||
}
|
||||
let args = args.join(", ");
|
||||
return format!("({args})");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app);
|
||||
sn_pat.into_owned()
|
||||
|
@ -353,37 +423,74 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo
|
|||
!has_disallowed
|
||||
}
|
||||
|
||||
/// Checks if the passed block is a simple identity referring to bindings created by the pattern
|
||||
fn expr_is_simple_identity(pat: &'_ Pat<'_>, expr: &'_ Expr<'_>) -> bool {
|
||||
// We support patterns with multiple bindings and tuples, like:
|
||||
// let ... = if let (Some(foo), bar) = g() { (foo, bar) } else { ... }
|
||||
/// Checks if the passed block is a simple identity referring to bindings created by the pattern,
|
||||
/// and if yes, returns a mapping between the relevant sub-pattern and the identifier it corresponds
|
||||
/// to.
|
||||
///
|
||||
/// We support patterns with multiple bindings and tuples, e.g.:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let (foo_o, bar_o) = if let (Some(foo), bar) = g() { (foo, bar) } else { ... }
|
||||
/// ```
|
||||
///
|
||||
/// The expected params would be:
|
||||
///
|
||||
/// ```ignore
|
||||
/// local_pat: (foo_o, bar_o)
|
||||
/// let_pat: (Some(foo), bar)
|
||||
/// expr: (foo, bar)
|
||||
/// ```
|
||||
///
|
||||
/// We build internal `sub_pats` so that it looks like `[foo_o, bar_o]` and `paths` so that it looks
|
||||
/// like `[foo, bar]`. Then we turn that into `FxHashMap [(foo) -> (foo_o), (bar) -> (bar_o)]` which
|
||||
/// we return.
|
||||
fn expr_simple_identity_map<'a, 'hir>(
|
||||
local_pat: &'a Pat<'hir>,
|
||||
let_pat: &'_ Pat<'hir>,
|
||||
expr: &'_ Expr<'hir>,
|
||||
) -> Option<FxHashMap<Symbol, &'a Pat<'hir>>> {
|
||||
let peeled = peel_blocks(expr);
|
||||
let paths = match peeled.kind {
|
||||
ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs,
|
||||
ExprKind::Path(_) => std::slice::from_ref(peeled),
|
||||
_ => return false,
|
||||
let (sub_pats, paths) = match (local_pat.kind, peeled.kind) {
|
||||
(PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) | (PatKind::Slice(pats, ..), ExprKind::Array(exprs)) => {
|
||||
(pats, exprs)
|
||||
},
|
||||
(_, ExprKind::Path(_)) => (slice::from_ref(local_pat), slice::from_ref(peeled)),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// There is some length mismatch, which indicates usage of .. in the patterns above e.g.:
|
||||
// let (a, ..) = if let [a, b, _c] = ex { (a, b) } else { ... };
|
||||
// We bail in these cases as they should be rare.
|
||||
if paths.len() != sub_pats.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut pat_bindings = FxHashSet::default();
|
||||
pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| {
|
||||
let_pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| {
|
||||
pat_bindings.insert(ident);
|
||||
});
|
||||
if pat_bindings.len() < paths.len() {
|
||||
return false;
|
||||
// This rebinds some bindings from the outer scope, or it repeats some copy-able bindings multiple
|
||||
// times. We don't support these cases so we bail here. E.g.:
|
||||
// let foo = 0;
|
||||
// let (new_foo, bar, bar_copied) = if let Some(bar) = Some(0) { (foo, bar, bar) } else { .. };
|
||||
return None;
|
||||
}
|
||||
for path in paths {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind;
|
||||
if let [path_seg] = path.segments;
|
||||
then {
|
||||
if !pat_bindings.remove(&path_seg.ident) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
let mut ident_map = FxHashMap::default();
|
||||
for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) {
|
||||
if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind &&
|
||||
let [path_seg] = path.segments
|
||||
{
|
||||
let ident = path_seg.ident;
|
||||
if !pat_bindings.remove(&ident) {
|
||||
return None;
|
||||
}
|
||||
ident_map.insert(ident.name, sub_pat);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
true
|
||||
Some(ident_map)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)]
|
||||
|
|
123
clippy_lints/src/manual_range_patterns.rs
Normal file
123
clippy_lints/src/manual_range_patterns.rs
Normal file
|
@ -0,0 +1,123 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_hir::PatKind;
|
||||
use rustc_hir::RangeEnd;
|
||||
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};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for combined OR patterns that are all contained in a specific range,
|
||||
/// e.g. `6 | 4 | 5 | 9 | 7 | 8` can be rewritten as `4..=9`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using an explicit range is more concise and easier to read.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = 6;
|
||||
/// let foo = matches!(x, 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = 6;
|
||||
/// let foo = matches!(x, 1..=10);
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub MANUAL_RANGE_PATTERNS,
|
||||
complexity,
|
||||
"manually writing range patterns using a combined OR pattern (`|`)"
|
||||
}
|
||||
declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]);
|
||||
|
||||
fn expr_as_u128(expr: &Expr<'_>) -> Option<u128> {
|
||||
if let ExprKind::Lit(lit) = expr.kind
|
||||
&& let LitKind::Int(num, _) = lit.node
|
||||
{
|
||||
Some(num)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for ManualRangePatterns {
|
||||
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
|
||||
if in_external_macro(cx.sess(), pat.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
// a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
|
||||
if let PatKind::Or(pats) = pat.kind
|
||||
&& pats.len() >= 3
|
||||
{
|
||||
let mut min = u128::MAX;
|
||||
let mut max = 0;
|
||||
let mut numbers_found = FxHashSet::default();
|
||||
let mut ranges_found = Vec::new();
|
||||
|
||||
for pat in pats {
|
||||
if let PatKind::Lit(lit) = pat.kind
|
||||
&& let Some(num) = expr_as_u128(lit)
|
||||
{
|
||||
numbers_found.insert(num);
|
||||
|
||||
min = min.min(num);
|
||||
max = max.max(num);
|
||||
} else if let PatKind::Range(Some(left), Some(right), end) = pat.kind
|
||||
&& let Some(left) = expr_as_u128(left)
|
||||
&& let Some(right) = expr_as_u128(right)
|
||||
&& right >= left
|
||||
{
|
||||
min = min.min(left);
|
||||
max = max.max(right);
|
||||
ranges_found.push(left..=match end {
|
||||
RangeEnd::Included => right,
|
||||
RangeEnd::Excluded => right - 1,
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let contains_whole_range = 'contains: {
|
||||
let mut num = min;
|
||||
while num <= max {
|
||||
if numbers_found.contains(&num) {
|
||||
num += 1;
|
||||
}
|
||||
// Given a list of (potentially overlapping) ranges like:
|
||||
// 1..=5, 3..=7, 6..=10
|
||||
// We want to find the range with the highest end that still contains the current number
|
||||
else if let Some(range) = ranges_found
|
||||
.iter()
|
||||
.filter(|range| range.contains(&num))
|
||||
.max_by_key(|range| range.end())
|
||||
{
|
||||
num = range.end() + 1;
|
||||
} else {
|
||||
break 'contains false;
|
||||
}
|
||||
}
|
||||
break 'contains true;
|
||||
};
|
||||
|
||||
if contains_whole_range {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_RANGE_PATTERNS,
|
||||
pat.span,
|
||||
"this OR pattern can be rewritten using a range",
|
||||
"try",
|
||||
format!("{min}..={max}"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
|
||||
use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash};
|
||||
use core::cmp::Ordering;
|
||||
use core::iter;
|
||||
use core::slice;
|
||||
|
@ -9,6 +9,7 @@ use rustc_ast::ast::LitKind;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
|
||||
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Symbol;
|
||||
|
@ -103,17 +104,21 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
|||
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
|
||||
for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
|
||||
if matches!(arm2.pat.kind, PatKind::Wild) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_SAME_ARMS,
|
||||
arm1.span,
|
||||
"this match arm has an identical body to the `_` wildcard arm",
|
||||
|diag| {
|
||||
diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
|
||||
.help("or try changing either arm body")
|
||||
.span_note(arm2.span, "`_` wildcard arm here");
|
||||
},
|
||||
);
|
||||
if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
|
||||
|| is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_SAME_ARMS,
|
||||
arm1.span,
|
||||
"this match arm has an identical body to the `_` wildcard arm",
|
||||
|diag| {
|
||||
diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
|
||||
.help("or try changing either arm body")
|
||||
.span_note(arm2.span, "`_` wildcard arm here");
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let back_block = backwards_blocking_idxs[j];
|
||||
let (keep_arm, move_arm) = if back_block < i || (back_block == 0 && forwards_blocking_idxs[i] <= j) {
|
||||
|
|
|
@ -25,7 +25,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
|
|||
let mut ident_bind_name = kw::Underscore;
|
||||
if !matching_wild {
|
||||
// Looking for unused bindings (i.e.: `_e`)
|
||||
for pat in inner.iter() {
|
||||
for pat in inner {
|
||||
if let PatKind::Binding(_, id, ident, None) = pat.kind {
|
||||
if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
|
||||
ident_bind_name = ident.name;
|
||||
|
|
|
@ -38,6 +38,11 @@ declare_clippy_lint! {
|
|||
/// Checks for matches with a single arm where an `if let`
|
||||
/// will usually suffice.
|
||||
///
|
||||
/// This intentionally does not lint if there are comments
|
||||
/// inside of the other arm, so as to allow the user to document
|
||||
/// why having another explicit pattern with an empty body is necessary,
|
||||
/// or because the comments need to be preserved for other reasons.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Just readability – `if let` nests less than a `match`.
|
||||
///
|
||||
|
@ -559,6 +564,9 @@ declare_clippy_lint! {
|
|||
/// ### What it does
|
||||
/// Checks for `match` with identical arm bodies.
|
||||
///
|
||||
/// Note: Does not lint on wildcards if the `non_exhaustive_omitted_patterns_lint` feature is
|
||||
/// enabled and disallowed.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably a copy & paste error. If arm bodies
|
||||
/// are the same on purpose, you can factor them
|
||||
|
@ -777,7 +785,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for temporaries returned from function calls in a match scrutinee that have the
|
||||
/// Checks for temporaries returned from function calls in a match scrutinee that have the
|
||||
/// `clippy::has_significant_drop` attribute.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
|
|
|
@ -41,7 +41,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
|
|||
cx.tcx.valtree_to_const_val((ty, min_val_const.to_valtree())),
|
||||
ty,
|
||||
);
|
||||
miri_to_const(cx.tcx, min_constant)?
|
||||
miri_to_const(cx, min_constant)?
|
||||
},
|
||||
};
|
||||
let rhs_const = match rhs {
|
||||
|
@ -52,7 +52,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
|
|||
cx.tcx.valtree_to_const_val((ty, max_val_const.to_valtree())),
|
||||
ty,
|
||||
);
|
||||
miri_to_const(cx.tcx, max_constant)?
|
||||
miri_to_const(cx, max_constant)?
|
||||
},
|
||||
};
|
||||
let lhs_val = lhs_const.int_value(cx, ty)?;
|
||||
|
|
|
@ -140,7 +140,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
for generic_arg in b.iter() {
|
||||
for generic_arg in *b {
|
||||
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
|
||||
if self.has_sig_drop_attr(cx, ty) {
|
||||
return true;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{expr_block, snippet};
|
||||
use clippy_utils::source::{expr_block, get_source_text, snippet};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
|
||||
use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
|
||||
use core::cmp::max;
|
||||
|
@ -7,10 +7,26 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
|
||||
|
||||
/// Checks if there are comments contained within a span.
|
||||
/// This is a very "naive" check, as it just looks for the literal characters // and /* in the
|
||||
/// source text. This won't be accurate if there are potentially expressions contained within the
|
||||
/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
|
||||
/// match arms.
|
||||
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
||||
if let Some(ff) = get_source_text(cx, span)
|
||||
&& let Some(text) = ff.as_str()
|
||||
{
|
||||
text.as_bytes().windows(2)
|
||||
.any(|w| w == b"//" || w == b"/*")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
|
||||
|
@ -25,7 +41,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
|||
return;
|
||||
}
|
||||
let els = arms[1].body;
|
||||
let els = if is_unit_expr(peel_blocks(els)) {
|
||||
let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) {
|
||||
None
|
||||
} else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
|
||||
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
|
||||
|
@ -35,7 +51,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
|||
// block with 2+ statements or 1 expr and 1+ statement
|
||||
Some(els)
|
||||
} else {
|
||||
// not a block, don't lint
|
||||
// not a block or an emtpy block w/ comments, don't lint
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
|||
/// Finds function return type by examining return expressions in match arms.
|
||||
fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
|
||||
if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr {
|
||||
for arm in arms.iter() {
|
||||
for arm in *arms {
|
||||
if let ExprKind::Ret(Some(ret)) = arm.body.kind {
|
||||
return Some(cx.typeck_results().expr_ty(ret));
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `std::mem::forget(t)` where `t` is
|
||||
/// `Drop`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `std::mem::forget(t)` prevents `t` from running its
|
||||
/// destructor, possibly causing leaks.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::mem;
|
||||
/// # use std::rc::Rc;
|
||||
/// mem::forget(Rc::new(55))
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MEM_FORGET,
|
||||
restriction,
|
||||
"`mem::forget` usage on `Drop` types, likely to cause memory leaks"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MemForget => [MEM_FORGET]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MemForget {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Call(path_expr, [ref first_arg, ..]) = e.kind {
|
||||
if let ExprKind::Path(ref qpath) = path_expr.kind {
|
||||
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
|
||||
if cx.tcx.is_diagnostic_item(sym::mem_forget, def_id) {
|
||||
let forgot_ty = cx.typeck_results().expr_ty(first_arg);
|
||||
|
||||
if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
|
||||
span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
85
clippy_lints/src/methods/drain_collect.rs
Normal file
85
clippy_lints/src/methods/drain_collect.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
use crate::methods::DRAIN_COLLECT;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_range_full;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_hir::Path;
|
||||
use rustc_hir::QPath;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::query::Key;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
/// Checks if both types match the given diagnostic item, e.g.:
|
||||
///
|
||||
/// `vec![1,2].drain(..).collect::<Vec<_>>()`
|
||||
/// ^^^^^^^^^ ^^^^^^ true
|
||||
/// `vec![1,2].drain(..).collect::<HashSet<_>>()`
|
||||
/// ^^^^^^^^^ ^^^^^^^^^^ false
|
||||
fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool {
|
||||
if let Some(expr_adt_did) = expr.ty_adt_id()
|
||||
&& let Some(recv_adt_did) = recv.ty_adt_id()
|
||||
{
|
||||
cx.tcx.is_diagnostic_item(sym, expr_adt_did) && cx.tcx.is_diagnostic_item(sym, recv_adt_did)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks `std::{vec::Vec, collections::VecDeque}`.
|
||||
fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool {
|
||||
(types_match_diagnostic_item(cx, expr, recv, sym::Vec)
|
||||
|| types_match_diagnostic_item(cx, expr, recv, sym::VecDeque))
|
||||
&& matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path)))
|
||||
}
|
||||
|
||||
/// Checks `std::string::String`
|
||||
fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool {
|
||||
is_type_lang_item(cx, expr, LangItem::String)
|
||||
&& is_type_lang_item(cx, recv, LangItem::String)
|
||||
&& matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path)))
|
||||
}
|
||||
|
||||
/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`.
|
||||
fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> {
|
||||
types_match_diagnostic_item(cx, expr, recv, sym::HashSet)
|
||||
.then_some("HashSet")
|
||||
.or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap"))
|
||||
.or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::BinaryHeap).then_some("BinaryHeap"))
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) {
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
let recv_ty_no_refs = recv_ty.peel_refs();
|
||||
|
||||
if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind
|
||||
&& let Some(typename) = check_vec(cx, args, expr_ty, recv_ty_no_refs, recv_path)
|
||||
.then_some("Vec")
|
||||
.or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
|
||||
.or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs))
|
||||
{
|
||||
let recv = snippet(cx, recv.span, "<expr>");
|
||||
let sugg = if let ty::Ref(..) = recv_ty.kind() {
|
||||
format!("std::mem::take({recv})")
|
||||
} else {
|
||||
format!("std::mem::take(&mut {recv})")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DRAIN_COLLECT,
|
||||
expr.span,
|
||||
&format!("you seem to be trying to move all elements into a new `{typename}`"),
|
||||
"consider using `mem::take`",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -23,21 +22,15 @@ pub(super) fn check<'tcx>(
|
|||
let mut applicability = Applicability::MachineApplicable;
|
||||
let expr_ty = cx.typeck_results().expr_ty(recv);
|
||||
let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability);
|
||||
let mut needs_ref;
|
||||
let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() {
|
||||
needs_ref = get_args_str.parse::<usize>().is_ok();
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) {
|
||||
needs_ref = get_args_str.parse::<usize>().is_ok();
|
||||
"Vec"
|
||||
} else if is_type_diagnostic_item(cx, expr_ty, sym::VecDeque) {
|
||||
needs_ref = get_args_str.parse::<usize>().is_ok();
|
||||
"VecDeque"
|
||||
} else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::HashMap) {
|
||||
needs_ref = true;
|
||||
"HashMap"
|
||||
} else if !is_mut && is_type_diagnostic_item(cx, expr_ty, sym::BTreeMap) {
|
||||
needs_ref = true;
|
||||
"BTreeMap"
|
||||
} else {
|
||||
return; // caller is not a type that we want to lint
|
||||
|
@ -45,18 +38,24 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
let mut span = expr.span;
|
||||
|
||||
// Handle the case where the result is immediately dereferenced
|
||||
// by not requiring ref and pulling the dereference into the
|
||||
// suggestion.
|
||||
if_chain! {
|
||||
if needs_ref;
|
||||
if let Some(parent) = get_parent_expr(cx, expr);
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind;
|
||||
then {
|
||||
needs_ref = false;
|
||||
// Handle the case where the result is immediately dereferenced,
|
||||
// either directly be the user, or as a result of a method call or the like
|
||||
// by not requiring an explicit reference
|
||||
let needs_ref = if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let hir::ExprKind::Unary(hir::UnOp::Deref, _)
|
||||
| hir::ExprKind::MethodCall(..)
|
||||
| hir::ExprKind::Field(..)
|
||||
| hir::ExprKind::Index(..) = parent.kind
|
||||
{
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind {
|
||||
// if the user explicitly dereferences the result, we can adjust
|
||||
// the span to also include the deref part
|
||||
span = parent.span;
|
||||
}
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
let mut_str = if is_mut { "_mut" } else { "" };
|
||||
let borrow_str = if !needs_ref {
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::symbol::{Symbol, sym};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
|
||||
use super::INEFFICIENT_TO_STRING;
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ pub(super) fn check<'tcx>(
|
|||
let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() {
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::Vec) {
|
||||
"Vec"
|
||||
"`Vec`"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::VecDeque) {
|
||||
"VecDeque"
|
||||
"`VecDeque`"
|
||||
} else {
|
||||
iter_nth_zero::check(cx, expr, nth_recv, nth_arg);
|
||||
return; // caller is not a type that we want to lint
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use clippy_utils::{is_lang_item_or_ctor, is_trait_method};
|
||||
use hir::{LangItem, OwnerNode};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -11,20 +11,21 @@ use rustc_span::sym;
|
|||
use super::ITER_NTH_ZERO;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_trait_method(cx, expr, sym::Iterator);
|
||||
if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_NTH_ZERO,
|
||||
expr.span,
|
||||
"called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent",
|
||||
"try calling `.next()` instead of `.nth(0)`",
|
||||
format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
if let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id))
|
||||
&& let def_id = item.owner_id.to_def_id()
|
||||
&& is_trait_method(cx, expr, sym::Iterator)
|
||||
&& let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg)
|
||||
&& !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext)
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_NTH_ZERO,
|
||||
expr.span,
|
||||
"called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent",
|
||||
"try calling `.next()` instead of `.nth(0)`",
|
||||
format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut app)),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ mod clone_on_copy;
|
|||
mod clone_on_ref_ptr;
|
||||
mod cloned_instead_of_copied;
|
||||
mod collapsible_str_replace;
|
||||
mod drain_collect;
|
||||
mod err_expect;
|
||||
mod expect_fun_call;
|
||||
mod expect_used;
|
||||
|
@ -93,6 +94,7 @@ mod unnecessary_fold;
|
|||
mod unnecessary_iter_cloned;
|
||||
mod unnecessary_join;
|
||||
mod unnecessary_lazy_eval;
|
||||
mod unnecessary_literal_unwrap;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_to_owned;
|
||||
mod unwrap_or_else_default;
|
||||
|
@ -273,6 +275,32 @@ declare_clippy_lint! {
|
|||
"using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `.unwrap()` related calls on `Result`s and `Option`s that are constructed.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is better to write the value directly without the indirection.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// let val1 = Some(1).unwrap();
|
||||
/// let val2 = Ok::<_, ()>(1).unwrap();
|
||||
/// let val3 = Err::<(), _>(1).unwrap_err();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let val1 = 1;
|
||||
/// let val2 = 1;
|
||||
/// let val3 = 1;
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub UNNECESSARY_LITERAL_UNWRAP,
|
||||
complexity,
|
||||
"using `unwrap()` related calls on `Result` and `Option` constructors"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
|
||||
|
@ -485,6 +513,7 @@ declare_clippy_lint! {
|
|||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// # fn some_function(foo: ()) -> usize { 1 }
|
||||
/// option.map(|a| a + 1).unwrap_or(0);
|
||||
/// option.map(|a| a > 10).unwrap_or(false);
|
||||
/// result.map(|a| a + 1).unwrap_or_else(some_function);
|
||||
/// ```
|
||||
///
|
||||
|
@ -494,6 +523,7 @@ declare_clippy_lint! {
|
|||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// # fn some_function(foo: ()) -> usize { 1 }
|
||||
/// option.map_or(0, |a| a + 1);
|
||||
/// option.is_some_and(|a| a > 10);
|
||||
/// result.map_or_else(some_function, |a| a + 1);
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
|
@ -3220,6 +3250,42 @@ declare_clippy_lint! {
|
|||
"manual reverse iteration of `DoubleEndedIterator`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `.drain()` that clear the collection, immediately followed by a call to `.collect()`.
|
||||
///
|
||||
/// > "Collection" in this context refers to any type with a `drain` method:
|
||||
/// > `Vec`, `VecDeque`, `BinaryHeap`, `HashSet`,`HashMap`, `String`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `mem::take` is faster as it avoids the allocation.
|
||||
/// When using `mem::take`, the old collection is replaced with an empty one and ownership of
|
||||
/// the old collection is returned.
|
||||
///
|
||||
/// ### Known issues
|
||||
/// `mem::take(&mut vec)` is almost equivalent to `vec.drain(..).collect()`, except that
|
||||
/// it also moves the **capacity**. The user might have explicitly written it this way
|
||||
/// to keep the capacity on the original `Vec`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
|
||||
/// v.drain(..).collect()
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::mem;
|
||||
/// fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
|
||||
/// mem::take(v)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub DRAIN_COLLECT,
|
||||
perf,
|
||||
"calling `.drain(..).collect()` to move all elements into a new collection"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
|
@ -3349,6 +3415,8 @@ impl_lint_pass!(Methods => [
|
|||
SUSPICIOUS_COMMAND_ARG_SPACE,
|
||||
CLEAR_WITH_DRAIN,
|
||||
MANUAL_NEXT_BACK,
|
||||
UNNECESSARY_LITERAL_UNWRAP,
|
||||
DRAIN_COLLECT
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
@ -3578,6 +3646,9 @@ impl Methods {
|
|||
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
|
||||
}
|
||||
},
|
||||
Some(("drain", recv, args, ..)) => {
|
||||
drain_collect::check(cx, args, expr, recv);
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
|
@ -3606,12 +3677,18 @@ impl Methods {
|
|||
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
|
||||
}
|
||||
},
|
||||
("expect", [_]) => match method_call(recv) {
|
||||
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
|
||||
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
|
||||
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
|
||||
("expect", [_]) => {
|
||||
match method_call(recv) {
|
||||
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
|
||||
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
|
||||
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
|
||||
}
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
},
|
||||
("expect_err", [_]) => {
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests);
|
||||
},
|
||||
("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
|
||||
("extend", [arg]) => {
|
||||
string_extend_chars::check(cx, expr, recv, arg);
|
||||
extend_with_drain::check(cx, expr, recv, arg);
|
||||
|
@ -3816,28 +3893,41 @@ impl Methods {
|
|||
},
|
||||
_ => {},
|
||||
}
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
|
||||
},
|
||||
("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
|
||||
("unwrap_or", [u_arg]) => match method_call(recv) {
|
||||
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => {
|
||||
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
|
||||
},
|
||||
Some(("map", m_recv, [m_arg], span, _)) => {
|
||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
|
||||
},
|
||||
Some(("then_some", t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
|
||||
},
|
||||
_ => {},
|
||||
("unwrap_err", []) => {
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests);
|
||||
},
|
||||
("unwrap_or_else", [u_arg]) => match method_call(recv) {
|
||||
Some(("map", recv, [map_arg], _, _))
|
||||
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
|
||||
_ => {
|
||||
unwrap_or_else_default::check(cx, expr, recv, u_arg);
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
||||
},
|
||||
("unwrap_or", [u_arg]) => {
|
||||
match method_call(recv) {
|
||||
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => {
|
||||
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
|
||||
},
|
||||
Some(("map", m_recv, [m_arg], span, _)) => {
|
||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
|
||||
},
|
||||
Some(("then_some", t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
},
|
||||
("unwrap_or_default", []) => {
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
}
|
||||
("unwrap_or_else", [u_arg]) => {
|
||||
match method_call(recv) {
|
||||
Some(("map", recv, [map_arg], _, _))
|
||||
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
|
||||
_ => {
|
||||
unwrap_or_else_default::check(cx, expr, recv, u_arg);
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
||||
},
|
||||
}
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
},
|
||||
("zip", [arg]) => {
|
||||
if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{walk_path, Visitor};
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_hir::Node;
|
||||
use rustc_hir::PatKind;
|
||||
use rustc_hir::QPath;
|
||||
use rustc_hir::{self, HirId, Path};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::{sym, Symbol};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MAP_UNWRAP_OR;
|
||||
|
||||
/// lint use of `map().unwrap_or()` for `Option`s
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &rustc_hir::Expr<'_>,
|
||||
|
@ -22,12 +29,27 @@ pub(super) fn check<'tcx>(
|
|||
unwrap_recv: &rustc_hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
map_span: Span,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) {
|
||||
if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
|
||||
// Do not lint if the `map` argument uses identifiers in the `map`
|
||||
// argument that are also used in the `unwrap_or` argument
|
||||
// Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
|
||||
// borrowck errors, see #10579 for one such instance.
|
||||
// In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
|
||||
// ```
|
||||
// let x = vec![1, 2];
|
||||
// x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
|
||||
// ```
|
||||
// This compiles, but changing it to `map_or` will produce a compile error:
|
||||
// ```
|
||||
// let x = vec![1, 2];
|
||||
// x.get(0..1).map_or(x, |s| s.to_vec())
|
||||
// ^ moving `x` here
|
||||
// ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
|
||||
// ```
|
||||
// So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
|
||||
// before the call to `unwrap_or`.
|
||||
|
||||
let mut unwrap_visitor = UnwrapVisitor {
|
||||
cx,
|
||||
|
@ -35,14 +57,18 @@ pub(super) fn check<'tcx>(
|
|||
};
|
||||
unwrap_visitor.visit_expr(unwrap_arg);
|
||||
|
||||
let mut map_expr_visitor = MapExprVisitor {
|
||||
let mut reference_visitor = ReferenceVisitor {
|
||||
cx,
|
||||
identifiers: unwrap_visitor.identifiers,
|
||||
found_identifier: false,
|
||||
found_reference: false,
|
||||
unwrap_or_span: unwrap_arg.span,
|
||||
};
|
||||
map_expr_visitor.visit_expr(map_arg);
|
||||
|
||||
if map_expr_visitor.found_identifier {
|
||||
let map = cx.tcx.hir();
|
||||
let body = map.body(map.body_owned_by(map.enclosing_body_owner(expr.hir_id)));
|
||||
reference_visitor.visit_body(body);
|
||||
|
||||
if reference_visitor.found_reference {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -51,16 +77,29 @@ pub(super) fn check<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
|
||||
let suggest_is_some_and = msrv.meets(msrvs::OPTION_IS_SOME_AND)
|
||||
&& matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
|
||||
if matches!(lit.node, rustc_ast::LitKind::Bool(false)));
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// get snippet for unwrap_or()
|
||||
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
|
||||
// lint message
|
||||
// comparing the snippet from source to raw text ("None") below is safe
|
||||
// because we already have checked the type.
|
||||
let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
|
||||
let arg = if unwrap_snippet == "None" {
|
||||
"None"
|
||||
} else if suggest_is_some_and {
|
||||
"false"
|
||||
} else {
|
||||
"<a>"
|
||||
};
|
||||
let unwrap_snippet_none = unwrap_snippet == "None";
|
||||
let suggest = if unwrap_snippet_none {
|
||||
"and_then(<f>)"
|
||||
} else if suggest_is_some_and {
|
||||
"is_some_and(<f>)"
|
||||
} else {
|
||||
"map_or(<a>, <f>)"
|
||||
};
|
||||
|
@ -75,12 +114,18 @@ pub(super) fn check<'tcx>(
|
|||
let mut suggestion = vec![
|
||||
(
|
||||
map_span,
|
||||
String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
|
||||
String::from(if unwrap_snippet_none {
|
||||
"and_then"
|
||||
} else if suggest_is_some_and {
|
||||
"is_some_and"
|
||||
} else {
|
||||
"map_or"
|
||||
}),
|
||||
),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
|
||||
];
|
||||
|
||||
if !unwrap_snippet_none {
|
||||
if !unwrap_snippet_none && !suggest_is_some_and {
|
||||
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
|
||||
}
|
||||
|
||||
|
@ -91,35 +136,18 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
struct UnwrapVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
identifiers: FxHashSet<Symbol>,
|
||||
identifiers: FxHashSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
|
||||
self.identifiers.insert(ident(path));
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
}
|
||||
|
||||
struct MapExprVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
identifiers: FxHashSet<Symbol>,
|
||||
found_identifier: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
|
||||
if self.identifiers.contains(&ident(path)) {
|
||||
self.found_identifier = true;
|
||||
return;
|
||||
fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
|
||||
if let Res::Local(local_id) = path.res
|
||||
&& let Some(Node::Pat(pat)) = self.cx.tcx.hir().find(local_id)
|
||||
&& let PatKind::Binding(_, local_id, ..) = pat.kind
|
||||
{
|
||||
self.identifiers.insert(local_id);
|
||||
}
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
@ -129,10 +157,35 @@ impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn ident(path: &Path<'_>) -> Symbol {
|
||||
path.segments
|
||||
.last()
|
||||
.expect("segments should be composed of at least 1 element")
|
||||
.ident
|
||||
.name
|
||||
struct ReferenceVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
identifiers: FxHashSet<HirId>,
|
||||
found_reference: bool,
|
||||
unwrap_or_span: Span,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ReferenceVisitor<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) {
|
||||
// If we haven't found a reference yet, check if this references
|
||||
// one of the locals that was moved in the `unwrap_or` argument.
|
||||
// We are only interested in exprs that appear before the `unwrap_or` call.
|
||||
if !self.found_reference {
|
||||
if expr.span < self.unwrap_or_span
|
||||
&& let ExprKind::Path(ref path) = expr.kind
|
||||
&& let QPath::Resolved(_, path) = path
|
||||
&& let Res::Local(local_id) = path.res
|
||||
&& let Some(Node::Pat(pat)) = self.cx.tcx.hir().find(local_id)
|
||||
&& let PatKind::Binding(_, local_id, ..) = pat.kind
|
||||
&& self.identifiers.contains(&local_id)
|
||||
{
|
||||
self.found_reference = true;
|
||||
}
|
||||
rustc_hir::intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,113 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::PatKind;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{source_map::Span, sym};
|
||||
|
||||
use super::UNNECESSARY_FOLD;
|
||||
|
||||
/// Do we need to suggest turbofish when suggesting a replacement method?
|
||||
/// Changing `fold` to `sum` needs it sometimes when the return type can't be
|
||||
/// inferred. This checks for some common cases where it can be safely omitted
|
||||
fn needs_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
let parent = cx.tcx.hir().get_parent(expr.hir_id);
|
||||
|
||||
// some common cases where turbofish isn't needed:
|
||||
// - assigned to a local variable with a type annotation
|
||||
if let hir::Node::Local(local) = parent
|
||||
&& local.ty.is_some()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// - part of a function call argument, can be inferred from the function signature (provided that
|
||||
// the parameter is not a generic type parameter)
|
||||
if let hir::Node::Expr(parent_expr) = parent
|
||||
&& let hir::ExprKind::Call(recv, args) = parent_expr.kind
|
||||
&& let hir::ExprKind::Path(ref qpath) = recv.kind
|
||||
&& let Some(fn_def_id) = cx.qpath_res(qpath, recv.hir_id).opt_def_id()
|
||||
&& let fn_sig = cx.tcx.fn_sig(fn_def_id).skip_binder().skip_binder()
|
||||
&& let Some(arg_pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
|
||||
&& let Some(ty) = fn_sig.inputs().get(arg_pos)
|
||||
&& !matches!(ty.kind(), ty::Param(_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if it's neither of those, stay on the safe side and suggest turbofish,
|
||||
// even if it could work!
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Replacement {
|
||||
method_name: &'static str,
|
||||
has_args: bool,
|
||||
has_generic_return: bool,
|
||||
}
|
||||
|
||||
fn check_fold_with_op(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
acc: &hir::Expr<'_>,
|
||||
fold_span: Span,
|
||||
op: hir::BinOpKind,
|
||||
replacement: Replacement,
|
||||
) {
|
||||
if_chain! {
|
||||
// Extract the body of the closure passed to fold
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
|
||||
// Check if the closure body is of the form `acc <op> some_expr(x)`
|
||||
if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
|
||||
if bin_op.node == op;
|
||||
|
||||
// Extract the names of the two arguments to the closure
|
||||
if let [param_a, param_b] = closure_body.params;
|
||||
if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind;
|
||||
if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind;
|
||||
|
||||
if path_to_local_id(left_expr, first_arg_id);
|
||||
if replacement.has_args || path_to_local_id(right_expr, second_arg_id);
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let turbofish = if replacement.has_generic_return {
|
||||
format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs())
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let sugg = if replacement.has_args {
|
||||
format!(
|
||||
"{method}{turbofish}(|{second_arg_ident}| {r})",
|
||||
method = replacement.method_name,
|
||||
r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{method}{turbofish}()",
|
||||
method = replacement.method_name,
|
||||
)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_FOLD,
|
||||
fold_span.with_hi(expr.span.hi()),
|
||||
// TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
|
||||
"this `.fold` can be written more succinctly using another method",
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
@ -18,60 +121,6 @@ pub(super) fn check(
|
|||
acc: &hir::Expr<'_>,
|
||||
fold_span: Span,
|
||||
) {
|
||||
fn check_fold_with_op(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
acc: &hir::Expr<'_>,
|
||||
fold_span: Span,
|
||||
op: hir::BinOpKind,
|
||||
replacement_method_name: &str,
|
||||
replacement_has_args: bool,
|
||||
) {
|
||||
if_chain! {
|
||||
// Extract the body of the closure passed to fold
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
|
||||
// Check if the closure body is of the form `acc <op> some_expr(x)`
|
||||
if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
|
||||
if bin_op.node == op;
|
||||
|
||||
// Extract the names of the two arguments to the closure
|
||||
if let [param_a, param_b] = closure_body.params;
|
||||
if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind;
|
||||
if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind;
|
||||
|
||||
if path_to_local_id(left_expr, first_arg_id);
|
||||
if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let sugg = if replacement_has_args {
|
||||
format!(
|
||||
"{replacement_method_name}(|{second_arg_ident}| {r})",
|
||||
r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{replacement_method_name}()",
|
||||
)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_FOLD,
|
||||
fold_span.with_hi(expr.span.hi()),
|
||||
// TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
|
||||
"this `.fold` can be written more succinctly using another method",
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that this is a call to Iterator::fold rather than just some function called fold
|
||||
if !is_trait_method(cx, expr, sym::Iterator) {
|
||||
return;
|
||||
|
@ -80,11 +129,59 @@ pub(super) fn check(
|
|||
// Check if the first argument to .fold is a suitable literal
|
||||
if let hir::ExprKind::Lit(lit) = init.kind {
|
||||
match lit.node {
|
||||
ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true),
|
||||
ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true),
|
||||
ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false),
|
||||
ast::LitKind::Bool(false) => {
|
||||
check_fold_with_op(
|
||||
cx,
|
||||
expr,
|
||||
acc,
|
||||
fold_span,
|
||||
hir::BinOpKind::Or,
|
||||
Replacement {
|
||||
has_args: true,
|
||||
has_generic_return: false,
|
||||
method_name: "any",
|
||||
},
|
||||
);
|
||||
},
|
||||
ast::LitKind::Bool(true) => {
|
||||
check_fold_with_op(
|
||||
cx,
|
||||
expr,
|
||||
acc,
|
||||
fold_span,
|
||||
hir::BinOpKind::And,
|
||||
Replacement {
|
||||
has_args: true,
|
||||
has_generic_return: false,
|
||||
method_name: "all",
|
||||
},
|
||||
);
|
||||
},
|
||||
ast::LitKind::Int(0, _) => check_fold_with_op(
|
||||
cx,
|
||||
expr,
|
||||
acc,
|
||||
fold_span,
|
||||
hir::BinOpKind::Add,
|
||||
Replacement {
|
||||
has_args: false,
|
||||
has_generic_return: needs_turbofish(cx, expr),
|
||||
method_name: "sum",
|
||||
},
|
||||
),
|
||||
ast::LitKind::Int(1, _) => {
|
||||
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false);
|
||||
check_fold_with_op(
|
||||
cx,
|
||||
expr,
|
||||
acc,
|
||||
fold_span,
|
||||
hir::BinOpKind::Mul,
|
||||
Replacement {
|
||||
has_args: false,
|
||||
has_generic_return: needs_turbofish(cx, expr),
|
||||
method_name: "product",
|
||||
},
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
|
95
clippy_lints/src/methods/unnecessary_literal_unwrap.rs
Normal file
95
clippy_lints/src/methods/unnecessary_literal_unwrap.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_then, is_res_lang_ctor, last_path_segment, path_res, MaybePath};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::UNNECESSARY_LITERAL_UNWRAP;
|
||||
|
||||
fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -> Option<&'a hir::Ty<'a>> {
|
||||
let args = args?;
|
||||
|
||||
if args.len() <= index {
|
||||
return None;
|
||||
}
|
||||
|
||||
match args[index] {
|
||||
hir::GenericArg::Type(ty) => match ty.kind {
|
||||
hir::TyKind::Infer => None,
|
||||
_ => Some(ty),
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
method: &str,
|
||||
args: &[hir::Expr<'_>],
|
||||
) {
|
||||
let init = clippy_utils::expr_or_init(cx, recv);
|
||||
|
||||
let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind {
|
||||
let Some(qpath) = call.qpath_opt() else { return };
|
||||
|
||||
let args = last_path_segment(qpath).args.map(|args| args.args);
|
||||
let res = cx.qpath_res(qpath, call.hir_id());
|
||||
|
||||
if is_res_lang_ctor(cx, res, hir::LangItem::OptionSome) {
|
||||
("Some", call_args, get_ty_from_args(args, 0))
|
||||
} else if is_res_lang_ctor(cx, res, hir::LangItem::ResultOk) {
|
||||
("Ok", call_args, get_ty_from_args(args, 0))
|
||||
} else if is_res_lang_ctor(cx, res, hir::LangItem::ResultErr) {
|
||||
("Err", call_args, get_ty_from_args(args, 1))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if is_res_lang_ctor(cx, path_res(cx, init), hir::LangItem::OptionNone) {
|
||||
let call_args: &[hir::Expr<'_>] = &[];
|
||||
("None", call_args, None)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let help_message = format!("used `{method}()` on `{constructor}` value");
|
||||
let suggestion_message = format!("remove the `{constructor}` and `{method}()`");
|
||||
|
||||
span_lint_and_then(cx, UNNECESSARY_LITERAL_UNWRAP, expr.span, &help_message, |diag| {
|
||||
let suggestions = match (constructor, method, ty) {
|
||||
("None", "unwrap", _) => Some(vec![(expr.span, "panic!()".to_string())]),
|
||||
("None", "expect", _) => Some(vec![
|
||||
(expr.span.with_hi(args[0].span.lo()), "panic!(".to_string()),
|
||||
(expr.span.with_lo(args[0].span.hi()), ")".to_string()),
|
||||
]),
|
||||
(_, _, Some(_)) => None,
|
||||
("Ok", "unwrap_err", None) | ("Err", "unwrap", None) => Some(vec![
|
||||
(
|
||||
recv.span.with_hi(call_args[0].span.lo()),
|
||||
"panic!(\"{:?}\", ".to_string(),
|
||||
),
|
||||
(expr.span.with_lo(call_args[0].span.hi()), ")".to_string()),
|
||||
]),
|
||||
("Ok", "expect_err", None) | ("Err", "expect", None) => Some(vec![
|
||||
(
|
||||
recv.span.with_hi(call_args[0].span.lo()),
|
||||
"panic!(\"{1}: {:?}\", ".to_string(),
|
||||
),
|
||||
(call_args[0].span.with_lo(args[0].span.lo()), ", ".to_string()),
|
||||
]),
|
||||
(_, _, None) => Some(vec![
|
||||
(recv.span.with_hi(call_args[0].span.lo()), String::new()),
|
||||
(expr.span.with_lo(call_args[0].span.hi()), String::new()),
|
||||
]),
|
||||
};
|
||||
|
||||
match (init.span == recv.span, suggestions) {
|
||||
(true, Some(suggestions)) => {
|
||||
diag.multipart_suggestion(suggestion_message, suggestions, Applicability::MachineApplicable);
|
||||
},
|
||||
_ => {
|
||||
diag.span_help(init.span, suggestion_message);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
|
@ -144,6 +144,11 @@ fn check_addr_of_expr(
|
|||
if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
|
||||
if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
|
||||
if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty);
|
||||
// Make sure that it's actually calling the right `.to_string()`, (#10033)
|
||||
// *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow)
|
||||
// but that's ok for Cow::into_owned specifically)
|
||||
if cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty
|
||||
|| is_cow_into_owned(cx, method_name, method_def_id);
|
||||
then {
|
||||
if n_receiver_refs > 0 {
|
||||
span_lint_and_sugg(
|
||||
|
|
153
clippy_lints/src/min_ident_chars.rs
Normal file
153
clippy_lints/src/min_ident_chars.rs
Normal file
|
@ -0,0 +1,153 @@
|
|||
use clippy_utils::{diagnostics::span_lint, is_from_proc_macro};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::{
|
||||
def::{DefKind, Res},
|
||||
intravisit::{walk_item, Visitor},
|
||||
GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for idents which comprise of a single letter.
|
||||
///
|
||||
/// Note: This lint can be very noisy when enabled; it may be desirable to only enable it
|
||||
/// temporarily.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In many cases it's not, but at times it can severely hinder readability. Some codebases may
|
||||
/// wish to disallow this to improve readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// for m in movies {
|
||||
/// let title = m.t;
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// for movie in movies {
|
||||
/// let title = movie.title;
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub MIN_IDENT_CHARS,
|
||||
restriction,
|
||||
"disallows idents that are too short"
|
||||
}
|
||||
impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MinIdentChars {
|
||||
pub allowed_idents_below_min_chars: FxHashSet<String>,
|
||||
pub min_ident_chars_threshold: u64,
|
||||
}
|
||||
|
||||
impl MinIdentChars {
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
fn is_ident_too_short(&self, cx: &LateContext<'_>, str: &str, span: Span) -> bool {
|
||||
!in_external_macro(cx.sess(), span)
|
||||
&& str.len() <= self.min_ident_chars_threshold as usize
|
||||
&& !str.starts_with('_')
|
||||
&& !str.is_empty()
|
||||
&& self.allowed_idents_below_min_chars.get(&str.to_owned()).is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for MinIdentChars {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if self.min_ident_chars_threshold == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
walk_item(&mut IdentVisitor { conf: self, cx }, item);
|
||||
}
|
||||
|
||||
// This is necessary as `Node::Pat`s are not visited in `visit_id`. :/
|
||||
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
|
||||
if let PatKind::Binding(_, _, ident, ..) = pat.kind
|
||||
&& let str = ident.as_str()
|
||||
&& self.is_ident_too_short(cx, str, ident.span)
|
||||
{
|
||||
emit_min_ident_chars(self, cx, str, ident.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IdentVisitor<'cx, 'tcx> {
|
||||
conf: &'cx MinIdentChars,
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
}
|
||||
|
||||
impl Visitor<'_> for IdentVisitor<'_, '_> {
|
||||
fn visit_id(&mut self, hir_id: HirId) {
|
||||
let Self { conf, cx } = *self;
|
||||
// FIXME(#112534) Reimplementation of `find`, as it uses indexing, which can (and will in
|
||||
// async functions, or really anything async) panic. This should probably be fixed on the
|
||||
// rustc side, this is just a temporary workaround.
|
||||
let node = if hir_id.local_id == ItemLocalId::from_u32(0) {
|
||||
// In this case, we can just use `find`, `Owner`'s `node` field is private anyway so we can't
|
||||
// reimplement it even if we wanted to
|
||||
cx.tcx.hir().find(hir_id)
|
||||
} else {
|
||||
let Some(owner) = cx.tcx.hir_owner_nodes(hir_id.owner).as_owner() else {
|
||||
return;
|
||||
};
|
||||
owner.nodes.get(hir_id.local_id).copied().flatten().map(|p| p.node)
|
||||
};
|
||||
let Some(node) = node else {
|
||||
return;
|
||||
};
|
||||
let Some(ident) = node.ident() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let str = ident.as_str();
|
||||
if conf.is_ident_too_short(cx, str, ident.span) {
|
||||
if let Node::Item(item) = node && let ItemKind::Use(..) = item.kind {
|
||||
return;
|
||||
}
|
||||
// `struct Awa<T>(T)`
|
||||
// ^
|
||||
if let Node::PathSegment(path) = node {
|
||||
if let Res::Def(def_kind, ..) = path.res && let DefKind::TyParam = def_kind {
|
||||
return;
|
||||
}
|
||||
if matches!(path.res, Res::PrimTy(..)) || path.res.opt_def_id().is_some_and(|def_id| !def_id.is_local())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
// `struct Awa<T>(T)`
|
||||
// ^
|
||||
if let Node::GenericParam(generic_param) = node
|
||||
&& let GenericParamKind::Type { .. } = generic_param.kind
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if is_from_proc_macro(cx, &ident) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit_min_ident_chars(conf, cx, str, ident.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_min_ident_chars(conf: &MinIdentChars, cx: &impl LintContext, ident: &str, span: Span) {
|
||||
let help = if conf.min_ident_chars_threshold == 1 {
|
||||
Cow::Borrowed("this ident consists of a single char")
|
||||
} else {
|
||||
Cow::Owned(format!(
|
||||
"this ident is too short ({} <= {})",
|
||||
ident.len(),
|
||||
conf.min_ident_chars_threshold,
|
||||
))
|
||||
};
|
||||
span_lint(cx, MIN_IDENT_CHARS, span, &help);
|
||||
}
|
|
@ -66,7 +66,7 @@ enum MinMax {
|
|||
Max,
|
||||
}
|
||||
|
||||
fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
|
||||
fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(path, args) => {
|
||||
if let ExprKind::Path(ref qpath) = path.kind {
|
||||
|
@ -99,12 +99,12 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons
|
|||
}
|
||||
}
|
||||
|
||||
fn fetch_const<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
fn fetch_const<'a, 'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
receiver: Option<&'a Expr<'a>>,
|
||||
args: &'a [Expr<'a>],
|
||||
m: MinMax,
|
||||
) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
|
||||
) -> Option<(MinMax, Constant<'tcx>, &'a Expr<'a>)> {
|
||||
let mut args = receiver.into_iter().chain(args);
|
||||
let first_arg = args.next()?;
|
||||
let second_arg = args.next()?;
|
||||
|
|
|
@ -13,7 +13,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, suffix: &str, lit_sni
|
|||
return;
|
||||
}
|
||||
let mut seen = (false, false);
|
||||
for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
|
||||
for ch in &lit_snip.as_bytes()[2..=maybe_last_sep_idx] {
|
||||
match ch {
|
||||
b'a'..=b'f' => seen.0 = true,
|
||||
b'A'..=b'F' => seen.1 = true,
|
||||
|
|
|
@ -339,6 +339,10 @@ impl EarlyLintPass for MiscEarlyLints {
|
|||
}
|
||||
|
||||
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
|
||||
if in_external_macro(cx.sess(), pat.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
unneeded_field_pattern::check(cx, pat);
|
||||
redundant_pattern::check(cx, pat);
|
||||
unneeded_wildcard_pattern::check(cx, pat);
|
||||
|
|
|
@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
|||
then {
|
||||
// get the name and span of the generic parameters in the Impl
|
||||
let mut impl_params = Vec::new();
|
||||
for p in generic_args.args.iter() {
|
||||
for p in generic_args.args {
|
||||
match p {
|
||||
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
|
||||
impl_params.push((path.segments[0].ident.to_string(), path.span)),
|
||||
|
|
|
@ -207,6 +207,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
|
|||
if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind
|
||||
&& let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
|
||||
&& let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind
|
||||
// don't trigger if self is a generic parameter, e.g. `impl<T> Debug for T`
|
||||
// this can only happen in core itself, where the trait is defined,
|
||||
// but it caused ICEs in the past:
|
||||
// https://github.com/rust-lang/rust-clippy/issues/10887
|
||||
&& !matches!(self_path.res, Res::Def(DefKind::TyParam, _))
|
||||
&& cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug])
|
||||
// don't trigger if this impl was derived
|
||||
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
|
||||
|
|
|
@ -114,7 +114,7 @@ struct DivergenceVisitor<'a, 'tcx> {
|
|||
impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
||||
fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
match e.kind {
|
||||
ExprKind::Closure { .. } => {},
|
||||
ExprKind::Closure(..) | ExprKind::If(..) | ExprKind::Loop(..) => {},
|
||||
ExprKind::Match(e, arms, _) => {
|
||||
self.visit_expr(e);
|
||||
for arm in arms {
|
||||
|
@ -128,6 +128,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
|||
_ => walk_expr(self, e),
|
||||
}
|
||||
}
|
||||
|
||||
fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
|
||||
span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
|
||||
}
|
||||
|
@ -136,6 +137,15 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
|||
impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
match e.kind {
|
||||
// fix #10776
|
||||
ExprKind::Block(block, ..) => match (block.stmts, block.expr) {
|
||||
([], Some(e)) => self.visit_expr(e),
|
||||
([stmt], None) => match stmt.kind {
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e),
|
||||
_ => {},
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
|
||||
ExprKind::Call(func, _) => {
|
||||
let typ = self.cx.typeck_results().expr_ty(func);
|
||||
|
|
|
@ -2,6 +2,7 @@ use rustc_ast::ast;
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::def_id::LOCAL_CRATE;
|
||||
use rustc_span::{FileName, SourceFile, Span, SyntaxContext};
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Component, Path};
|
||||
|
@ -90,7 +91,14 @@ impl EarlyLintPass for ModStyle {
|
|||
// `{ foo => path/to/foo.rs, .. }
|
||||
let mut file_map = FxHashMap::default();
|
||||
for file in files.iter() {
|
||||
if let FileName::Real(name) = &file.name && let Some(lp) = name.local_path() {
|
||||
if let FileName::Real(name) = &file.name
|
||||
&& let Some(lp) = name.local_path()
|
||||
&& file.cnum == LOCAL_CRATE
|
||||
{
|
||||
// [#8887](https://github.com/rust-lang/rust-clippy/issues/8887)
|
||||
// Only check files in the current crate.
|
||||
// Fix false positive that crate dependency in workspace sub directory
|
||||
// is checked unintentionally.
|
||||
let path = if lp.is_relative() {
|
||||
lp
|
||||
} else if let Ok(relative) = lp.strip_prefix(trim_to_src) {
|
||||
|
|
|
@ -92,10 +92,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
75
clippy_lints/src/needless_if.rs
Normal file
75
clippy_lints/src/needless_if.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, higher::If, is_from_proc_macro, source::snippet_opt};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty `if` branches with no else branch.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can be entirely omitted, and often the condition too.
|
||||
///
|
||||
/// ### Known issues
|
||||
/// This will usually only suggest to remove the `if` statement, not the condition. Other lints
|
||||
/// such as `no_effect` will take care of removing the condition if it's unnecessary.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if really_expensive_condition(&i) {}
|
||||
/// if really_expensive_condition_with_side_effects(&mut i) {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// // <omitted>
|
||||
/// really_expensive_condition_with_side_effects(&mut i);
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub NEEDLESS_IF,
|
||||
complexity,
|
||||
"checks for empty if branches"
|
||||
}
|
||||
declare_lint_pass!(NeedlessIf => [NEEDLESS_IF]);
|
||||
|
||||
impl LateLintPass<'_> for NeedlessIf {
|
||||
fn check_stmt<'tcx>(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) {
|
||||
if let StmtKind::Expr(expr) = stmt.kind
|
||||
&& let Some(If {cond, then, r#else: None }) = If::hir(expr)
|
||||
&& let ExprKind::Block(block, ..) = then.kind
|
||||
&& block.stmts.is_empty()
|
||||
&& block.expr.is_none()
|
||||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
&& !is_from_proc_macro(cx, expr)
|
||||
&& let Some(then_snippet) = snippet_opt(cx, then.span)
|
||||
// Ignore
|
||||
// - empty macro expansions
|
||||
// - empty reptitions in macro expansions
|
||||
// - comments
|
||||
// - #[cfg]'d out code
|
||||
&& then_snippet.chars().all(|ch| matches!(ch, '{' | '}') || ch.is_ascii_whitespace())
|
||||
&& let Some(cond_snippet) = snippet_opt(cx, cond.span)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_IF,
|
||||
stmt.span,
|
||||
"this `if` branch is empty",
|
||||
"you can remove it",
|
||||
if cond.can_have_side_effects() || !cx.tcx.hir().attrs(stmt.hir_id).is_empty() {
|
||||
// `{ foo }` or `{ foo } && bar` placed into a statement position would be
|
||||
// interpreted as a block statement, force it to be an expression
|
||||
if cond_snippet.starts_with('{') {
|
||||
format!("({cond_snippet});")
|
||||
} else {
|
||||
format!("{cond_snippet};")
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,13 +7,12 @@ use clippy_utils::ty::{
|
|||
use clippy_utils::{get_trait_def_id, is_self, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind,
|
||||
};
|
||||
use rustc_hir::{HirIdMap, HirIdSet, LangItem};
|
||||
use rustc_hir::{HirIdSet, LangItem};
|
||||
use rustc_hir_typeck::expr_use_visitor as euv;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -126,9 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
.filter_map(|pred| {
|
||||
// Note that we do not want to deal with qualified predicates here.
|
||||
match pred.kind().no_bound_vars() {
|
||||
Some(ty::ClauseKind::Trait(pred)) if pred.def_id() != sized_trait => {
|
||||
Some(pred)
|
||||
},
|
||||
Some(ty::ClauseKind::Trait(pred)) if pred.def_id() != sized_trait => Some(pred),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
|
@ -136,11 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
|
||||
// Collect moved variables and spans which will need dereferencings from the
|
||||
// function body.
|
||||
let MovedVariablesCtxt {
|
||||
moved_vars,
|
||||
spans_need_deref,
|
||||
..
|
||||
} = {
|
||||
let MovedVariablesCtxt { moved_vars } = {
|
||||
let mut ctx = MovedVariablesCtxt::default();
|
||||
let infcx = cx.tcx.infer_ctxt().build();
|
||||
euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
|
||||
|
@ -211,7 +204,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
}
|
||||
}
|
||||
|
||||
let deref_span = spans_need_deref.get(&canonical_id);
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, ty, sym::Vec);
|
||||
if let Some(clone_spans) =
|
||||
|
@ -247,7 +239,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
}
|
||||
|
||||
// cannot be destructured, no need for `*` suggestion
|
||||
assert!(deref_span.is_none());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -275,23 +266,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
);
|
||||
}
|
||||
|
||||
assert!(deref_span.is_none());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
|
||||
let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
|
||||
|
||||
// Suggests adding `*` to dereference the added reference.
|
||||
if let Some(deref_span) = deref_span {
|
||||
spans.extend(
|
||||
deref_span
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
|
||||
);
|
||||
spans.sort_by_key(|&(span, _)| span);
|
||||
}
|
||||
multispan_sugg(diag, "consider taking a reference instead", spans);
|
||||
};
|
||||
|
||||
|
@ -320,9 +300,6 @@ fn requires_exact_signature(attrs: &[Attribute]) -> bool {
|
|||
#[derive(Default)]
|
||||
struct MovedVariablesCtxt {
|
||||
moved_vars: HirIdSet,
|
||||
/// Spans which need to be prefixed with `*` for dereferencing the
|
||||
/// suggested additional reference.
|
||||
spans_need_deref: HirIdMap<FxHashSet<Span>>,
|
||||
}
|
||||
|
||||
impl MovedVariablesCtxt {
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use clippy_utils::peel_blocks;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::has_drop;
|
||||
use clippy_utils::{get_parent_node, is_lint_allowed};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
|
||||
use rustc_hir::{
|
||||
is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, FnRetTy, ItemKind, Node, PatKind, Stmt, StmtKind,
|
||||
UnsafeSource,
|
||||
};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_infer::infer::TyCtxtInferExt as _;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -86,7 +91,43 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
|||
fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
if let StmtKind::Semi(expr) = stmt.kind {
|
||||
if has_no_effect(cx, expr) {
|
||||
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
NO_EFFECT,
|
||||
expr.hir_id,
|
||||
stmt.span,
|
||||
"statement with no effect",
|
||||
|diag| {
|
||||
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
|
||||
if let Node::Item(item) = parent.1
|
||||
&& let ItemKind::Fn(sig, ..) = item.kind
|
||||
&& let FnRetTy::Return(ret_ty) = sig.decl.output
|
||||
&& let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id)
|
||||
&& let [.., final_stmt] = block.stmts
|
||||
&& final_stmt.hir_id == stmt.hir_id
|
||||
{
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
let mut ret_ty = hir_ty_to_ty(cx.tcx, ret_ty);
|
||||
|
||||
// Remove `impl Future<Output = T>` to get `T`
|
||||
if cx.tcx.ty_is_opaque_future(ret_ty) &&
|
||||
let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
|
||||
{
|
||||
ret_ty = true_ret_ty;
|
||||
}
|
||||
|
||||
if ret_ty == expr_ty {
|
||||
diag.span_suggestion(
|
||||
stmt.span.shrink_to_lo(),
|
||||
"did you mean to return it?",
|
||||
"return ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
} else if let StmtKind::Local(local) = stmt.kind {
|
||||
|
|
|
@ -166,15 +166,15 @@ fn is_value_unfrozen_raw<'tcx>(
|
|||
// have a value that is a frozen variant with a generic param (an example is
|
||||
// `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`).
|
||||
// However, it prevents a number of false negatives that is, I think, important:
|
||||
// 1. assoc consts in trait defs referring to consts of themselves
|
||||
// (an example is `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
|
||||
// 2. a path expr referring to assoc consts whose type is doesn't have
|
||||
// any frozen variants in trait defs (i.e. without substitute for `Self`).
|
||||
// (e.g. borrowing `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
|
||||
// 3. similar to the false positive above;
|
||||
// but the value is an unfrozen variant, or the type has no enums. (An example is
|
||||
// `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT`
|
||||
// and `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
|
||||
// 1. assoc consts in trait defs referring to consts of themselves (an example is
|
||||
// `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
|
||||
// 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait
|
||||
// defs (i.e. without substitute for `Self`). (e.g. borrowing
|
||||
// `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
|
||||
// 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no
|
||||
// enums. (An example is
|
||||
// `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and
|
||||
// `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
|
||||
// One might be able to prevent these FNs correctly, and replace this with `false`;
|
||||
// e.g. implementing `has_frozen_variant` described above, and not running this function
|
||||
// when the type doesn't have any frozen variants would be the 'correct' way for the 2nd
|
||||
|
|
|
@ -85,7 +85,7 @@ fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static
|
|||
}
|
||||
}
|
||||
|
||||
fn is_allowed(val: &Constant) -> bool {
|
||||
fn is_allowed(val: &Constant<'_>) -> bool {
|
||||
match val {
|
||||
&Constant::F32(f) => f == 0.0 || f.is_infinite(),
|
||||
&Constant::F64(f) => f == 0.0 || f.is_infinite(),
|
||||
|
|
|
@ -140,6 +140,9 @@ fn try_get_option_occurrence<'tcx>(
|
|||
let (as_ref, as_mut) = match &expr.kind {
|
||||
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
|
||||
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
|
||||
_ if let Some(mutb) = cx.typeck_results().expr_ty(expr).ref_mutability() => {
|
||||
(mutb == Mutability::Not, mutb == Mutability::Mut)
|
||||
}
|
||||
_ => (bind_annotation == BindingAnnotation::REF, bind_annotation == BindingAnnotation::REF_MUT),
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use clippy_utils::source::snippet_opt;
|
|||
use clippy_utils::ty::expr_sig;
|
||||
use clippy_utils::visitors::contains_unsafe_block;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
|
||||
use hir::LifetimeName;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::{Applicability, MultiSpan};
|
||||
use rustc_hir::def_id::DefId;
|
||||
|
@ -15,6 +16,7 @@ use rustc_hir::{
|
|||
ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
|
||||
TyKind, Unsafety,
|
||||
};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -166,6 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
|||
cx,
|
||||
cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder().inputs(),
|
||||
sig.decl.inputs,
|
||||
&sig.decl.output,
|
||||
&[],
|
||||
)
|
||||
.filter(|arg| arg.mutability() == Mutability::Not)
|
||||
|
@ -218,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
|||
check_mut_from_ref(cx, sig, Some(body));
|
||||
let decl = sig.decl;
|
||||
let sig = cx.tcx.fn_sig(item_id).subst_identity().skip_binder();
|
||||
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
|
||||
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, &decl.output, body.params)
|
||||
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
|
||||
.collect();
|
||||
let results = check_ptr_arg_usage(cx, body, &lint_args);
|
||||
|
@ -407,29 +410,27 @@ impl<'tcx> DerefTy<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check_fn_args<'cx, 'tcx: 'cx>(
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
tys: &'tcx [Ty<'tcx>],
|
||||
hir_tys: &'tcx [hir::Ty<'tcx>],
|
||||
ret_ty: &'tcx FnRetTy<'tcx>,
|
||||
params: &'tcx [Param<'tcx>],
|
||||
) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
|
||||
tys.iter()
|
||||
.zip(hir_tys.iter())
|
||||
.enumerate()
|
||||
.filter_map(|(i, (ty, hir_ty))| {
|
||||
if_chain! {
|
||||
if let ty::Ref(_, ty, mutability) = *ty.kind();
|
||||
if let ty::Adt(adt, substs) = *ty.kind();
|
||||
|
||||
if let TyKind::Ref(lt, ref ty) = hir_ty.kind;
|
||||
if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
|
||||
|
||||
.filter_map(move |(i, (ty, hir_ty))| {
|
||||
if let ty::Ref(_, ty, mutability) = *ty.kind()
|
||||
&& let ty::Adt(adt, substs) = *ty.kind()
|
||||
&& let TyKind::Ref(lt, ref ty) = hir_ty.kind
|
||||
&& let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind
|
||||
// Check that the name as typed matches the actual name of the type.
|
||||
// e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
|
||||
if let [.., name] = path.segments;
|
||||
if cx.tcx.item_name(adt.did()) == name.ident.name;
|
||||
|
||||
then {
|
||||
&& let [.., name] = path.segments
|
||||
&& cx.tcx.item_name(adt.did()) == name.ident.name
|
||||
{
|
||||
let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
|
||||
let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
|
||||
Some(sym::Vec) => (
|
||||
|
@ -454,30 +455,65 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
|||
DerefTy::Path,
|
||||
),
|
||||
Some(sym::Cow) if mutability == Mutability::Not => {
|
||||
let ty_name = name.args
|
||||
if let Some((lifetime, ty)) = name.args
|
||||
.and_then(|args| {
|
||||
args.args.iter().find_map(|a| match a {
|
||||
GenericArg::Type(x) => Some(x),
|
||||
_ => None,
|
||||
})
|
||||
if let [GenericArg::Lifetime(lifetime), ty] = args.args {
|
||||
return Some((lifetime, ty));
|
||||
}
|
||||
None
|
||||
})
|
||||
.and_then(|arg| snippet_opt(cx, arg.span))
|
||||
.unwrap_or_else(|| substs.type_at(1).to_string());
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
PTR_ARG,
|
||||
emission_id,
|
||||
hir_ty.span,
|
||||
"using a reference to `Cow` is not recommended",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
hir_ty.span,
|
||||
"change this to",
|
||||
format!("&{}{ty_name}", mutability.prefix_str()),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
{
|
||||
if !lifetime.is_anonymous()
|
||||
&& let FnRetTy::Return(ret_ty) = ret_ty
|
||||
&& let ret_ty = hir_ty_to_ty(cx.tcx, ret_ty)
|
||||
&& ret_ty
|
||||
.walk()
|
||||
.filter_map(|arg| {
|
||||
arg.as_region().and_then(|lifetime| {
|
||||
match lifetime.kind() {
|
||||
ty::ReEarlyBound(r) => Some(r.def_id),
|
||||
ty::ReLateBound(_, r) => r.kind.get_id(),
|
||||
ty::ReFree(r) => r.bound_region.get_id(),
|
||||
ty::ReStatic
|
||||
| ty::ReVar(_)
|
||||
| ty::RePlaceholder(_)
|
||||
| ty::ReErased
|
||||
| ty::ReError(_) => None,
|
||||
}
|
||||
})
|
||||
})
|
||||
.any(|def_id| {
|
||||
matches!(
|
||||
lifetime.res,
|
||||
LifetimeName::Param(param_def_id) if def_id
|
||||
.as_local()
|
||||
.is_some_and(|def_id| def_id == param_def_id),
|
||||
)
|
||||
})
|
||||
{
|
||||
// `&Cow<'a, T>` when the return type uses 'a is okay
|
||||
return None;
|
||||
}
|
||||
);
|
||||
|
||||
let ty_name =
|
||||
snippet_opt(cx, ty.span()).unwrap_or_else(|| substs.type_at(1).to_string());
|
||||
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
PTR_ARG,
|
||||
emission_id,
|
||||
hir_ty.span,
|
||||
"using a reference to `Cow` is not recommended",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
hir_ty.span,
|
||||
"change this to",
|
||||
format!("&{}{ty_name}", mutability.prefix_str()),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
return None;
|
||||
},
|
||||
_ => return None,
|
||||
|
@ -495,7 +531,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
|||
},
|
||||
deref_ty,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{
|
||||
eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id,
|
||||
peel_blocks, peel_blocks_with_stmt,
|
||||
};
|
||||
use clippy_utils::{higher, is_path_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::declare_tool_lint;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, symbol::Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -41,7 +42,16 @@ declare_clippy_lint! {
|
|||
"checks for expressions that could be replaced by the question mark operator"
|
||||
}
|
||||
|
||||
declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
|
||||
#[derive(Default)]
|
||||
pub struct QuestionMark {
|
||||
/// Keeps track of how many try blocks we are in at any point during linting.
|
||||
/// This allows us to answer the question "are we inside of a try block"
|
||||
/// very quickly, without having to walk up the parent chain, by simply checking
|
||||
/// if it is greater than zero.
|
||||
/// As for why we need this in the first place: <https://github.com/rust-lang/rust-clippy/issues/8628>
|
||||
try_block_depth_stack: Vec<u32>,
|
||||
}
|
||||
impl_lint_pass!(QuestionMark => [QUESTION_MARK]);
|
||||
|
||||
enum IfBlockType<'hir> {
|
||||
/// An `if x.is_xxx() { a } else { b } ` expression.
|
||||
|
@ -68,98 +78,6 @@ enum IfBlockType<'hir> {
|
|||
),
|
||||
}
|
||||
|
||||
/// Checks if the given expression on the given context matches the following structure:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if option.is_none() {
|
||||
/// return None;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```ignore
|
||||
/// if result.is_err() {
|
||||
/// return result;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If it matches, it will suggest to use the question mark operator instead
|
||||
fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind;
|
||||
let caller_ty = cx.typeck_results().expr_ty(caller);
|
||||
let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
|
||||
if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
|
||||
let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) &&
|
||||
!matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
|
||||
let sugg = if let Some(else_inner) = r#else {
|
||||
if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
|
||||
format!("Some({receiver_str}?)")
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" })
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
QUESTION_MARK,
|
||||
expr.span,
|
||||
"this block may be rewritten with the `?` operator",
|
||||
"replace it with",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind;
|
||||
if ddpos.as_opt_usize().is_none();
|
||||
if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind;
|
||||
let caller_ty = cx.typeck_results().expr_ty(let_expr);
|
||||
let if_block = IfBlockType::IfLet(
|
||||
cx.qpath_res(path1, let_pat.hir_id),
|
||||
caller_ty,
|
||||
ident.name,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else
|
||||
);
|
||||
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|
||||
|| is_early_return(sym::Result, cx, &if_block);
|
||||
if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
|
||||
let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
|
||||
let sugg = format!(
|
||||
"{receiver_str}{}?{}",
|
||||
if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
|
||||
if requires_semi { ";" } else { "" }
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
QUESTION_MARK,
|
||||
expr.span,
|
||||
"this block may be rewritten with the `?` operator",
|
||||
"replace it with",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
|
||||
match *if_block {
|
||||
IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
|
||||
|
@ -230,11 +148,147 @@ fn expr_return_none_or_err(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !in_constant(cx, expr.hir_id) {
|
||||
check_is_none_or_err_and_early_return(cx, expr);
|
||||
check_if_let_some_or_err_and_early_return(cx, expr);
|
||||
impl QuestionMark {
|
||||
fn inside_try_block(&self) -> bool {
|
||||
self.try_block_depth_stack.last() > Some(&0)
|
||||
}
|
||||
|
||||
/// Checks if the given expression on the given context matches the following structure:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if option.is_none() {
|
||||
/// return None;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```ignore
|
||||
/// if result.is_err() {
|
||||
/// return result;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If it matches, it will suggest to use the question mark operator instead
|
||||
fn check_is_none_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if !self.inside_try_block();
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind;
|
||||
let caller_ty = cx.typeck_results().expr_ty(caller);
|
||||
let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
|
||||
if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
|
||||
let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) &&
|
||||
!matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
|
||||
let sugg = if let Some(else_inner) = r#else {
|
||||
if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
|
||||
format!("Some({receiver_str}?)")
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" })
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
QUESTION_MARK,
|
||||
expr.span,
|
||||
"this block may be rewritten with the `?` operator",
|
||||
"replace it with",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_let_some_or_err_and_early_return<'tcx>(&self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if !self.inside_try_block();
|
||||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind;
|
||||
if ddpos.as_opt_usize().is_none();
|
||||
if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind;
|
||||
let caller_ty = cx.typeck_results().expr_ty(let_expr);
|
||||
let if_block = IfBlockType::IfLet(
|
||||
cx.qpath_res(path1, let_pat.hir_id),
|
||||
caller_ty,
|
||||
ident.name,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else
|
||||
);
|
||||
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|
||||
|| is_early_return(sym::Result, cx, &if_block);
|
||||
if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
|
||||
let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
|
||||
let sugg = format!(
|
||||
"{receiver_str}{}?{}",
|
||||
if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
|
||||
if requires_semi { ";" } else { "" }
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
QUESTION_MARK,
|
||||
expr.span,
|
||||
"this block may be rewritten with the `?` operator",
|
||||
"replace it with",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool {
|
||||
if let Some(expr) = bl.expr
|
||||
&& let rustc_hir::ExprKind::Call(callee, _) = expr.kind
|
||||
{
|
||||
is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !in_constant(cx, expr.hir_id) {
|
||||
self.check_is_none_or_err_and_early_return(cx, expr);
|
||||
self.check_if_let_some_or_err_and_early_return(cx, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx rustc_hir::Block<'tcx>) {
|
||||
if is_try_block(cx, block) {
|
||||
*self
|
||||
.try_block_depth_stack
|
||||
.last_mut()
|
||||
.expect("blocks are always part of bodies and must have a depth") += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Body<'tcx>) {
|
||||
self.try_block_depth_stack.push(0);
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &'tcx rustc_hir::Body<'tcx>) {
|
||||
self.try_block_depth_stack.pop();
|
||||
}
|
||||
|
||||
fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx rustc_hir::Block<'tcx>) {
|
||||
if is_try_block(cx, block) {
|
||||
*self
|
||||
.try_block_depth_stack
|
||||
.last_mut()
|
||||
.expect("blocks are always part of bodies and must have a depth") -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -296,8 +296,8 @@ fn check_possible_range_contains(
|
|||
}
|
||||
}
|
||||
|
||||
struct RangeBounds<'a> {
|
||||
val: Constant,
|
||||
struct RangeBounds<'a, 'tcx> {
|
||||
val: Constant<'tcx>,
|
||||
expr: &'a Expr<'a>,
|
||||
id: HirId,
|
||||
name_span: Span,
|
||||
|
@ -309,7 +309,7 @@ struct RangeBounds<'a> {
|
|||
// Takes a binary expression such as x <= 2 as input
|
||||
// Breaks apart into various pieces, such as the value of the number,
|
||||
// hir id of the variable, and direction/inclusiveness of the operator
|
||||
fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
|
||||
fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a, 'tcx>> {
|
||||
if let ExprKind::Binary(ref op, l, r) = ex.kind {
|
||||
let (inclusive, ordering) = match op.node {
|
||||
BinOpKind::Gt => (false, Ordering::Greater),
|
||||
|
|
143
clippy_lints/src/raw_strings.rs
Normal file
143
clippy_lints/src/raw_strings.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use std::{iter::once, ops::ControlFlow};
|
||||
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
|
||||
use rustc_ast::{
|
||||
ast::{Expr, ExprKind},
|
||||
token::LitKind,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for raw string literals where a string literal can be used instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's just unnecessary, but there are many cases where using a raw string literal is more
|
||||
/// idiomatic than a string literal, so it's opt-in.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let r = r"Hello, world!";
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let r = "Hello, world!";
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub NEEDLESS_RAW_STRINGS,
|
||||
restriction,
|
||||
"suggests using a string literal when a raw string literal is unnecessary"
|
||||
}
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for raw string literals with an unnecessary amount of hashes around them.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's just unnecessary, and makes it look like there's more escaping needed than is actually
|
||||
/// necessary.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let r = r###"Hello, "world"!"###;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let r = r#"Hello, "world"!"#;
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub NEEDLESS_RAW_STRING_HASHES,
|
||||
style,
|
||||
"suggests reducing the number of hashes around a raw string literal"
|
||||
}
|
||||
impl_lint_pass!(RawStrings => [NEEDLESS_RAW_STRINGS, NEEDLESS_RAW_STRING_HASHES]);
|
||||
|
||||
pub struct RawStrings {
|
||||
pub needless_raw_string_hashes_allow_one: bool,
|
||||
}
|
||||
|
||||
impl EarlyLintPass for RawStrings {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if !in_external_macro(cx.sess(), expr.span)
|
||||
&& let ExprKind::Lit(lit) = expr.kind
|
||||
&& let LitKind::StrRaw(max) | LitKind::ByteStrRaw(max) | LitKind::CStrRaw(max) = lit.kind
|
||||
{
|
||||
let str = lit.symbol.as_str();
|
||||
let prefix = match lit.kind {
|
||||
LitKind::StrRaw(..) => "r",
|
||||
LitKind::ByteStrRaw(..) => "br",
|
||||
LitKind::CStrRaw(..) => "cr",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !str.contains(['\\', '"']) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_RAW_STRINGS,
|
||||
expr.span,
|
||||
"unnecessary raw string literal",
|
||||
"try",
|
||||
format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let req = {
|
||||
let mut following_quote = false;
|
||||
let mut req = 0;
|
||||
// `once` so a raw string ending in hashes is still checked
|
||||
let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| {
|
||||
match b {
|
||||
b'"' => (following_quote, req) = (true, 1),
|
||||
// I'm a bit surprised the compiler didn't optimize this out, there's no
|
||||
// branch but it still ends up doing an unnecessary comparison, it's:
|
||||
// - cmp r9b,1h
|
||||
// - sbb cl,-1h
|
||||
// which will add 1 if it's true. With this change, it becomes:
|
||||
// - add cl,r9b
|
||||
// isn't that so much nicer?
|
||||
b'#' => req += u8::from(following_quote),
|
||||
_ => {
|
||||
if following_quote {
|
||||
following_quote = false;
|
||||
|
||||
if req == max {
|
||||
return ControlFlow::Break(req);
|
||||
}
|
||||
|
||||
return ControlFlow::Continue(acc.max(req));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
ControlFlow::Continue(acc)
|
||||
});
|
||||
|
||||
match num {
|
||||
ControlFlow::Continue(num) | ControlFlow::Break(num) => num,
|
||||
}
|
||||
};
|
||||
|
||||
if req < max {
|
||||
let hashes = "#".repeat(req as usize);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_RAW_STRING_HASHES,
|
||||
expr.span,
|
||||
"unnecessary hashes around raw string literal",
|
||||
"try",
|
||||
format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.32.0"]
|
||||
pub REDUNDANT_CLONE,
|
||||
perf,
|
||||
nursery,
|
||||
"`clone()` of an owned value that is going to be dropped immediately"
|
||||
}
|
||||
|
||||
|
@ -320,8 +320,6 @@ fn base_local_and_movability<'tcx>(
|
|||
mir: &mir::Body<'tcx>,
|
||||
place: mir::Place<'tcx>,
|
||||
) -> (mir::Local, CannotMoveOut) {
|
||||
use rustc_middle::mir::PlaceRef;
|
||||
|
||||
// Dereference. You cannot move things out from a borrowed value.
|
||||
let mut deref = false;
|
||||
// Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
|
||||
|
@ -330,17 +328,14 @@ fn base_local_and_movability<'tcx>(
|
|||
// underlying type implements Copy
|
||||
let mut slice = false;
|
||||
|
||||
let PlaceRef { local, mut projection } = place.as_ref();
|
||||
while let [base @ .., elem] = projection {
|
||||
projection = base;
|
||||
for (base, elem) in place.as_ref().iter_projections() {
|
||||
let base_ty = base.ty(&mir.local_decls, cx.tcx).ty;
|
||||
deref |= matches!(elem, mir::ProjectionElem::Deref);
|
||||
field |= matches!(elem, mir::ProjectionElem::Field(..))
|
||||
&& has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
|
||||
slice |= matches!(elem, mir::ProjectionElem::Index(..))
|
||||
&& !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
|
||||
field |= matches!(elem, mir::ProjectionElem::Field(..)) && has_drop(cx, base_ty);
|
||||
slice |= matches!(elem, mir::ProjectionElem::Index(..)) && !is_copy(cx, base_ty);
|
||||
}
|
||||
|
||||
(local, deref || field || slice)
|
||||
(place.local, deref || field || slice)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use crate::rustc_lint::LintContext;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_ast::visit as ast_visit;
|
||||
use rustc_ast::visit::Visitor as AstVisitor;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit as hir_visit;
|
||||
use rustc_hir::intravisit::Visitor as HirVisitor;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -51,59 +51,136 @@ impl ReturnVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor {
|
||||
fn visit_expr(&mut self, ex: &'ast ast::Expr) {
|
||||
if let ast::ExprKind::Ret(_) | ast::ExprKind::Try(_) = ex.kind {
|
||||
impl<'tcx> Visitor<'tcx> for ReturnVisitor {
|
||||
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
|
||||
if let hir::ExprKind::Ret(_) | hir::ExprKind::Match(.., hir::MatchSource::TryDesugar) = ex.kind {
|
||||
self.found_return = true;
|
||||
} else {
|
||||
hir_visit::walk_expr(self, ex);
|
||||
}
|
||||
|
||||
ast_visit::walk_expr(self, ex);
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for RedundantClosureCall {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
if_chain! {
|
||||
if let ast::ExprKind::Call(ref paren, _) = expr.kind;
|
||||
if let ast::ExprKind::Paren(ref closure) = paren.kind;
|
||||
if let ast::ExprKind::Closure(box ast::Closure { ref asyncness, ref fn_decl, ref body, .. }) = closure.kind;
|
||||
then {
|
||||
let mut visitor = ReturnVisitor::new();
|
||||
visitor.visit_expr(body);
|
||||
if !visitor.found_return {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_CLOSURE_CALL,
|
||||
expr.span,
|
||||
"try not to call a closure in the expression where it is declared",
|
||||
|diag| {
|
||||
if fn_decl.inputs.is_empty() {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app);
|
||||
|
||||
if asyncness.is_async() {
|
||||
// `async x` is a syntax error, so it becomes `async { x }`
|
||||
if !matches!(body.kind, ast::ExprKind::Block(_, _)) {
|
||||
hint = hint.blockify();
|
||||
}
|
||||
|
||||
hint = hint.asyncify();
|
||||
}
|
||||
|
||||
diag.span_suggestion(expr.span, "try doing something like", hint.to_string(), app);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Checks if the body is owned by an async closure
|
||||
fn is_async_closure(body: &hir::Body<'_>) -> bool {
|
||||
if let hir::ExprKind::Closure(closure) = body.value.kind
|
||||
&& let [resume_ty] = closure.fn_decl.inputs
|
||||
&& let hir::TyKind::Path(hir::QPath::LangItem(hir::LangItem::ResumeTy, ..)) = resume_ty.kind
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to find the innermost closure:
|
||||
/// ```rust,ignore
|
||||
/// (|| || || || 42)()()()()
|
||||
/// ^^^^^^^^^^^^^^ given this nested closure expression
|
||||
/// ^^^^^ we want to return this closure
|
||||
/// ```
|
||||
/// It also has a parameter for how many steps to go in at most, so as to
|
||||
/// not take more closures than there are calls.
|
||||
fn find_innermost_closure<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
mut expr: &'tcx hir::Expr<'tcx>,
|
||||
mut steps: usize,
|
||||
) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, hir::IsAsync)> {
|
||||
let mut data = None;
|
||||
|
||||
while let hir::ExprKind::Closure(closure) = expr.kind
|
||||
&& let body = cx.tcx.hir().body(closure.body)
|
||||
&& {
|
||||
let mut visitor = ReturnVisitor::new();
|
||||
visitor.visit_expr(body.value);
|
||||
!visitor.found_return
|
||||
}
|
||||
&& steps > 0
|
||||
{
|
||||
expr = body.value;
|
||||
data = Some((body.value, closure.fn_decl, if is_async_closure(body) {
|
||||
hir::IsAsync::Async
|
||||
} else {
|
||||
hir::IsAsync::NotAsync
|
||||
}));
|
||||
steps -= 1;
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
|
||||
/// "Walks up" the chain of calls to find the outermost call expression, and returns the depth:
|
||||
/// ```rust,ignore
|
||||
/// (|| || || 3)()()()
|
||||
/// ^^ this is the call expression we were given
|
||||
/// ^^ this is what we want to return (and the depth is 3)
|
||||
/// ```
|
||||
fn get_parent_call_exprs<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
mut expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> (&'tcx hir::Expr<'tcx>, usize) {
|
||||
let mut depth = 1;
|
||||
while let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let hir::ExprKind::Call(recv, _) = parent.kind
|
||||
&& expr.span == recv.span
|
||||
{
|
||||
expr = parent;
|
||||
depth += 1;
|
||||
}
|
||||
(expr, depth)
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Call(recv, _) = expr.kind
|
||||
// don't lint if the receiver is a call, too.
|
||||
// we do this in order to prevent linting multiple times; consider:
|
||||
// `(|| || 1)()()`
|
||||
// ^^ we only want to lint for this call (but we walk up the calls to consider both calls).
|
||||
// without this check, we'd end up linting twice.
|
||||
&& !matches!(recv.kind, hir::ExprKind::Call(..))
|
||||
&& let (full_expr, call_depth) = get_parent_call_exprs(cx, expr)
|
||||
&& let Some((body, fn_decl, generator_kind)) = find_innermost_closure(cx, recv, call_depth)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_CLOSURE_CALL,
|
||||
full_expr.span,
|
||||
"try not to call a closure in the expression where it is declared",
|
||||
|diag| {
|
||||
if fn_decl.inputs.is_empty() {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let mut hint = Sugg::hir_with_context(cx, body, full_expr.span.ctxt(), "..", &mut applicability);
|
||||
|
||||
if generator_kind.is_async()
|
||||
&& let hir::ExprKind::Closure(closure) = body.kind
|
||||
{
|
||||
let async_closure_body = cx.tcx.hir().body(closure.body);
|
||||
|
||||
// `async x` is a syntax error, so it becomes `async { x }`
|
||||
if !matches!(async_closure_body.value.kind, hir::ExprKind::Block(_, _)) {
|
||||
hint = hint.blockify();
|
||||
}
|
||||
|
||||
hint = hint.asyncify();
|
||||
}
|
||||
|
||||
diag.span_suggestion(
|
||||
full_expr.span,
|
||||
"try doing something like",
|
||||
hint.maybe_par(),
|
||||
applicability
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
|
||||
fn count_closure_usage<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue