Merge commit '51d49c1ae2
' into clippy-subtree-update
This commit is contained in:
parent
c5196736b2
commit
9da9ddb7db
273 changed files with 5679 additions and 795 deletions
2
.github/deploy.sh
vendored
2
.github/deploy.sh
vendored
|
@ -45,6 +45,8 @@ if [[ -n $TAG_NAME ]]; then
|
||||||
git add "$TAG_NAME"
|
git add "$TAG_NAME"
|
||||||
# Update the symlink
|
# Update the symlink
|
||||||
git add stable
|
git add stable
|
||||||
|
# Update the index.html file
|
||||||
|
git add index.html
|
||||||
git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
|
git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
|
||||||
elif [[ $BETA = "true" ]]; then
|
elif [[ $BETA = "true" ]]; then
|
||||||
if git diff --exit-code --quiet -- beta/; 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
|
# 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?
|
# 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
|
./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" box_default.stderr > normalized.stderr
|
sed -e "/= help: for/d" string_to_string.stderr > normalized.stderr
|
||||||
diff -u normalized.stderr tests/ui/box_default.stderr
|
diff -u normalized.stderr tests/ui/string_to_string.stderr
|
||||||
|
|
||||||
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
|
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
|
||||||
SYSROOT=$(rustc --print sysroot)
|
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
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
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:
|
base:
|
||||||
needs: changelog
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
@ -119,7 +89,6 @@ jobs:
|
||||||
OS: ${{ runner.os }}
|
OS: ${{ runner.os }}
|
||||||
|
|
||||||
metadata_collection:
|
metadata_collection:
|
||||||
needs: changelog
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -138,7 +107,6 @@ jobs:
|
||||||
run: cargo collect-metadata
|
run: cargo collect-metadata
|
||||||
|
|
||||||
integration_build:
|
integration_build:
|
||||||
needs: changelog
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -228,7 +196,7 @@ jobs:
|
||||||
INTEGRATION: ${{ matrix.integration }}
|
INTEGRATION: ${{ matrix.integration }}
|
||||||
|
|
||||||
conclusion:
|
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,
|
# 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
|
# 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
|
# 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
|
name: Lintcheck
|
||||||
|
|
||||||
on: pull_request
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'book/**'
|
||||||
|
- 'util/**'
|
||||||
|
- 'tests/**'
|
||||||
|
- '*.md'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
|
|
2
.github/workflows/remark.yml
vendored
2
.github/workflows/remark.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
||||||
- name: Install mdbook
|
- name: Install mdbook
|
||||||
run: |
|
run: |
|
||||||
mkdir mdbook
|
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
|
echo `pwd`/mdbook >> $GITHUB_PATH
|
||||||
|
|
||||||
# Run
|
# 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_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_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_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_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_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
|
[`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_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_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_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_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_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_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_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_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_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_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
|
[`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_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_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_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
|
[`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
|
[`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
|
[`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_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
|
[`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
|
[`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
|
[`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
|
[`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
|
[`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_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_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_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_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_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_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_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
|
[`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_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
|
[`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
|
[`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
|
[`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_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_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_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_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
|
[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
|
||||||
[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
|
[`vec_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
|
responding to issues there may not always be enough time to stay on top of it
|
||||||
all.
|
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
|
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
|
want Clippy to crash on your code and we want it to be as reliable as the
|
||||||
suggestions from Rust compiler errors.
|
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.
|
e.g. by removing a lint again, so it doesn't hit beta/stable.
|
||||||
|
|
||||||
[triage]: https://forge.rust-lang.org/release/triage-procedure.html
|
[triage]: https://forge.rust-lang.org/release/triage-procedure.html
|
||||||
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
|
[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE
|
||||||
[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
|
[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-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
|
||||||
[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
|
[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
|
||||||
[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
|
[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"]
|
keywords = ["clippy", "lint", "plugin"]
|
||||||
categories = ["development-tools", "development-tools::cargo-plugins"]
|
categories = ["development-tools", "development-tools::cargo-plugins"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|
|
@ -6,7 +6,7 @@ src = "src"
|
||||||
title = "Clippy Documentation"
|
title = "Clippy Documentation"
|
||||||
|
|
||||||
[rust]
|
[rust]
|
||||||
edition = "2018"
|
edition = "2024"
|
||||||
|
|
||||||
[output.html]
|
[output.html]
|
||||||
edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}"
|
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
|
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].
|
either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
|
||||||
|
|
||||||
In short, the `EarlyLintPass` runs before type checking and
|
`EarlyLintPass` runs before type checking and
|
||||||
[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass`
|
[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering, while `LateLintPass`
|
||||||
has access to type information. Consider using the `LateLintPass` unless you need
|
runs after these stages, providing access to type information. The `cargo dev new_lint` command
|
||||||
something specific from the `EarlyLintPass`.
|
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
|
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
|
`--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
|
If the command was executed successfully, you can copy the code over to where
|
||||||
you are implementing your lint.
|
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
|
## Print HIR lint
|
||||||
|
|
||||||
|
@ -552,7 +553,7 @@ attribute to expressions you often need to enable
|
||||||
_Clippy_.
|
_Clippy_.
|
||||||
|
|
||||||
[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
|
[_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
|
## 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
|
[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
|
[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
|
[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
|
[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
|
[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
|
[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
|
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.
|
lint, and it will be in `mod.rs` if you defined a type-specific lint.
|
||||||
|
|
||||||
The macro looks something like this:
|
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
|
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`
|
## 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_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_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_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_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_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)
|
* [`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)
|
* [`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)
|
* [`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)
|
* [`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_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)
|
* [`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)
|
* [`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_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)
|
* [`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_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)
|
* [`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)
|
* [`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
|
# begin autogenerated version
|
||||||
version = "0.1.86"
|
version = "0.1.86"
|
||||||
# end autogenerated version
|
# end autogenerated version
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# 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_pattern_char_comparison,
|
||||||
manual_range_contains,
|
manual_range_contains,
|
||||||
manual_rem_euclid,
|
manual_rem_euclid,
|
||||||
|
manual_repeat_n,
|
||||||
manual_retain,
|
manual_retain,
|
||||||
manual_split_once,
|
manual_split_once,
|
||||||
manual_str_repeat,
|
manual_str_repeat,
|
||||||
|
@ -631,11 +632,13 @@ define_Conf! {
|
||||||
mem_replace_with_default,
|
mem_replace_with_default,
|
||||||
missing_const_for_fn,
|
missing_const_for_fn,
|
||||||
needless_borrow,
|
needless_borrow,
|
||||||
|
non_std_lazy_statics,
|
||||||
option_as_ref_deref,
|
option_as_ref_deref,
|
||||||
option_map_unwrap_or,
|
option_map_unwrap_or,
|
||||||
ptr_as_ptr,
|
ptr_as_ptr,
|
||||||
redundant_field_names,
|
redundant_field_names,
|
||||||
redundant_static_lifetimes,
|
redundant_static_lifetimes,
|
||||||
|
same_item_push,
|
||||||
seek_from_current,
|
seek_from_current,
|
||||||
seek_rewind,
|
seek_rewind,
|
||||||
transmute_ptr_to_ref,
|
transmute_ptr_to_ref,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
name = "clippy_dev"
|
name = "clippy_dev"
|
||||||
description = "Clippy developer tooling"
|
description = "Clippy developer tooling"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick = "1.0"
|
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})");
|
eprintln!("error: unable to get the absolute path of rustc ({err})");
|
||||||
return Err(());
|
return Err(());
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = path.join("compiler");
|
let path = path.join("compiler");
|
||||||
|
|
|
@ -842,7 +842,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
|
||||||
Ok(file) => drop(file),
|
Ok(file) => drop(file),
|
||||||
Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
|
Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
|
||||||
Err(e) => panic_file(e, new_name, "create"),
|
Err(e) => panic_file(e, new_name, "create"),
|
||||||
};
|
}
|
||||||
match fs::rename(old_name, new_name) {
|
match fs::rename(old_name, new_name) {
|
||||||
Ok(()) => true,
|
Ok(()) => true,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_dummy" # rename to clippy before publishing
|
name = "clippy_dummy" # rename to clippy before publishing
|
||||||
version = "0.0.303"
|
version = "0.0.303"
|
||||||
edition = "2018"
|
edition = "2024"
|
||||||
readme = "crates-readme.md"
|
readme = "crates-readme.md"
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust."
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust."
|
||||||
build = 'build.rs'
|
build = 'build.rs'
|
||||||
|
|
|
@ -8,7 +8,7 @@ repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
keywords = ["clippy", "lint", "plugin"]
|
keywords = ["clippy", "lint", "plugin"]
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
arrayvec = { version = "0.7", default-features = false }
|
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.
|
#[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
|
||||||
use SourceItemOrderingTraitAssocItemKind::*;
|
use SourceItemOrderingTraitAssocItemKind::*;
|
||||||
match value {
|
match value {
|
||||||
AssocItemKind::Const { .. } => Const,
|
AssocItemKind::Const => Const,
|
||||||
AssocItemKind::Type { .. } => Type,
|
AssocItemKind::Type => Type,
|
||||||
AssocItemKind::Fn { .. } => Fn,
|
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
|
// 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.
|
// be sure the argument to clone_from will be a reference.
|
||||||
arg_sugg = arg_sugg.addr();
|
arg_sugg = arg_sugg.addr();
|
||||||
};
|
}
|
||||||
|
|
||||||
format!("{receiver_sugg}.clone_from({arg_sugg})")
|
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);
|
outer_attr_kind.insert(kind);
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,15 @@ declare_clippy_lint! {
|
||||||
/// [dependencies]
|
/// [dependencies]
|
||||||
/// regex = "*"
|
/// 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"]
|
#[clippy::version = "1.32.0"]
|
||||||
pub WILDCARD_DEPENDENCIES,
|
pub WILDCARD_DEPENDENCIES,
|
||||||
cargo,
|
cargo,
|
||||||
|
|
|
@ -84,6 +84,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
|
||||||
diag
|
diag
|
||||||
.note("`usize` and `isize` may be as small as 16 bits on some platforms")
|
.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");
|
.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),
|
// - uncertain if there are any uncertain values (because they could be negative or positive),
|
||||||
Sign::Uncertain => return Sign::Uncertain,
|
Sign::Uncertain => return Sign::Uncertain,
|
||||||
Sign::ZeroOrPositive => (),
|
Sign::ZeroOrPositive => (),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A mul/div is:
|
// 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),
|
// - uncertain if there are any uncertain values (because they could be negative or positive),
|
||||||
Sign::Uncertain => return Sign::Uncertain,
|
Sign::Uncertain => return Sign::Uncertain,
|
||||||
Sign::ZeroOrPositive => positive_count += 1,
|
Sign::ZeroOrPositive => positive_count += 1,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A sum is:
|
// A sum is:
|
||||||
|
|
|
@ -273,7 +273,7 @@ fn get_types_from_cast<'a>(
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
|
||||||
crate::doc::DOC_MARKDOWN_INFO,
|
crate::doc::DOC_MARKDOWN_INFO,
|
||||||
crate::doc::DOC_NESTED_REFDEFS_INFO,
|
crate::doc::DOC_NESTED_REFDEFS_INFO,
|
||||||
|
crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO,
|
||||||
crate::doc::EMPTY_DOCS_INFO,
|
crate::doc::EMPTY_DOCS_INFO,
|
||||||
crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||||
crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_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::INFALLIBLE_DESTRUCTURING_MATCH_INFO,
|
||||||
crate::matches::MANUAL_FILTER_INFO,
|
crate::matches::MANUAL_FILTER_INFO,
|
||||||
crate::matches::MANUAL_MAP_INFO,
|
crate::matches::MANUAL_MAP_INFO,
|
||||||
|
crate::matches::MANUAL_OK_ERR_INFO,
|
||||||
crate::matches::MANUAL_UNWRAP_OR_INFO,
|
crate::matches::MANUAL_UNWRAP_OR_INFO,
|
||||||
crate::matches::MATCH_AS_REF_INFO,
|
crate::matches::MATCH_AS_REF_INFO,
|
||||||
crate::matches::MATCH_BOOL_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_IS_VARIANT_AND_INFO,
|
||||||
crate::methods::MANUAL_NEXT_BACK_INFO,
|
crate::methods::MANUAL_NEXT_BACK_INFO,
|
||||||
crate::methods::MANUAL_OK_OR_INFO,
|
crate::methods::MANUAL_OK_OR_INFO,
|
||||||
|
crate::methods::MANUAL_REPEAT_N_INFO,
|
||||||
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
|
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
|
||||||
crate::methods::MANUAL_SPLIT_ONCE_INFO,
|
crate::methods::MANUAL_SPLIT_ONCE_INFO,
|
||||||
crate::methods::MANUAL_STR_REPEAT_INFO,
|
crate::methods::MANUAL_STR_REPEAT_INFO,
|
||||||
|
@ -466,6 +469,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
|
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
|
||||||
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
|
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
|
||||||
crate::methods::SKIP_WHILE_NEXT_INFO,
|
crate::methods::SKIP_WHILE_NEXT_INFO,
|
||||||
|
crate::methods::SLICED_STRING_AS_BYTES_INFO,
|
||||||
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
||||||
crate::methods::STRING_EXTEND_CHARS_INFO,
|
crate::methods::STRING_EXTEND_CHARS_INFO,
|
||||||
crate::methods::STRING_LIT_CHARS_ANY_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_OR_DEFAULT_INFO,
|
||||||
crate::methods::UNWRAP_USED_INFO,
|
crate::methods::UNWRAP_USED_INFO,
|
||||||
crate::methods::USELESS_ASREF_INFO,
|
crate::methods::USELESS_ASREF_INFO,
|
||||||
|
crate::methods::USELESS_NONZERO_NEW_UNCHECKED_INFO,
|
||||||
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
|
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
|
||||||
crate::methods::VERBOSE_FILE_READS_INFO,
|
crate::methods::VERBOSE_FILE_READS_INFO,
|
||||||
crate::methods::WAKER_CLONE_WAKE_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_expressive_names::SIMILAR_NAMES_INFO,
|
||||||
crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_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_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::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO,
|
||||||
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
|
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
|
||||||
crate::octal_escapes::OCTAL_ESCAPES_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_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
|
||||||
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
|
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
|
||||||
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_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_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
|
||||||
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
|
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
|
||||||
|
crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO,
|
||||||
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
|
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
|
||||||
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
|
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
|
||||||
crate::unused_async::UNUSED_ASYNC_INFO,
|
crate::unused_async::UNUSED_ASYNC_INFO,
|
||||||
|
|
|
@ -80,6 +80,6 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
|
||||||
String::new(),
|
String::new(),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,19 +223,17 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
|
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
|
||||||
match pat.kind {
|
if let PatKind::Expr(&PatExpr {
|
||||||
PatKind::Expr(&PatExpr {
|
|
||||||
hir_id,
|
hir_id,
|
||||||
kind: PatExprKind::Lit { lit, .. },
|
kind: PatExprKind::Lit { lit, .. },
|
||||||
..
|
..
|
||||||
}) => {
|
}) = pat.kind
|
||||||
|
{
|
||||||
let ty = self.cx.typeck_results().node_type(hir_id);
|
let ty = self.cx.typeck_results().node_type(hir_id);
|
||||||
self.check_lit(lit, ty, hir_id);
|
self.check_lit(lit, ty, hir_id);
|
||||||
return;
|
return;
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
}
|
||||||
walk_pat(self, pat)
|
walk_pat(self, pat);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
|
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
|
||||||
|
|
|
@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||||
deref_count += 1;
|
deref_count += 1;
|
||||||
},
|
},
|
||||||
None => break None,
|
None => break None,
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let use_node = use_cx.use_node(cx);
|
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 itertools::Itertools;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span};
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::ops::Range;
|
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 {
|
fn map_container_to_text(c: &super::Container) -> &'static str {
|
||||||
match c {
|
match c {
|
||||||
|
@ -28,55 +29,34 @@ pub(super) fn check(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blockquote
|
||||||
let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
|
let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
|
||||||
let blockquote_level = containers
|
let blockquote_level = containers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|c| matches!(c, super::Container::Blockquote))
|
.filter(|c| matches!(c, super::Container::Blockquote))
|
||||||
.count();
|
.count();
|
||||||
let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count();
|
if ccount < blockquote_level {
|
||||||
let list_indentation = containers
|
span_lint_and_then(
|
||||||
.iter()
|
cx,
|
||||||
.map(|c| {
|
DOC_LAZY_CONTINUATION,
|
||||||
if let super::Container::List(indent) = c {
|
span,
|
||||||
*indent
|
"doc quote line without `>` marker",
|
||||||
} else {
|
|diag| {
|
||||||
0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.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 {
|
|
||||||
// simpler suggestion style for indentation
|
|
||||||
let indent = list_indentation - lcount;
|
|
||||||
diag.span_suggestion_verbose(
|
|
||||||
span.shrink_to_hi(),
|
|
||||||
"indent this line",
|
|
||||||
std::iter::repeat(" ").take(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 doc_start_range = &doc[range];
|
||||||
let mut suggested = String::new();
|
let mut suggested = String::new();
|
||||||
for c in containers {
|
for c in containers {
|
||||||
let text = map_container_to_text(c);
|
let text = map_container_to_text(c);
|
||||||
if doc_start_range.starts_with(text) {
|
if doc_start_range.starts_with(text) {
|
||||||
doc_start_range = &doc_start_range[text.len()..];
|
doc_start_range = &doc_start_range[text.len()..];
|
||||||
span = span
|
span = span.with_lo(
|
||||||
.with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")));
|
span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")),
|
||||||
|
);
|
||||||
} else if matches!(c, super::Container::Blockquote)
|
} else if matches!(c, super::Container::Blockquote)
|
||||||
&& let Some(i) = doc_start_range.find('>')
|
&& let Some(i) = doc_start_range.find('>')
|
||||||
{
|
{
|
||||||
doc_start_range = &doc_start_range[i + 1..];
|
doc_start_range = &doc_start_range[i + 1..];
|
||||||
span =
|
span = span
|
||||||
span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
|
.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
|
||||||
} else {
|
} else {
|
||||||
suggested.push_str(text);
|
suggested.push_str(text);
|
||||||
}
|
}
|
||||||
|
@ -88,6 +68,58 @@ pub(super) fn check(
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
|
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| {
|
||||||
|
if let super::Container::List(indent) = c {
|
||||||
|
*indent
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
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 - leading_spaces;
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
span.shrink_to_hi(),
|
||||||
|
"indent this line",
|
||||||
|
std::iter::repeat_n(" ", indent).join(""),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
diag.help("if this is supposed to be its own paragraph, add a blank line");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Ordering::Greater => {
|
||||||
|
let sugg = std::iter::repeat_n(" ", list_indentation).join("");
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
DOC_OVERINDENTED_LIST_ITEMS,
|
||||||
|
span,
|
||||||
|
"doc list item overindented",
|
||||||
|
format!("try using `{sugg}` ({list_indentation} spaces)"),
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Ordering::Equal => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -428,6 +428,39 @@ declare_clippy_lint! {
|
||||||
"require every line of a paragraph to be indented and marked"
|
"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! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks if the first paragraph in the documentation of items listed in the module page is too long.
|
/// 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,
|
SUSPICIOUS_DOC_COMMENTS,
|
||||||
EMPTY_DOCS,
|
EMPTY_DOCS,
|
||||||
DOC_LAZY_CONTINUATION,
|
DOC_LAZY_CONTINUATION,
|
||||||
|
DOC_OVERINDENTED_LIST_ITEMS,
|
||||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
|
||||||
var.span,
|
var.span,
|
||||||
"C-like enum variant discriminant is not portable to 32-bit targets",
|
"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::source::snippet_opt;
|
||||||
use clippy_utils::ty::get_type_diagnostic_name;
|
use clippy_utils::ty::get_type_diagnostic_name;
|
||||||
use clippy_utils::usage::{local_used_after_expr, local_used_in};
|
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_errors::Applicability;
|
||||||
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind};
|
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
@ -101,8 +103,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
|
||||||
};
|
};
|
||||||
|
|
||||||
if body.value.span.from_expansion() {
|
if body.value.span.from_expansion() {
|
||||||
if body.params.is_empty() {
|
if body.params.is_empty()
|
||||||
if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
|
&& 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`
|
// replace `|| vec![]` with `Vec::new`
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
|
@ -110,11 +114,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
|
||||||
expr.span,
|
expr.span,
|
||||||
"redundant closure",
|
"redundant closure",
|
||||||
"replace the closure with `Vec::new`",
|
"replace the closure with `Vec::new`",
|
||||||
"std::vec::Vec::new".into(),
|
format!("{vec_crate}::vec::Vec::new"),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// skip `foo(|| macro!())`
|
// skip `foo(|| macro!())`
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
self.emit_sugg(spans, msg, help);
|
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 {
|
for param in generics.params {
|
||||||
if param.is_impl_trait() {
|
if param.is_impl_trait() {
|
||||||
report(cx, param, generics);
|
report(cx, param, generics);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@ fn check_needless_must_use(
|
||||||
&& !is_must_use_ty(cx, future_ty)
|
&& !is_must_use_ty(cx, future_ty)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span_lint_and_help(
|
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);
|
let ecx = ConstEvalCtxt::new(cx);
|
||||||
if let Some(Constant::Int(c)) = ecx.eval(r) {
|
if let Some(Constant::Int(c)) = ecx.eval(r) {
|
||||||
return Some((c, op.node, l));
|
return Some((c, op.node, l));
|
||||||
};
|
}
|
||||||
if let Some(Constant::Int(c)) = ecx.eval(l) {
|
if let Some(Constant::Int(c)) = ecx.eval(l) {
|
||||||
return Some((c, invert_op(op.node)?, r));
|
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() {
|
if cx.typeck_results().expr_ty(cond_left).is_signed() {
|
||||||
} else {
|
} else {
|
||||||
print_lint_and_sugg(cx, var_name, expr);
|
print_lint_and_sugg(cx, var_name, expr);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprKind::Path(QPath::TypeRelative(_, name)) => {
|
ExprKind::Path(QPath::TypeRelative(_, name)) => {
|
||||||
|
|
|
@ -65,6 +65,6 @@ impl LateLintPass<'_> for IterOverHashType {
|
||||||
expr.span,
|
expr.span,
|
||||||
"iteration over unordered hash-based type",
|
"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())),
|
Some(ty.span.with_lo(local.pat.span.hi())),
|
||||||
"remove the explicit type `_` declaration",
|
"remove the explicit type `_` declaration",
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,6 +279,7 @@ mod non_copy_const;
|
||||||
mod non_expressive_names;
|
mod non_expressive_names;
|
||||||
mod non_octal_unix_permissions;
|
mod non_octal_unix_permissions;
|
||||||
mod non_send_fields_in_send_ty;
|
mod non_send_fields_in_send_ty;
|
||||||
|
mod non_std_lazy_statics;
|
||||||
mod non_zero_suggestions;
|
mod non_zero_suggestions;
|
||||||
mod nonstandard_macro_braces;
|
mod nonstandard_macro_braces;
|
||||||
mod octal_escapes;
|
mod octal_escapes;
|
||||||
|
@ -372,8 +373,10 @@ mod unnecessary_literal_bound;
|
||||||
mod unnecessary_map_on_constructor;
|
mod unnecessary_map_on_constructor;
|
||||||
mod unnecessary_owned_empty_strings;
|
mod unnecessary_owned_empty_strings;
|
||||||
mod unnecessary_self_imports;
|
mod unnecessary_self_imports;
|
||||||
|
mod unnecessary_semicolon;
|
||||||
mod unnecessary_struct_initialization;
|
mod unnecessary_struct_initialization;
|
||||||
mod unnecessary_wraps;
|
mod unnecessary_wraps;
|
||||||
|
mod unneeded_struct_pattern;
|
||||||
mod unnested_or_patterns;
|
mod unnested_or_patterns;
|
||||||
mod unsafe_removed_from_name;
|
mod unsafe_removed_from_name;
|
||||||
mod unused_async;
|
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(|_| Box::new(unit_types::UnitTypes));
|
||||||
store.register_late_pass(move |_| Box::new(loops::Loops::new(conf)));
|
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::<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(entry::HashMapPass));
|
||||||
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
|
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
|
||||||
store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
|
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(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
|
||||||
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
|
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(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`
|
// 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::diagnostics::{span_lint, span_lint_and_then};
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::trait_ref_of_method;
|
use clippy_utils::trait_ref_of_method;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_ast::visit::{try_visit, walk_list};
|
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::map::Map;
|
||||||
use rustc_middle::hir::nested_filter as middle_nested_filter;
|
use rustc_middle::hir::nested_filter as middle_nested_filter;
|
||||||
use rustc_middle::lint::in_external_macro;
|
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::Span;
|
||||||
use rustc_span::def_id::LocalDefId;
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::symbol::{Ident, kw};
|
use rustc_span::symbol::{Ident, kw};
|
||||||
|
@ -91,7 +93,19 @@ declare_clippy_lint! {
|
||||||
"unused lifetimes in function definitions"
|
"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 {
|
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||||
|
@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||||
..
|
..
|
||||||
} = item.kind
|
} = 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 {
|
} else if let ItemKind::Impl(impl_) = item.kind {
|
||||||
if !item.span.from_expansion() {
|
if !item.span.from_expansion() {
|
||||||
report_extra_impl_lifetimes(cx, impl_);
|
report_extra_impl_lifetimes(cx, impl_);
|
||||||
|
@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||||
item.generics,
|
item.generics,
|
||||||
item.span,
|
item.span,
|
||||||
report_extra_lifetimes,
|
report_extra_lifetimes,
|
||||||
|
&self.msrv,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||||
TraitFn::Required(sig) => (None, Some(sig)),
|
TraitFn::Required(sig) => (None, Some(sig)),
|
||||||
TraitFn::Provided(id) => (Some(id), None),
|
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>(
|
fn check_fn_inner<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
sig: &'tcx FnSig<'_>,
|
sig: &'tcx FnSig<'_>,
|
||||||
|
@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>(
|
||||||
generics: &'tcx Generics<'_>,
|
generics: &'tcx Generics<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
report_extra_lifetimes: bool,
|
report_extra_lifetimes: bool,
|
||||||
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) {
|
if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) {
|
||||||
return;
|
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)) {
|
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -216,6 +235,7 @@ fn could_use_elision<'tcx>(
|
||||||
body: Option<BodyId>,
|
body: Option<BodyId>,
|
||||||
trait_sig: Option<&[Ident]>,
|
trait_sig: Option<&[Ident]>,
|
||||||
named_generics: &'tcx [GenericParam<'_>],
|
named_generics: &'tcx [GenericParam<'_>],
|
||||||
|
msrv: &Msrv,
|
||||||
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
|
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
|
||||||
// There are two scenarios where elision works:
|
// There are two scenarios where elision works:
|
||||||
// * no output references, all input references have different LT
|
// * 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 input_lts = input_visitor.lts;
|
||||||
let output_lts = output_visitor.lts;
|
let output_lts = output_visitor.lts;
|
||||||
|
|
||||||
if let Some(trait_sig) = trait_sig {
|
if let Some(trait_sig) = trait_sig
|
||||||
if explicit_self_type(cx, func, trait_sig.first().copied()) {
|
&& non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv)
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(body_id) = body {
|
if let Some(body_id) = body {
|
||||||
let body = cx.tcx.hir().body(body_id);
|
let body = cx.tcx.hir().body(body_id);
|
||||||
|
|
||||||
let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefI
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// elision doesn't work for explicit self types, see rust-lang/rust#69064
|
// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064
|
||||||
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
|
fn non_elidable_self_type<'tcx>(
|
||||||
if let Some(ident) = ident
|
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
|
&& ident.name == kw::SelfLower
|
||||||
&& !func.implicit_self.has_implicit_self()
|
&& !func.implicit_self.has_implicit_self()
|
||||||
&& let Some(self_ty) = func.inputs.first()
|
&& let Some(self_ty) = func.inputs.first()
|
||||||
|
@ -488,11 +514,13 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
struct Usage {
|
struct Usage {
|
||||||
lifetime: Lifetime,
|
lifetime: Lifetime,
|
||||||
in_where_predicate: bool,
|
in_where_predicate: bool,
|
||||||
in_bounded_ty: bool,
|
in_bounded_ty: bool,
|
||||||
in_generics_arg: bool,
|
in_generics_arg: bool,
|
||||||
|
lifetime_elision_impossible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LifetimeChecker<'cx, 'tcx, F> {
|
struct LifetimeChecker<'cx, 'tcx, F> {
|
||||||
|
@ -501,6 +529,7 @@ struct LifetimeChecker<'cx, 'tcx, F> {
|
||||||
where_predicate_depth: usize,
|
where_predicate_depth: usize,
|
||||||
bounded_ty_depth: usize,
|
bounded_ty_depth: usize,
|
||||||
generic_args_depth: usize,
|
generic_args_depth: usize,
|
||||||
|
lifetime_elision_impossible: bool,
|
||||||
phantom: std::marker::PhantomData<F>,
|
phantom: std::marker::PhantomData<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,6 +554,7 @@ where
|
||||||
where_predicate_depth: 0,
|
where_predicate_depth: 0,
|
||||||
bounded_ty_depth: 0,
|
bounded_ty_depth: 0,
|
||||||
generic_args_depth: 0,
|
generic_args_depth: 0,
|
||||||
|
lifetime_elision_impossible: false,
|
||||||
phantom: std::marker::PhantomData,
|
phantom: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -566,6 +596,7 @@ where
|
||||||
in_where_predicate: self.where_predicate_depth != 0,
|
in_where_predicate: self.where_predicate_depth != 0,
|
||||||
in_bounded_ty: self.bounded_ty_depth != 0,
|
in_bounded_ty: self.bounded_ty_depth != 0,
|
||||||
in_generics_arg: self.generic_args_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;
|
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 {
|
fn nested_visit_map(&mut self) -> Self::Map {
|
||||||
self.cx.tcx.hir()
|
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<'_>) {
|
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);
|
let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, generics);
|
||||||
|
|
||||||
|
@ -662,6 +726,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
|
||||||
Usage {
|
Usage {
|
||||||
lifetime,
|
lifetime,
|
||||||
in_where_predicate: false,
|
in_where_predicate: false,
|
||||||
|
lifetime_elision_impossible: false,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
] = usages.as_slice()
|
] = usages.as_slice()
|
||||||
|
@ -719,7 +784,7 @@ fn report_elidable_lifetimes(
|
||||||
|diag| {
|
|diag| {
|
||||||
if !include_suggestions {
|
if !include_suggestions {
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
|
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
|
||||||
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
|
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
|
||||||
|
|
|
@ -251,7 +251,7 @@ impl LiteralDigitGrouping {
|
||||||
);
|
);
|
||||||
if !consistent {
|
if !consistent {
|
||||||
return Err(WarningType::InconsistentDigitGrouping);
|
return Err(WarningType::InconsistentDigitGrouping);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -29,9 +29,9 @@ declare_clippy_lint! {
|
||||||
/// let y = "hello";
|
/// let y = "hello";
|
||||||
/// x.expect(&format!("{y:?}"));
|
/// x.expect(&format!("{y:?}"));
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.83.0"]
|
#[clippy::version = "1.85.0"]
|
||||||
pub LITERAL_STRING_WITH_FORMATTING_ARGS,
|
pub LITERAL_STRING_WITH_FORMATTING_ARGS,
|
||||||
suspicious,
|
nursery,
|
||||||
"Checks if string literals have formatting arguments"
|
"Checks if string literals have formatting arguments"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -830,7 +830,7 @@ impl Loops {
|
||||||
for_kv_map::check(cx, pat, arg, body);
|
for_kv_map::check(cx, pat, arg, body);
|
||||||
mut_range_bound::check(cx, arg, body);
|
mut_range_bound::check(cx, arg, body);
|
||||||
single_element_loop::check(cx, pat, arg, body, expr);
|
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_flatten::check(cx, pat, arg, body, span);
|
||||||
manual_find::check(cx, pat, arg, body, span, expr);
|
manual_find::check(cx, pat, arg, body, span, expr);
|
||||||
unused_enumerate_index::check(cx, pat, arg, body);
|
unused_enumerate_index::check(cx, pat, arg, body);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use super::SAME_ITEM_PUSH;
|
use super::SAME_ITEM_PUSH;
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::path_to_local;
|
use clippy_utils::msrvs::Msrv;
|
||||||
use clippy_utils::source::snippet_with_context;
|
use clippy_utils::source::snippet_with_context;
|
||||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
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_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
|
@ -19,19 +20,30 @@ pub(super) fn check<'tcx>(
|
||||||
_: &'tcx Expr<'_>,
|
_: &'tcx Expr<'_>,
|
||||||
body: &'tcx Expr<'_>,
|
body: &'tcx Expr<'_>,
|
||||||
_: &'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 mut app = Applicability::Unspecified;
|
||||||
let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0;
|
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;
|
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,
|
cx,
|
||||||
SAME_ITEM_PUSH,
|
SAME_ITEM_PUSH,
|
||||||
vec.span,
|
vec.span,
|
||||||
"it looks like the same item is being pushed into this Vec",
|
"it looks like the same item is being pushed into this `Vec`",
|
||||||
None,
|
|diag| {
|
||||||
format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"),
|
diag.help(format!("consider using `vec![{item_str};SIZE]`"))
|
||||||
|
.help(secondary_help);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,11 +79,11 @@ pub(super) fn check<'tcx>(
|
||||||
{
|
{
|
||||||
match init.kind {
|
match init.kind {
|
||||||
// immutable bindings that are initialized with literal
|
// 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
|
// immutable bindings that are initialized with constant
|
||||||
ExprKind::Path(ref path) => {
|
ExprKind::Path(ref path) => {
|
||||||
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
|
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
|
// 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_ast::ast::LitKind;
|
||||||
use rustc_data_structures::packed::Pu128;
|
use rustc_data_structures::packed::Pu128;
|
||||||
use rustc_errors::Applicability;
|
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_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::impl_lint_pass;
|
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 QPath::Resolved(_, count_func_path) = count_func_qpath
|
||||||
&& let Some(segment_zero) = count_func_path.segments.first()
|
&& let Some(segment_zero) = count_func_path.segments.first()
|
||||||
&& let Some(args) = segment_zero.args
|
&& 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()
|
&& 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)
|
&& 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);
|
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);
|
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 {
|
fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||||
match expr_ty.peel_refs().kind() {
|
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::LitKind::{Byte, Char};
|
||||||
use rustc_ast::ast::RangeLimits;
|
use rustc_ast::ast::RangeLimits;
|
||||||
use rustc_errors::Applicability;
|
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_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::impl_lint_pass;
|
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 {
|
fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange {
|
||||||
if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind
|
if let PatExprKind::Lit {
|
||||||
&& let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind
|
lit: start_lit,
|
||||||
|
negated: false,
|
||||||
|
} = &start.kind
|
||||||
|
&& let PatExprKind::Lit {
|
||||||
|
lit: end_lit,
|
||||||
|
negated: false,
|
||||||
|
} = &end.kind
|
||||||
{
|
{
|
||||||
check_lit_range(start_lit, end_lit)
|
check_lit_range(start_lit, end_lit)
|
||||||
} else {
|
} 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);
|
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(..)
|
PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
let ty = typeck_results.pat_ty(pat);
|
let ty = typeck_results.pat_ty(pat);
|
||||||
// Option and Result are allowed, everything else isn't.
|
// 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)) {
|
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,
|
_ => return,
|
||||||
};
|
}
|
||||||
|
|
||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0;
|
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);
|
let target_res = cx.qpath_res(target_path, target_arg.hir_id);
|
||||||
if target_res == Res::Err {
|
if target_res == Res::Err {
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
if let Res::Local(hir_id) = target_res
|
if let Res::Local(hir_id) = target_res
|
||||||
&& let Some(used_mutably) = mutated_variables(then, cx)
|
&& 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
|
needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the
|
||||||
* cond */
|
* cond */
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
None
|
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() {
|
if block.stmts.is_empty() {
|
||||||
return block.expr;
|
return block.expr;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
None
|
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)
|
&& is_res_lang_ctor(cx, path_res(cx, callee), OptionSome)
|
||||||
&& path_to_local_id(arg, target);
|
&& path_to_local_id(arg, target);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
|
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
|
||||||
return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone);
|
return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone);
|
||||||
};
|
}
|
||||||
false
|
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,
|
None => return None,
|
||||||
};
|
}
|
||||||
|
|
||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::is_unit_expr;
|
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 clippy_utils::sugg::Sugg;
|
||||||
use rustc_ast::LitKind;
|
use rustc_ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -17,17 +17,28 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
|
||||||
cx,
|
cx,
|
||||||
MATCH_BOOL,
|
MATCH_BOOL,
|
||||||
expr.span,
|
expr.span,
|
||||||
"you seem to be trying to match on a boolean expression",
|
"`match` on a boolean expression",
|
||||||
move |diag| {
|
move |diag| {
|
||||||
if arms.len() == 2 {
|
if arms.len() == 2 {
|
||||||
// no guards
|
let mut app = Applicability::MachineApplicable;
|
||||||
let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
|
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 {
|
if let PatExprKind::Lit { lit, .. } = arm_bool.kind {
|
||||||
match lit.node {
|
match &lit.node {
|
||||||
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
|
LitKind::Bool(true) => Some(test),
|
||||||
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),
|
LitKind::Bool(false) => Some(!test),
|
||||||
_ => None,
|
_ => 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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -35,39 +46,31 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((true_expr, false_expr)) = exprs {
|
if let Some(test_sugg) = test_sugg {
|
||||||
let mut app = Applicability::HasPlaceholders;
|
|
||||||
let ctxt = expr.span.ctxt();
|
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)) {
|
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
|
||||||
(false, false) => Some(format!(
|
(false, false) => Some(format!(
|
||||||
"if {} {} else {}",
|
"if {} {} else {}",
|
||||||
snippet(cx, scrutinee.span, "b"),
|
test_sugg,
|
||||||
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
|
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
|
||||||
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||||
)),
|
)),
|
||||||
(false, true) => Some(format!(
|
(false, true) => Some(format!(
|
||||||
"if {} {}",
|
"if {} {}",
|
||||||
snippet(cx, scrutinee.span, "b"),
|
test_sugg,
|
||||||
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
|
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||||
)),
|
)),
|
||||||
(true, false) => {
|
(true, false) => Some(format!(
|
||||||
let test = Sugg::hir(cx, scrutinee, "..");
|
|
||||||
Some(format!(
|
|
||||||
"if {} {}",
|
"if {} {}",
|
||||||
!test,
|
!test_sugg,
|
||||||
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
|
||||||
))
|
)),
|
||||||
},
|
|
||||||
(true, true) => None,
|
(true, true) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(sugg) = sugg {
|
if let Some(sugg) = sugg {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app);
|
||||||
expr.span,
|
|
||||||
"consider using an `if`/`else` expression",
|
|
||||||
sugg,
|
|
||||||
Applicability::HasPlaceholders,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ where
|
||||||
if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
|
if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
|
||||||
ex_new = ex_inner;
|
ex_new = ex_inner;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
MATCH_LIKE_MATCHES_MACRO,
|
MATCH_LIKE_MATCHES_MACRO,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item;
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
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_lint::LateContext;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
|
@ -170,7 +170,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CommonPrefixSearcher<'a> {
|
enum CommonPrefixSearcher<'a> {
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod collapsible_match;
|
||||||
mod infallible_destructuring_match;
|
mod infallible_destructuring_match;
|
||||||
mod manual_filter;
|
mod manual_filter;
|
||||||
mod manual_map;
|
mod manual_map;
|
||||||
|
mod manual_ok_err;
|
||||||
mod manual_unwrap_or;
|
mod manual_unwrap_or;
|
||||||
mod manual_utils;
|
mod manual_utils;
|
||||||
mod match_as_ref;
|
mod match_as_ref;
|
||||||
|
@ -972,6 +973,40 @@ declare_clippy_lint! {
|
||||||
"checks for unnecessary guards in match expressions"
|
"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 {
|
pub struct Matches {
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
infallible_destructuring_match_linted: bool,
|
infallible_destructuring_match_linted: bool,
|
||||||
|
@ -1013,6 +1048,7 @@ impl_lint_pass!(Matches => [
|
||||||
MANUAL_MAP,
|
MANUAL_MAP,
|
||||||
MANUAL_FILTER,
|
MANUAL_FILTER,
|
||||||
REDUNDANT_GUARDS,
|
REDUNDANT_GUARDS,
|
||||||
|
MANUAL_OK_ERR,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
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_unwrap_or::check_match(cx, expr, ex, arms);
|
||||||
manual_map::check_match(cx, expr, ex, arms);
|
manual_map::check_match(cx, expr, ex, arms);
|
||||||
manual_filter::check_match(cx, ex, arms, expr);
|
manual_filter::check_match(cx, ex, arms, expr);
|
||||||
|
manual_ok_err::check_match(cx, expr, ex, arms);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.infallible_destructuring_match_linted {
|
if self.infallible_destructuring_match_linted {
|
||||||
|
@ -1134,6 +1171,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||||
if_let.if_then,
|
if_let.if_then,
|
||||||
else_expr,
|
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(
|
redundant_pattern_match::check_if_let(
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::ops::ControlFlow;
|
||||||
|
|
||||||
use crate::FxHashSet;
|
use crate::FxHashSet;
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
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::ty::{for_each_top_level_late_bound_region, is_copy};
|
||||||
use clippy_utils::{get_attr, is_lint_allowed};
|
use clippy_utils::{get_attr, is_lint_allowed};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -152,7 +152,7 @@ fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
|
||||||
diag.multipart_suggestion(
|
diag.multipart_suggestion(
|
||||||
suggestion_message,
|
suggestion_message,
|
||||||
vec![
|
vec![
|
||||||
(expr.span.shrink_to_lo(), replacement),
|
(first_line_of_span(cx, expr.span).shrink_to_lo(), replacement),
|
||||||
(found.found_span, scrutinee_replacement),
|
(found.found_span, scrutinee_replacement),
|
||||||
],
|
],
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
|
@ -441,8 +441,9 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> {
|
||||||
let parent_expr_before = self.parent_expr.replace(ex);
|
let parent_expr_before = self.parent_expr.replace(ex);
|
||||||
|
|
||||||
match ex.kind {
|
match ex.kind {
|
||||||
// Skip blocks because values in blocks will be dropped as usual.
|
// Skip blocks because values in blocks will be dropped as usual, and await
|
||||||
ExprKind::Block(..) => (),
|
// desugaring because temporary insides the future will have been dropped.
|
||||||
|
ExprKind::Block(..) | ExprKind::Match(_, _, MatchSource::AwaitDesugar) => (),
|
||||||
_ => walk_expr(self, ex),
|
_ => walk_expr(self, ex),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
||||||
err_ty = ty;
|
err_ty = ty;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) {
|
||||||
&& has_non_exhaustive_attr(cx.tcx, *adt_def)
|
&& has_non_exhaustive_attr(cx.tcx, *adt_def)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
if let PatKind::Or(fields) = arm.pat.kind {
|
if let PatKind::Or(fields) = arm.pat.kind {
|
||||||
// look for multiple fields in this arm that contains at least one Wild pattern
|
// look for multiple fields in this arm that contains at least one Wild pattern
|
||||||
|
|
|
@ -62,5 +62,5 @@ pub(super) fn check<'tcx>(
|
||||||
),
|
),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,5 +32,5 @@ pub(super) fn check<'tcx>(
|
||||||
),
|
),
|
||||||
applicability,
|
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()"),
|
format!("{receiver}.as_bytes().get({n}).copied()"),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span,
|
||||||
// &T where T: Copy
|
// &T where T: Copy
|
||||||
ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
|
ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
}
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
CLONED_INSTEAD_OF_COPIED,
|
CLONED_INSTEAD_OF_COPIED,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::methods::DRAIN_COLLECT;
|
use crate::methods::DRAIN_COLLECT;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::is_range_full;
|
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::is_type_lang_item;
|
use clippy_utils::ty::is_type_lang_item;
|
||||||
|
use clippy_utils::{is_range_full, std_or_core};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
|
use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re
|
||||||
.then_some("Vec")
|
.then_some("Vec")
|
||||||
.or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
|
.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))
|
.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 recv = snippet(cx, recv.span, "<expr>");
|
||||||
let sugg = if let ty::Ref(..) = recv_ty.kind() {
|
let sugg = if let ty::Ref(..) = recv_ty.kind() {
|
||||||
format!("std::mem::take({recv})")
|
format!("{exec_context}::mem::take({recv})")
|
||||||
} else {
|
} else {
|
||||||
format!("std::mem::take(&mut {recv})")
|
format!("{exec_context}::mem::take(&mut {recv})")
|
||||||
};
|
};
|
||||||
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub(super) fn check(
|
||||||
"expect_err".to_string(),
|
"expect_err".to_string(),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a `Result<T, E>` type, return its data (`T`).
|
/// 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) {
|
if ty.is_str() && can_be_static_str(cx, arg) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
|
||||||
"into_iter()".to_string(),
|
"into_iter()".to_string(),
|
||||||
Applicability::MaybeIncorrect,
|
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::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::sugg::Sugg;
|
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::LitKind;
|
||||||
use rustc_ast::ast::RangeLimits;
|
use rustc_ast::ast::RangeLimits;
|
||||||
use rustc_data_structures::packed::Pu128;
|
use rustc_data_structures::packed::Pu128;
|
||||||
|
@ -75,6 +75,7 @@ pub(super) fn check(
|
||||||
} = body_hir
|
} = body_hir
|
||||||
&& !usage::BindingUsageFinder::are_params_used(cx, body_hir)
|
&& !usage::BindingUsageFinder::are_params_used(cx, body_hir)
|
||||||
&& let Some(count) = extract_count_with_applicability(cx, range, &mut applicability)
|
&& 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 method_to_use_name;
|
||||||
let new_span;
|
let new_span;
|
||||||
|
@ -105,7 +106,7 @@ pub(super) fn check(
|
||||||
let mut parts = vec![
|
let mut parts = vec![
|
||||||
(
|
(
|
||||||
receiver.span.to(method_call_span),
|
receiver.span.to(method_call_span),
|
||||||
format!("std::iter::{method_to_use_name}"),
|
format!("{exec_context}::iter::{method_to_use_name}"),
|
||||||
),
|
),
|
||||||
new_span,
|
new_span,
|
||||||
];
|
];
|
||||||
|
|
|
@ -58,6 +58,7 @@ mod manual_inspect;
|
||||||
mod manual_is_variant_and;
|
mod manual_is_variant_and;
|
||||||
mod manual_next_back;
|
mod manual_next_back;
|
||||||
mod manual_ok_or;
|
mod manual_ok_or;
|
||||||
|
mod manual_repeat_n;
|
||||||
mod manual_saturating_arithmetic;
|
mod manual_saturating_arithmetic;
|
||||||
mod manual_str_repeat;
|
mod manual_str_repeat;
|
||||||
mod manual_try_fold;
|
mod manual_try_fold;
|
||||||
|
@ -101,6 +102,7 @@ mod single_char_add_str;
|
||||||
mod single_char_insert_string;
|
mod single_char_insert_string;
|
||||||
mod single_char_push_string;
|
mod single_char_push_string;
|
||||||
mod skip_while_next;
|
mod skip_while_next;
|
||||||
|
mod sliced_string_as_bytes;
|
||||||
mod stable_sort_primitive;
|
mod stable_sort_primitive;
|
||||||
mod str_split;
|
mod str_split;
|
||||||
mod str_splitn;
|
mod str_splitn;
|
||||||
|
@ -130,6 +132,7 @@ mod unnecessary_to_owned;
|
||||||
mod unused_enumerate_index;
|
mod unused_enumerate_index;
|
||||||
mod unwrap_expect_used;
|
mod unwrap_expect_used;
|
||||||
mod useless_asref;
|
mod useless_asref;
|
||||||
|
mod useless_nonzero_new_unchecked;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod vec_resize_to_zero;
|
mod vec_resize_to_zero;
|
||||||
mod verbose_file_reads;
|
mod verbose_file_reads;
|
||||||
|
@ -2416,14 +2419,14 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### 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?
|
/// ### Why is this bad?
|
||||||
/// This can be written more clearly with `if .. else ..`
|
/// This can be written more clearly with `if .. else ..`
|
||||||
///
|
///
|
||||||
/// ### Limitations
|
/// ### Limitations
|
||||||
/// This lint currently only looks for usages of
|
/// 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.
|
/// to account for similar patterns.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
|
@ -4311,6 +4314,84 @@ declare_clippy_lint! {
|
||||||
"using `Iterator::last` on a `DoubleEndedIterator`"
|
"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 {
|
pub struct Methods {
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
|
@ -4477,6 +4558,9 @@ impl_lint_pass!(Methods => [
|
||||||
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
|
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
|
||||||
UNNECESSARY_MAP_OR,
|
UNNECESSARY_MAP_OR,
|
||||||
DOUBLE_ENDED_ITERATOR_LAST,
|
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.
|
/// 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);
|
from_iter_instead_of_collect::check(cx, expr, args, func);
|
||||||
unnecessary_fallible_conversions::check_function(cx, expr, func);
|
unnecessary_fallible_conversions::check_function(cx, expr, func);
|
||||||
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
|
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, _) => {
|
ExprKind::MethodCall(method_call, receiver, args, _) => {
|
||||||
let method_span = method_call.ident.span;
|
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) {
|
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
|
||||||
redundant_as_str::check(cx, expr, recv, as_str_span, span);
|
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_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||||
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
|
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
|
||||||
|
@ -4924,8 +5010,8 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("is_empty", []) => {
|
("is_empty", []) => {
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some(("as_bytes", prev_recv, [], _, _)) => {
|
Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => {
|
||||||
needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span);
|
needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span);
|
||||||
},
|
},
|
||||||
Some(("as_str", recv, [], as_str_span, _)) => {
|
Some(("as_str", recv, [], as_str_span, _)) => {
|
||||||
redundant_as_str::check(cx, expr, recv, as_str_span, 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);
|
double_ended_iterator_last::check(cx, expr, recv, call_span);
|
||||||
},
|
},
|
||||||
("len", []) => {
|
("len", []) => {
|
||||||
if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
|
if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) {
|
||||||
needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span);
|
needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("lock", []) => {
|
("lock", []) => {
|
||||||
|
@ -5015,7 +5101,7 @@ impl Methods {
|
||||||
option_map_or_none::check(cx, expr, recv, def, map);
|
option_map_or_none::check(cx, expr, recv, def, map);
|
||||||
manual_ok_or::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);
|
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]) => {
|
("map_or_else", [def, map]) => {
|
||||||
result_map_or_else_none::check(cx, expr, recv, 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),
|
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
|
||||||
("take", [arg]) => {
|
("take", [arg]) => {
|
||||||
iter_out_of_bounds::check_take(cx, expr, recv, 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) {
|
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
||||||
iter_overeager_cloned::check(
|
iter_overeager_cloned::check(
|
||||||
cx,
|
cx,
|
||||||
|
@ -5220,8 +5307,8 @@ impl Methods {
|
||||||
Some(("map", m_recv, [m_arg], span, _)) => {
|
Some(("map", m_recv, [m_arg], span, _)) => {
|
||||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
|
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
|
||||||
},
|
},
|
||||||
Some(("then_some", t_recv, [t_arg], _, _)) => {
|
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
|
||||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_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;
|
use super::NEEDLESS_AS_BYTES;
|
||||||
|
|
||||||
pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) {
|
pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, 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();
|
||||||
&& let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs()
|
if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() {
|
||||||
&& (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
|
|
||||||
{
|
|
||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
|
let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
NEEDLESS_AS_BYTES,
|
NEEDLESS_AS_BYTES,
|
||||||
span,
|
span,
|
||||||
"needless call to `as_bytes()`",
|
format!("needless call to `{prev_method}`"),
|
||||||
format!("`{method}()` can be called directly on strings"),
|
format!("`{method}()` can be called directly on strings"),
|
||||||
format!("{sugg}.{method}()"),
|
format!("{sugg}.{method}()"),
|
||||||
app,
|
app,
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use super::OBFUSCATED_IF_ELSE;
|
use super::OBFUSCATED_IF_ELSE;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
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::source::snippet_with_applicability;
|
||||||
|
use clippy_utils::sugg::Sugg;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::ExprKind;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
|
||||||
pub(super) fn check<'tcx>(
|
pub(super) fn check<'tcx>(
|
||||||
|
@ -11,19 +14,30 @@ pub(super) fn check<'tcx>(
|
||||||
then_recv: &'tcx hir::Expr<'_>,
|
then_recv: &'tcx hir::Expr<'_>,
|
||||||
then_arg: &'tcx hir::Expr<'_>,
|
then_arg: &'tcx hir::Expr<'_>,
|
||||||
unwrap_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);
|
let recv_ty = cx.typeck_results().expr_ty(then_recv);
|
||||||
|
|
||||||
if recv_ty.is_bool() {
|
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!(
|
let sugg = format!(
|
||||||
"if {} {{ {} }} else {{ {} }}",
|
"if {} {{ {} }} else {{ {} }}",
|
||||||
snippet_with_applicability(cx, then_recv.span, "..", &mut applicability),
|
Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability),
|
||||||
snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
|
if_then,
|
||||||
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
|
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -31,8 +45,7 @@ pub(super) fn check<'tcx>(
|
||||||
cx,
|
cx,
|
||||||
OBFUSCATED_IF_ELSE,
|
OBFUSCATED_IF_ELSE,
|
||||||
expr.span,
|
expr.span,
|
||||||
"use of `.then_some(..).unwrap_or(..)` can be written \
|
"this method chain can be written more clearly with `if .. else ..`",
|
||||||
more clearly with `if .. else ..`",
|
|
||||||
"try",
|
"try",
|
||||||
sugg,
|
sugg,
|
||||||
applicability,
|
applicability,
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub(super) fn check(
|
||||||
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
|
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
|
||||||
} else {
|
} else {
|
||||||
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
|
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
|
||||||
};
|
}
|
||||||
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
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
|
// skip lint
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// the lint should not be executed if no violation happens
|
// the lint should not be executed if no violation happens
|
||||||
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
|
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use std::borrow::Cow;
|
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::eager_or_lazy::switch_to_eager_eval;
|
||||||
use clippy_utils::msrvs::{self, Msrv};
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::source::snippet_opt;
|
|
||||||
use clippy_utils::sugg::{Sugg, make_binop};
|
use clippy_utils::sugg::{Sugg, make_binop};
|
||||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
|
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
|
||||||
use clippy_utils::visitors::is_local_used;
|
use clippy_utils::visitors::is_local_used;
|
||||||
|
@ -12,7 +11,7 @@ use rustc_ast::LitKind::Bool;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::sym;
|
use rustc_span::{Span, sym};
|
||||||
|
|
||||||
use super::UNNECESSARY_MAP_OR;
|
use super::UNNECESSARY_MAP_OR;
|
||||||
|
|
||||||
|
@ -42,13 +41,14 @@ pub(super) fn check<'a>(
|
||||||
recv: &Expr<'_>,
|
recv: &Expr<'_>,
|
||||||
def: &Expr<'_>,
|
def: &Expr<'_>,
|
||||||
map: &Expr<'_>,
|
map: &Expr<'_>,
|
||||||
|
method_span: Span,
|
||||||
msrv: &Msrv,
|
msrv: &Msrv,
|
||||||
) {
|
) {
|
||||||
let ExprKind::Lit(def_kind) = def.kind else {
|
let ExprKind::Lit(def_kind) = def.kind else {
|
||||||
return;
|
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 {
|
let Bool(def_bool) = def_kind.node else {
|
||||||
return;
|
return;
|
||||||
|
@ -60,6 +60,8 @@ pub(super) fn check<'a>(
|
||||||
Some(_) | None => return,
|
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 (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind
|
||||||
&& let closure_body = cx.tcx.hir().body(map_closure.body)
|
&& let closure_body = cx.tcx.hir().body(map_closure.body)
|
||||||
&& let closure_body_value = closure_body.value.peel_blocks()
|
&& let closure_body_value = closure_body.value.peel_blocks()
|
||||||
|
@ -114,26 +116,17 @@ pub(super) fn check<'a>(
|
||||||
}
|
}
|
||||||
.into_string();
|
.into_string();
|
||||||
|
|
||||||
(sugg, "a standard comparison", app)
|
(vec![(expr.span, sugg)], "a standard comparison", app)
|
||||||
} else if !def_bool
|
} else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) {
|
||||||
&& 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())
|
|
||||||
{
|
|
||||||
let suggested_name = variant.method_name();
|
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,
|
suggested_name,
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
)
|
)
|
||||||
} else if def_bool
|
} else if def_bool && matches!(variant, Variant::Some) && msrv.meets(msrvs::IS_NONE_OR) {
|
||||||
&& 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())
|
|
||||||
{
|
|
||||||
(
|
(
|
||||||
format!("{recv_callsite}.is_none_or({span_callsite})"),
|
vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())],
|
||||||
"is_none_or",
|
"is_none_or",
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
)
|
)
|
||||||
|
@ -145,13 +138,13 @@ pub(super) fn check<'a>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
UNNECESSARY_MAP_OR,
|
UNNECESSARY_MAP_OR,
|
||||||
expr.span,
|
expr.span,
|
||||||
"this `map_or` can be simplified",
|
"this `map_or` can be simplified",
|
||||||
format!("use {method} instead"),
|
|diag| {
|
||||||
sugg,
|
diag.multipart_suggestion_verbose(format!("use {method} instead"), sugg, applicability);
|
||||||
applicability,
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,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))
|
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||||
},
|
},
|
||||||
// The two exprs are method calls.
|
// The two exprs are method calls.
|
||||||
// Check to see that the function is the same and the arguments are mirrored
|
// Check to see that the function is the same and the arguments and receivers are mirrored
|
||||||
// This is enough because the receiver of the method is listed in the arguments
|
|
||||||
(
|
(
|
||||||
ExprKind::MethodCall(left_segment, left_receiver, left_args, _),
|
ExprKind::MethodCall(left_segment, left_receiver, left_args, _),
|
||||||
ExprKind::MethodCall(right_segment, right_receiver, right_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, &[])
|
&& implements_trait(cx, parent_ty, iterator_trait_id, &[])
|
||||||
&& let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
|
&& let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
|
||||||
&& let Some(receiver_snippet) = receiver.span.get_source_text(cx)
|
&& 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) {
|
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
|
let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
|
||||||
"copied"
|
"copied"
|
||||||
} else {
|
} 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
|
if let StmtKind::Semi(expr) = stmt.kind
|
||||||
&& let ExprKind::Binary(ref binop, a, b) = expr.kind
|
&& let ExprKind::Binary(binop, a, b) = &expr.kind
|
||||||
&& (binop.node == BinOpKind::And || binop.node == BinOpKind::Or)
|
&& matches!(binop.node, BinOpKind::And | BinOpKind::Or)
|
||||||
&& let Some(sugg) = Sugg::hir_opt(cx, a)
|
&& !stmt.span.from_expansion()
|
||||||
|
&& expr.span.eq_ctxt(stmt.span)
|
||||||
{
|
{
|
||||||
span_lint_hir_and_then(
|
span_lint_hir_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -227,16 +228,14 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
||||||
stmt.span,
|
stmt.span,
|
||||||
"boolean short circuit operator in statement may be clearer using an explicit test",
|
"boolean short circuit operator in statement may be clearer using an explicit test",
|
||||||
|diag| {
|
|diag| {
|
||||||
let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
|
let mut app = Applicability::MachineApplicable;
|
||||||
diag.span_suggestion(
|
let test = Sugg::hir_with_context(cx, a, expr.span.ctxt(), "_", &mut app);
|
||||||
stmt.span,
|
let test = if binop.node == BinOpKind::Or { !test } else { test };
|
||||||
"replace it with",
|
let then = Sugg::hir_with_context(cx, b, expr.span.ctxt(), "_", &mut app);
|
||||||
format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),),
|
diag.span_suggestion(stmt.span, "replace it with", format!("if {test} {{ {then}; }}"), app);
|
||||||
Applicability::MachineApplicable, // snippet
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
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)),
|
}) => impl_params.push((path.segments[0].ident.to_string(), path.span)),
|
||||||
GenericArg::Type(_) => return,
|
GenericArg::Type(_) => return,
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the type that the Impl is for
|
// find the type that the Impl is for
|
||||||
|
|
|
@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||||
| hir::ItemKind::GlobalAsm(..)
|
| hir::ItemKind::GlobalAsm(..)
|
||||||
| hir::ItemKind::Impl { .. }
|
| hir::ItemKind::Impl { .. }
|
||||||
| hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span),
|
| 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());
|
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
|
// this prevents ICEs such as when self is a type parameter or a primitive type
|
||||||
// (see #10887, #11063)
|
// (see #10887, #11063)
|
||||||
&& let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res
|
&& 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
|
// don't trigger if this impl was derived
|
||||||
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
|
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
|
||||||
&& !item.span.from_expansion()
|
&& !item.span.from_expansion()
|
||||||
|
|
|
@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
||||||
| hir::ItemKind::ForeignMod { .. }
|
| hir::ItemKind::ForeignMod { .. }
|
||||||
| hir::ItemKind::Impl { .. }
|
| hir::ItemKind::Impl { .. }
|
||||||
| hir::ItemKind::Use(..) => {},
|
| hir::ItemKind::Use(..) => {},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
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, _) = &expr.kind {
|
||||||
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
|
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
|
||||||
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
||||||
};
|
}
|
||||||
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
|
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
|
||||||
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,7 @@ fn collect_unsafe_exprs<'tcx>(
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
}
|
||||||
|
|
||||||
Continue::<(), _>(Descend::Yes)
|
Continue::<(), _>(Descend::Yes)
|
||||||
});
|
});
|
||||||
|
|
|
@ -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::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),
|
ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
|
||||||
_ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg),
|
_ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,10 @@ struct LocalAssign {
|
||||||
|
|
||||||
impl LocalAssign {
|
impl LocalAssign {
|
||||||
fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
|
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 let ExprKind::Assign(lhs, rhs, _) = expr.kind {
|
||||||
if lhs.span.from_expansion() {
|
if lhs.span.from_expansion() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -336,7 +340,7 @@ fn check<'tcx>(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
}
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,9 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
|
impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
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 attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||||
let mut app = Applicability::MaybeIncorrect;
|
let mut app = Applicability::MaybeIncorrect;
|
||||||
let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app);
|
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.
|
/// 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
|
/// ### Example
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
306
clippy_lints/src/non_std_lazy_statics.rs
Normal file
306
clippy_lints/src/non_std_lazy_statics.rs
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
use clippy_config::Conf;
|
||||||
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||||
|
use clippy_utils::msrvs::Msrv;
|
||||||
|
use clippy_utils::visitors::for_each_expr;
|
||||||
|
use clippy_utils::{def_path_def_ids, fn_def_id, path_def_id};
|
||||||
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::def::{DefKind, Res};
|
||||||
|
use rustc_hir::def_id::{CrateNum, DefId};
|
||||||
|
use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::lint::in_external_macro;
|
||||||
|
use rustc_session::impl_lint_pass;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Lints when `once_cell::sync::Lazy` or `lazy_static!` are used to define a static variable,
|
||||||
|
/// and suggests replacing such cases with `std::sync::LazyLock` instead.
|
||||||
|
///
|
||||||
|
/// Note: This lint will not trigger in crate with `no_std` context, or with MSRV < 1.80.0. It
|
||||||
|
/// also will not trigger on `once_cell::sync::Lazy` usage in crates which use other types
|
||||||
|
/// from `once_cell`, such as `once_cell::race::OnceBox`.
|
||||||
|
///
|
||||||
|
/// ### Why restrict this?
|
||||||
|
/// - Reduces the need for an extra dependency
|
||||||
|
/// - Enforce convention of using standard library types when possible
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```ignore
|
||||||
|
/// lazy_static! {
|
||||||
|
/// static ref FOO: String = "foo".to_uppercase();
|
||||||
|
/// }
|
||||||
|
/// static BAR: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(|| "BAR".to_lowercase());
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```ignore
|
||||||
|
/// static FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "FOO".to_lowercase());
|
||||||
|
/// static BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "BAR".to_lowercase());
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.81.0"]
|
||||||
|
pub NON_STD_LAZY_STATICS,
|
||||||
|
pedantic,
|
||||||
|
"lazy static that could be replaced by `std::sync::LazyLock`"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list containing functions with corresponding replacements in `LazyLock`.
|
||||||
|
///
|
||||||
|
/// Some functions could be replaced as well if we have replaced `Lazy` to `LazyLock`,
|
||||||
|
/// therefore after suggesting replace the type, we need to make sure the function calls can be
|
||||||
|
/// replaced, otherwise the suggestions cannot be applied thus the applicability should be
|
||||||
|
/// `Unspecified` or `MaybeIncorret`.
|
||||||
|
static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[
|
||||||
|
("once_cell::sync::Lazy::force", Some("std::sync::LazyLock::force")),
|
||||||
|
("once_cell::sync::Lazy::get", None), // `std::sync::LazyLock::get` is experimental
|
||||||
|
("once_cell::sync::Lazy::new", Some("std::sync::LazyLock::new")),
|
||||||
|
// Note: `Lazy::{into_value, get_mut, force_mut}` are not in the list.
|
||||||
|
// Because the lint only checks for `static`s, and using these functions with statics
|
||||||
|
// will either be a hard error or triggers `static_mut_ref` that will be hard errors.
|
||||||
|
// But keep in mind that if somehow we decide to expand this lint to catch non-statics,
|
||||||
|
// add those functions into the list.
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct NonStdLazyStatic {
|
||||||
|
msrv: Msrv,
|
||||||
|
lazy_static_lazy_static: Vec<DefId>,
|
||||||
|
once_cell_crate: Vec<CrateNum>,
|
||||||
|
once_cell_sync_lazy: Vec<DefId>,
|
||||||
|
once_cell_sync_lazy_new: Vec<DefId>,
|
||||||
|
sugg_map: FxIndexMap<DefId, Option<String>>,
|
||||||
|
lazy_type_defs: FxIndexMap<DefId, LazyInfo>,
|
||||||
|
uses_other_once_cell_types: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NonStdLazyStatic {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(conf: &'static Conf) -> Self {
|
||||||
|
Self {
|
||||||
|
msrv: conf.msrv.clone(),
|
||||||
|
lazy_static_lazy_static: Vec::new(),
|
||||||
|
once_cell_crate: Vec::new(),
|
||||||
|
once_cell_sync_lazy: Vec::new(),
|
||||||
|
once_cell_sync_lazy_new: Vec::new(),
|
||||||
|
sugg_map: FxIndexMap::default(),
|
||||||
|
lazy_type_defs: FxIndexMap::default(),
|
||||||
|
uses_other_once_cell_types: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(NonStdLazyStatic => [NON_STD_LAZY_STATICS]);
|
||||||
|
|
||||||
|
/// Return if current MSRV does not meet the requirement for `lazy_cell` feature,
|
||||||
|
/// or current context has `no_std` attribute.
|
||||||
|
macro_rules! ensure_prerequisite {
|
||||||
|
($msrv:expr, $cx:ident) => {
|
||||||
|
if !$msrv.meets(clippy_utils::msrvs::LAZY_CELL) || clippy_utils::is_no_std_crate($cx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
|
||||||
|
extract_msrv_attr!(LateContext);
|
||||||
|
|
||||||
|
fn check_crate(&mut self, cx: &LateContext<'hir>) {
|
||||||
|
// Do not lint if current crate does not support `LazyLock`.
|
||||||
|
ensure_prerequisite!(self.msrv, cx);
|
||||||
|
|
||||||
|
// Fetch def_ids for external paths
|
||||||
|
self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect();
|
||||||
|
self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect();
|
||||||
|
self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect();
|
||||||
|
// And CrateNums for `once_cell` crate
|
||||||
|
self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect();
|
||||||
|
|
||||||
|
// Convert hardcoded fn replacement list into a map with def_id
|
||||||
|
for (path, sugg) in FUNCTION_REPLACEMENTS {
|
||||||
|
let path_vec: Vec<&str> = path.split("::").collect();
|
||||||
|
for did in def_path_def_ids(cx.tcx, &path_vec) {
|
||||||
|
self.sugg_map.insert(did, sugg.map(ToOwned::to_owned));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) {
|
||||||
|
ensure_prerequisite!(self.msrv, cx);
|
||||||
|
|
||||||
|
if let ItemKind::Static(..) = item.kind
|
||||||
|
&& let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span)
|
||||||
|
&& self.lazy_static_lazy_static.contains(¯o_call.def_id)
|
||||||
|
{
|
||||||
|
span_lint(
|
||||||
|
cx,
|
||||||
|
NON_STD_LAZY_STATICS,
|
||||||
|
macro_call.span,
|
||||||
|
"this macro has been superceded by `std::sync::LazyLock`",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if in_external_macro(cx.sess(), item.span) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) {
|
||||||
|
self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &Expr<'hir>) {
|
||||||
|
ensure_prerequisite!(self.msrv, cx);
|
||||||
|
|
||||||
|
// All functions in the `FUNCTION_REPLACEMENTS` have only one args
|
||||||
|
if let ExprKind::Call(callee, [arg]) = expr.kind
|
||||||
|
&& let Some(call_def_id) = fn_def_id(cx, expr)
|
||||||
|
&& self.sugg_map.contains_key(&call_def_id)
|
||||||
|
&& let ExprKind::Path(qpath) = arg.peel_borrows().kind
|
||||||
|
&& let Some(arg_def_id) = cx.typeck_results().qpath_res(&qpath, arg.hir_id).opt_def_id()
|
||||||
|
&& let Some(lazy_info) = self.lazy_type_defs.get_mut(&arg_def_id)
|
||||||
|
{
|
||||||
|
lazy_info.calls_span_and_id.insert(callee.span, call_def_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_ty(&mut self, cx: &LateContext<'hir>, ty: &'hir rustc_hir::Ty<'hir, rustc_hir::AmbigArg>) {
|
||||||
|
ensure_prerequisite!(self.msrv, cx);
|
||||||
|
|
||||||
|
// Record if types from `once_cell` besides `sync::Lazy` are used.
|
||||||
|
if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind
|
||||||
|
&& let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id()
|
||||||
|
// Is from `once_cell` crate
|
||||||
|
&& self.once_cell_crate.contains(&ty_def_id.krate)
|
||||||
|
// And is NOT `once_cell::sync::Lazy`
|
||||||
|
&& !self.once_cell_sync_lazy.contains(&ty_def_id)
|
||||||
|
{
|
||||||
|
self.uses_other_once_cell_types = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_crate_post(&mut self, cx: &LateContext<'hir>) {
|
||||||
|
ensure_prerequisite!(self.msrv, cx);
|
||||||
|
|
||||||
|
if !self.uses_other_once_cell_types {
|
||||||
|
for (_, lazy_info) in &self.lazy_type_defs {
|
||||||
|
lazy_info.lint(cx, &self.sugg_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LazyInfo {
|
||||||
|
/// Span of the [`hir::Ty`] without including args.
|
||||||
|
/// i.e.:
|
||||||
|
/// ```ignore
|
||||||
|
/// static FOO: Lazy<String> = Lazy::new(...);
|
||||||
|
/// // ^^^^
|
||||||
|
/// ```
|
||||||
|
ty_span_no_args: Span,
|
||||||
|
/// `Span` and `DefId` of calls on `Lazy` type.
|
||||||
|
/// i.e.:
|
||||||
|
/// ```ignore
|
||||||
|
/// static FOO: Lazy<String> = Lazy::new(...);
|
||||||
|
/// // ^^^^^^^^^
|
||||||
|
/// ```
|
||||||
|
calls_span_and_id: FxIndexMap<Span, DefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LazyInfo {
|
||||||
|
fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> {
|
||||||
|
// Check if item is a `once_cell:sync::Lazy` static.
|
||||||
|
if let ItemKind::Static(ty, _, body_id) = item.kind
|
||||||
|
&& let Some(path_def_id) = path_def_id(cx, ty)
|
||||||
|
&& let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
|
||||||
|
&& state.once_cell_sync_lazy.contains(&path_def_id)
|
||||||
|
{
|
||||||
|
let ty_span_no_args = path_span_without_args(path);
|
||||||
|
let body = cx.tcx.hir().body(body_id);
|
||||||
|
|
||||||
|
// visit body to collect `Lazy::new` calls
|
||||||
|
let mut new_fn_calls = FxIndexMap::default();
|
||||||
|
for_each_expr::<(), ()>(cx, body, |ex| {
|
||||||
|
if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id)
|
||||||
|
&& state.once_cell_sync_lazy_new.contains(&fn_did)
|
||||||
|
{
|
||||||
|
new_fn_calls.insert(call_span, fn_did);
|
||||||
|
}
|
||||||
|
std::ops::ControlFlow::Continue(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(LazyInfo {
|
||||||
|
ty_span_no_args,
|
||||||
|
calls_span_and_id: new_fn_calls,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap<DefId, Option<String>>) {
|
||||||
|
// Applicability might get adjusted to `Unspecified` later if any calls
|
||||||
|
// in `calls_span_and_id` are not replaceable judging by the `sugg_map`.
|
||||||
|
let mut appl = Applicability::MachineApplicable;
|
||||||
|
let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())];
|
||||||
|
|
||||||
|
for (span, def_id) in &self.calls_span_and_id {
|
||||||
|
let maybe_sugg = sugg_map.get(def_id).cloned().flatten();
|
||||||
|
if let Some(sugg) = maybe_sugg {
|
||||||
|
suggs.push((*span, sugg));
|
||||||
|
} else {
|
||||||
|
// If NO suggested replacement, not machine applicable
|
||||||
|
appl = Applicability::Unspecified;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
NON_STD_LAZY_STATICS,
|
||||||
|
self.ty_span_no_args,
|
||||||
|
"this type has been superceded by `LazyLock` in the standard library",
|
||||||
|
|diag| {
|
||||||
|
diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the span of a given `Path` without including any of its args.
|
||||||
|
///
|
||||||
|
/// NB: Re-write of a private function `rustc_lint::non_local_def::path_span_without_args`.
|
||||||
|
fn path_span_without_args(path: &hir::Path<'_>) -> Span {
|
||||||
|
path.segments
|
||||||
|
.last()
|
||||||
|
.and_then(|seg| seg.args)
|
||||||
|
.map_or(path.span, |args| path.span.until(args.span_ext))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `DefId` and `Span` of the callee if the given expression is a function call.
|
||||||
|
///
|
||||||
|
/// NB: Modified from [`clippy_utils::fn_def_id`], to support calling in an static `Item`'s body.
|
||||||
|
fn fn_def_id_and_span_from_body(cx: &LateContext<'_>, expr: &Expr<'_>, body_id: BodyId) -> Option<(DefId, Span)> {
|
||||||
|
// FIXME: find a way to cache the result.
|
||||||
|
let typeck = cx.tcx.typeck_body(body_id);
|
||||||
|
match &expr.kind {
|
||||||
|
ExprKind::Call(
|
||||||
|
Expr {
|
||||||
|
kind: ExprKind::Path(qpath),
|
||||||
|
hir_id: path_hir_id,
|
||||||
|
span,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..,
|
||||||
|
) => {
|
||||||
|
// Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
|
||||||
|
// deref to fn pointers, dyn Fn, impl Fn - #8850
|
||||||
|
if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
|
||||||
|
typeck.qpath_res(qpath, *path_hir_id)
|
||||||
|
{
|
||||||
|
Some((id, *span))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
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