Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
145d5adf04
282 changed files with 5798 additions and 885 deletions
2
.github/deploy.sh
vendored
2
.github/deploy.sh
vendored
|
@ -45,6 +45,8 @@ if [[ -n $TAG_NAME ]]; then
|
|||
git add "$TAG_NAME"
|
||||
# Update the symlink
|
||||
git add stable
|
||||
# Update the index.html file
|
||||
git add index.html
|
||||
git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
|
||||
elif [[ $BETA = "true" ]]; then
|
||||
if git diff --exit-code --quiet -- beta/; then
|
||||
|
|
6
.github/driver.sh
vendored
6
.github/driver.sh
vendored
|
@ -47,9 +47,9 @@ unset CARGO_MANIFEST_DIR
|
|||
|
||||
# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
|
||||
# FIXME: How to match the clippy invocation in compile-test.rs?
|
||||
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/box_default.rs 2>box_default.stderr && exit 1
|
||||
sed -e "/= help: for/d" box_default.stderr > normalized.stderr
|
||||
diff -u normalized.stderr tests/ui/box_default.stderr
|
||||
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/string_to_string.rs 2>string_to_string.stderr && exit 1
|
||||
sed -e "/= help: for/d" string_to_string.stderr > normalized.stderr
|
||||
diff -u normalized.stderr tests/ui/string_to_string.stderr
|
||||
|
||||
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
|
||||
SYSROOT=$(rustc --print sysroot)
|
||||
|
|
59
.github/workflows/clippy_changelog.yml
vendored
Normal file
59
.github/workflows/clippy_changelog.yml
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
name: Clippy changelog check
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, edited]
|
||||
|
||||
concurrency:
|
||||
# For a given workflow, if we push to the same PR, cancel all previous builds on that PR.
|
||||
# If the push is not attached to a PR, we will cancel all builds on the same branch.
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
changelog:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
# Run
|
||||
- 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" | \
|
||||
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
|
||||
echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it."
|
||||
exit 1
|
||||
else
|
||||
echo "changelog: $output"
|
||||
fi
|
||||
env:
|
||||
PYTHONIOENCODING: 'utf-8'
|
||||
PR_NUMBER: '${{ github.event.number }}'
|
||||
|
||||
# We need to have the "conclusion" job also on PR CI, to make it possible
|
||||
# to add PRs to a merge queue.
|
||||
conclusion_changelog:
|
||||
needs: [ changelog ]
|
||||
# We need to ensure this job does *not* get skipped if its dependencies fail,
|
||||
# because a skipped job is considered a success by GitHub. So we have to
|
||||
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
|
||||
# when the workflow is canceled manually.
|
||||
#
|
||||
# ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Manually check the status of all dependencies. `if: failure()` does not work.
|
||||
- name: Conclusion
|
||||
run: |
|
||||
# Print the dependent jobs to see them in the CI log
|
||||
jq -C <<< '${{ toJson(needs) }}'
|
||||
# Check if all jobs that we depend on (in the needs array) were successful.
|
||||
jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
|
34
.github/workflows/clippy_mq.yml
vendored
34
.github/workflows/clippy_mq.yml
vendored
|
@ -15,37 +15,7 @@ defaults:
|
|||
shell: bash
|
||||
|
||||
jobs:
|
||||
changelog:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
# Unsetting this would make so that any malicious package could get our Github Token
|
||||
persist-credentials: false
|
||||
|
||||
# Run
|
||||
- name: Check Changelog
|
||||
run: |
|
||||
MESSAGE=$(git log --format=%B -n 1)
|
||||
PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
|
||||
body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
|
||||
python -c "import sys, json; print(json.load(sys.stdin)['body'])")
|
||||
output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
|
||||
echo "ERROR: PR body must contain 'changelog: ...'"
|
||||
exit 1
|
||||
}
|
||||
if [[ "$output" = "none" ]]; then
|
||||
echo "WARNING: changelog is 'none'"
|
||||
else
|
||||
echo "changelog: $output"
|
||||
fi
|
||||
env:
|
||||
PYTHONIOENCODING: 'utf-8'
|
||||
base:
|
||||
needs: changelog
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
|
@ -119,7 +89,6 @@ jobs:
|
|||
OS: ${{ runner.os }}
|
||||
|
||||
metadata_collection:
|
||||
needs: changelog
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
@ -138,7 +107,6 @@ jobs:
|
|||
run: cargo collect-metadata
|
||||
|
||||
integration_build:
|
||||
needs: changelog
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
@ -228,7 +196,7 @@ jobs:
|
|||
INTEGRATION: ${{ matrix.integration }}
|
||||
|
||||
conclusion:
|
||||
needs: [ changelog, base, metadata_collection, integration_build, integration ]
|
||||
needs: [ base, metadata_collection, integration_build, integration ]
|
||||
# We need to ensure this job does *not* get skipped if its dependencies fail,
|
||||
# because a skipped job is considered a success by GitHub. So we have to
|
||||
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
|
||||
|
|
8
.github/workflows/lintcheck.yml
vendored
8
.github/workflows/lintcheck.yml
vendored
|
@ -1,6 +1,12 @@
|
|||
name: Lintcheck
|
||||
|
||||
on: pull_request
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'book/**'
|
||||
- 'util/**'
|
||||
- 'tests/**'
|
||||
- '*.md'
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
|
2
.github/workflows/remark.yml
vendored
2
.github/workflows/remark.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
|||
- name: Install mdbook
|
||||
run: |
|
||||
mkdir mdbook
|
||||
curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
|
||||
curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.43/mdbook-v0.4.43-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
|
||||
echo `pwd`/mdbook >> $GITHUB_PATH
|
||||
|
||||
# Run
|
||||
|
|
|
@ -5533,6 +5533,7 @@ Released 2018-09-13
|
|||
[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
|
||||
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
|
||||
[`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs
|
||||
[`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items
|
||||
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
|
||||
[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last
|
||||
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
|
||||
|
@ -5762,11 +5763,13 @@ Released 2018-09-13
|
|||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
|
||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err
|
||||
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
||||
[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison
|
||||
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
|
||||
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
|
||||
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
|
||||
[`manual_repeat_n`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n
|
||||
[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
|
||||
[`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate
|
||||
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
|
||||
|
@ -5904,6 +5907,7 @@ Released 2018-09-13
|
|||
[`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg
|
||||
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
|
||||
[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
|
||||
[`non_std_lazy_statics`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics
|
||||
[`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions
|
||||
[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
|
||||
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
|
||||
|
@ -6063,6 +6067,7 @@ Released 2018-09-13
|
|||
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
|
||||
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
[`sliced_string_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#sliced_string_as_bytes
|
||||
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
|
||||
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
||||
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
|
||||
|
@ -6172,12 +6177,14 @@ Released 2018-09-13
|
|||
[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
|
||||
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
|
||||
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
|
||||
[`unnecessary_semicolon`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon
|
||||
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
|
||||
[`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization
|
||||
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
|
||||
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
|
||||
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
|
||||
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
|
||||
[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern
|
||||
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
|
||||
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
|
||||
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
|
||||
|
@ -6216,6 +6223,7 @@ Released 2018-09-13
|
|||
[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
|
||||
[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
|
||||
[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
|
||||
[`useless_nonzero_new_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_nonzero_new_unchecked
|
||||
[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
|
||||
[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
|
||||
[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
|
||||
|
|
|
@ -199,7 +199,7 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and
|
|||
responding to issues there may not always be enough time to stay on top of it
|
||||
all.
|
||||
|
||||
Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
|
||||
Our highest priority is fixing [ICEs][I-ICE] and [bugs][C-bug], for example
|
||||
an ICE in a popular crate that many other crates depend on. We don't
|
||||
want Clippy to crash on your code and we want it to be as reliable as the
|
||||
suggestions from Rust compiler errors.
|
||||
|
@ -213,8 +213,8 @@ Or rather: before the sync this should be addressed,
|
|||
e.g. by removing a lint again, so it doesn't hit beta/stable.
|
||||
|
||||
[triage]: https://forge.rust-lang.org/release/triage-procedure.html
|
||||
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
|
||||
[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
|
||||
[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE
|
||||
[C-bug]: https://github.com/rust-lang/rust-clippy/labels/C-bug
|
||||
[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
|
||||
[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
|
||||
[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
|
||||
|
|
|
@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0"
|
|||
keywords = ["clippy", "lint", "plugin"]
|
||||
categories = ["development-tools", "development-tools::cargo-plugins"]
|
||||
build = "build.rs"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
|
@ -71,3 +71,10 @@ harness = false
|
|||
[[test]]
|
||||
name = "dogfood"
|
||||
harness = false
|
||||
|
||||
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
|
||||
# due to the number of conditions in the clippy_lints crate
|
||||
# and enabling optimizations for that specific dependency helps a bit
|
||||
# without increasing total build times.
|
||||
[profile.dev.package.quine-mc_cluskey]
|
||||
opt-level = 3
|
||||
|
|
|
@ -159,11 +159,11 @@ line. (You can swap `clippy::all` with the specific lint category you are target
|
|||
You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
|
||||
|
||||
* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`).
|
||||
Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
|
||||
Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
|
||||
|
||||
* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
|
||||
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
|
||||
lints prone to false positives.
|
||||
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
|
||||
lints prone to false positives.
|
||||
|
||||
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ src = "src"
|
|||
title = "Clippy Documentation"
|
||||
|
||||
[rust]
|
||||
edition = "2018"
|
||||
edition = "2024"
|
||||
|
||||
[output.html]
|
||||
edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}"
|
||||
|
|
|
@ -299,10 +299,11 @@ This is good, because it makes writing this particular lint less complicated.
|
|||
We have to make this decision with every new Clippy lint. It boils down to using
|
||||
either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
|
||||
|
||||
In short, the `EarlyLintPass` runs before type checking and
|
||||
[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass`
|
||||
has access to type information. Consider using the `LateLintPass` unless you need
|
||||
something specific from the `EarlyLintPass`.
|
||||
`EarlyLintPass` runs before type checking and
|
||||
[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering, while `LateLintPass`
|
||||
runs after these stages, providing access to type information. The `cargo dev new_lint` command
|
||||
defaults to the recommended `LateLintPass`, but you can specify `--pass=early` if your lint
|
||||
only needs AST level analysis.
|
||||
|
||||
Since we don't need type information for checking the function name, we used
|
||||
`--pass=early` when running the new lint automation and all the imports were
|
||||
|
@ -537,7 +538,7 @@ via `Tools -> Clippy` and you should see the generated code in the output below.
|
|||
If the command was executed successfully, you can copy the code over to where
|
||||
you are implementing your lint.
|
||||
|
||||
[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
|
||||
[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
|
||||
|
||||
## Print HIR lint
|
||||
|
||||
|
@ -552,7 +553,7 @@ attribute to expressions you often need to enable
|
|||
_Clippy_.
|
||||
|
||||
[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
|
||||
[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
|
||||
[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=daf14db3a7f39ca467cd1b86c34b9afb
|
||||
|
||||
## Documentation
|
||||
|
||||
|
|
|
@ -265,10 +265,10 @@ functions to deal with macros:
|
|||
```
|
||||
|
||||
[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
|
||||
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
|
||||
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html
|
||||
[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
|
||||
[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
|
||||
[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
|
||||
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
|
||||
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
|
||||
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.pat_ty
|
||||
[paths]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/paths/index.html
|
||||
|
|
|
@ -139,10 +139,10 @@ Untracked files:
|
|||
```
|
||||
|
||||
|
||||
## The `define_clippy_lints` macro
|
||||
## The `declare_clippy_lint` macro
|
||||
|
||||
After `cargo dev new_lint`, you should see a macro with the name
|
||||
`define_clippy_lints`. It will be in the same file if you defined a standalone
|
||||
`declare_clippy_lint`. It will be in the same file if you defined a standalone
|
||||
lint, and it will be in `mod.rs` if you defined a type-specific lint.
|
||||
|
||||
The macro looks something like this:
|
||||
|
|
|
@ -96,9 +96,9 @@ git tag rust-1.XX.0 # XX should be exchanged with the correspondin
|
|||
git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote
|
||||
```
|
||||
|
||||
After this, the release should be available on the Clippy [release page].
|
||||
After this, the release should be available on the Clippy [tags page].
|
||||
|
||||
[release page]: https://github.com/rust-lang/rust-clippy/releases
|
||||
[tags page]: https://github.com/rust-lang/rust-clippy/tags
|
||||
|
||||
## Publish `clippy_utils`
|
||||
|
||||
|
|
|
@ -750,6 +750,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`manual_pattern_char_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison)
|
||||
* [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
|
||||
* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
|
||||
* [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n)
|
||||
* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
|
||||
* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
|
||||
* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
|
||||
|
@ -762,11 +763,13 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
|
||||
* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
|
||||
* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
|
||||
* [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics)
|
||||
* [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
|
||||
* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or)
|
||||
* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
|
||||
* [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
|
||||
* [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
|
||||
* [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push)
|
||||
* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
|
||||
* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
|
||||
* [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "clippy_config"
|
|||
# begin autogenerated version
|
||||
version = "0.1.86"
|
||||
# end autogenerated version
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
|
@ -619,6 +619,7 @@ define_Conf! {
|
|||
manual_pattern_char_comparison,
|
||||
manual_range_contains,
|
||||
manual_rem_euclid,
|
||||
manual_repeat_n,
|
||||
manual_retain,
|
||||
manual_split_once,
|
||||
manual_str_repeat,
|
||||
|
@ -631,11 +632,13 @@ define_Conf! {
|
|||
mem_replace_with_default,
|
||||
missing_const_for_fn,
|
||||
needless_borrow,
|
||||
non_std_lazy_statics,
|
||||
option_as_ref_deref,
|
||||
option_map_unwrap_or,
|
||||
ptr_as_ptr,
|
||||
redundant_field_names,
|
||||
redundant_static_lifetimes,
|
||||
same_item_push,
|
||||
seek_from_current,
|
||||
seek_rewind,
|
||||
transmute_ptr_to_ref,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "clippy_dev"
|
||||
description = "Clippy developer tooling"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "1.0"
|
||||
|
|
|
@ -62,7 +62,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
|
|||
eprintln!("error: unable to get the absolute path of rustc ({err})");
|
||||
return Err(());
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let path = path.join("compiler");
|
||||
|
|
|
@ -842,7 +842,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
|
|||
Ok(file) => drop(file),
|
||||
Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
|
||||
Err(e) => panic_file(e, new_name, "create"),
|
||||
};
|
||||
}
|
||||
match fs::rename(old_name, new_name) {
|
||||
Ok(()) => true,
|
||||
Err(e) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_dummy" # rename to clippy before publishing
|
||||
version = "0.0.303"
|
||||
edition = "2018"
|
||||
edition = "2024"
|
||||
readme = "crates-readme.md"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust."
|
||||
build = 'build.rs'
|
||||
|
|
|
@ -8,7 +8,7 @@ repository = "https://github.com/rust-lang/rust-clippy"
|
|||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["clippy", "lint", "plugin"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
arrayvec = { version = "0.7", default-features = false }
|
||||
|
|
|
@ -445,8 +445,8 @@ fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssoc
|
|||
#[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
|
||||
use SourceItemOrderingTraitAssocItemKind::*;
|
||||
match value {
|
||||
AssocItemKind::Const { .. } => Const,
|
||||
AssocItemKind::Type { .. } => Type,
|
||||
AssocItemKind::Const => Const,
|
||||
AssocItemKind::Type => Type,
|
||||
AssocItemKind::Fn { .. } => Fn,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -257,7 +257,7 @@ fn build_sugg<'tcx>(
|
|||
// The receiver may have been a value type, so we need to add an `&` to
|
||||
// be sure the argument to clone_from will be a reference.
|
||||
arg_sugg = arg_sugg.addr();
|
||||
};
|
||||
}
|
||||
|
||||
format!("{receiver_sugg}.clone_from({arg_sugg})")
|
||||
},
|
||||
|
|
|
@ -60,7 +60,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute])
|
|||
}
|
||||
outer_attr_kind.insert(kind);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,6 +161,15 @@ declare_clippy_lint! {
|
|||
/// [dependencies]
|
||||
/// regex = "*"
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// # allow patch updates, but not minor or major version changes
|
||||
/// some_crate_1 = "~1.2.3"
|
||||
///
|
||||
/// # pin the version to a specific version
|
||||
/// some_crate_2 = "=1.2.3"
|
||||
/// ```
|
||||
#[clippy::version = "1.32.0"]
|
||||
pub WILDCARD_DEPENDENCIES,
|
||||
cargo,
|
||||
|
|
|
@ -84,6 +84,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
|
|||
diag
|
||||
.note("`usize` and `isize` may be as small as 16 bits on some platforms")
|
||||
.note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types");
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -205,7 +205,7 @@ fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
|
|||
// - uncertain if there are any uncertain values (because they could be negative or positive),
|
||||
Sign::Uncertain => return Sign::Uncertain,
|
||||
Sign::ZeroOrPositive => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// A mul/div is:
|
||||
|
@ -236,7 +236,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
|
|||
// - uncertain if there are any uncertain values (because they could be negative or positive),
|
||||
Sign::Uncertain => return Sign::Uncertain,
|
||||
Sign::ZeroOrPositive => positive_count += 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// A sum is:
|
||||
|
|
|
@ -273,7 +273,7 @@ fn get_types_from_cast<'a>(
|
|||
},
|
||||
_ => {},
|
||||
}
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
@ -142,6 +142,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
||||
crate::doc::DOC_MARKDOWN_INFO,
|
||||
crate::doc::DOC_NESTED_REFDEFS_INFO,
|
||||
crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO,
|
||||
crate::doc::EMPTY_DOCS_INFO,
|
||||
crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
|
@ -335,6 +336,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO,
|
||||
crate::matches::MANUAL_FILTER_INFO,
|
||||
crate::matches::MANUAL_MAP_INFO,
|
||||
crate::matches::MANUAL_OK_ERR_INFO,
|
||||
crate::matches::MANUAL_UNWRAP_OR_INFO,
|
||||
crate::matches::MATCH_AS_REF_INFO,
|
||||
crate::matches::MATCH_BOOL_INFO,
|
||||
|
@ -419,6 +421,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
|
||||
crate::methods::MANUAL_NEXT_BACK_INFO,
|
||||
crate::methods::MANUAL_OK_OR_INFO,
|
||||
crate::methods::MANUAL_REPEAT_N_INFO,
|
||||
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
|
||||
crate::methods::MANUAL_SPLIT_ONCE_INFO,
|
||||
crate::methods::MANUAL_STR_REPEAT_INFO,
|
||||
|
@ -466,6 +469,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
|
||||
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
|
||||
crate::methods::SKIP_WHILE_NEXT_INFO,
|
||||
crate::methods::SLICED_STRING_AS_BYTES_INFO,
|
||||
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
||||
crate::methods::STRING_EXTEND_CHARS_INFO,
|
||||
crate::methods::STRING_LIT_CHARS_ANY_INFO,
|
||||
|
@ -495,6 +499,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::methods::UNWRAP_OR_DEFAULT_INFO,
|
||||
crate::methods::UNWRAP_USED_INFO,
|
||||
crate::methods::USELESS_ASREF_INFO,
|
||||
crate::methods::USELESS_NONZERO_NEW_UNCHECKED_INFO,
|
||||
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
|
||||
crate::methods::VERBOSE_FILE_READS_INFO,
|
||||
crate::methods::WAKER_CLONE_WAKE_INFO,
|
||||
|
@ -572,6 +577,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::non_expressive_names::SIMILAR_NAMES_INFO,
|
||||
crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO,
|
||||
crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO,
|
||||
crate::non_std_lazy_statics::NON_STD_LAZY_STATICS_INFO,
|
||||
crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO,
|
||||
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
|
||||
crate::octal_escapes::OCTAL_ESCAPES_INFO,
|
||||
|
@ -753,8 +759,10 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
|
||||
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
|
||||
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
|
||||
crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO,
|
||||
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
|
||||
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
|
||||
crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO,
|
||||
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
|
||||
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
|
||||
crate::unused_async::UNUSED_ASYNC_INFO,
|
||||
|
|
|
@ -80,6 +80,6 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
|
|||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,19 +223,17 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
|
||||
match pat.kind {
|
||||
PatKind::Expr(&PatExpr {
|
||||
hir_id,
|
||||
kind: PatExprKind::Lit { lit, .. },
|
||||
..
|
||||
}) => {
|
||||
let ty = self.cx.typeck_results().node_type(hir_id);
|
||||
self.check_lit(lit, ty, hir_id);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
if let PatKind::Expr(&PatExpr {
|
||||
hir_id,
|
||||
kind: PatExprKind::Lit { lit, .. },
|
||||
..
|
||||
}) = pat.kind
|
||||
{
|
||||
let ty = self.cx.typeck_results().node_type(hir_id);
|
||||
self.check_lit(lit, ty, hir_id);
|
||||
return;
|
||||
}
|
||||
walk_pat(self, pat)
|
||||
walk_pat(self, pat);
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
|
||||
|
|
|
@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
deref_count += 1;
|
||||
},
|
||||
None => break None,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let use_node = use_cx.use_node(cx);
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{BytePos, Span};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Range;
|
||||
|
||||
use super::DOC_LAZY_CONTINUATION;
|
||||
use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS};
|
||||
|
||||
fn map_container_to_text(c: &super::Container) -> &'static str {
|
||||
match c {
|
||||
|
@ -28,12 +29,57 @@ pub(super) fn check(
|
|||
return;
|
||||
}
|
||||
|
||||
// Blockquote
|
||||
let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
|
||||
let blockquote_level = containers
|
||||
.iter()
|
||||
.filter(|c| matches!(c, super::Container::Blockquote))
|
||||
.count();
|
||||
let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count();
|
||||
if ccount < blockquote_level {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
span,
|
||||
"doc quote line without `>` marker",
|
||||
|diag| {
|
||||
let mut doc_start_range = &doc[range];
|
||||
let mut suggested = String::new();
|
||||
for c in containers {
|
||||
let text = map_container_to_text(c);
|
||||
if doc_start_range.starts_with(text) {
|
||||
doc_start_range = &doc_start_range[text.len()..];
|
||||
span = span.with_lo(
|
||||
span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")),
|
||||
);
|
||||
} else if matches!(c, super::Container::Blockquote)
|
||||
&& let Some(i) = doc_start_range.find('>')
|
||||
{
|
||||
doc_start_range = &doc_start_range[i + 1..];
|
||||
span = span
|
||||
.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
|
||||
} else {
|
||||
suggested.push_str(text);
|
||||
}
|
||||
}
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"add markers to start of line",
|
||||
suggested,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ccount != 0 && blockquote_level != 0 {
|
||||
// If this doc is a blockquote, we don't go further.
|
||||
return;
|
||||
}
|
||||
|
||||
// List
|
||||
let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count();
|
||||
let list_indentation = containers
|
||||
.iter()
|
||||
.map(|c| {
|
||||
|
@ -44,50 +90,36 @@ pub(super) fn check(
|
|||
}
|
||||
})
|
||||
.sum();
|
||||
if ccount < blockquote_level || lcount < list_indentation {
|
||||
let msg = if ccount < blockquote_level {
|
||||
"doc quote line without `>` marker"
|
||||
} else {
|
||||
"doc list item without indentation"
|
||||
};
|
||||
span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
|
||||
if ccount == 0 && blockquote_level == 0 {
|
||||
match leading_spaces.cmp(&list_indentation) {
|
||||
Ordering::Less => span_lint_and_then(
|
||||
cx,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
span,
|
||||
"doc list item without indentation",
|
||||
|diag| {
|
||||
// simpler suggestion style for indentation
|
||||
let indent = list_indentation - lcount;
|
||||
let indent = list_indentation - leading_spaces;
|
||||
diag.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"indent this line",
|
||||
std::iter::repeat(" ").take(indent).join(""),
|
||||
std::iter::repeat_n(" ", indent).join(""),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("if this is supposed to be its own paragraph, add a blank line");
|
||||
return;
|
||||
}
|
||||
let mut doc_start_range = &doc[range];
|
||||
let mut suggested = String::new();
|
||||
for c in containers {
|
||||
let text = map_container_to_text(c);
|
||||
if doc_start_range.starts_with(text) {
|
||||
doc_start_range = &doc_start_range[text.len()..];
|
||||
span = span
|
||||
.with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")));
|
||||
} else if matches!(c, super::Container::Blockquote)
|
||||
&& let Some(i) = doc_start_range.find('>')
|
||||
{
|
||||
doc_start_range = &doc_start_range[i + 1..];
|
||||
span =
|
||||
span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
|
||||
} else {
|
||||
suggested.push_str(text);
|
||||
}
|
||||
}
|
||||
diag.span_suggestion_verbose(
|
||||
},
|
||||
),
|
||||
Ordering::Greater => {
|
||||
let sugg = std::iter::repeat_n(" ", list_indentation).join("");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
span,
|
||||
"add markers to start of line",
|
||||
suggested,
|
||||
Applicability::MachineApplicable,
|
||||
"doc list item overindented",
|
||||
format!("try using `{sugg}` ({list_indentation} spaces)"),
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
|
||||
});
|
||||
},
|
||||
Ordering::Equal => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -428,6 +428,39 @@ declare_clippy_lint! {
|
|||
"require every line of a paragraph to be indented and marked"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Detects overindented list items in doc comments where the continuation
|
||||
/// lines are indented more than necessary.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Overindented list items in doc comments can lead to inconsistent and
|
||||
/// poorly formatted documentation when rendered. Excessive indentation may
|
||||
/// cause the text to be misinterpreted as a nested list item or code block,
|
||||
/// affecting readability and the overall structure of the documentation.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
///
|
||||
/// Fixes this into:
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub DOC_OVERINDENTED_LIST_ITEMS,
|
||||
style,
|
||||
"ensure list items are not overindented"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if the first paragraph in the documentation of items listed in the module page is too long.
|
||||
|
@ -617,6 +650,7 @@ impl_lint_pass!(Documentation => [
|
|||
SUSPICIOUS_DOC_COMMENTS,
|
||||
EMPTY_DOCS,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
|
|
|
@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
|
|||
var.span,
|
||||
"C-like enum variant discriminant is not portable to 32-bit targets",
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ use clippy_utils::higher::VecArgs;
|
|||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::get_type_diagnostic_name;
|
||||
use clippy_utils::usage::{local_used_after_expr, local_used_in};
|
||||
use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
|
||||
use clippy_utils::{
|
||||
get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
|
@ -101,19 +103,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
|
|||
};
|
||||
|
||||
if body.value.span.from_expansion() {
|
||||
if body.params.is_empty() {
|
||||
if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
|
||||
// replace `|| vec![]` with `Vec::new`
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_CLOSURE,
|
||||
expr.span,
|
||||
"redundant closure",
|
||||
"replace the closure with `Vec::new`",
|
||||
"std::vec::Vec::new".into(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if body.params.is_empty()
|
||||
&& let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value)
|
||||
{
|
||||
let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" };
|
||||
// replace `|| vec![]` with `Vec::new`
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_CLOSURE,
|
||||
expr.span,
|
||||
"redundant closure",
|
||||
"replace the closure with `Vec::new`",
|
||||
format!("{vec_crate}::vec::Vec::new"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
// skip `foo(|| macro!())`
|
||||
return;
|
||||
|
|
|
@ -183,7 +183,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
|
|||
.collect()
|
||||
};
|
||||
self.emit_sugg(spans, msg, help);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
|
|||
for param in generics.params {
|
||||
if param.is_impl_trait() {
|
||||
report(cx, param, generics);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ fn check_needless_must_use(
|
|||
&& !is_must_use_ty(cx, future_ty)
|
||||
{
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
span_lint_and_help(
|
||||
|
|
|
@ -120,7 +120,7 @@ fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, B
|
|||
let ecx = ConstEvalCtxt::new(cx);
|
||||
if let Some(Constant::Int(c)) = ecx.eval(r) {
|
||||
return Some((c, op.node, l));
|
||||
};
|
||||
}
|
||||
if let Some(Constant::Int(c)) = ecx.eval(l) {
|
||||
return Some((c, invert_op(op.node)?, r));
|
||||
}
|
||||
|
|
|
@ -350,7 +350,7 @@ fn check_with_condition<'tcx>(
|
|||
if cx.typeck_results().expr_ty(cond_left).is_signed() {
|
||||
} else {
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Path(QPath::TypeRelative(_, name)) => {
|
||||
|
|
|
@ -65,6 +65,6 @@ impl LateLintPass<'_> for IterOverHashType {
|
|||
expr.span,
|
||||
"iteration over unordered hash-based type",
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,6 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
|
|||
Some(ty.span.with_lo(local.pat.span.hi())),
|
||||
"remove the explicit type `_` declaration",
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -279,6 +279,7 @@ mod non_copy_const;
|
|||
mod non_expressive_names;
|
||||
mod non_octal_unix_permissions;
|
||||
mod non_send_fields_in_send_ty;
|
||||
mod non_std_lazy_statics;
|
||||
mod non_zero_suggestions;
|
||||
mod nonstandard_macro_braces;
|
||||
mod octal_escapes;
|
||||
|
@ -372,8 +373,10 @@ mod unnecessary_literal_bound;
|
|||
mod unnecessary_map_on_constructor;
|
||||
mod unnecessary_owned_empty_strings;
|
||||
mod unnecessary_self_imports;
|
||||
mod unnecessary_semicolon;
|
||||
mod unnecessary_struct_initialization;
|
||||
mod unnecessary_wraps;
|
||||
mod unneeded_struct_pattern;
|
||||
mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
mod unused_async;
|
||||
|
@ -680,7 +683,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
|
||||
store.register_late_pass(move |_| Box::new(loops::Loops::new(conf)));
|
||||
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
|
||||
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
|
||||
store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(entry::HashMapPass));
|
||||
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
|
||||
store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
|
||||
|
@ -970,5 +973,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
|
||||
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
|
||||
store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default());
|
||||
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::visit::{try_visit, walk_list};
|
||||
|
@ -20,7 +22,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
|||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::hir::nested_filter as middle_nested_filter;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::symbol::{Ident, kw};
|
||||
|
@ -91,7 +93,19 @@ declare_clippy_lint! {
|
|||
"unused lifetimes in function definitions"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
|
||||
pub struct Lifetimes {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl Lifetimes {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
|
@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
..
|
||||
} = item.kind
|
||||
{
|
||||
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true);
|
||||
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv);
|
||||
} else if let ItemKind::Impl(impl_) = item.kind {
|
||||
if !item.span.from_expansion() {
|
||||
report_extra_impl_lifetimes(cx, impl_);
|
||||
|
@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
item.generics,
|
||||
item.span,
|
||||
report_extra_lifetimes,
|
||||
&self.msrv,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
|||
TraitFn::Required(sig) => (None, Some(sig)),
|
||||
TraitFn::Provided(id) => (Some(id), None),
|
||||
};
|
||||
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true);
|
||||
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_fn_inner<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
sig: &'tcx FnSig<'_>,
|
||||
|
@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>(
|
|||
generics: &'tcx Generics<'_>,
|
||||
span: Span,
|
||||
report_extra_lifetimes: bool,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) {
|
||||
return;
|
||||
|
@ -195,7 +214,7 @@ fn check_fn_inner<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) {
|
||||
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) {
|
||||
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
|
||||
return;
|
||||
}
|
||||
|
@ -216,6 +235,7 @@ fn could_use_elision<'tcx>(
|
|||
body: Option<BodyId>,
|
||||
trait_sig: Option<&[Ident]>,
|
||||
named_generics: &'tcx [GenericParam<'_>],
|
||||
msrv: &Msrv,
|
||||
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
|
||||
// There are two scenarios where elision works:
|
||||
// * no output references, all input references have different LT
|
||||
|
@ -249,17 +269,17 @@ fn could_use_elision<'tcx>(
|
|||
let input_lts = input_visitor.lts;
|
||||
let output_lts = output_visitor.lts;
|
||||
|
||||
if let Some(trait_sig) = trait_sig {
|
||||
if explicit_self_type(cx, func, trait_sig.first().copied()) {
|
||||
return None;
|
||||
}
|
||||
if let Some(trait_sig) = trait_sig
|
||||
&& non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(body_id) = body {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
|
||||
if explicit_self_type(cx, func, first_ident) {
|
||||
if non_elidable_self_type(cx, func, first_ident, msrv) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefI
|
|||
.collect()
|
||||
}
|
||||
|
||||
// elision doesn't work for explicit self types, see rust-lang/rust#69064
|
||||
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
|
||||
if let Some(ident) = ident
|
||||
// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064
|
||||
fn non_elidable_self_type<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
func: &FnDecl<'tcx>,
|
||||
ident: Option<Ident>,
|
||||
msrv: &Msrv,
|
||||
) -> bool {
|
||||
if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION)
|
||||
&& let Some(ident) = ident
|
||||
&& ident.name == kw::SelfLower
|
||||
&& !func.implicit_self.has_implicit_self()
|
||||
&& let Some(self_ty) = func.inputs.first()
|
||||
|
@ -488,11 +514,13 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
|
|||
false
|
||||
}
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct Usage {
|
||||
lifetime: Lifetime,
|
||||
in_where_predicate: bool,
|
||||
in_bounded_ty: bool,
|
||||
in_generics_arg: bool,
|
||||
lifetime_elision_impossible: bool,
|
||||
}
|
||||
|
||||
struct LifetimeChecker<'cx, 'tcx, F> {
|
||||
|
@ -501,6 +529,7 @@ struct LifetimeChecker<'cx, 'tcx, F> {
|
|||
where_predicate_depth: usize,
|
||||
bounded_ty_depth: usize,
|
||||
generic_args_depth: usize,
|
||||
lifetime_elision_impossible: bool,
|
||||
phantom: std::marker::PhantomData<F>,
|
||||
}
|
||||
|
||||
|
@ -525,6 +554,7 @@ where
|
|||
where_predicate_depth: 0,
|
||||
bounded_ty_depth: 0,
|
||||
generic_args_depth: 0,
|
||||
lifetime_elision_impossible: false,
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -566,6 +596,7 @@ where
|
|||
in_where_predicate: self.where_predicate_depth != 0,
|
||||
in_bounded_ty: self.bounded_ty_depth != 0,
|
||||
in_generics_arg: self.generic_args_depth != 0,
|
||||
lifetime_elision_impossible: self.lifetime_elision_impossible,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -592,11 +623,44 @@ where
|
|||
self.generic_args_depth -= 1;
|
||||
}
|
||||
|
||||
fn visit_fn_decl(&mut self, fd: &'tcx FnDecl<'tcx>) -> Self::Result {
|
||||
self.lifetime_elision_impossible = !is_candidate_for_elision(fd);
|
||||
walk_fn_decl(self, fd);
|
||||
self.lifetime_elision_impossible = false;
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if `fd` supports function elision with an anonymous (or elided) lifetime,
|
||||
/// and has a lifetime somewhere in its output type.
|
||||
fn is_candidate_for_elision(fd: &FnDecl<'_>) -> bool {
|
||||
struct V;
|
||||
|
||||
impl Visitor<'_> for V {
|
||||
type Result = ControlFlow<bool>;
|
||||
|
||||
fn visit_lifetime(&mut self, lifetime: &Lifetime) -> Self::Result {
|
||||
ControlFlow::Break(lifetime.is_elided() || lifetime.is_anonymous())
|
||||
}
|
||||
}
|
||||
|
||||
if fd.lifetime_elision_allowed
|
||||
&& let Return(ret_ty) = fd.output
|
||||
&& walk_unambig_ty(&mut V, ret_ty).is_break()
|
||||
{
|
||||
// The first encountered input lifetime will either be one on `self`, or will be the only lifetime.
|
||||
fd.inputs
|
||||
.iter()
|
||||
.find_map(|ty| walk_unambig_ty(&mut V, ty).break_value())
|
||||
.unwrap()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
|
||||
let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, generics);
|
||||
|
||||
|
@ -662,6 +726,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
|
|||
Usage {
|
||||
lifetime,
|
||||
in_where_predicate: false,
|
||||
lifetime_elision_impossible: false,
|
||||
..
|
||||
},
|
||||
] = usages.as_slice()
|
||||
|
@ -719,7 +784,7 @@ fn report_elidable_lifetimes(
|
|||
|diag| {
|
||||
if !include_suggestions {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
|
||||
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
|
||||
|
|
|
@ -251,7 +251,7 @@ impl LiteralDigitGrouping {
|
|||
);
|
||||
if !consistent {
|
||||
return Err(WarningType::InconsistentDigitGrouping);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -29,9 +29,9 @@ declare_clippy_lint! {
|
|||
/// let y = "hello";
|
||||
/// x.expect(&format!("{y:?}"));
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub LITERAL_STRING_WITH_FORMATTING_ARGS,
|
||||
suspicious,
|
||||
nursery,
|
||||
"Checks if string literals have formatting arguments"
|
||||
}
|
||||
|
||||
|
|
|
@ -830,7 +830,7 @@ impl Loops {
|
|||
for_kv_map::check(cx, pat, arg, body);
|
||||
mut_range_bound::check(cx, arg, body);
|
||||
single_element_loop::check(cx, pat, arg, body, expr);
|
||||
same_item_push::check(cx, pat, arg, body, expr);
|
||||
same_item_push::check(cx, pat, arg, body, expr, &self.msrv);
|
||||
manual_flatten::check(cx, pat, arg, body, span);
|
||||
manual_find::check(cx, pat, arg, body, span, expr);
|
||||
unused_enumerate_index::check(cx, pat, arg, body);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use super::SAME_ITEM_PUSH;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::path_to_local;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{msrvs, path_to_local, std_or_core};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
@ -19,19 +20,30 @@ pub(super) fn check<'tcx>(
|
|||
_: &'tcx Expr<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
_: &'tcx Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) {
|
||||
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext, msrv: &Msrv) {
|
||||
let mut app = Applicability::Unspecified;
|
||||
let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0;
|
||||
let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0;
|
||||
|
||||
span_lint_and_help(
|
||||
let secondary_help = if msrv.meets(msrvs::REPEAT_N)
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
{
|
||||
format!("or `{vec_str}.extend({std_or_core}::iter::repeat_n({item_str}, SIZE))`")
|
||||
} else {
|
||||
format!("or `{vec_str}.resize(NEW_SIZE, {item_str})`")
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SAME_ITEM_PUSH,
|
||||
vec.span,
|
||||
"it looks like the same item is being pushed into this Vec",
|
||||
None,
|
||||
format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"),
|
||||
"it looks like the same item is being pushed into this `Vec`",
|
||||
|diag| {
|
||||
diag.help(format!("consider using `vec![{item_str};SIZE]`"))
|
||||
.help(secondary_help);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -67,11 +79,11 @@ pub(super) fn check<'tcx>(
|
|||
{
|
||||
match init.kind {
|
||||
// immutable bindings that are initialized with literal
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv),
|
||||
// immutable bindings that are initialized with constant
|
||||
ExprKind::Path(ref path) => {
|
||||
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
|
||||
emit_lint(cx, vec, pushed_item, ctxt);
|
||||
emit_lint(cx, vec, pushed_item, ctxt, msrv);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
@ -79,11 +91,11 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
},
|
||||
// constant
|
||||
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt, msrv),
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_context;
|
|||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
@ -99,7 +99,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
|
|||
&& let QPath::Resolved(_, count_func_path) = count_func_qpath
|
||||
&& let Some(segment_zero) = count_func_path.segments.first()
|
||||
&& let Some(args) = segment_zero.args
|
||||
&& let Some(real_ty_span) = args.args.first().map(|arg| arg.span())
|
||||
&& let Some(real_ty_span) = args.args.first().map(GenericArg::span)
|
||||
&& let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
|
||||
{
|
||||
|
|
|
@ -102,12 +102,48 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
|
|||
{
|
||||
build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability);
|
||||
}
|
||||
|
||||
// (x + (Y - 1)) / Y
|
||||
if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, div_rhs) {
|
||||
build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability);
|
||||
}
|
||||
|
||||
// ((Y - 1) + x) / Y
|
||||
if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, div_rhs) {
|
||||
build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability);
|
||||
}
|
||||
|
||||
// (x - (-Y - 1)) / Y
|
||||
if inner_op.node == BinOpKind::Sub
|
||||
&& let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = div_rhs.kind
|
||||
&& differ_by_one(abs_div_rhs, inner_rhs)
|
||||
{
|
||||
build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Checks if two expressions represent non-zero integer literals such that `small_expr + 1 ==
|
||||
/// large_expr`.
|
||||
fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Lit(small) = small_expr.kind
|
||||
&& let ExprKind::Lit(large) = large_expr.kind
|
||||
&& let LitKind::Int(s, _) = small.node
|
||||
&& let LitKind::Int(l, _) = large.node
|
||||
{
|
||||
Some(l.get()) == s.get().checked_add(1)
|
||||
} else if let ExprKind::Unary(UnOp::Neg, small_inner_expr) = small_expr.kind
|
||||
&& let ExprKind::Unary(UnOp::Neg, large_inner_expr) = large_expr.kind
|
||||
{
|
||||
differ_by_one(large_inner_expr, small_inner_expr)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
match expr_ty.peel_refs().kind() {
|
||||
|
|
|
@ -7,7 +7,7 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator
|
|||
use rustc_ast::LitKind::{Byte, Char};
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit};
|
||||
use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, RangeEnd};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
@ -202,8 +202,14 @@ fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
|
|||
}
|
||||
|
||||
fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange {
|
||||
if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind
|
||||
&& let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind
|
||||
if let PatExprKind::Lit {
|
||||
lit: start_lit,
|
||||
negated: false,
|
||||
} = &start.kind
|
||||
&& let PatExprKind::Lit {
|
||||
lit: end_lit,
|
||||
negated: false,
|
||||
} = &end.kind
|
||||
{
|
||||
check_lit_range(start_lit, end_lit)
|
||||
} else {
|
||||
|
|
|
@ -106,7 +106,7 @@ impl<'tcx> QuestionMark {
|
|||
emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +295,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo
|
|||
PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..)
|
||||
) {
|
||||
return;
|
||||
};
|
||||
}
|
||||
let ty = typeck_results.pat_ty(pat);
|
||||
// Option and Result are allowed, everything else isn't.
|
||||
if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) {
|
||||
|
|
|
@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
|
|||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
}
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0;
|
||||
|
|
|
@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
let target_res = cx.qpath_res(target_path, target_arg.hir_id);
|
||||
if target_res == Res::Err {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
if let Res::Local(hir_id) = target_res
|
||||
&& let Some(used_mutably) = mutated_variables(then, cx)
|
||||
|
|
|
@ -34,7 +34,7 @@ fn get_cond_expr<'tcx>(
|
|||
needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the
|
||||
* cond */
|
||||
});
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>>
|
|||
if block.stmts.is_empty() {
|
||||
return block.expr;
|
||||
}
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -68,14 +68,14 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr:
|
|||
&& is_res_lang_ctor(cx, path_res(cx, callee), OptionSome)
|
||||
&& path_to_local_id(arg, target);
|
||||
}
|
||||
};
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
|
||||
return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone);
|
||||
};
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
|
135
clippy_lints/src/matches/manual_ok_err.rs
Normal file
135
clippy_lints/src/matches/manual_ok_err.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::option_arg_ty;
|
||||
use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks, span_contains_comment};
|
||||
use rustc_ast::BindingMode;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, Path, QPath};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
use super::MANUAL_OK_ERR;
|
||||
|
||||
pub(crate) fn check_if_let(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
let_pat: &Pat<'_>,
|
||||
let_expr: &Expr<'_>,
|
||||
if_then: &Expr<'_>,
|
||||
else_expr: &Expr<'_>,
|
||||
) {
|
||||
if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr))
|
||||
&& let Some((is_ok, ident)) = is_ok_or_err(cx, let_pat)
|
||||
&& is_some_ident(cx, if_then, ident, inner_expr_ty)
|
||||
&& is_none(cx, else_expr)
|
||||
{
|
||||
apply_lint(cx, expr, let_expr, is_ok);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) {
|
||||
if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr))
|
||||
&& arms.len() == 2
|
||||
&& arms.iter().all(|arm| arm.guard.is_none())
|
||||
&& let Some((idx, is_ok)) = arms.iter().enumerate().find_map(|(arm_idx, arm)| {
|
||||
// Check if the arm is a `Ok(x) => x` or `Err(x) => x` alternative.
|
||||
// In this case, return its index and whether it uses `Ok` or `Err`.
|
||||
if let Some((is_ok, ident)) = is_ok_or_err(cx, arm.pat)
|
||||
&& is_some_ident(cx, arm.body, ident, inner_expr_ty)
|
||||
{
|
||||
Some((arm_idx, is_ok))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
// Accept wildcard only as the second arm
|
||||
&& is_variant_or_wildcard(cx, arms[1-idx].pat, idx == 0, is_ok)
|
||||
// Check that the body of the non `Ok`/`Err` arm is `None`
|
||||
&& is_none(cx, arms[1 - idx].body)
|
||||
{
|
||||
apply_lint(cx, expr, scrutinee, is_ok);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that `pat` applied to a `Result` only matches `Ok(_)`, `Err(_)`, not a subset or a
|
||||
/// superset of it. If `can_be_wild` is `true`, wildcards are also accepted. In the case of
|
||||
/// a non-wildcard, `must_match_err` indicates whether the `Err` or the `Ok` variant should be
|
||||
/// accepted.
|
||||
fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool, must_match_err: bool) -> bool {
|
||||
match pat.kind {
|
||||
PatKind::Wild | PatKind::Path(..) | PatKind::Binding(_, _, _, None) if can_be_wild => true,
|
||||
PatKind::TupleStruct(qpath, ..) => {
|
||||
is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err
|
||||
},
|
||||
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => {
|
||||
is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `Some((true, IDENT))` if `pat` contains `Ok(IDENT)`, `Some((false, IDENT))` if it
|
||||
/// contains `Err(IDENT)`, `None` otherwise.
|
||||
fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'hir Ident)> {
|
||||
if let PatKind::TupleStruct(qpath, [arg], _) = &pat.kind
|
||||
&& let PatKind::Binding(BindingMode::NONE, _, ident, _) = &arg.kind
|
||||
&& let res = cx.qpath_res(qpath, pat.hir_id)
|
||||
&& let Res::Def(DefKind::Ctor(..), id) = res
|
||||
&& let id @ Some(_) = cx.tcx.opt_parent(id)
|
||||
{
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
if id == lang_items.result_ok_variant() {
|
||||
return Some((true, ident));
|
||||
} else if id == lang_items.result_err_variant() {
|
||||
return Some((false, ident));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if `expr` contains `Some(ident)`, possibly as a block
|
||||
fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool {
|
||||
if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome)
|
||||
&& cx.typeck_results().expr_ty(body_arg) == ty
|
||||
&& let ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
segments: [segment], ..
|
||||
},
|
||||
)) = body_arg.kind
|
||||
{
|
||||
segment.ident.name == ident.name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if `expr` is `None`, possibly as a block
|
||||
fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone)
|
||||
}
|
||||
|
||||
/// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or
|
||||
/// `err`, depending on `is_ok`.
|
||||
fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok: bool) {
|
||||
let method = if is_ok { "ok" } else { "err" };
|
||||
let mut app = if span_contains_comment(cx.sess().source_map(), expr.span) {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_ERR,
|
||||
expr.span,
|
||||
format!("manual implementation of `{method}`"),
|
||||
"replace with",
|
||||
format!("{scrut}.{method}()"),
|
||||
app,
|
||||
);
|
||||
}
|
|
@ -109,7 +109,7 @@ where
|
|||
}
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
}
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_unit_expr;
|
||||
use clippy_utils::source::{expr_block, snippet};
|
||||
use clippy_utils::source::expr_block;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -17,17 +17,28 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
|
|||
cx,
|
||||
MATCH_BOOL,
|
||||
expr.span,
|
||||
"you seem to be trying to match on a boolean expression",
|
||||
"`match` on a boolean expression",
|
||||
move |diag| {
|
||||
if arms.len() == 2 {
|
||||
// no guards
|
||||
let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
|
||||
let test = Sugg::hir_with_applicability(cx, scrutinee, "_", &mut app);
|
||||
if let PatExprKind::Lit { lit, .. } = arm_bool.kind {
|
||||
match lit.node {
|
||||
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
|
||||
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),
|
||||
match &lit.node {
|
||||
LitKind::Bool(true) => Some(test),
|
||||
LitKind::Bool(false) => Some(!test),
|
||||
_ => None,
|
||||
}
|
||||
.map(|test| {
|
||||
if let Some(guard) = &arms[0]
|
||||
.guard
|
||||
.map(|g| Sugg::hir_with_applicability(cx, g, "_", &mut app))
|
||||
{
|
||||
test.and(guard)
|
||||
} else {
|
||||
test
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -35,39 +46,31 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
|
|||
None
|
||||
};
|
||||
|
||||
if let Some((true_expr, false_expr)) = exprs {
|
||||
let mut app = Applicability::HasPlaceholders;
|
||||
if let Some(test_sugg) = test_sugg {
|
||||
let ctxt = expr.span.ctxt();
|
||||
let (true_expr, false_expr) = (arms[0].body, arms[1].body);
|
||||
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
|
||||
(false, false) => Some(format!(
|
||||
"if {} {} else {}",
|
||||
snippet(cx, scrutinee.span, "b"),
|
||||
test_sugg,
|
||||
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
|
||||
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||
)),
|
||||
(false, true) => Some(format!(
|
||||
"if {} {}",
|
||||
snippet(cx, scrutinee.span, "b"),
|
||||
test_sugg,
|
||||
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||
)),
|
||||
(true, false) => {
|
||||
let test = Sugg::hir(cx, scrutinee, "..");
|
||||
Some(format!(
|
||||
"if {} {}",
|
||||
!test,
|
||||
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||
))
|
||||
},
|
||||
(true, false) => Some(format!(
|
||||
"if {} {}",
|
||||
!test_sugg,
|
||||
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||
)),
|
||||
(true, true) => None,
|
||||
};
|
||||
|
||||
if let Some(sugg) = sugg {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider using an `if`/`else` expression",
|
||||
sugg,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ where
|
|||
if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
|
||||
ex_new = ex_inner;
|
||||
}
|
||||
};
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MATCH_LIKE_MATCHES_MACRO,
|
||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item;
|
|||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatExpr, PatExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Span;
|
||||
|
|
|
@ -170,7 +170,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
|
|||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum CommonPrefixSearcher<'a> {
|
||||
|
|
|
@ -2,6 +2,7 @@ mod collapsible_match;
|
|||
mod infallible_destructuring_match;
|
||||
mod manual_filter;
|
||||
mod manual_map;
|
||||
mod manual_ok_err;
|
||||
mod manual_unwrap_or;
|
||||
mod manual_utils;
|
||||
mod match_as_ref;
|
||||
|
@ -972,6 +973,40 @@ declare_clippy_lint! {
|
|||
"checks for unnecessary guards in match expressions"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for manual implementation of `.ok()` or `.err()`
|
||||
/// on `Result` values.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `.ok()` or `.err()` rather than a `match` or
|
||||
/// `if let` is less complex and more readable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # fn func() -> Result<u32, &'static str> { Ok(0) }
|
||||
/// let a = match func() {
|
||||
/// Ok(v) => Some(v),
|
||||
/// Err(_) => None,
|
||||
/// };
|
||||
/// let b = if let Err(v) = func() {
|
||||
/// Some(v)
|
||||
/// } else {
|
||||
/// None
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # fn func() -> Result<u32, &'static str> { Ok(0) }
|
||||
/// let a = func().ok();
|
||||
/// let b = func().err();
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub MANUAL_OK_ERR,
|
||||
complexity,
|
||||
"find manual implementations of `.ok()` or `.err()` on `Result`"
|
||||
}
|
||||
|
||||
pub struct Matches {
|
||||
msrv: Msrv,
|
||||
infallible_destructuring_match_linted: bool,
|
||||
|
@ -1013,6 +1048,7 @@ impl_lint_pass!(Matches => [
|
|||
MANUAL_MAP,
|
||||
MANUAL_FILTER,
|
||||
REDUNDANT_GUARDS,
|
||||
MANUAL_OK_ERR,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
|
@ -1091,6 +1127,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
manual_unwrap_or::check_match(cx, expr, ex, arms);
|
||||
manual_map::check_match(cx, expr, ex, arms);
|
||||
manual_filter::check_match(cx, ex, arms, expr);
|
||||
manual_ok_err::check_match(cx, expr, ex, arms);
|
||||
}
|
||||
|
||||
if self.infallible_destructuring_match_linted {
|
||||
|
@ -1134,6 +1171,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
if_let.if_then,
|
||||
else_expr,
|
||||
);
|
||||
manual_ok_err::check_if_let(
|
||||
cx,
|
||||
expr,
|
||||
if_let.let_pat,
|
||||
if_let.let_expr,
|
||||
if_let.if_then,
|
||||
else_expr,
|
||||
);
|
||||
}
|
||||
}
|
||||
redundant_pattern_match::check_if_let(
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::ops::ControlFlow;
|
|||
|
||||
use crate::FxHashSet;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{indent_of, snippet};
|
||||
use clippy_utils::source::{first_line_of_span, indent_of, snippet};
|
||||
use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
|
||||
use clippy_utils::{get_attr, is_lint_allowed};
|
||||
use itertools::Itertools;
|
||||
|
@ -152,7 +152,7 @@ fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
|
|||
diag.multipart_suggestion(
|
||||
suggestion_message,
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), replacement),
|
||||
(first_line_of_span(cx, expr.span).shrink_to_lo(), replacement),
|
||||
(found.found_span, scrutinee_replacement),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
|
@ -441,8 +441,9 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> {
|
|||
let parent_expr_before = self.parent_expr.replace(ex);
|
||||
|
||||
match ex.kind {
|
||||
// Skip blocks because values in blocks will be dropped as usual.
|
||||
ExprKind::Block(..) => (),
|
||||
// Skip blocks because values in blocks will be dropped as usual, and await
|
||||
// desugaring because temporary insides the future will have been dropped.
|
||||
ExprKind::Block(..) | ExprKind::Match(_, _, MatchSource::AwaitDesugar) => (),
|
||||
_ => walk_expr(self, ex),
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
|||
err_ty = ty;
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
|
@ -13,7 +13,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) {
|
|||
&& has_non_exhaustive_attr(cx.tcx, *adt_def)
|
||||
{
|
||||
return;
|
||||
};
|
||||
}
|
||||
for arm in arms {
|
||||
if let PatKind::Or(fields) = arm.pat.kind {
|
||||
// look for multiple fields in this arm that contains at least one Wild pattern
|
||||
|
|
|
@ -62,5 +62,5 @@ pub(super) fn check<'tcx>(
|
|||
),
|
||||
applicability,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,5 +32,5 @@ pub(super) fn check<'tcx>(
|
|||
),
|
||||
applicability,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,5 +46,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
|
|||
format!("{receiver}.as_bytes().get({n}).copied()"),
|
||||
applicability,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span,
|
|||
// &T where T: Copy
|
||||
ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
|
||||
_ => return,
|
||||
};
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CLONED_INSTEAD_OF_COPIED,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::methods::DRAIN_COLLECT;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_range_full;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use clippy_utils::{is_range_full, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re
|
|||
.then_some("Vec")
|
||||
.or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
|
||||
.or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs))
|
||||
&& let Some(exec_context) = std_or_core(cx)
|
||||
{
|
||||
let recv = snippet(cx, recv.span, "<expr>");
|
||||
let sugg = if let ty::Ref(..) = recv_ty.kind() {
|
||||
format!("std::mem::take({recv})")
|
||||
format!("{exec_context}::mem::take({recv})")
|
||||
} else {
|
||||
format!("std::mem::take(&mut {recv})")
|
||||
format!("{exec_context}::mem::take(&mut {recv})")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
|
|
|
@ -37,7 +37,7 @@ pub(super) fn check(
|
|||
"expect_err".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `Result<T, E>` type, return its data (`T`).
|
||||
|
|
|
@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(
|
|||
if ty.is_str() && can_be_static_str(cx, arg) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
|
|||
"into_iter()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
43
clippy_lints/src/methods/manual_repeat_n.rs
Normal file
43
clippy_lints/src/methods/manual_repeat_n.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{snippet, snippet_with_context};
|
||||
use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MANUAL_REPEAT_N;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
repeat_expr: &Expr<'_>,
|
||||
take_arg: &Expr<'_>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if msrv.meets(msrvs::REPEAT_N)
|
||||
&& !expr.span.from_expansion()
|
||||
&& is_trait_method(cx, expr, sym::Iterator)
|
||||
&& let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind
|
||||
&& let Some(def_id) = fn_def_id(cx, repeat_expr)
|
||||
&& cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id)
|
||||
&& !expr_use_ctxt(cx, expr).is_ty_unified
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_REPEAT_N,
|
||||
expr.span,
|
||||
"this `repeat().take()` can be written more concisely",
|
||||
"consider using `repeat_n()` instead",
|
||||
format!(
|
||||
"{std_or_core}::iter::repeat_n({}, {})",
|
||||
snippet_with_context(cx, repeat_arg.span, expr.span.ctxt(), "..", &mut app).0,
|
||||
snippet(cx, take_arg.span, "..")
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{eager_or_lazy, higher, usage};
|
||||
use clippy_utils::{eager_or_lazy, higher, std_or_core, usage};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
|
@ -75,6 +75,7 @@ pub(super) fn check(
|
|||
} = body_hir
|
||||
&& !usage::BindingUsageFinder::are_params_used(cx, body_hir)
|
||||
&& let Some(count) = extract_count_with_applicability(cx, range, &mut applicability)
|
||||
&& let Some(exec_context) = std_or_core(cx)
|
||||
{
|
||||
let method_to_use_name;
|
||||
let new_span;
|
||||
|
@ -105,7 +106,7 @@ pub(super) fn check(
|
|||
let mut parts = vec![
|
||||
(
|
||||
receiver.span.to(method_call_span),
|
||||
format!("std::iter::{method_to_use_name}"),
|
||||
format!("{exec_context}::iter::{method_to_use_name}"),
|
||||
),
|
||||
new_span,
|
||||
];
|
||||
|
|
|
@ -58,6 +58,7 @@ mod manual_inspect;
|
|||
mod manual_is_variant_and;
|
||||
mod manual_next_back;
|
||||
mod manual_ok_or;
|
||||
mod manual_repeat_n;
|
||||
mod manual_saturating_arithmetic;
|
||||
mod manual_str_repeat;
|
||||
mod manual_try_fold;
|
||||
|
@ -101,6 +102,7 @@ mod single_char_add_str;
|
|||
mod single_char_insert_string;
|
||||
mod single_char_push_string;
|
||||
mod skip_while_next;
|
||||
mod sliced_string_as_bytes;
|
||||
mod stable_sort_primitive;
|
||||
mod str_split;
|
||||
mod str_splitn;
|
||||
|
@ -130,6 +132,7 @@ mod unnecessary_to_owned;
|
|||
mod unused_enumerate_index;
|
||||
mod unwrap_expect_used;
|
||||
mod useless_asref;
|
||||
mod useless_nonzero_new_unchecked;
|
||||
mod utils;
|
||||
mod vec_resize_to_zero;
|
||||
mod verbose_file_reads;
|
||||
|
@ -2416,14 +2419,14 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `.then_some(..).unwrap_or(..)`
|
||||
/// Checks for unnecessary method chains that can be simplified into `if .. else ..`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can be written more clearly with `if .. else ..`
|
||||
///
|
||||
/// ### Limitations
|
||||
/// This lint currently only looks for usages of
|
||||
/// `.then_some(..).unwrap_or(..)`, but will be expanded
|
||||
/// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded
|
||||
/// to account for similar patterns.
|
||||
///
|
||||
/// ### Example
|
||||
|
@ -4311,6 +4314,84 @@ declare_clippy_lint! {
|
|||
"using `Iterator::last` on a `DoubleEndedIterator`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for `NonZero*::new_unchecked()` being used in a `const` context.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a
|
||||
/// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical
|
||||
/// runtime performances while not requiring `unsafe`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::num::NonZeroUsize;
|
||||
/// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// use std::num::NonZeroUsize;
|
||||
/// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap();
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub USELESS_NONZERO_NEW_UNCHECKED,
|
||||
complexity,
|
||||
"using `NonZero::new_unchecked()` in a `const` context"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for `repeat().take()` that can be replaced with `repeat_n()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = std::iter::repeat(10).take(3);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = std::iter::repeat_n(10, 3);
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub MANUAL_REPEAT_N,
|
||||
style,
|
||||
"detect `repeat().take()` that can be replaced with `repeat_n()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for string slices immediantly followed by `as_bytes`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// In some cases, the UTF-8 validation and potential panic from string slicing may be required for
|
||||
/// the code's correctness. If you need to ensure the slice boundaries fall on valid UTF-8 character
|
||||
/// boundaries, the original form (`s[1..5].as_bytes()`) should be preferred.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let s = "Lorem ipsum";
|
||||
/// s[1..5].as_bytes();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let s = "Lorem ipsum";
|
||||
/// &s.as_bytes()[1..5];
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub SLICED_STRING_AS_BYTES,
|
||||
perf,
|
||||
"slicing a string and immediately calling as_bytes is less efficient and can lead to panics"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
|
@ -4477,6 +4558,9 @@ impl_lint_pass!(Methods => [
|
|||
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
|
||||
UNNECESSARY_MAP_OR,
|
||||
DOUBLE_ENDED_ITERATOR_LAST,
|
||||
USELESS_NONZERO_NEW_UNCHECKED,
|
||||
MANUAL_REPEAT_N,
|
||||
SLICED_STRING_AS_BYTES,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
@ -4505,6 +4589,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
from_iter_instead_of_collect::check(cx, expr, args, func);
|
||||
unnecessary_fallible_conversions::check_function(cx, expr, func);
|
||||
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
|
||||
useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv);
|
||||
},
|
||||
ExprKind::MethodCall(method_call, receiver, args, _) => {
|
||||
let method_span = method_call.ident.span;
|
||||
|
@ -4743,6 +4828,7 @@ impl Methods {
|
|||
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
|
||||
redundant_as_str::check(cx, expr, recv, as_str_span, span);
|
||||
}
|
||||
sliced_string_as_bytes::check(cx, expr, recv);
|
||||
},
|
||||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
|
||||
|
@ -4924,8 +5010,8 @@ impl Methods {
|
|||
},
|
||||
("is_empty", []) => {
|
||||
match method_call(recv) {
|
||||
Some(("as_bytes", prev_recv, [], _, _)) => {
|
||||
needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span);
|
||||
Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => {
|
||||
needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span);
|
||||
},
|
||||
Some(("as_str", recv, [], as_str_span, _)) => {
|
||||
redundant_as_str::check(cx, expr, recv, as_str_span, span);
|
||||
|
@ -4962,8 +5048,8 @@ impl Methods {
|
|||
double_ended_iterator_last::check(cx, expr, recv, call_span);
|
||||
},
|
||||
("len", []) => {
|
||||
if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
|
||||
needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span);
|
||||
if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) {
|
||||
needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span);
|
||||
}
|
||||
},
|
||||
("lock", []) => {
|
||||
|
@ -5015,7 +5101,7 @@ impl Methods {
|
|||
option_map_or_none::check(cx, expr, recv, def, map);
|
||||
manual_ok_or::check(cx, expr, recv, def, map);
|
||||
option_map_or_err_ok::check(cx, expr, recv, def, map);
|
||||
unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv);
|
||||
unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv);
|
||||
},
|
||||
("map_or_else", [def, map]) => {
|
||||
result_map_or_else_none::check(cx, expr, recv, def, map);
|
||||
|
@ -5146,6 +5232,7 @@ impl Methods {
|
|||
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
|
||||
("take", [arg]) => {
|
||||
iter_out_of_bounds::check_take(cx, expr, recv, arg);
|
||||
manual_repeat_n::check(cx, expr, recv, arg, &self.msrv);
|
||||
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
||||
iter_overeager_cloned::check(
|
||||
cx,
|
||||
|
@ -5220,8 +5307,8 @@ impl Methods {
|
|||
Some(("map", m_recv, [m_arg], span, _)) => {
|
||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
|
||||
},
|
||||
Some(("then_some", t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
|
||||
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
|
|
@ -8,18 +8,16 @@ use rustc_span::Span;
|
|||
|
||||
use super::NEEDLESS_AS_BYTES;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) {
|
||||
if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
|
||||
&& let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs()
|
||||
&& (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
|
||||
{
|
||||
pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) {
|
||||
let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs();
|
||||
if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_AS_BYTES,
|
||||
span,
|
||||
"needless call to `as_bytes()`",
|
||||
format!("needless call to `{prev_method}`"),
|
||||
format!("`{method}()` can be called directly on strings"),
|
||||
format!("{sugg}.{method}()"),
|
||||
app,
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use super::OBFUSCATED_IF_ELSE;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
|
@ -11,19 +14,30 @@ pub(super) fn check<'tcx>(
|
|||
then_recv: &'tcx hir::Expr<'_>,
|
||||
then_arg: &'tcx hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||
then_method_name: &str,
|
||||
) {
|
||||
// something.then_some(blah).unwrap_or(blah)
|
||||
// ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
|
||||
|
||||
let recv_ty = cx.typeck_results().expr_ty(then_recv);
|
||||
|
||||
if recv_ty.is_bool() {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
let if_then = match then_method_name {
|
||||
"then" if let ExprKind::Closure(closure) = then_arg.kind => {
|
||||
let body = cx.tcx.hir().body(closure.body);
|
||||
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
|
||||
},
|
||||
"then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
|
||||
_ => String::new().into(),
|
||||
};
|
||||
|
||||
let sugg = format!(
|
||||
"if {} {{ {} }} else {{ {} }}",
|
||||
snippet_with_applicability(cx, then_recv.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
|
||||
Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability),
|
||||
if_then,
|
||||
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
|
||||
);
|
||||
|
||||
|
@ -31,8 +45,7 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
OBFUSCATED_IF_ELSE,
|
||||
expr.span,
|
||||
"use of `.then_some(..).unwrap_or(..)` can be written \
|
||||
more clearly with `if .. else ..`",
|
||||
"this method chain can be written more clearly with `if .. else ..`",
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
|
|
|
@ -37,7 +37,7 @@ pub(super) fn check(
|
|||
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
|
||||
} else {
|
||||
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
|
||||
};
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
29
clippy_lints/src/methods/sliced_string_as_bytes.rs
Normal file
29
clippy_lints/src/methods/sliced_string_as_bytes.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::SLICED_STRING_AS_BYTES;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) {
|
||||
if let ExprKind::Index(indexed, index, _) = recv.kind
|
||||
&& is_range_literal(index)
|
||||
&& let ty = cx.typeck_results().expr_ty(indexed).peel_refs()
|
||||
&& (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String))
|
||||
{
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability);
|
||||
let range = snippet_with_applicability(cx, index.span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SLICED_STRING_AS_BYTES,
|
||||
expr.span,
|
||||
"calling `as_bytes` after slicing a string",
|
||||
"try",
|
||||
format!("&{stringish}.as_bytes()[{range}]"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -87,7 +87,7 @@ pub fn check_for_loop_iter(
|
|||
// skip lint
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// the lint should not be executed if no violation happens
|
||||
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::sugg::{Sugg, make_binop};
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
|
@ -12,7 +11,7 @@ use rustc_ast::LitKind::Bool;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use super::UNNECESSARY_MAP_OR;
|
||||
|
||||
|
@ -42,13 +41,14 @@ pub(super) fn check<'a>(
|
|||
recv: &Expr<'_>,
|
||||
def: &Expr<'_>,
|
||||
map: &Expr<'_>,
|
||||
method_span: Span,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
let ExprKind::Lit(def_kind) = def.kind else {
|
||||
return;
|
||||
};
|
||||
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
|
||||
|
||||
let Bool(def_bool) = def_kind.node else {
|
||||
return;
|
||||
|
@ -60,6 +60,8 @@ pub(super) fn check<'a>(
|
|||
Some(_) | None => return,
|
||||
};
|
||||
|
||||
let ext_def_span = def.span.until(map.span);
|
||||
|
||||
let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind
|
||||
&& let closure_body = cx.tcx.hir().body(map_closure.body)
|
||||
&& let closure_body_value = closure_body.value.peel_blocks()
|
||||
|
@ -114,26 +116,17 @@ pub(super) fn check<'a>(
|
|||
}
|
||||
.into_string();
|
||||
|
||||
(sugg, "a standard comparison", app)
|
||||
} else if !def_bool
|
||||
&& msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
|
||||
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
|
||||
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
|
||||
{
|
||||
(vec![(expr.span, sugg)], "a standard comparison", app)
|
||||
} else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) {
|
||||
let suggested_name = variant.method_name();
|
||||
(
|
||||
format!("{recv_callsite}.{suggested_name}({span_callsite})",),
|
||||
vec![(method_span, suggested_name.into()), (ext_def_span, String::default())],
|
||||
suggested_name,
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
} else if def_bool
|
||||
&& matches!(variant, Variant::Some)
|
||||
&& msrv.meets(msrvs::IS_NONE_OR)
|
||||
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
|
||||
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
|
||||
{
|
||||
} else if def_bool && matches!(variant, Variant::Some) && msrv.meets(msrvs::IS_NONE_OR) {
|
||||
(
|
||||
format!("{recv_callsite}.is_none_or({span_callsite})"),
|
||||
vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())],
|
||||
"is_none_or",
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
|
@ -145,13 +138,13 @@ pub(super) fn check<'a>(
|
|||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNNECESSARY_MAP_OR,
|
||||
expr.span,
|
||||
"this `map_or` can be simplified",
|
||||
format!("use {method} instead"),
|
||||
sugg,
|
||||
applicability,
|
||||
|diag| {
|
||||
diag.multipart_suggestion_verbose(format!("use {method} instead"), sugg, applicability);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
@ -43,8 +44,7 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
|
|||
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
},
|
||||
// The two exprs are method calls.
|
||||
// Check to see that the function is the same and the arguments are mirrored
|
||||
// This is enough because the receiver of the method is listed in the arguments
|
||||
// Check to see that the function is the same and the arguments and receivers are mirrored
|
||||
(
|
||||
ExprKind::MethodCall(left_segment, left_receiver, left_args, _),
|
||||
ExprKind::MethodCall(right_segment, right_receiver, right_args, _),
|
||||
|
|
|
@ -217,10 +217,13 @@ fn check_into_iter_call_arg(
|
|||
&& implements_trait(cx, parent_ty, iterator_trait_id, &[])
|
||||
&& let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
|
||||
&& let Some(receiver_snippet) = receiver.span.get_source_text(cx)
|
||||
// If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624.
|
||||
&& !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow)
|
||||
{
|
||||
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
|
||||
"copied"
|
||||
} else {
|
||||
|
|
59
clippy_lints/src/methods/useless_nonzero_new_unchecked.rs
Normal file
59
clippy_lints/src/methods/useless_nonzero_new_unchecked.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::is_inside_always_const_context;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::USELESS_NONZERO_NEW_UNCHECKED;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'tcx>, args: &[Expr<'_>], msrv: &Msrv) {
|
||||
if msrv.meets(msrvs::CONST_UNWRAP)
|
||||
&& let ExprKind::Path(QPath::TypeRelative(ty, segment)) = func.kind
|
||||
&& segment.ident.name == sym::new_unchecked
|
||||
&& let [init_arg] = args
|
||||
&& is_inside_always_const_context(cx.tcx, expr.hir_id)
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero)
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let ty_str = snippet_with_applicability(cx, ty.span, "_", &mut app);
|
||||
let msg = format!("`{ty_str}::new()` and `Option::unwrap()` can be safely used in a `const` context");
|
||||
let sugg = format!(
|
||||
"{ty_str}::new({}).unwrap()",
|
||||
snippet_with_applicability(cx, init_arg.span, "_", &mut app)
|
||||
);
|
||||
|
||||
if let Node::Block(Block {
|
||||
stmts: [],
|
||||
span: block_span,
|
||||
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||
..
|
||||
}) = cx.tcx.parent_hir_node(expr.hir_id)
|
||||
{
|
||||
if !block_span.from_expansion() {
|
||||
// The expression is the only component of an `unsafe` block. Propose
|
||||
// to replace the block altogether.
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
USELESS_NONZERO_NEW_UNCHECKED,
|
||||
*block_span,
|
||||
msg,
|
||||
"use instead",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// The expression is enclosed in a larger `unsafe` context. Indicate that
|
||||
// this may no longer be needed for the fixed expression.
|
||||
span_lint_and_then(cx, USELESS_NONZERO_NEW_UNCHECKED, expr.span, msg, |diagnostic| {
|
||||
diagnostic
|
||||
.span_suggestion(expr.span, "use instead", sugg, app)
|
||||
.note("the fixed expression does not require an `unsafe` context");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -214,11 +214,12 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
if let StmtKind::Semi(expr) = stmt.kind
|
||||
&& let ExprKind::Binary(ref binop, a, b) = expr.kind
|
||||
&& (binop.node == BinOpKind::And || binop.node == BinOpKind::Or)
|
||||
&& let Some(sugg) = Sugg::hir_opt(cx, a)
|
||||
&& let ExprKind::Binary(binop, a, b) = &expr.kind
|
||||
&& matches!(binop.node, BinOpKind::And | BinOpKind::Or)
|
||||
&& !stmt.span.from_expansion()
|
||||
&& expr.span.eq_ctxt(stmt.span)
|
||||
{
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
|
@ -227,16 +228,14 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||
stmt.span,
|
||||
"boolean short circuit operator in statement may be clearer using an explicit test",
|
||||
|diag| {
|
||||
let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
|
||||
diag.span_suggestion(
|
||||
stmt.span,
|
||||
"replace it with",
|
||||
format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let test = Sugg::hir_with_context(cx, a, expr.span.ctxt(), "_", &mut app);
|
||||
let test = if binop.node == BinOpKind::Or { !test } else { test };
|
||||
let then = Sugg::hir_with_context(cx, b, expr.span.ctxt(), "_", &mut app);
|
||||
diag.span_suggestion(stmt.span, "replace it with", format!("if {test} {{ {then}; }}"), app);
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
|
|
|
@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
|
|||
}) => impl_params.push((path.segments[0].ident.to_string(), path.span)),
|
||||
GenericArg::Type(_) => return,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// find the type that the Impl is for
|
||||
|
|
|
@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
|||
| hir::ItemKind::GlobalAsm(..)
|
||||
| hir::ItemKind::Impl { .. }
|
||||
| hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span),
|
||||
};
|
||||
}
|
||||
|
||||
let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
|
|||
// this prevents ICEs such as when self is a type parameter or a primitive type
|
||||
// (see #10887, #11063)
|
||||
&& let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res
|
||||
&& cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug])
|
||||
&& cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id)
|
||||
// don't trigger if this impl was derived
|
||||
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
|
||||
&& !item.span.from_expansion()
|
||||
|
|
|
@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
|||
| hir::ItemKind::ForeignMod { .. }
|
||||
| hir::ItemKind::Impl { .. }
|
||||
| hir::ItemKind::Use(..) => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
|
|
|
@ -56,10 +56,10 @@ impl EarlyLintPass for MultiAssignments {
|
|||
if let ExprKind::Assign(target, source, _) = &expr.kind {
|
||||
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
|
||||
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
||||
};
|
||||
}
|
||||
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
|
||||
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ fn collect_unsafe_exprs<'tcx>(
|
|||
},
|
||||
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
Continue::<(), _>(Descend::Yes)
|
||||
});
|
||||
|
|
|
@ -96,10 +96,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex {
|
|||
ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
|
||||
ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
|
||||
_ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,10 @@ struct LocalAssign {
|
|||
|
||||
impl LocalAssign {
|
||||
fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
|
||||
if expr.span.from_expansion() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
|
||||
if lhs.span.from_expansion() {
|
||||
return None;
|
||||
|
@ -336,7 +340,7 @@ fn check<'tcx>(
|
|||
);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind {
|
||||
if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind
|
||||
&& !item.span.from_expansion()
|
||||
{
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app);
|
||||
|
|
|
@ -89,16 +89,6 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// The `const` value should be stored inside a `static` item.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// When an enum has variants with interior mutability, use of its non
|
||||
/// interior mutable variants can generate false positives. See issue
|
||||
/// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
|
||||
///
|
||||
/// Types that have underlying or potential interior mutability trigger the lint whether
|
||||
/// the interior mutable field is used or not. See issues
|
||||
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
|
||||
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue