Skip to content

Clippy subtree update #139337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 184 commits into from
Closed
Changes from all commits
Commits
Show all changes
184 commits
Select commit Hold shift + click to select a range
e0175f8
Document how to benchmark with lintcheck --perf
blyxyas Feb 10, 2025
0ee39a2
Several improvements on lintcheck perf (desc.)
blyxyas Feb 10, 2025
4035fc2
Follow remark guidelines
blyxyas Feb 10, 2025
9c3b5cf
more natural suggestions for `cmp_owned`
lapla-cogito Feb 18, 2025
f04bfae
correct version attribute for `io_other_error`
lapla-cogito Feb 23, 2025
517766c
Update as_conversions.rs
dnbln Feb 23, 2025
92fc2bb
Emit `collapsible_match` at the right node
Alexendoo Feb 27, 2025
e1d6f1f
Use a lintcheck specific Clippy configuration file in the CI
samueltardieu Feb 16, 2025
3caa1f2
Add regression tests for `suspicious_doc_comments`
samueltardieu Feb 21, 2025
10cf3cb
Add helper methods checking for "#[non_exhaustive] that's active"
meithecatte Mar 3, 2025
0af0445
Make `visit_map` happy path more evident
smoelius Mar 8, 2025
1066ee0
fix: `redundant_clone` FP on enum cast
profetia Mar 12, 2025
80ce9d1
fix: `borrow_deref_ref` suggests wrongly when coerce to mut
profetia Mar 13, 2025
95d7281
fix: `manual_find` suggests wrongly when early return
profetia Mar 14, 2025
9576c71
Teach rustfmt to handle postfix yield
eholk Mar 13, 2025
ed1778c
Fix clippy
eholk Mar 13, 2025
17351bd
Change frequency to 3000
blyxyas Feb 16, 2025
1fdb9cf
Merge from rustc
Mar 16, 2025
ae3216e
Add comment annotations
heshanpadmasiri Mar 16, 2025
5d7e55c
Mention about .fixed files
heshanpadmasiri Mar 17, 2025
6cf2d72
expand: Leave traces when expanding `cfg_attr` attributes
petrochenkov Mar 14, 2025
e3f1bc8
Refactor YieldKind so postfix yield must have an expression
eholk Mar 18, 2025
22be689
extend `obfuscated_if_else` to support `.then().unwrap_or_default()` …
lapla-cogito Mar 18, 2025
e4daa5a
apply `obfuscated_if_else` to clippy source
lapla-cogito Mar 18, 2025
dd92647
Use `Option<Ident>` for lowered param names.
nnethercote Mar 13, 2025
a463154
set the applicability of `op_ref` to `MachineApplicable`
lapla-cogito Mar 19, 2025
e85fcab
Rollup merge of #138001 - meithecatte:privately-uninhabited, r=Nadrieril
matthiaskrgr Mar 19, 2025
8b5e701
Merge from rustc
Mar 20, 2025
919e767
Rollup merge of #138435 - eholk:prefix-yield, r=oli-obk
matthiaskrgr Mar 20, 2025
d9aec19
Rollup merge of #138685 - nnethercote:use-Option-Ident-for-lowered-pa…
matthiaskrgr Mar 20, 2025
6fb13c6
Auto merge of #138515 - petrochenkov:cfgtrace, r=nnethercote
bors Mar 20, 2025
3d0d04c
Rollup merge of #138435 - eholk:prefix-yield, r=oli-obk
matthiaskrgr Mar 20, 2025
e18fe28
Rollup merge of #138685 - nnethercote:use-Option-Ident-for-lowered-pa…
matthiaskrgr Mar 20, 2025
c86216e
Merge commit '1e5237f4a56ae958af7e5824343eacf737b67083' into clippy-s…
flip1995 Mar 20, 2025
c128221
Auto merge of #138747 - matthiaskrgr:rollup-68x44rw, r=matthiaskrgr
bors Mar 20, 2025
19c7c46
rename `rust-toolchain` to `rust-toolchain.toml`
lapla-cogito Mar 20, 2025
aadda46
Add `ignore_without_reason` lint
smoelius Jan 2, 2025
475d10d
Add test annotation
smoelius Mar 3, 2025
25f1f90
Auto merge of #138760 - matthiaskrgr:rollup-2edtg2h, r=matthiaskrgr
bors Mar 21, 2025
721ac28
fix: `filter_map_bool_then` suggest wrongly when contain return
profetia Mar 7, 2025
3f9ecbe
fix: `filter_map_bool_then`: suggests wrongly when mut capture in then
profetia Mar 11, 2025
4f2ee8c
Auto merge of #138761 - flip1995:clippy-subtree-update, r=Manishearth
bors Mar 21, 2025
6c8400e
remove `feature(inline_const_pat)`
lcnr Mar 14, 2025
90f5f55
rename `rust-toolchain` to `rust-toolchain.toml` (#14442)
flip1995 Mar 21, 2025
7a92626
fix: `filter_map_bool_then` suggest wrongly when the closure cannot b…
blyxyas Mar 21, 2025
da4f5a5
expand `neg_multiply` to lint float numbers as well
lapla-cogito Mar 21, 2025
07f7a9e
Merge from rustc
RalfJung Mar 21, 2025
c418714
expand `neg_multiply` to lint float numbers as well (#14447)
dswij Mar 21, 2025
0a141ab
fix: `redundant_clone` FP on enum cast (#14395)
dswij Mar 21, 2025
5b234c9
Change category to pedantic
smoelius Mar 21, 2025
d5a6688
Use a lintcheck specific Clippy configuration file in the CI (#14233)
flip1995 Mar 22, 2025
37e1b80
fix: `manual_find` suggests wrongly when early return (#14405)
flip1995 Mar 22, 2025
cdf2e7c
fix: `borrow_deref_ref` suggests wrongly when coerce to mut (#14403)
flip1995 Mar 22, 2025
e8e0126
Add comment annotations (#14414)
flip1995 Mar 22, 2025
8238160
Lint more cases in `collapsible_if`
samueltardieu Feb 16, 2025
d993432
set the applicability of `op_ref` to `MachineApplicable` (#14438)
Jarcho Mar 22, 2025
282b61b
fix: `nonminimal_bool` wrongly showed the macro definition
profetia Mar 17, 2025
b41f2e4
Emit `collapsible_match` at the right node (#14311)
Jarcho Mar 22, 2025
a65cc36
Add regression tests for `suspicious_doc_comments` (#14268)
Jarcho Mar 22, 2025
6166f60
Add `ignore_without_reason` lint (#13931)
Alexendoo Mar 22, 2025
c033a4c
Document and improve (a lot) lintcheck --perf (#14194)
Alexendoo Mar 23, 2025
8bc164b
fix: `option_if_let_else` suggests wrongly when coercion
profetia Mar 23, 2025
63e7815
Fix example for unconditional_recursion
alex-semenyuk Mar 10, 2025
cad9083
Lint more cases in `collapsible_if` (#14231)
flip1995 Mar 23, 2025
07e7414
fix: `nonminimal_bool` wrongly showed the macro definition (#14424)
flip1995 Mar 23, 2025
d8dff61
set concurrency for the `deploy` job
lapla-cogito Mar 21, 2025
3786c07
remove the notation of the `deploy` job
lapla-cogito Mar 23, 2025
e9aed87
set concurrency for the `deploy` job (#14448)
flip1995 Mar 23, 2025
b27a2bb
Clarify example for unconditional_recursion (#14385)
Alexendoo Mar 23, 2025
88b590b
`lint-inconsistent-...` -> `check-inconsistent-...`
smoelius Feb 23, 2025
969b5ad
Don't suggests deprecated congurations
smoelius Feb 23, 2025
de3aa1d
Add new lint `manual_dangling_ptr`
aaron-ang Feb 21, 2025
7b1f9d8
Update tests files for `manual_dangling_ptr` lint
aaron-ang Feb 21, 2025
962329f
Rename `Sugg::maybe_par()` into `Sugg::maybe_paren()`
samueltardieu Mar 24, 2025
315e9aa
Don't check deprecated configs in `configs_are_tested` test
smoelius Mar 8, 2025
e8c06a7
remove obsolete "Known Problems" in `ok_expect`
lapla-cogito Mar 24, 2025
809c931
`wildcard_imports`: lint on `pub use` if asked to
samueltardieu Feb 9, 2025
621911a
Fix various typos in lint messages, descriptions and comments
samueltardieu Mar 24, 2025
9c6cb51
`wildcard_imports`: lint on `pub use` if asked to (#14182)
y21 Mar 25, 2025
0ff7fad
Use `code` for references to other lints in as_conversions docs (#14283)
samueltardieu Mar 25, 2025
de8c404
Fix type error in lint description
samueltardieu Mar 25, 2025
b7cbb4e
Add myself to reviewer rotation
samueltardieu Mar 25, 2025
ca48e46
Add myself to reviewer rotation (#14468)
samueltardieu Mar 25, 2025
d88818d
Rename `inconsistent_struct_constructor` configuration; don't suggest…
y21 Mar 25, 2025
f5d81a3
Fix type error in lint description (#14466)
blyxyas Mar 25, 2025
e78216c
suggest `is_some_and` instead of `map_or` in `case_sensitive_file_ext…
lapla-cogito Mar 6, 2025
1d89038
Move `uninlined_format_args` to `style`
nyurik Feb 6, 2025
d063e09
Move `uninlined_format_args` to `style`
nyurik Feb 15, 2025
8e50cfa
make tests consistent
nyurik Feb 16, 2025
9d78426
suggest `is_some_and` instead of `map_or` in `case_sensitive_file_ext…
samueltardieu Mar 25, 2025
1ca79a8
test fixes
nyurik Mar 25, 2025
d95bdcf
Rename `Sugg::maybe_par()` into `Sugg::maybe_paren()` (#14457)
blyxyas Mar 25, 2025
a001ae3
Move `uninlined_format_args` back to `style` (#14160)
Alexendoo Mar 25, 2025
aba76d0
Allow defining opaques in statics and consts
compiler-errors Mar 24, 2025
94233fb
Unify `manual_unwrap_or` and `manual_unwrap_or_default` code
samueltardieu Mar 1, 2025
1893e1e
Unify `manual_unwrap_or` and `manual_unwrap_or_default` code (#14332)
Manishearth Mar 25, 2025
6509de3
Fix situations identified by `collapsible_if` new hits
samueltardieu Feb 16, 2025
d09e658
Apply collapsible if to Clippy sources (#14451)
flip1995 Mar 25, 2025
4517b42
Fix various typos in lint messages, descriptions and comments (#14459)
dswij Mar 26, 2025
939d5f9
fix: `option_if_let_else` suggests wrongly when coercion requires exp…
dswij Mar 26, 2025
a8bfafe
chore: fix some comments
todaymoon Mar 26, 2025
fe4dd5b
chore: fix some comments (#14475)
samueltardieu Mar 26, 2025
1b92712
expand: Leave traces when expanding `cfg` attributes
petrochenkov Mar 22, 2025
9b1945d
Prevent including preceeding whitespaces if line contains non blanks
samueltardieu Mar 26, 2025
c4789a0
Prevent including preceeding whitespaces if line contains non blanks …
Manishearth Mar 26, 2025
3e52867
more natural suggestions for `cmp_owned` (#14247)
Centri3 Mar 27, 2025
764c1b6
remove obsolete "Known Problems" in `ok_expect` (#14458)
dswij Mar 27, 2025
d9913dd
Do not warn about shadowing in a destructuring assigment
samueltardieu Mar 9, 2025
a895265
Do not warn about shadowing in a destructuring assigment (#14381)
samueltardieu Mar 27, 2025
9df5177
Convert the `collapsible_if` early lint to a late one
samueltardieu Mar 26, 2025
cd70152
Make `collapsible_if` recognize the `let_chains` feature
samueltardieu Mar 26, 2025
79c6911
Apply `collapsible_if` to Clippy itself
samueltardieu Mar 26, 2025
01820a9
Do not make incomplete or invalid suggestions
samueltardieu Mar 27, 2025
61b38e7
Do not make incomplete or invalid suggestions (#14487)
Manishearth Mar 27, 2025
c2922d1
Make `collapsible_if` recognize the `let_chains` feature (#14481)
flip1995 Mar 28, 2025
3e960c1
Make missing_const_for_fn operate on non-optimized MIR
1c3t3a Jan 15, 2025
d78622e
Make missing_const_for_fn operate on non-optimized MIR (#14003)
blyxyas Mar 28, 2025
ecb421f
Rollup merge of #139065 - RalfJung:miri-sync, r=RalfJung
matthiaskrgr Mar 28, 2025
432a8a7
Properly handle expansion in `single_match`
samueltardieu Mar 28, 2025
bc70192
Take advantage of match ergonomics in `clippy_utils::hir_utils`
samueltardieu Mar 29, 2025
bc28421
Take advantage of match ergonomics in `clippy_utils::hir_utils` (#14499)
dswij Mar 29, 2025
2df6bba
fix: `unnested_or_patterns` suggests wrongly in `let`
profetia Mar 13, 2025
4c610e3
fix: `unnested_or_patterns` suggests wrongly in `let` (#14401)
y21 Mar 29, 2025
ffaecd0
manually fulfill lint expectations for all unsafe blocks with metavars
y21 Mar 29, 2025
8f3a0db
don't hardcode repository owner in changelog CI
y21 Mar 29, 2025
fb32aaf
new lint: `chars_enumerate_for_byte_indices`
y21 Sep 21, 2024
32cf884
add a note for `str::bytes`
y21 Dec 3, 2024
d5d2189
rename lint to `char_indices_as_byte_indices`
y21 Dec 3, 2024
c739027
add comment and `.clone()` test case
y21 Mar 30, 2025
2eee361
use `is_automatically_derived()` instead of `has_attr(sym::automatica…
WeiTheShinobi Mar 30, 2025
cd67b3b
Don't hardcode repository in changelog CI (#14500)
flip1995 Mar 30, 2025
4f58673
new lint: `char_indices_as_byte_indices` (#13435)
Centri3 Mar 30, 2025
5f26d0e
Drop `clippy::invalid_null_ptr_usage`
Urgau Mar 9, 2025
eee7c58
use `is_automatically_derived()` instead of `has_attr()` (#14506)
Jarcho Mar 31, 2025
5791b9c
Auto merge of #119220 - Urgau:uplift-invalid_null_ptr_usage, r=fee1-dead
bors Mar 31, 2025
b46b311
add `manual_dangling_ptr` lint (#14107)
Jarcho Mar 31, 2025
bb0d09b
Make `visit_map` happy path more evident (#14376)
Jarcho Mar 31, 2025
9172556
Suggest `./x test src/tools/clippy --bless` when in `rust-lang/rust`
Alexendoo Mar 31, 2025
ce39784
Move `FindPanicUnwrap` to `missing_headers.rs`
Alexendoo Mar 14, 2025
c893685
Fix partially const bodies not linting `missing_panics_doc`
Alexendoo Mar 14, 2025
6ee4f34
Abide by `allow`/`expect` in bodies for `missing_panics_doc`
Alexendoo Mar 14, 2025
f894a81
Fulfill expectations after first `missing-panics-doc`
Alexendoo Mar 22, 2025
b96fecf
Allow `#[expect]` and `#[allow]` within function bodies for `missing_…
Jarcho Mar 31, 2025
3e837ec
Enable triagebot's merge conflict notifications
Alexendoo Mar 31, 2025
1e78abc
expand `obfuscated_if_else` to support `{then(), then_some()}.unwrap_…
Manishearth Mar 31, 2025
7d3d824
Properly handle expansion in `single_match` (#14495)
Manishearth Mar 31, 2025
9e6c7af
Suggest `./x test src/tools/clippy --bless` when in `rust-lang/rust` …
flip1995 Mar 31, 2025
6366e31
Enable triagebot's merge conflict notifications (#14508)
flip1995 Mar 31, 2025
a50fb22
Avoid `kw::Empty` use for `AuxParamsAttr`.
nnethercote Mar 27, 2025
d28d234
correct version attribute for `io_other_error` (#14282)
samueltardieu Mar 31, 2025
5101c8e
Move `ast::Item::ident` into `ast::ItemKind`.
nnethercote Mar 20, 2025
3f752b4
Address review comments.
nnethercote Apr 1, 2025
93d0885
Auto merge of #138740 - nnethercote:ast-ItemKind-idents, r=fmease
bors Apr 1, 2025
86e6cb5
Decouple trait impls of different traits wrt incremental
oli-obk Mar 27, 2025
88c46ea
Install cmake to restore lintcheck run
samueltardieu Apr 1, 2025
4304fa2
Auto merge of #138492 - lcnr:rm-inline_const_pat, r=oli-obk
bors Apr 1, 2025
54994b2
Fix the primary span of redundant_pub_crate when flagging nameless items
fmease Apr 1, 2025
227e43a
Install cmake to restore lintcheck runs (#14514)
Alexendoo Apr 1, 2025
6ca6b09
Fix the primary span of `redundant_pub_crate` when flagging nameless …
Manishearth Apr 1, 2025
d81396b
Do not build `tokio-rustls` in the CI for the time being
samueltardieu Apr 1, 2025
46878e3
Do not build `tokio-rustls` in the CI for the time being (#14517)
Alexendoo Apr 1, 2025
e429bde
Manually fulfill lint expectations for all unsafe blocks with metavar…
blyxyas Apr 1, 2025
dc7d9ec
Validate paths in `disallowed_*` configurations
smoelius Mar 12, 2025
e975563
Validate paths in `disallowed_*` configurations (#14397)
samueltardieu Apr 1, 2025
130af3f
Move methods from `Map` to `TyCtxt`, part 5.
nnethercote Apr 1, 2025
777c7cd
Remove a function that has no necessary callers
oli-obk Apr 1, 2025
2e181e7
Auto merge of #139018 - oli-obk:incremental-trait-impls, r=compiler-e…
bors Apr 2, 2025
2d119ce
Stop relabelling PRs with merge conflicts
Alexendoo Apr 2, 2025
978ed96
Rollup merge of #139232 - nnethercote:remove-Map-5, r=Zalathar
TaKO8Ki Apr 2, 2025
0ca62d1
Stop relabelling PRs with merge conflicts (#14521)
flip1995 Apr 2, 2025
416acd8
Don't use `f16` and `f128` directly in `clippy_utils`
beetrees Apr 2, 2025
6f0f22c
Fix a typo in derive.rs comment
smoelius Apr 3, 2025
f9a1a1f
Fix a typo in derive.rs comment (#14529)
samueltardieu Apr 3, 2025
193e9f2
Don't use `f16` and `f128` directly in `clippy_utils` (#14528)
Jarcho Apr 3, 2025
893a6a3
Update to new rinja version (askama)
GuillaumeGomez Apr 3, 2025
41d7e45
Update to new rinja version (askama) (#14530)
flip1995 Apr 3, 2025
ab7e525
Merge remote-tracking branch 'upstream/master' into rustup
flip1995 Apr 3, 2025
c44191a
Bump nightly version -> 2025-04-03
flip1995 Apr 3, 2025
c97bd74
Bump Clippy version -> 0.1.88
flip1995 Apr 3, 2025
944dfc7
Changelog for Clippy 1.86
flip1995 Apr 3, 2025
a2251a8
Changelog for Clippy 1.86 :penguin: (#14538)
y21 Apr 3, 2025
7bb54d9
Rustup (#14539)
flip1995 Apr 3, 2025
7be4565
Merge commit '7bb54d91be1af212faaa078786c1d2271a67d4f9' into clippy-s…
flip1995 Apr 3, 2025
57b6898
Update Cargo.lock
flip1995 Apr 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
63 changes: 57 additions & 6 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -186,6 +186,48 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"

[[package]]
name = "askama"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a4e46abb203e00ef226442d452769233142bbfdd79c3941e84c8e61c4112543"
dependencies = [
"askama_derive",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]

[[package]]
name = "askama_derive"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54398906821fd32c728135f7b351f0c7494ab95ae421d41b6f5a020e158f28a6"
dependencies = [
"askama_parser",
"basic-toml",
"memchr",
"proc-macro2",
"quote",
"rustc-hash 2.1.1",
"serde",
"serde_derive",
"syn 2.0.100",
]

[[package]]
name = "askama_parser"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
dependencies = [
"memchr",
"serde",
"serde_derive",
"winnow 0.7.4",
]

[[package]]
name = "autocfg"
version = "1.4.0"
@@ -504,9 +546,10 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"

[[package]]
name = "clippy"
version = "0.1.87"
version = "0.1.88"
dependencies = [
"anstream",
"askama",
"cargo_metadata 0.18.1",
"clippy_config",
"clippy_lints",
@@ -520,7 +563,6 @@ dependencies = [
"pulldown-cmark 0.11.3",
"quote",
"regex",
"rinja",
"rustc_tools_util 0.4.2",
"serde",
"serde_json",
@@ -535,7 +577,7 @@ dependencies = [

[[package]]
name = "clippy_config"
version = "0.1.87"
version = "0.1.88"
dependencies = [
"clippy_utils",
"itertools",
@@ -560,7 +602,7 @@ dependencies = [

[[package]]
name = "clippy_lints"
version = "0.1.87"
version = "0.1.88"
dependencies = [
"arrayvec",
"cargo_metadata 0.18.1",
@@ -583,7 +625,7 @@ dependencies = [

[[package]]
name = "clippy_utils"
version = "0.1.87"
version = "0.1.88"
dependencies = [
"arrayvec",
"itertools",
@@ -5418,7 +5460,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
"winnow 0.5.40",
]

[[package]]
@@ -6429,6 +6471,15 @@ dependencies = [
"memchr",
]

[[package]]
name = "winnow"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
dependencies = [
"memchr",
]

[[package]]
name = "winsplit"
version = "0.1.0"
2 changes: 1 addition & 1 deletion src/tools/clippy/.github/workflows/clippy_changelog.yml
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ jobs:
- name: Check Changelog
if: ${{ github.event_name == 'pull_request' }}
run: |
body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \
body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | \
python -c "import sys, json; print(json.load(sys.stdin)['body'])")
output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g")
if [ -z "$output" ]; then
4 changes: 4 additions & 0 deletions src/tools/clippy/.github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -8,6 +8,10 @@ on:
tags:
- rust-1.**

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

env:
TARGET_BRANCH: 'gh-pages'
SHA: '${{ github.sha }}'
4 changes: 2 additions & 2 deletions src/tools/clippy/.github/workflows/lintcheck.yml
Original file line number Diff line number Diff line change
@@ -66,7 +66,7 @@ jobs:

- name: Run lintcheck
if: steps.cache-json.outputs.cache-hit != 'true'
run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml

- name: Upload base JSON
uses: actions/upload-artifact@v4
@@ -97,7 +97,7 @@ jobs:
run: cargo build --manifest-path=lintcheck/Cargo.toml

- name: Run lintcheck
run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
run: env CLIPPY_CONF_DIR="$PWD/lintcheck/ci-config" ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml

- name: Upload head JSON
uses: actions/upload-artifact@v4
67 changes: 64 additions & 3 deletions src/tools/clippy/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -6,11 +6,68 @@ document.

## Unreleased / Beta / In Rust Nightly

[609cd310...master](https://github.com/rust-lang/rust-clippy/compare/609cd310...master)
[3e3715c3...master](https://github.com/rust-lang/rust-clippy/compare/3e3715c3...master)

## Rust 1.86

Current stable, released 2025-04-03

[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-12-27T15%3A11%3A38Z..2025-02-06T13%3A57%3A58Z+base%3Amaster)

### New Lints

* Added [`unneeded_struct_pattern`] to `style` [#13465](https://github.com/rust-lang/rust-clippy/pull/13465)
* Added [`doc_overindented_list_items`] to `style` [#13711](https://github.com/rust-lang/rust-clippy/pull/13711)
* Added [`manual_ok_err`] to `complexity` [#13740](https://github.com/rust-lang/rust-clippy/pull/13740)
* Added [`non_std_lazy_statics`] to `pedantic` [#13770](https://github.com/rust-lang/rust-clippy/pull/13770)
* Added [`manual_repeat_n`] to `style` [#13858](https://github.com/rust-lang/rust-clippy/pull/13858)
* Added [`manual_option_as_slice`] to `complexity` [#13901](https://github.com/rust-lang/rust-clippy/pull/13901)
* Added [`double_ended_iterator_last`] to `perf` [#13922](https://github.com/rust-lang/rust-clippy/pull/13922)
* Added [`useless_nonzero_new_unchecked`] to `complexity` [#13993](https://github.com/rust-lang/rust-clippy/pull/13993)
* Added [`sliced_string_as_bytes`] to `perf` [#14002](https://github.com/rust-lang/rust-clippy/pull/14002)
* Added [`unnecessary_semicolon`] to `pedantic` [#14032](https://github.com/rust-lang/rust-clippy/pull/14032)
* Added [`return_and_then`] to `restriction` [#14051](https://github.com/rust-lang/rust-clippy/pull/14051)
* Added [`manual_slice_fill`] to `style` [#14082](https://github.com/rust-lang/rust-clippy/pull/14082)
* Added [`precedence_bits`] to `restriction` [#14115](https://github.com/rust-lang/rust-clippy/pull/14115)

### Moves and Deprecations

* Moved [`redundant_locals`] to `suspicious` (from `correctness`, now warn-by-default)
[#13747](https://github.com/rust-lang/rust-clippy/pull/13747)
* Moved [`format_push_string`] to `pedantic` (from `restriction`)
[#13894](https://github.com/rust-lang/rust-clippy/pull/13894)
* Moved [`format_collect`] to `pedantic` (from `perf`, now allow-by-default)
[#13894](https://github.com/rust-lang/rust-clippy/pull/13894)
* Moved [`mutex_integer`] to `restriction` (from `nursery`) [#14110](https://github.com/rust-lang/rust-clippy/pull/14110)

### Enhancements

* Add `lint-inconsistent-struct-field-initializers` configuration option to [`inconsistent_struct_constructor`]
[#13737](https://github.com/rust-lang/rust-clippy/pull/13737)
* [`len_zero`] now also triggers if deref target implements `is_empty()`
[#13871](https://github.com/rust-lang/rust-clippy/pull/13871)
* [`obfuscated_if_else`] now also triggers for the `.then(..).unwrap_or(..)` pattern
[#14021](https://github.com/rust-lang/rust-clippy/pull/14021)

### False Positive Fixes

* [`trailing_empty_array`] no longer triggers in tests [#13844](https://github.com/rust-lang/rust-clippy/pull/13844)
* [`missing_const_for_fn`] no longer triggers in tests [#13945](https://github.com/rust-lang/rust-clippy/pull/13945)
* [`significant_drop_in_scrutinee`]: do not falsely warn for temporaries created by `.await` expansion
[#13985](https://github.com/rust-lang/rust-clippy/pull/13985)

### ICE Fixes

* [`borrow_interior_mutable_const`] Fix an ICE that can occur when taking a reference to a tuple/`struct` field of an
interior mutable `const` [#13877](https://github.com/rust-lang/rust-clippy/pull/13877)

### Others

* Clippy now uses Rust edition 2024 [#13751](https://github.com/rust-lang/rust-clippy/pull/13751)

## Rust 1.85

Current stable, released 2025-02-20
Released 2025-02-20

[View all 72 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-11-15T19%3A31%3A08Z..2024-12-26T13%3A59%3A48Z+base%3Amaster)

@@ -5516,6 +5573,7 @@ Released 2018-09-13
[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
[`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test
[`char_indices_as_byte_indices`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_indices_as_byte_indices
[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
@@ -5681,6 +5739,7 @@ Released 2018-09-13
[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
[`ignore_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignore_without_reason
[`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns
[`impl_hash_borrow_with_str_and_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_hash_borrow_with_str_and_bytes
[`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params
@@ -5789,6 +5848,7 @@ Released 2018-09-13
[`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
[`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains
[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr
[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil
[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
@@ -6346,6 +6406,7 @@ Released 2018-09-13
[`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types
[`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish
[`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests
[`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros
@@ -6362,7 +6423,7 @@ Released 2018-09-13
[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold
[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers
[`lint-commented-code`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-commented-code
[`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold
[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else
[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools
4 changes: 2 additions & 2 deletions src/tools/clippy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "clippy"
# begin autogenerated version
version = "0.1.87"
version = "0.1.88"
# end autogenerated version
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
@@ -42,7 +42,7 @@ walkdir = "2.3"
filetime = "0.2.9"
itertools = "0.12"
pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] }
rinja = { version = "0.3", default-features = false, features = ["config"] }
askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] }

# UI test dependencies
clippy_utils = { path = "clippy_utils" }
File renamed without changes.
1 change: 1 addition & 0 deletions src/tools/clippy/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
- [Updating the Changelog](development/infrastructure/changelog_update.md)
- [Release a New Version](development/infrastructure/release.md)
- [The Clippy Book](development/infrastructure/book.md)
- [Benchmarking Clippy](development/infrastructure/benchmarking.md)
- [Proposals](development/proposals/README.md)
- [Roadmap 2021](development/proposals/roadmap-2021.md)
- [Syntax Tree Patterns](development/proposals/syntax-tree-patterns.md)
18 changes: 12 additions & 6 deletions src/tools/clippy/book/src/development/adding_lints.md
Original file line number Diff line number Diff line change
@@ -99,19 +99,22 @@ struct A;
impl A {
pub fn fo(&self) {}
pub fn foo(&self) {}
//~^ foo_functions
pub fn food(&self) {}
}

// Default trait methods
trait B {
fn fo(&self) {}
fn foo(&self) {}
//~^ foo_functions
fn food(&self) {}
}

// Plain functions
fn fo() {}
fn foo() {}
//~^ foo_functions
fn food() {}

fn main() {
@@ -122,17 +125,20 @@ fn main() {
}
```

Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently
this test is meaningless though.
Note that we are adding comment annotations with the name of our lint to mark
lines where we expect an error. Once we have implemented our lint we can run
`TESTNAME=foo_functions cargo uibless` to generate the `.stderr` file. If our
lint makes use of structured suggestions then this command will also generate
the corresponding `.fixed` file.

While we are working on implementing our lint, we can keep running the UI test.
That allows us to check if the output is turning into what we want by checking the
`.stderr` file that gets updated on every test run.

Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we
commit our lint, we need to commit the generated `.stderr` files, too. In
general, you should only commit files changed by `cargo bless` for the
specific lint you are creating/editing.
Once we have implemented our lint running `TESTNAME=foo_functions cargo uitest`
should pass on its own. When we commit our lint, we need to commit the generated
`.stderr` and if applicable `.fixed` files, too. In general, you should only
commit files changed by `cargo bless` for the specific lint you are creating/editing.

> _Note:_ you can run multiple test files by specifying a comma separated list:
> `TESTNAME=foo_functions,test2,test3`.
2 changes: 1 addition & 1 deletion src/tools/clippy/book/src/development/basics.md
Original file line number Diff line number Diff line change
@@ -147,7 +147,7 @@ following:

First, take note of the toolchain
[override](https://rust-lang.github.io/rustup/overrides.html) in
`/rust-toolchain`. We will use this override to install Clippy into the right
`/rust-toolchain.toml`. We will use this override to install Clippy into the right
toolchain.

> Tip: You can view the active toolchain for the current directory with `rustup
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Benchmarking Clippy

Benchmarking Clippy is similar to using our Lintcheck tool, in fact, it even
uses the same tool! Just by adding a `--perf` flag it will transform Lintcheck
into a very simple but powerful benchmarking tool!

It requires having the [`perf` tool][perf] installed, as `perf` is what's actually
profiling Clippy under the hood.

The lintcheck `--perf` tool generates a series of `perf.data` in the
`target/lintcheck/sources/<package>-<version>` directories. Each `perf.data`
corresponds to the package which is contained.

Lintcheck uses the `-g` flag, meaning that you can get stack traces for richer
analysis, including with tools such as [flamegraph][flamegraph-perf]
(or [`flamegraph-rs`][flamegraph-rs]).

Currently, we only measure instruction count, as it's the most reproducible metric
and [rustc-perf][rustc-perf] also considers it the main number to focus on.

## Benchmarking a PR

Having a benchmarking tool directly implemented into lintcheck gives us the
ability to benchmark any given PR just by making a before and after

Here's the way you can get into any PR, benchmark it, and then benchmark
`master`.

The first `perf.data` will not have any numbers appended, but any subsequent
benchmark will be written to `perf.data.number` with a number growing for 0.
All benchmarks are compressed so that you can

```bash
git fetch upstream pull/<PR_NUMBER>/head:<BRANCH_NAME>
git switch BRANCHNAME

# Bench
cargo lintcheck --perf

# Get last common commit, checkout that
LAST_COMMIT=$(git log BRANCHNAME..master --oneline | tail -1 | cut -c 1-11)
git switch -c temporary $LAST_COMMIT

# We're now on master

# Bench
cargo lintcheck --perf
perf diff ./target/lintcheck/sources/CRATE/perf.data ./target/lintcheck/sources/CRATE/perf.data.0
```


[perf]: https://perfwiki.github.io/main/
[flamegraph-perf]: https://github.com/brendangregg/FlameGraph
[flamegraph-rs]: https://github.com/flamegraph-rs/flamegraph
[rustc-perf]: https://github.com/rust-lang/rustc-perf
Original file line number Diff line number Diff line change
@@ -88,9 +88,6 @@ git push upstream stable
After updating the `stable` branch, tag the HEAD commit and push it to the
Clippy repo.

> Note: Only push the tag once the Deploy GitHub action of the `beta` branch is
> finished. Otherwise the deploy for the tag might fail.
```bash
git tag rust-1.XX.0 # XX should be exchanged with the corresponding version
git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ to be run inside the `rust` directory):
4. Bump the nightly version in the Clippy repository by running these commands:
```bash
cargo dev sync update_nightly
git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain clippy_utils/README.md
git commit -m "Bump nightly version -> YYYY-MM-DD" rust-toolchain.toml clippy_utils/README.md
```
5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
38 changes: 30 additions & 8 deletions src/tools/clippy/book/src/development/writing_tests.md
Original file line number Diff line number Diff line change
@@ -41,20 +41,23 @@ Update the file with some examples to get started:
struct A;
impl A {
pub fn fo(&self) {}
pub fn foo(&self) {} //~ ERROR: function called "foo"
pub fn foo(&self) {}
//~^ foo_functions
pub fn food(&self) {}
}

// Default trait methods
trait B {
fn fo(&self) {}
fn foo(&self) {} //~ ERROR: function called "foo"
fn foo(&self) {}
//~^ foo_functions
fn food(&self) {}
}

// Plain functions
fn fo() {}
fn foo() {} //~ ERROR: function called "foo"
fn foo() {}
//~^ foo_functions
fn food() {}

fn main() {
@@ -66,19 +69,38 @@ fn main() {
```

Without actual lint logic to emit the lint when we see a `foo` function name,
this test will just pass, because no lint will be emitted. However, we can now
run the test with the following command:
this test will fail, because we expect errors at lines marked with
`//~^ foo_functions`. However, we can now run the test with the following command:

```sh
$ TESTNAME=foo_functions cargo uitest
```

Clippy will compile and it will conclude with an `ok` for the tests:
Clippy will compile and it will fail complaining it didn't receive any errors:

```
...Clippy warnings and test outputs...
test compile_test ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s
error: diagnostic code `clippy::foo_functions` not found on line 8
--> tests/ui/foo_functions.rs:9:10
|
9 | //~^ foo_functions
| ^^^^^^^^^^^^^ expected because of this pattern
|
error: diagnostic code `clippy::foo_functions` not found on line 16
--> tests/ui/foo_functions.rs:17:10
|
17 | //~^ foo_functions
| ^^^^^^^^^^^^^ expected because of this pattern
|
error: diagnostic code `clippy::foo_functions` not found on line 23
--> tests/ui/foo_functions.rs:24:6
|
24 | //~^ foo_functions
| ^^^^^^^^^^^^^ expected because of this pattern
|
```

This is normal. After all, we wrote a bunch of Rust code but we haven't really
54 changes: 33 additions & 21 deletions src/tools/clippy/book/src/lint_configuration.md
Original file line number Diff line number Diff line change
@@ -425,6 +425,33 @@ Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
* [`incompatible_msrv`](https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv)


## `check-inconsistent-struct-field-initializers`
Whether to suggest reordering constructor fields when initializers are present.

Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
suggested code would compile, it can change semantics if the initializer expressions have side effects. The
following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:

```rust
struct MyStruct {
vector: Vec<u32>,
length: usize
}
fn main() {
let vector = vec![1,2,3];
MyStruct { length: vector.len(), vector};
}
```

[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924

**Default Value:** `false`

---
**Affected lints:**
* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor)


## `check-private-items`
Whether to also run the listed lints on private items.

@@ -613,31 +640,15 @@ The maximum size of the `Err`-variant in a `Result` returned from a function
* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)


## `lint-inconsistent-struct-field-initializers`
Whether to suggest reordering constructor fields when initializers are present.

Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
suggested code would compile, it can change semantics if the initializer expressions have side effects. The
following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:

```rust
struct MyStruct {
vector: Vec<u32>,
length: usize
}
fn main() {
let vector = vec![1,2,3];
MyStruct { length: vector.len(), vector};
}
```

[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
## `lint-commented-code`
Whether collapsible `if` chains are linted if they contain comments inside the parts
that would be collapsed.

**Default Value:** `false`

---
**Affected lints:**
* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor)
* [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if)


## `literal-representation-threshold`
@@ -1059,7 +1070,8 @@ The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'


## `warn-on-all-wildcard-imports`
Whether to allow certain wildcard imports (prelude, super in tests).
Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests,
or for `pub use` reexports.

**Default Value:** `false`

7 changes: 6 additions & 1 deletion src/tools/clippy/clippy.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
avoid-breaking-exported-api = false

lint-inconsistent-struct-field-initializers = true
check-inconsistent-struct-field-initializers = true

lint-commented-code = true

[[disallowed-methods]]
path = "rustc_lint::context::LintContext::lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
allow-invalid = true

[[disallowed-methods]]
path = "rustc_lint::context::LintContext::span_lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
allow-invalid = true

[[disallowed-methods]]
path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
allow-invalid = true
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_config/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "clippy_config"
# begin autogenerated version
version = "0.1.87"
version = "0.1.88"
# end autogenerated version
edition = "2024"
publish = false
204 changes: 153 additions & 51 deletions src/tools/clippy/clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
@@ -120,12 +120,7 @@ impl ConfError {
Self {
message: message.into(),
suggestion,
span: Span::new(
file.start_pos + BytePos::from_usize(span.start),
file.start_pos + BytePos::from_usize(span.end),
SyntaxContext::root(),
None,
),
span: span_from_toml_range(file, span),
}
}
}
@@ -176,11 +171,61 @@ macro_rules! default_text {
};
}

macro_rules! deserialize {
($map:expr, $ty:ty, $errors:expr, $file:expr) => {{
let raw_value = $map.next_value::<toml::Spanned<toml::Value>>()?;
let value_span = raw_value.span();
let value = match <$ty>::deserialize(raw_value.into_inner()) {
Err(e) => {
$errors.push(ConfError::spanned(
$file,
e.to_string().replace('\n', " ").trim(),
None,
value_span,
));
continue;
},
Ok(value) => value,
};
(value, value_span)
}};

($map:expr, $ty:ty, $errors:expr, $file:expr, $replacements_allowed:expr) => {{
let array = $map.next_value::<Vec<toml::Spanned<toml::Value>>>()?;
let mut disallowed_paths_span = Range {
start: usize::MAX,
end: usize::MIN,
};
let mut disallowed_paths = Vec::new();
for raw_value in array {
let value_span = raw_value.span();
let mut disallowed_path = match DisallowedPath::<$replacements_allowed>::deserialize(raw_value.into_inner())
{
Err(e) => {
$errors.push(ConfError::spanned(
$file,
e.to_string().replace('\n', " ").trim(),
None,
value_span,
));
continue;
},
Ok(disallowed_path) => disallowed_path,
};
disallowed_paths_span = union(&disallowed_paths_span, &value_span);
disallowed_path.set_span(span_from_toml_range($file, value_span));
disallowed_paths.push(disallowed_path);
}
(disallowed_paths, disallowed_paths_span)
}};
}

macro_rules! define_Conf {
($(
$(#[doc = $doc:literal])+
$(#[conf_deprecated($dep:literal, $new_conf:ident)])?
$(#[default_text = $default_text:expr])?
$(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])?
$(#[lints($($for_lints:ident),* $(,)?)])?
$name:ident: $ty:ty = $default:expr,
)*) => {
@@ -218,42 +263,46 @@ macro_rules! define_Conf {
let mut value_spans = HashMap::new();
let mut errors = Vec::new();
let mut warnings = Vec::new();

// Declare a local variable for each field available to a configuration file.
$(let mut $name = None;)*

// could get `Field` here directly, but get `String` first for diagnostics
while let Some(name) = map.next_key::<toml::Spanned<String>>()? {
match Field::deserialize(name.get_ref().as_str().into_deserializer()) {
let field = match Field::deserialize(name.get_ref().as_str().into_deserializer()) {
Err(e) => {
let e: FieldError = e;
errors.push(ConfError::spanned(self.0, e.error, e.suggestion, name.span()));
continue;
}
$(Ok(Field::$name) => {
Ok(field) => field
};

match field {
$(Field::$name => {
// Is this a deprecated field, i.e., is `$dep` set? If so, push a warning.
$(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)?
let raw_value = map.next_value::<toml::Spanned<toml::Value>>()?;
let value_span = raw_value.span();
match <$ty>::deserialize(raw_value.into_inner()) {
Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), None, value_span)),
Ok(value) => match $name {
Some(_) => {
errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span()));
}
None => {
$name = Some(value);
value_spans.insert(name.get_ref().as_str().to_string(), value_span);
// $new_conf is the same as one of the defined `$name`s, so
// this variable is defined in line 2 of this function.
$(match $new_conf {
Some(_) => errors.push(ConfError::spanned(self.0, concat!(
"duplicate field `", stringify!($new_conf),
"` (provided as `", stringify!($name), "`)"
), None, name.span())),
None => $new_conf = $name.clone(),
})?
},
}
let (value, value_span) =
deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?);
// Was this field set previously?
if $name.is_some() {
errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span()));
continue;
}
$name = Some(value);
value_spans.insert(name.get_ref().as_str().to_string(), value_span);
// If this is a deprecated field, was the new field (`$new_conf`) set previously?
// Note that `$new_conf` is one of the defined `$name`s.
$(match $new_conf {
Some(_) => errors.push(ConfError::spanned(self.0, concat!(
"duplicate field `", stringify!($new_conf),
"` (provided as `", stringify!($name), "`)"
), None, name.span())),
None => $new_conf = $name.clone(),
})?
})*
// ignore contents of the third_party key
Ok(Field::third_party) => drop(map.next_value::<IgnoredAny>())
Field::third_party => drop(map.next_value::<IgnoredAny>())
}
}
let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
@@ -275,6 +324,22 @@ macro_rules! define_Conf {
};
}

fn union(x: &Range<usize>, y: &Range<usize>) -> Range<usize> {
Range {
start: cmp::min(x.start, y.start),
end: cmp::max(x.end, y.end),
}
}

fn span_from_toml_range(file: &SourceFile, span: Range<usize>) -> Span {
Span::new(
file.start_pos + BytePos::from_usize(span.start),
file.start_pos + BytePos::from_usize(span.end),
SyntaxContext::root(),
None,
)
}

define_Conf! {
/// Which crates to allow absolute paths from
#[lints(absolute_paths)]
@@ -461,6 +526,7 @@ define_Conf! {
)]
avoid_breaking_exported_api: bool = true,
/// The list of types which may not be held across an await point.
#[disallowed_paths_allow_replacements = false]
#[lints(await_holding_invalid_type)]
await_holding_invalid_types: Vec<DisallowedPathWithoutReplacement> = Vec::new(),
/// DEPRECATED LINT: BLACKLISTED_NAME.
@@ -474,6 +540,26 @@ define_Conf! {
/// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code.
#[lints(incompatible_msrv)]
check_incompatible_msrv_in_tests: bool = false,
/// Whether to suggest reordering constructor fields when initializers are present.
///
/// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
/// suggested code would compile, it can change semantics if the initializer expressions have side effects. The
/// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
///
/// ```rust
/// struct MyStruct {
/// vector: Vec<u32>,
/// length: usize
/// }
/// fn main() {
/// let vector = vec![1,2,3];
/// MyStruct { length: vector.len(), vector};
/// }
/// ```
///
/// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
#[lints(inconsistent_struct_constructor)]
check_inconsistent_struct_field_initializers: bool = false,
/// Whether to also run the listed lints on private items.
#[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)]
check_private_items: bool = false,
@@ -486,9 +572,11 @@ define_Conf! {
#[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
cyclomatic_complexity_threshold: u64 = 25,
/// The list of disallowed macros, written as fully qualified paths.
#[disallowed_paths_allow_replacements = true]
#[lints(disallowed_macros)]
disallowed_macros: Vec<DisallowedPath> = Vec::new(),
/// The list of disallowed methods, written as fully qualified paths.
#[disallowed_paths_allow_replacements = true]
#[lints(disallowed_methods)]
disallowed_methods: Vec<DisallowedPath> = Vec::new(),
/// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
@@ -497,6 +585,7 @@ define_Conf! {
#[lints(disallowed_names)]
disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(),
/// The list of disallowed types, written as fully qualified paths.
#[disallowed_paths_allow_replacements = true]
#[lints(disallowed_types)]
disallowed_types: Vec<DisallowedPath> = Vec::new(),
/// The list of words this lint should not consider as identifiers needing ticks. The value
@@ -549,25 +638,15 @@ define_Conf! {
/// The maximum size of the `Err`-variant in a `Result` returned from a function
#[lints(result_large_err)]
large_error_threshold: u64 = 128,
/// Whether collapsible `if` chains are linted if they contain comments inside the parts
/// that would be collapsed.
#[lints(collapsible_if)]
lint_commented_code: bool = false,
/// Whether to suggest reordering constructor fields when initializers are present.
/// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers
///
/// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
/// suggested code would compile, it can change semantics if the initializer expressions have side effects. The
/// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
///
/// ```rust
/// struct MyStruct {
/// vector: Vec<u32>,
/// length: usize
/// }
/// fn main() {
/// let vector = vec![1,2,3];
/// MyStruct { length: vector.len(), vector};
/// }
/// ```
///
/// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
#[lints(inconsistent_struct_constructor)]
/// Use the `check-inconsistent-struct-field-initializers` configuration instead.
#[conf_deprecated("Please use `check-inconsistent-struct-field-initializers` instead", check_inconsistent_struct_field_initializers)]
lint_inconsistent_struct_field_initializers: bool = false,
/// The lower bound for linting decimal literals
#[lints(decimal_literal_representation)]
@@ -760,7 +839,8 @@ define_Conf! {
/// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
#[lints(verbose_bit_mask)]
verbose_bit_mask_threshold: u64 = 1,
/// Whether to allow certain wildcard imports (prelude, super in tests).
/// Whether to emit warnings on all wildcard imports, including those from `prelude`, from `super` in tests,
/// or for `pub use` reexports.
#[lints(wildcard_imports)]
warn_on_all_wildcard_imports: bool = false,
/// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
@@ -981,7 +1061,23 @@ impl serde::de::Error for FieldError {
// set and allows it.
use fmt::Write;

let mut expected = expected.to_vec();
let metadata = get_configuration_metadata();
let deprecated = metadata
.iter()
.filter_map(|conf| {
if conf.deprecation_reason.is_some() {
Some(conf.name.as_str())
} else {
None
}
})
.collect::<Vec<_>>();

let mut expected = expected
.iter()
.copied()
.filter(|name| !deprecated.contains(name))
.collect::<Vec<_>>();
expected.sort_unstable();

let (rows, column_widths) = calculate_dimensions(&expected);
@@ -1064,7 +1160,13 @@ mod tests {
fn configs_are_tested() {
let mut names: HashSet<String> = crate::get_configuration_metadata()
.into_iter()
.map(|meta| meta.name.replace('_', "-"))
.filter_map(|meta| {
if meta.deprecation_reason.is_none() {
Some(meta.name.replace('_', "-"))
} else {
None
}
})
.collect();

let toml_files = WalkDir::new("../tests")
1 change: 1 addition & 0 deletions src/tools/clippy/clippy_config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
rustc::untranslatable_diagnostic
)]

extern crate rustc_data_structures;
extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_middle;
114 changes: 103 additions & 11 deletions src/tools/clippy/clippy_config/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use clippy_utils::def_path_def_ids;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diag};
use rustc_hir::PrimTy;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefIdMap;
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
@@ -21,6 +23,17 @@ pub struct DisallowedPath<const REPLACEMENT_ALLOWED: bool = true> {
path: String,
reason: Option<String>,
replacement: Option<String>,
/// Setting `allow_invalid` to true suppresses a warning if `path` does not refer to an existing
/// definition.
///
/// This could be useful when conditional compilation is used, or when a clippy.toml file is
/// shared among multiple projects.
allow_invalid: bool,
/// The span of the `DisallowedPath`.
///
/// Used for diagnostics.
#[serde(skip_serializing)]
span: Span,
}

impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<REPLACEMENT_ALLOWED> {
@@ -36,6 +49,8 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<R
path: enum_.path().to_owned(),
reason: enum_.reason().map(ToOwned::to_owned),
replacement: enum_.replacement().map(ToOwned::to_owned),
allow_invalid: enum_.allow_invalid(),
span: Span::default(),
})
}
}
@@ -50,6 +65,8 @@ enum DisallowedPathEnum {
path: String,
reason: Option<String>,
replacement: Option<String>,
#[serde(rename = "allow-invalid")]
allow_invalid: Option<bool>,
},
}

@@ -58,7 +75,7 @@ impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
&self.path
}

pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_, REPLACEMENT_ALLOWED> {
pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) {
move |diag| {
if let Some(replacement) = &self.replacement {
diag.span_suggestion(
@@ -72,6 +89,14 @@ impl<const REPLACEMENT_ALLOWED: bool> DisallowedPath<REPLACEMENT_ALLOWED> {
}
}
}

pub fn span(&self) -> Span {
self.span
}

pub fn set_span(&mut self, span: Span) {
self.span = span;
}
}

impl DisallowedPathEnum {
@@ -94,20 +119,87 @@ impl DisallowedPathEnum {
Self::Simple(_) => None,
}
}

fn allow_invalid(&self) -> bool {
match &self {
Self::WithReason { allow_invalid, .. } => allow_invalid.unwrap_or_default(),
Self::Simple(_) => false,
}
}
}

/// Creates a map of disallowed items to the reason they were disallowed.
#[allow(clippy::type_complexity)]
pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
tcx: TyCtxt<'_>,
disallowed: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
) -> DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> {
disallowed
.iter()
.map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x))
.flat_map(|(name, path, disallowed_path)| {
def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path)))
})
.collect()
disallowed_paths: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
def_kind_predicate: impl Fn(DefKind) -> bool,
predicate_description: &str,
allow_prim_tys: bool,
) -> (
DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)>,
) {
let mut def_ids: DefIdMap<(&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> = DefIdMap::default();
let mut prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath<REPLACEMENT_ALLOWED>)> =
FxHashMap::default();
for disallowed_path in disallowed_paths {
let path = disallowed_path.path();
let mut resolutions = clippy_utils::def_path_res(tcx, &path.split("::").collect::<Vec<_>>());

let mut found_def_id = None;
let mut found_prim_ty = false;
resolutions.retain(|res| match res {
Res::Def(def_kind, def_id) => {
found_def_id = Some(*def_id);
def_kind_predicate(*def_kind)
},
Res::PrimTy(_) => {
found_prim_ty = true;
allow_prim_tys
},
_ => false,
});

if resolutions.is_empty() {
let span = disallowed_path.span();

if let Some(def_id) = found_def_id {
tcx.sess.dcx().span_warn(
span,
format!(
"expected a {predicate_description}, found {} {}",
tcx.def_descr_article(def_id),
tcx.def_descr(def_id)
),
);
} else if found_prim_ty {
tcx.sess.dcx().span_warn(
span,
format!("expected a {predicate_description}, found a primitive type",),
);
} else if !disallowed_path.allow_invalid {
tcx.sess.dcx().span_warn(
span,
format!("`{path}` does not refer to an existing {predicate_description}"),
);
}
}

for res in resolutions {
match res {
Res::Def(_, def_id) => {
def_ids.insert(def_id, (path, disallowed_path));
},
Res::PrimTy(ty) => {
prim_tys.insert(ty, (path, disallowed_path));
},
_ => unreachable!(),
}
}
}

(def_ids, prim_tys)
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_dev/src/main.rs
Original file line number Diff line number Diff line change
@@ -334,7 +334,7 @@ struct SyncCommand {
#[derive(Subcommand)]
enum SyncSubcommand {
#[command(name = "update_nightly")]
/// Update nightly version in rust-toolchain and `clippy_utils`
/// Update nightly version in `rust-toolchain.toml` and `clippy_utils`
UpdateNightly,
}

2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_dev/src/setup/toolchain.rs
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) {

println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`");
if !standalone {
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain.toml` changes");
}
}

2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_dev/src/sync.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ pub fn update_nightly() {
let date = Utc::now().format("%Y-%m-%d").to_string();
replace_region_in_file(
UpdateMode::Change,
Path::new("rust-toolchain"),
Path::new("rust-toolchain.toml"),
"# begin autogenerated nightly\n",
"# end autogenerated nightly",
|res| {
76 changes: 38 additions & 38 deletions src/tools/clippy/clippy_dev/src/update_lints.rs
Original file line number Diff line number Diff line change
@@ -402,53 +402,53 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
}
}

if path.exists() {
if let Some(lint) = lints.iter().find(|l| l.name == name) {
if lint.module == name {
// The lint name is the same as the file, we can just delete the entire file
fs::remove_file(path)?;
} else {
// We can't delete the entire file, just remove the declaration

if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
// Remove clippy_lints/src/some_mod/some_lint.rs
let mut lint_mod_path = path.to_path_buf();
lint_mod_path.set_file_name(name);
lint_mod_path.set_extension("rs");
if path.exists()
&& let Some(lint) = lints.iter().find(|l| l.name == name)
{
if lint.module == name {
// The lint name is the same as the file, we can just delete the entire file
fs::remove_file(path)?;
} else {
// We can't delete the entire file, just remove the declaration

let _ = fs::remove_file(lint_mod_path);
}
if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
// Remove clippy_lints/src/some_mod/some_lint.rs
let mut lint_mod_path = path.to_path_buf();
lint_mod_path.set_file_name(name);
lint_mod_path.set_extension("rs");

let mut content =
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
let _ = fs::remove_file(lint_mod_path);
}

eprintln!(
"warn: you will have to manually remove any code related to `{name}` from `{}`",
path.display()
);
let mut content =
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));

assert!(
content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
"error: `{}` does not contain lint `{}`'s declaration",
path.display(),
lint.name
);
eprintln!(
"warn: you will have to manually remove any code related to `{name}` from `{}`",
path.display()
);

// Remove lint declaration (declare_clippy_lint!)
content.replace_range(lint.declaration_range.clone(), "");
assert!(
content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
"error: `{}` does not contain lint `{}`'s declaration",
path.display(),
lint.name
);

// Remove the module declaration (mod xyz;)
let mod_decl = format!("\nmod {name};");
content = content.replacen(&mod_decl, "", 1);
// Remove lint declaration (declare_clippy_lint!)
content.replace_range(lint.declaration_range.clone(), "");

remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
}
// Remove the module declaration (mod xyz;)
let mod_decl = format!("\nmod {name};");
content = content.replacen(&mod_decl, "", 1);

remove_test_assets(name);
remove_lint(name, lints);
return Ok(true);
remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
}

remove_test_assets(name);
remove_lint(name, lints);
return Ok(true);
}

Ok(false)
8 changes: 4 additions & 4 deletions src/tools/clippy/clippy_dev/src/utils.rs
Original file line number Diff line number Diff line change
@@ -30,10 +30,10 @@ pub fn clippy_project_root() -> PathBuf {
let current_dir = std::env::current_dir().unwrap();
for path in current_dir.ancestors() {
let result = fs::read_to_string(path.join("Cargo.toml"));
if let Err(err) = &result {
if err.kind() == io::ErrorKind::NotFound {
continue;
}
if let Err(err) = &result
&& err.kind() == io::ErrorKind::NotFound
{
continue;
}

let content = result.unwrap();
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "clippy_lints"
# begin autogenerated version
version = "0.1.87"
version = "0.1.88"
# end autogenerated version
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
Original file line number Diff line number Diff line change
@@ -263,10 +263,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
continue;
}

if let Some(cur_v) = cur_v {
if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span {
Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
}
if let Some(cur_v) = cur_v
&& cur_v.ident.name.as_str() > variant.ident.name.as_str()
&& cur_v.span != variant.span
{
Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
}
cur_v = Some(variant);
}
@@ -278,10 +279,11 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
continue;
}

if let Some(cur_f) = cur_f {
if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span {
Self::lint_member_name(cx, &field.ident, &cur_f.ident);
}
if let Some(cur_f) = cur_f
&& cur_f.ident.name.as_str() > field.ident.name.as_str()
&& cur_f.span != field.span
{
Self::lint_member_name(cx, &field.ident, &cur_f.ident);
}
cur_f = Some(field);
}
22 changes: 11 additions & 11 deletions src/tools/clippy/clippy_lints/src/as_conversions.rs
Original file line number Diff line number Diff line change
@@ -12,17 +12,17 @@ declare_clippy_lint! {
/// regardless of whether good alternatives exist or not. If you want more
/// precise lints for `as`, please consider using these separate lints:
///
/// - clippy::cast_lossless
/// - clippy::cast_possible_truncation
/// - clippy::cast_possible_wrap
/// - clippy::cast_precision_loss
/// - clippy::cast_sign_loss
/// - clippy::char_lit_as_u8
/// - clippy::fn_to_numeric_cast
/// - clippy::fn_to_numeric_cast_with_truncation
/// - clippy::ptr_as_ptr
/// - clippy::unnecessary_cast
/// - invalid_reference_casting
/// - `clippy::cast_lossless`
/// - `clippy::cast_possible_truncation`
/// - `clippy::cast_possible_wrap`
/// - `clippy::cast_precision_loss`
/// - `clippy::cast_sign_loss`
/// - `clippy::char_lit_as_u8`
/// - `clippy::fn_to_numeric_cast`
/// - `clippy::fn_to_numeric_cast_with_truncation`
/// - `clippy::ptr_as_ptr`
/// - `clippy::unnecessary_cast`
/// - `invalid_reference_casting`
///
/// There is a good explanation the reason why this lint should work in this
/// way and how it is useful [in this
6 changes: 3 additions & 3 deletions src/tools/clippy/clippy_lints/src/assigning_clones.rs
Original file line number Diff line number Diff line change
@@ -243,7 +243,7 @@ fn build_sugg<'tcx>(
// `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
Sugg::hir_with_applicability(cx, lhs, "_", app)
}
.maybe_par();
.maybe_paren();

// Determine whether we need to reference the argument to clone_from().
let clone_receiver_type = cx.typeck_results().expr_ty(fn_arg);
@@ -284,7 +284,7 @@ fn build_sugg<'tcx>(
let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
// `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)`
// `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)`
let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_par();
let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_paren();
let inner_type = cx.typeck_results().expr_ty(ref_expr);
// If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it
// deref to a mutable reference.
@@ -296,7 +296,7 @@ fn build_sugg<'tcx>(
} else {
// `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)`
// `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)`
Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_par().mut_addr()
Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_paren().mut_addr()
};

match call_kind {
Original file line number Diff line number Diff line change
@@ -8,17 +8,18 @@ use rustc_span::{DUMMY_SP, sym};

pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) {
for lint in items {
if let Some(lint_name) = extract_clippy_lint(lint) {
if lint_name.as_str() == "restriction" && name != sym::allow {
span_lint_and_help(
cx,
BLANKET_CLIPPY_RESTRICTION_LINTS,
lint.span(),
"`clippy::restriction` is not meant to be enabled as a group",
None,
"enable the restriction lints you need individually",
);
}
if let Some(lint_name) = extract_clippy_lint(lint)
&& lint_name.as_str() == "restriction"
&& name != sym::allow
{
span_lint_and_help(
cx,
BLANKET_CLIPPY_RESTRICTION_LINTS,
lint.span(),
"`clippy::restriction` is not meant to be enabled as a group",
None,
"enable the restriction lints you need individually",
);
}
}
}
8 changes: 4 additions & 4 deletions src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs
Original file line number Diff line number Diff line change
@@ -6,10 +6,10 @@ use rustc_span::Span;
use semver::Version;

pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) {
if let LitKind::Str(is, _) = lit.kind {
if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() {
return;
}
if let LitKind::Str(is, _) = lit.kind
&& (is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok())
{
return;
}
span_lint(
cx,
Original file line number Diff line number Diff line change
@@ -36,10 +36,7 @@ fn check_duplicated_attr(
}
let Some(ident) = attr.ident() else { return };
let name = ident.name;
if name == sym::doc
|| name == sym::cfg_attr_trace
|| name == sym::rustc_on_unimplemented
|| name == sym::reason {
if name == sym::doc || name == sym::cfg_attr_trace || name == sym::rustc_on_unimplemented || name == sym::reason {
// FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
// conditions are the same.
// `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
86 changes: 64 additions & 22 deletions src/tools/clippy/clippy_lints/src/attrs/mod.rs
Original file line number Diff line number Diff line change
@@ -14,8 +14,9 @@ mod useless_attribute;
mod utils;

use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::msrvs::{self, Msrv, MsrvStack};
use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind};
use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind};
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@@ -448,6 +449,31 @@ declare_clippy_lint! {
"duplicated attribute"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for ignored tests without messages.
///
/// ### Why is this bad?
/// The reason for ignoring the test may not be obvious.
///
/// ### Example
/// ```no_run
/// #[test]
/// #[ignore]
/// fn test() {}
/// ```
/// Use instead:
/// ```no_run
/// #[test]
/// #[ignore = "Some good reason"]
/// fn test() {}
/// ```
#[clippy::version = "1.85.0"]
pub IGNORE_WITHOUT_REASON,
pedantic,
"ignored tests without messages"
}

pub struct Attributes {
msrv: Msrv,
}
@@ -532,6 +558,7 @@ impl_lint_pass!(PostExpansionEarlyAttributes => [
ALLOW_ATTRIBUTES,
ALLOW_ATTRIBUTES_WITHOUT_REASON,
DEPRECATED_SEMVER,
IGNORE_WITHOUT_REASON,
USELESS_ATTRIBUTE,
BLANKET_CLIPPY_RESTRICTION_LINTS,
SHOULD_PANIC_WITHOUT_EXPECT,
@@ -546,35 +573,50 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
}

fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
if let Some(items) = &attr.meta_item_list() {
if let Some(ident) = attr.ident() {
if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
allow_attributes::check(cx, attr);
}
if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION)
if let Some(items) = &attr.meta_item_list()
&& let Some(ident) = attr.ident()
{
if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
allow_attributes::check(cx, attr);
}
if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
allow_attributes_without_reason::check(cx, ident.name, items, attr);
}
if is_lint_level(ident.name, attr.id) {
blanket_clippy_restriction_lints::check(cx, ident.name, items);
}
if items.is_empty() || !attr.has_name(sym::deprecated) {
return;
}
for item in items {
if let MetaItemInner::MetaItem(mi) = &item
&& let MetaItemKind::NameValue(lit) = &mi.kind
&& mi.has_name(sym::since)
{
allow_attributes_without_reason::check(cx, ident.name, items, attr);
}
if is_lint_level(ident.name, attr.id) {
blanket_clippy_restriction_lints::check(cx, ident.name, items);
}
if items.is_empty() || !attr.has_name(sym::deprecated) {
return;
}
for item in items {
if let MetaItemInner::MetaItem(mi) = &item
&& let MetaItemKind::NameValue(lit) = &mi.kind
&& mi.has_name(sym::since)
{
deprecated_semver::check(cx, item.span(), lit);
}
deprecated_semver::check(cx, item.span(), lit);
}
}
}

if attr.has_name(sym::should_panic) {
should_panic_without_expect::check(cx, attr);
}

if attr.has_name(sym::ignore)
&& match &attr.kind {
AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }),
AttrKind::DocComment(..) => true,
}
{
span_lint_and_help(
cx,
IGNORE_WITHOUT_REASON,
attr.span,
"`#[ignore]` without reason",
None,
"add a reason with `= \"..\"`",
);
}
}

fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) {
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute],
diag.warn(
"unqualified `#[repr(packed)]` defaults to `#[repr(Rust, packed)]`, which has no stable ABI",
)
.help("qualify the desired ABI explicity via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
.help("qualify the desired ABI explicitly via `#[repr(C, packed)]` or `#[repr(Rust, packed)]`")
.span_label(packed_span, "`packed` representation set here");
},
);
130 changes: 65 additions & 65 deletions src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
Original file line number Diff line number Diff line change
@@ -14,75 +14,75 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
if attr.span.in_external_macro(cx.sess().source_map()) {
return;
}
if let Some(lint_list) = &attr.meta_item_list() {
if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) {
for lint in lint_list {
match item.kind {
ItemKind::Use(..) => {
let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else {
return;
};
if let Some(lint_list) = &attr.meta_item_list()
&& attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id))
{
for lint in lint_list {
match item.kind {
ItemKind::Use(..) => {
let (namespace @ (Some(sym::clippy) | None), Some(name)) = namespace_and_lint(lint) else {
return;
};

if namespace.is_none()
&& matches!(
name.as_str(),
"ambiguous_glob_reexports"
| "dead_code"
| "deprecated"
| "hidden_glob_reexports"
| "unreachable_pub"
| "unused"
| "unused_braces"
| "unused_import_braces"
| "unused_imports"
)
{
return;
}
if namespace.is_none()
&& matches!(
name.as_str(),
"ambiguous_glob_reexports"
| "dead_code"
| "deprecated"
| "hidden_glob_reexports"
| "unreachable_pub"
| "unused"
| "unused_braces"
| "unused_import_braces"
| "unused_imports"
)
{
return;
}

if namespace == Some(sym::clippy)
&& matches!(
name.as_str(),
"wildcard_imports"
| "enum_glob_use"
| "redundant_pub_crate"
| "macro_use_imports"
| "unsafe_removed_from_name"
| "module_name_repetitions"
| "single_component_path_imports"
| "disallowed_types"
| "unused_trait_names"
)
{
return;
}
},
ItemKind::ExternCrate(..) => {
if is_word(lint, sym::unused_imports) && skip_unused_imports {
return;
}
if is_word(lint, sym!(unused_extern_crates)) {
return;
}
},
_ => {},
}
if namespace == Some(sym::clippy)
&& matches!(
name.as_str(),
"wildcard_imports"
| "enum_glob_use"
| "redundant_pub_crate"
| "macro_use_imports"
| "unsafe_removed_from_name"
| "module_name_repetitions"
| "single_component_path_imports"
| "disallowed_types"
| "unused_trait_names"
)
{
return;
}
},
ItemKind::ExternCrate(..) => {
if is_word(lint, sym::unused_imports) && skip_unused_imports {
return;
}
if is_word(lint, sym!(unused_extern_crates)) {
return;
}
},
_ => {},
}
let line_span = first_line_of_span(cx, attr.span);
}
let line_span = first_line_of_span(cx, attr.span);

if let Some(src) = line_span.get_source_text(cx) {
if src.contains("#[") {
#[expect(clippy::collapsible_span_lint_calls)]
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
diag.span_suggestion(
line_span,
"if you just forgot a `!`, use",
src.replacen("#[", "#![", 1),
Applicability::MaybeIncorrect,
);
});
}
}
if let Some(src) = line_span.get_source_text(cx)
&& src.contains("#[")
{
#[expect(clippy::collapsible_span_lint_calls)]
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
diag.span_suggestion(
line_span,
"if you just forgot a `!`, use",
src.replacen("#[", "#![", 1),
Applicability::MaybeIncorrect,
);
});
}
}
}
16 changes: 10 additions & 6 deletions src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
Original file line number Diff line number Diff line change
@@ -179,9 +179,14 @@ pub struct AwaitHolding {

impl AwaitHolding {
pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
Self {
def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types),
}
let (def_ids, _) = create_disallowed_map(
tcx,
&conf.await_holding_invalid_types,
crate::disallowed_types::def_kind_predicate,
"type",
false,
);
Self { def_ids }
}
}

@@ -192,10 +197,9 @@ impl<'tcx> LateLintPass<'tcx> for AwaitHolding {
def_id,
..
}) = expr.kind
&& let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id)
{
if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) {
self.check_interior_types(cx, coroutine_layout);
}
self.check_interior_types(cx, coroutine_layout);
}
}
}
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
s
};

let into_snippet = snippet.clone().maybe_par();
let into_snippet = snippet.clone().maybe_paren();
let as_snippet = snippet.as_ty(ty);

span_lint_and_then(
34 changes: 19 additions & 15 deletions src/tools/clippy/clippy_lints/src/booleans.rs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, sym};
use rustc_span::{Span, SyntaxContext, sym};

declare_clippy_lint! {
/// ### What it does
@@ -242,11 +242,11 @@ struct Hir2Qmm<'a, 'tcx, 'v> {
impl<'v> Hir2Qmm<'_, '_, 'v> {
fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
for a in a {
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
if binop.node == op {
v = self.extract(op, &[lhs, rhs], v)?;
continue;
}
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind
&& binop.node == op
{
v = self.extract(op, &[lhs, rhs], v)?;
continue;
}
v.push(self.run(a)?);
}
@@ -349,9 +349,13 @@ impl SuggestContext<'_, '_, '_> {
if let Some(str) = simplify_not(self.cx, self.msrv, terminal) {
self.output.push_str(&str);
} else {
self.output.push('!');
self.output
.push_str(&Sugg::hir_opt(self.cx, terminal)?.maybe_par().to_string());
let mut app = Applicability::MachineApplicable;
let snip = Sugg::hir_with_context(self.cx, terminal, SyntaxContext::root(), "", &mut app);
// Ignore the case If the expression is inside a macro expansion, or the default snippet is used
if app != Applicability::MachineApplicable {
return None;
}
self.output.push_str(&(!snip).to_string());
}
},
True | False | Not(_) => {
@@ -414,12 +418,12 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
let lhs_snippet = lhs.span.get_source_text(cx)?;
let rhs_snippet = rhs.span.get_source_text(cx)?;

if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) {
// e.g. `(a as u64) < b`. Without the parens the `<` is
// interpreted as a start of generic arguments for `u64`
return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
}
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')'))
&& let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node)
{
// e.g. `(a as u64) < b`. Without the parens the `<` is
// interpreted as a start of generic arguments for `u64`
return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
}

Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
13 changes: 8 additions & 5 deletions src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ use crate::reference::DEREF_ADDROF;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed};
use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
@@ -73,6 +73,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
}
})
&& !is_from_proc_macro(cx, e)
&& let e_ty = cx.typeck_results().expr_ty_adjusted(e)
// check if the reference is coercing to a mutable reference
&& (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target))
&& let Some(deref_text) = deref_target.span.get_source_text(cx)
{
span_lint_and_then(
@@ -90,10 +93,10 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {

// has deref trait -> give 2 help
// doesn't have deref trait -> give 1 help
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() {
if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
return;
}
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait()
&& !implements_trait(cx, *inner_ty, deref_trait_id, &[])
{
return;
}

diag.span_suggestion(
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ pub(super) fn check(
span,
format!("casting the result of `{cast_from}::abs()` to {cast_to}"),
"replace with",
format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()),
format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_paren()),
Applicability::MachineApplicable,
);
}
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ pub(super) fn check(
diag.span_suggestion_verbose(
expr.span,
"use `Into::into` instead",
format!("{}.into()", from_sugg.maybe_par()),
format!("{}.into()", from_sugg.maybe_paren()),
applicability,
);
},
Original file line number Diff line number Diff line change
@@ -64,11 +64,11 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX))
},
ExprKind::MethodCall(method, _, [lo, hi], _) => {
if method.ident.as_str() == "clamp" {
if method.ident.as_str() == "clamp"
//FIXME: make this a diagnostic item
if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
return lo_bits.max(hi_bits);
}
&& let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi))
{
return lo_bits.max(hi_bits);
}
nbits
},
@@ -185,7 +185,7 @@ fn offer_suggestion(
) {
let cast_to_snip = snippet(cx, cast_to_span, "..");
let suggestion = if cast_to_snip == "_" {
format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_par())
format!("{}.try_into()", Sugg::hir(cx, cast_expr, "..").maybe_paren())
} else {
format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
};
19 changes: 9 additions & 10 deletions src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
Original file line number Diff line number Diff line change
@@ -19,16 +19,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
cx.typeck_results().expr_ty(expr),
);
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind {
if method_path.ident.name.as_str() == "cast"
&& let Some(generic_args) = method_path.args
&& let [GenericArg::Type(cast_to)] = generic_args.args
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
&& !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty())
{
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
}
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind
&& method_path.ident.name.as_str() == "cast"
&& let Some(generic_args) = method_path.args
&& let [GenericArg::Type(cast_to)] = generic_args.args
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
&& !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty())
{
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
}
}

Original file line number Diff line number Diff line change
@@ -21,42 +21,41 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv)
start_ty,
end_ty,
}) = expr_cast_chain_tys(cx, expr)
&& let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty))
{
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
let from_size = from_layout.size.bytes();
let to_size = to_layout.size.bytes();
if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
span_lint_and_then(
cx,
CAST_SLICE_DIFFERENT_SIZES,
expr.span,
format!(
"casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
start_ty.ty, end_ty.ty,
),
|diag| {
let ptr_snippet = source::snippet(cx, left_cast.span, "..");
let from_size = from_layout.size.bytes();
let to_size = to_layout.size.bytes();
if from_size != to_size && from_size != 0 && to_size != 0 && msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
span_lint_and_then(
cx,
CAST_SLICE_DIFFERENT_SIZES,
expr.span,
format!(
"casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
start_ty.ty, end_ty.ty,
),
|diag| {
let ptr_snippet = source::snippet(cx, left_cast.span, "..");

let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
Mutability::Mut => ("_mut", "mut"),
Mutability::Not => ("", "const"),
};
let sugg = format!(
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
// get just the ty from the TypeAndMut so that the printed type isn't something like `mut
// T`, extract just the `T`
end_ty.ty
);
let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
Mutability::Mut => ("_mut", "mut"),
Mutability::Not => ("", "const"),
};
let sugg = format!(
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
// get just the ty from the TypeAndMut so that the printed type isn't something like `mut
// T`, extract just the `T`
end_ty.ty
);

diag.span_suggestion(
expr.span,
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
sugg,
rustc_errors::Applicability::HasPlaceholders,
);
},
);
}
diag.span_suggestion(
expr.span,
format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
sugg,
rustc_errors::Applicability::HasPlaceholders,
);
},
);
}
}
}
82 changes: 82 additions & 0 deletions src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_normalizable;
use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
use rustc_lint::LateContext;
use rustc_span::source_map::Spanned;

use super::MANUAL_DANGLING_PTR;

pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
if let TyKind::Ptr(ref ptr_ty) = to.kind {
let init_expr = expr_or_init(cx, from);
if is_expr_const_aligned(cx, init_expr, ptr_ty.ty)
&& let Some(std_or_core) = std_or_core(cx)
{
let sugg_fn = match ptr_ty.mutbl {
Mutability::Not => "ptr::dangling",
Mutability::Mut => "ptr::dangling_mut",
};

let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind {
format!("{std_or_core}::{sugg_fn}()")
} else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) {
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
} else {
return;
};

span_lint_and_sugg(
cx,
MANUAL_DANGLING_PTR,
expr.span,
"manual creation of a dangling pointer",
"use",
sugg,
Applicability::MachineApplicable,
);
}
}
}

// Checks if the given expression is a call to `align_of` whose generic argument matches the target
// type, or a positive constant literal that matches the target type's alignment.
fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool {
match expr.kind {
ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to),
ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to),
_ => false,
}
}

fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
&& let Some(fun_id) = path_def_id(cx, fun)
&& match_def_path(cx, fun_id, &paths::ALIGN_OF)
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
&& let [GenericArg::Type(generic_ty)] = args.args
{
let typeck = cx.typeck_results();
return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id);
}
false
}

fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned<LitKind>, to: &Ty<'_>) -> bool {
let LitKind::Int(val, _) = lit.node else { return false };
if val == 0 {
return false;
}
let to_mid_ty = cx.typeck_results().node_type(to.hir_id);
is_normalizable(cx, cx.param_env, to_mid_ty)
&& cx
.tcx
.layout_of(cx.typing_env().as_query_input(to_mid_ty))
.is_ok_and(|layout| {
let align = u128::from(layout.align.abi.bytes());
u128::from(val) <= align
})
}
34 changes: 33 additions & 1 deletion src/tools/clippy/clippy_lints/src/casts/mod.rs
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ mod char_lit_as_u8;
mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any;
mod fn_to_numeric_cast_with_truncation;
mod manual_dangling_ptr;
mod ptr_as_ptr;
mod ptr_cast_constness;
mod ref_as_ptr;
@@ -71,7 +72,7 @@ declare_clippy_lint! {
/// ### Example
/// ```no_run
/// let y: i8 = -1;
/// y as u128; // will return 18446744073709551615
/// y as u64; // will return 18446744073709551615
/// ```
#[clippy::version = "pre 1.29.0"]
pub CAST_SIGN_LOSS,
@@ -759,6 +760,32 @@ declare_clippy_lint! {
"detects `as *mut _` and `as *const _` conversion"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
///
/// ### Why is this bad?
/// This creates a dangling pointer and is better expressed as
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
///
/// ### Example
/// ```no_run
/// let ptr = 4 as *const u32;
/// let aligned = std::mem::align_of::<u32>() as *const u32;
/// let mut_ptr: *mut i64 = 8 as *mut _;
/// ```
/// Use instead:
/// ```no_run
/// let ptr = std::ptr::dangling::<u32>();
/// let aligned = std::ptr::dangling::<u32>();
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
/// ```
#[clippy::version = "1.87.0"]
pub MANUAL_DANGLING_PTR,
style,
"casting small constant literals to pointers to create dangling pointers"
}

pub struct Casts {
msrv: Msrv,
}
@@ -795,6 +822,7 @@ impl_lint_pass!(Casts => [
ZERO_PTR,
REF_AS_PTR,
AS_POINTER_UNDERSCORE,
MANUAL_DANGLING_PTR,
]);

impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -823,6 +851,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);

if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) {
manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
}

if cast_to.is_numeric() {
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
if cast_from.is_numeric() {
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) {

(
"try `pointer::cast`, a safer alternative",
format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_paren()),
)
};

Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ pub(super) fn check<'tcx>(
expr.span,
"`as` casting between raw pointers while changing only its constness",
format!("try `pointer::cast_{constness}`, a safer alternative"),
format!("{}.cast_{constness}()", sugg.maybe_par()),
format!("{}.cast_{constness}()", sugg.maybe_paren()),
Applicability::MachineApplicable,
);
}
10 changes: 5 additions & 5 deletions src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
Original file line number Diff line number Diff line change
@@ -130,11 +130,11 @@ pub(super) fn check<'tcx>(
| LitKind::Float(_, LitFloatType::Suffixed(_))
if cast_from.kind() == cast_to.kind() =>
{
if let Some(src) = cast_expr.span.get_source_text(cx) {
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
return true;
}
if let Some(src) = cast_expr.span.get_source_text(cx)
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
{
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
return true;
}
},
_ => {},
8 changes: 4 additions & 4 deletions src/tools/clippy/clippy_lints/src/checked_conversions.rs
Original file line number Diff line number Diff line change
@@ -253,11 +253,11 @@ fn get_types_from_cast<'a>(
match limit.kind {
// `from_type::from(_)`
ExprKind::Call(path, _) => {
if let ExprKind::Path(ref path) = path.kind {
if let ExprKind::Path(ref path) = path.kind
// `to_type`
if let Some(to_type) = get_implementing_type(path, types, func) {
return Some((from_type, to_type));
}
&& let Some(to_type) = get_implementing_type(path, types, func)
{
return Some((from_type, to_type));
}
},
// `to_type::MAX`
221 changes: 135 additions & 86 deletions src/tools/clippy/clippy_lints/src/collapsible_if.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
use clippy_utils::sugg::Sugg;
use rustc_ast::ast;
use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability};
use rustc_ast::BinOpKind;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;
use rustc_hir::{Block, Expr, ExprKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TyCtxt;
use rustc_session::impl_lint_pass;
use rustc_span::Span;

declare_clippy_lint! {
@@ -75,105 +77,152 @@ declare_clippy_lint! {
"nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
}

declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
pub struct CollapsibleIf {
let_chains_enabled: bool,
lint_commented_code: bool,
}

impl CollapsibleIf {
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
Self {
let_chains_enabled: tcx.features().let_chains(),
lint_commented_code: conf.lint_commented_code,
}
}

impl EarlyLintPass for CollapsibleIf {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
if let ast::ExprKind::If(cond, then, else_) = &expr.kind
fn check_collapsible_else_if(cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) {
if !block_starts_with_comment(cx, else_block)
&& let Some(else_) = expr_block(else_block)
&& cx.tcx.hir_attrs(else_.hir_id).is_empty()
&& !else_.span.from_expansion()
&& let ExprKind::If(..) = else_.kind
{
// Prevent "elseif"
// Check that the "else" is followed by whitespace
let up_to_else = then_span.between(else_block.span);
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
!c.is_whitespace()
} else {
false
};

let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
COLLAPSIBLE_ELSE_IF,
else_block.span,
"this `else { if .. }` block can be collapsed",
"collapse nested if block",
format!(
"{}{}",
if requires_space { " " } else { "" },
snippet_block_with_applicability(cx, else_.span, "..", Some(else_block.span), &mut applicability)
),
applicability,
);
}
}

fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: &Expr<'_>, then: &Block<'_>) {
if let Some(inner) = expr_block(then)
&& cx.tcx.hir_attrs(inner.hir_id).is_empty()
&& let ExprKind::If(check_inner, _, None) = &inner.kind
&& self.eligible_condition(check_inner)
&& let ctxt = expr.span.ctxt()
&& inner.span.ctxt() == ctxt
&& (self.lint_commented_code || !block_starts_with_comment(cx, then))
{
span_lint_and_then(
cx,
COLLAPSIBLE_IF,
expr.span,
"this `if` statement can be collapsed",
|diag| {
let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span();
let then_closing_bracket = {
let end = then.span.shrink_to_hi();
end.with_lo(end.lo() - rustc_span::BytePos(1))
.with_leading_whitespace(cx)
.into_span()
};
let inner_if = inner.span.split_at(2).0;
let mut sugg = vec![
// Remove the outer then block `{`
(then_open_bracket, String::new()),
// Remove the outer then block '}'
(then_closing_bracket, String::new()),
// Replace inner `if` by `&&`
(inner_if, String::from("&&")),
];
sugg.extend(parens_around(check));
sugg.extend(parens_around(check_inner));

diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable);
},
);
}
}

pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool {
self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..))
}
}

impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);

impl LateLintPass<'_> for CollapsibleIf {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::If(cond, then, else_) = &expr.kind
&& !expr.span.from_expansion()
{
if let Some(else_) = else_ {
check_collapsible_maybe_if_let(cx, then.span, else_);
} else if !matches!(cond.kind, ast::ExprKind::Let(..)) {
check_collapsible_no_if_let(cx, expr, cond, then);
if let Some(else_) = else_
&& let ExprKind::Block(else_, None) = else_.kind
{
Self::check_collapsible_else_if(cx, then.span, else_);
} else if else_.is_none()
&& self.eligible_condition(cond)
&& let ExprKind::Block(then, None) = then.kind
{
self.check_collapsible_if_if(cx, expr, cond, then);
}
}
}
}

fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
// We trim all opening braces and whitespaces and then check if the next string is a comment.
let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
let trimmed_block_text = snippet_block(cx, block.span, "..", None)
.trim_start_matches(|c: char| c.is_whitespace() || c == '{')
.to_owned();
trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
}

fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
if let ast::ExprKind::Block(ref block, _) = else_.kind
&& !block_starts_with_comment(cx, block)
&& let Some(else_) = expr_block(block)
&& else_.attrs.is_empty()
&& !else_.span.from_expansion()
&& let ast::ExprKind::If(..) = else_.kind
{
// Prevent "elseif"
// Check that the "else" is followed by whitespace
let up_to_else = then_span.between(block.span);
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
!c.is_whitespace()
} else {
false
};

let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
COLLAPSIBLE_ELSE_IF,
block.span,
"this `else { if .. }` block can be collapsed",
"collapse nested if block",
format!(
"{}{}",
if requires_space { " " } else { "" },
snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
),
applicability,
);
}
}

fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
if !block_starts_with_comment(cx, then)
&& let Some(inner) = expr_block(then)
&& inner.attrs.is_empty()
&& let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind
// Prevent triggering on `if c { if let a = b { .. } }`.
&& !matches!(check_inner.kind, ast::ExprKind::Let(..))
&& let ctxt = expr.span.ctxt()
&& inner.span.ctxt() == ctxt
{
span_lint_and_then(
cx,
COLLAPSIBLE_IF,
expr.span,
"this `if` statement can be collapsed",
|diag| {
let mut app = Applicability::MachineApplicable;
let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
diag.span_suggestion(
expr.span,
"collapse nested if block",
format!(
"if {} {}",
lhs.and(&rhs),
snippet_block(cx, content.span, "..", Some(expr.span)),
),
app, // snippet
);
},
);
/// If `block` is a block with either one expression or a statement containing an expression,
/// return the expression. We don't peel blocks recursively, as extra blocks might be intentional.
fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
match block.stmts {
[] => block.expr,
[stmt] => {
if let StmtKind::Semi(expr) = stmt.kind {
Some(expr)
} else {
None
}
},
_ => None,
}
}

/// If the block contains only one expression, return it.
fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
if let [stmt] = &*block.stmts
&& let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind
/// If the expression is a `||`, suggest parentheses around it.
fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> {
if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind
&& op.node == BinOpKind::Or
{
Some(expr)
vec![
(expr.span.shrink_to_lo(), String::from("(")),
(expr.span.shrink_to_hi(), String::from(")")),
]
} else {
None
vec![]
}
}
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/comparison_chain.rs
Original file line number Diff line number Diff line change
@@ -125,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
let ExprKind::Binary(_, lhs, rhs) = conds[0].kind else {
unreachable!();
};
let lhs = Sugg::hir(cx, lhs, "..").maybe_par();
let lhs = Sugg::hir(cx, lhs, "..").maybe_paren();
let rhs = Sugg::hir(cx, rhs, "..").addr();
span_lint_and_sugg(
cx,
10 changes: 5 additions & 5 deletions src/tools/clippy/clippy_lints/src/copies.rs
Original file line number Diff line number Diff line change
@@ -256,7 +256,7 @@ fn lint_branches_sharing_code<'tcx>(
let suggestion = reindent_multiline(&suggestion, true, indent);

let span = span.with_hi(last_block.span.hi());
// Improve formatting if the inner block has indention (i.e. normal Rust formatting)
// Improve formatting if the inner block has indentation (i.e. normal Rust formatting)
let span = span
.map_range(cx, |src, range| {
(range.start > 4 && src.get(range.start - 4..range.start)? == " ")
@@ -539,10 +539,10 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
.filter(|stmt| !ignore_span.overlaps(stmt.span))
.try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));

if let Some(expr) = block.expr {
if res.is_continue() {
res = intravisit::walk_expr(&mut walker, expr);
}
if let Some(expr) = block.expr
&& res.is_continue()
{
res = intravisit::walk_expr(&mut walker, expr);
}

res.is_break()
5 changes: 4 additions & 1 deletion src/tools/clippy/clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
@@ -52,6 +52,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO,
crate::attrs::DEPRECATED_SEMVER_INFO,
crate::attrs::DUPLICATED_ATTRIBUTES_INFO,
crate::attrs::IGNORE_WITHOUT_REASON_INFO,
crate::attrs::INLINE_ALWAYS_INFO,
crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO,
crate::attrs::NON_MINIMAL_CFG_INFO,
@@ -96,6 +97,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::casts::FN_TO_NUMERIC_CAST_INFO,
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
crate::casts::MANUAL_DANGLING_PTR_INFO,
crate::casts::PTR_AS_PTR_INFO,
crate::casts::PTR_CAST_CONSTNESS_INFO,
crate::casts::REF_AS_PTR_INFO,
@@ -286,6 +288,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::literal_representation::UNREADABLE_LITERAL_INFO,
crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO,
crate::loops::CHAR_INDICES_AS_BYTE_INDICES_INFO,
crate::loops::EMPTY_LOOP_INFO,
crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,
@@ -334,7 +337,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
crate::manual_string_new::MANUAL_STRING_NEW_INFO,
crate::manual_strip::MANUAL_STRIP_INFO,
crate::manual_unwrap_or_default::MANUAL_UNWRAP_OR_DEFAULT_INFO,
crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO,
crate::match_result_ok::MATCH_RESULT_OK_INFO,
@@ -344,6 +346,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::matches::MANUAL_MAP_INFO,
crate::matches::MANUAL_OK_ERR_INFO,
crate::matches::MANUAL_UNWRAP_OR_INFO,
crate::matches::MANUAL_UNWRAP_OR_DEFAULT_INFO,
crate::matches::MATCH_AS_REF_INFO,
crate::matches::MATCH_BOOL_INFO,
crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO,
105 changes: 52 additions & 53 deletions src/tools/clippy/clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
@@ -1133,61 +1133,60 @@ fn report<'tcx>(

impl<'tcx> Dereferencing<'tcx> {
fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
if let Some(pat) = outer_pat {
// Check for auto-deref
if !matches!(
cx.typeck_results().expr_adjustments(e),
[
Adjustment {
kind: Adjust::Deref(_),
..
},
Adjustment {
kind: Adjust::Deref(_),
..
},
if let Some(outer_pat) = self.ref_locals.get_mut(&local)
&& let Some(pat) = outer_pat
// Check for auto-deref
&& !matches!(
cx.typeck_results().expr_adjustments(e),
[
Adjustment {
kind: Adjust::Deref(_),
..
]
) {
match get_parent_expr(cx, e) {
// Field accesses are the same no matter the number of references.
Some(Expr {
kind: ExprKind::Field(..),
..
}) => (),
Some(&Expr {
span,
kind: ExprKind::Unary(UnOp::Deref, _),
..
}) if !span.from_expansion() => {
// Remove explicit deref.
let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
pat.replacements.push((span, snip.into()));
},
Some(parent) if !parent.span.from_expansion() => {
// Double reference might be needed at this point.
if parent.precedence() == ExprPrecedence::Unambiguous {
// Parentheses would be needed here, don't lint.
*outer_pat = None;
} else {
pat.always_deref = false;
let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
pat.replacements.push((e.span, format!("&{snip}")));
}
},
_ if !e.span.from_expansion() => {
// Double reference might be needed at this point.
pat.always_deref = false;
let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
pat.replacements.push((e.span, format!("&{snip}")));
},
// Edge case for macros. The span of the identifier will usually match the context of the
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
// macros
_ => *outer_pat = None,
},
Adjustment {
kind: Adjust::Deref(_),
..
},
..
]
)
{
match get_parent_expr(cx, e) {
// Field accesses are the same no matter the number of references.
Some(Expr {
kind: ExprKind::Field(..),
..
}) => (),
Some(&Expr {
span,
kind: ExprKind::Unary(UnOp::Deref, _),
..
}) if !span.from_expansion() => {
// Remove explicit deref.
let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
pat.replacements.push((span, snip.into()));
},
Some(parent) if !parent.span.from_expansion() => {
// Double reference might be needed at this point.
if parent.precedence() == ExprPrecedence::Unambiguous {
// Parentheses would be needed here, don't lint.
*outer_pat = None;
} else {
pat.always_deref = false;
let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
pat.replacements.push((e.span, format!("&{snip}")));
}
}
},
_ if !e.span.from_expansion() => {
// Double reference might be needed at this point.
pat.always_deref = false;
let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
pat.replacements.push((e.span, format!("&{snip}")));
},
// Edge case for macros. The span of the identifier will usually match the context of the
// binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
// macros
_ => *outer_pat = None,
}
}
}
26 changes: 13 additions & 13 deletions src/tools/clippy/clippy_lints/src/derivable_impls.rs
Original file line number Diff line number Diff line change
@@ -94,18 +94,18 @@ fn check_struct<'tcx>(
ty_args: GenericArgsRef<'_>,
typeck_results: &'tcx TypeckResults<'tcx>,
) {
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
if let Some(PathSegment { args, .. }) = p.segments.last() {
let args = args.map(|a| a.args).unwrap_or(&[]);

// ty_args contains the generic parameters of the type declaration, while args contains the
// arguments used at instantiation time. If both len are not equal, it means that some
// parameters were not provided (which means that the default values were used); in this
// case we will not risk suggesting too broad a rewrite. We won't either if any argument
// is a type or a const.
if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
return;
}
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind
&& let Some(PathSegment { args, .. }) = p.segments.last()
{
let args = args.map(|a| a.args).unwrap_or(&[]);

// ty_args contains the generic parameters of the type declaration, while args contains the
// arguments used at instantiation time. If both len are not equal, it means that some
// parameters were not provided (which means that the default values were used); in this
// case we will not risk suggesting too broad a rewrite. We won't either if any argument
// is a type or a const.
if ty_args.len() != args.len() || args.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))) {
return;
}
}

@@ -188,7 +188,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
self_ty,
..
}) = item.kind
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
&& !cx.tcx.is_automatically_derived(item.owner_id.to_def_id())
&& !item.span.from_expansion()
&& let Some(def_id) = trait_ref.trait_def_id()
&& cx.tcx.is_diagnostic_item(sym::Default, def_id)
16 changes: 8 additions & 8 deletions src/tools/clippy/clippy_lints/src/derive.rs
Original file line number Diff line number Diff line change
@@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
}) = item.kind
{
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id());

check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
@@ -235,7 +235,7 @@ fn check_hash_peq<'tcx>(
{
// Look for the PartialEq implementations for `ty`
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);

if !hash_is_automatically_derived || peq_is_automatically_derived {
return;
@@ -278,7 +278,7 @@ fn check_ord_partial_ord<'tcx>(
{
// Look for the PartialOrd implementations for `ty`
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);

if partial_ord_is_automatically_derived == ord_is_automatically_derived {
return;
@@ -426,10 +426,10 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
}

fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result {
if let ExprKind::Block(block, _) = expr.kind {
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
return ControlFlow::Break(());
}
if let ExprKind::Block(block, _) = expr.kind
&& block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
{
return ControlFlow::Break(());
}

walk_expr(self, expr)
@@ -479,7 +479,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De
tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some()
}

/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
/// Creates the `ParamEnv` used for the given type's derived `Eq` impl.
fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> {
// Initial map from generic index to param def.
// Vec<(param_def, needs_eq)>
10 changes: 9 additions & 1 deletion src/tools/clippy/clippy_lints/src/disallowed_macros.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{
AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
@@ -72,8 +73,15 @@ pub struct DisallowedMacros {

impl DisallowedMacros {
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_macros,
|def_kind| matches!(def_kind, DefKind::Macro(_)),
"macro",
false,
);
Self {
disallowed: create_disallowed_map(tcx, &conf.disallowed_macros),
disallowed,
seen: FxHashSet::default(),
derive_src: None,
earlies,
Loading