Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2025-01-28 18:28:57 +01:00
commit 145d5adf04
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
282 changed files with 5798 additions and 885 deletions

2
.github/deploy.sh vendored
View file

@ -45,6 +45,8 @@ if [[ -n $TAG_NAME ]]; then
git add "$TAG_NAME"
# Update the symlink
git add stable
# Update the index.html file
git add index.html
git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}"
elif [[ $BETA = "true" ]]; then
if git diff --exit-code --quiet -- beta/; then

6
.github/driver.sh vendored
View file

@ -47,9 +47,9 @@ unset CARGO_MANIFEST_DIR
# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
# FIXME: How to match the clippy invocation in compile-test.rs?
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/box_default.rs 2>box_default.stderr && exit 1
sed -e "/= help: for/d" box_default.stderr > normalized.stderr
diff -u normalized.stderr tests/ui/box_default.stderr
./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/string_to_string.rs 2>string_to_string.stderr && exit 1
sed -e "/= help: for/d" string_to_string.stderr > normalized.stderr
diff -u normalized.stderr tests/ui/string_to_string.stderr
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
SYSROOT=$(rustc --print sysroot)

59
.github/workflows/clippy_changelog.yml vendored Normal file
View 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) }}'

View file

@ -15,37 +15,7 @@ defaults:
shell: bash
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
# Run
- name: Check Changelog
run: |
MESSAGE=$(git log --format=%B -n 1)
PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
python -c "import sys, json; print(json.load(sys.stdin)['body'])")
output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
echo "ERROR: PR body must contain 'changelog: ...'"
exit 1
}
if [[ "$output" = "none" ]]; then
echo "WARNING: changelog is 'none'"
else
echo "changelog: $output"
fi
env:
PYTHONIOENCODING: 'utf-8'
base:
needs: changelog
strategy:
matrix:
include:
@ -119,7 +89,6 @@ jobs:
OS: ${{ runner.os }}
metadata_collection:
needs: changelog
runs-on: ubuntu-latest
steps:
@ -138,7 +107,6 @@ jobs:
run: cargo collect-metadata
integration_build:
needs: changelog
runs-on: ubuntu-latest
steps:
@ -228,7 +196,7 @@ jobs:
INTEGRATION: ${{ matrix.integration }}
conclusion:
needs: [ changelog, base, metadata_collection, integration_build, integration ]
needs: [ base, metadata_collection, integration_build, integration ]
# We need to ensure this job does *not* get skipped if its dependencies fail,
# because a skipped job is considered a success by GitHub. So we have to
# overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run

View file

@ -1,6 +1,12 @@
name: Lintcheck
on: pull_request
on:
pull_request:
paths-ignore:
- 'book/**'
- 'util/**'
- 'tests/**'
- '*.md'
env:
RUST_BACKTRACE: 1

View file

@ -27,7 +27,7 @@ jobs:
- name: Install mdbook
run: |
mkdir mdbook
curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.43/mdbook-v0.4.43-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
echo `pwd`/mdbook >> $GITHUB_PATH
# Run

View file

@ -5533,6 +5533,7 @@ Released 2018-09-13
[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs
[`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
@ -5762,11 +5763,13 @@ Released 2018-09-13
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
[`manual_repeat_n`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n
[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
[`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
@ -5904,6 +5907,7 @@ Released 2018-09-13
[`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
[`non_std_lazy_statics`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics
[`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions
[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
@ -6063,6 +6067,7 @@ Released 2018-09-13
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
[`sliced_string_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#sliced_string_as_bytes
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
@ -6172,12 +6177,14 @@ Released 2018-09-13
[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_semicolon`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
[`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
@ -6216,6 +6223,7 @@ Released 2018-09-13
[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
[`useless_nonzero_new_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_nonzero_new_unchecked
[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box

View file

@ -199,7 +199,7 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and
responding to issues there may not always be enough time to stay on top of it
all.
Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
Our highest priority is fixing [ICEs][I-ICE] and [bugs][C-bug], for example
an ICE in a popular crate that many other crates depend on. We don't
want Clippy to crash on your code and we want it to be as reliable as the
suggestions from Rust compiler errors.
@ -213,8 +213,8 @@ Or rather: before the sync this should be addressed,
e.g. by removing a lint again, so it doesn't hit beta/stable.
[triage]: https://forge.rust-lang.org/release/triage-procedure.html
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE
[C-bug]: https://github.com/rust-lang/rust-clippy/labels/C-bug
[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high

View file

@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0"
keywords = ["clippy", "lint", "plugin"]
categories = ["development-tools", "development-tools::cargo-plugins"]
build = "build.rs"
edition = "2021"
edition = "2024"
publish = false
[[bin]]
@ -71,3 +71,10 @@ harness = false
[[test]]
name = "dogfood"
harness = false
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
# due to the number of conditions in the clippy_lints crate
# and enabling optimizations for that specific dependency helps a bit
# without increasing total build times.
[profile.dev.package.quine-mc_cluskey]
opt-level = 3

View file

@ -159,11 +159,11 @@ line. (You can swap `clippy::all` with the specific lint category you are target
You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`).
Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
lints prone to false positives.
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
lints prone to false positives.
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)

View file

@ -6,7 +6,7 @@ src = "src"
title = "Clippy Documentation"
[rust]
edition = "2018"
edition = "2024"
[output.html]
edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}"

View file

@ -299,10 +299,11 @@ This is good, because it makes writing this particular lint less complicated.
We have to make this decision with every new Clippy lint. It boils down to using
either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
In short, the `EarlyLintPass` runs before type checking and
[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass`
has access to type information. Consider using the `LateLintPass` unless you need
something specific from the `EarlyLintPass`.
`EarlyLintPass` runs before type checking and
[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering, while `LateLintPass`
runs after these stages, providing access to type information. The `cargo dev new_lint` command
defaults to the recommended `LateLintPass`, but you can specify `--pass=early` if your lint
only needs AST level analysis.
Since we don't need type information for checking the function name, we used
`--pass=early` when running the new lint automation and all the imports were
@ -537,7 +538,7 @@ via `Tools -> Clippy` and you should see the generated code in the output below.
If the command was executed successfully, you can copy the code over to where
you are implementing your lint.
[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
## Print HIR lint
@ -552,7 +553,7 @@ attribute to expressions you often need to enable
_Clippy_.
[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=daf14db3a7f39ca467cd1b86c34b9afb
## Documentation

View file

@ -265,10 +265,10 @@ functions to deal with macros:
```
[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html
[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.pat_ty
[paths]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/paths/index.html

View file

@ -139,10 +139,10 @@ Untracked files:
```
## The `define_clippy_lints` macro
## The `declare_clippy_lint` macro
After `cargo dev new_lint`, you should see a macro with the name
`define_clippy_lints`. It will be in the same file if you defined a standalone
`declare_clippy_lint`. It will be in the same file if you defined a standalone
lint, and it will be in `mod.rs` if you defined a type-specific lint.
The macro looks something like this:

View file

@ -96,9 +96,9 @@ git tag rust-1.XX.0 # XX should be exchanged with the correspondin
git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote
```
After this, the release should be available on the Clippy [release page].
After this, the release should be available on the Clippy [tags page].
[release page]: https://github.com/rust-lang/rust-clippy/releases
[tags page]: https://github.com/rust-lang/rust-clippy/tags
## Publish `clippy_utils`

View file

@ -750,6 +750,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`manual_pattern_char_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison)
* [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
* [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n)
* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
@ -762,11 +763,13 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
* [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics)
* [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or)
* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
* [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
* [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
* [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push)
* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
* [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)

View file

@ -3,7 +3,7 @@ name = "clippy_config"
# begin autogenerated version
version = "0.1.86"
# end autogenerated version
edition = "2021"
edition = "2024"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -619,6 +619,7 @@ define_Conf! {
manual_pattern_char_comparison,
manual_range_contains,
manual_rem_euclid,
manual_repeat_n,
manual_retain,
manual_split_once,
manual_str_repeat,
@ -631,11 +632,13 @@ define_Conf! {
mem_replace_with_default,
missing_const_for_fn,
needless_borrow,
non_std_lazy_statics,
option_as_ref_deref,
option_map_unwrap_or,
ptr_as_ptr,
redundant_field_names,
redundant_static_lifetimes,
same_item_push,
seek_from_current,
seek_rewind,
transmute_ptr_to_ref,

View file

@ -2,7 +2,7 @@
name = "clippy_dev"
description = "Clippy developer tooling"
version = "0.0.1"
edition = "2021"
edition = "2024"
[dependencies]
aho-corasick = "1.0"

View file

@ -62,7 +62,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
eprintln!("error: unable to get the absolute path of rustc ({err})");
return Err(());
},
};
}
}
let path = path.join("compiler");

View file

@ -842,7 +842,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
Ok(file) => drop(file),
Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
Err(e) => panic_file(e, new_name, "create"),
};
}
match fs::rename(old_name, new_name) {
Ok(()) => true,
Err(e) => {

View file

@ -1,7 +1,7 @@
[package]
name = "clippy_dummy" # rename to clippy before publishing
version = "0.0.303"
edition = "2018"
edition = "2024"
readme = "crates-readme.md"
description = "A bunch of helpful lints to avoid common pitfalls in Rust."
build = 'build.rs'

View file

@ -8,7 +8,7 @@ repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["clippy", "lint", "plugin"]
edition = "2021"
edition = "2024"
[dependencies]
arrayvec = { version = "0.7", default-features = false }

View file

@ -445,8 +445,8 @@ fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssoc
#[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
use SourceItemOrderingTraitAssocItemKind::*;
match value {
AssocItemKind::Const { .. } => Const,
AssocItemKind::Type { .. } => Type,
AssocItemKind::Const => Const,
AssocItemKind::Type => Type,
AssocItemKind::Fn { .. } => Fn,
}
}

View file

@ -257,7 +257,7 @@ fn build_sugg<'tcx>(
// The receiver may have been a value type, so we need to add an `&` to
// be sure the argument to clone_from will be a reference.
arg_sugg = arg_sugg.addr();
};
}
format!("{receiver_sugg}.clone_from({arg_sugg})")
},

View file

@ -60,7 +60,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute])
}
outer_attr_kind.insert(kind);
},
};
}
}
}

View file

@ -161,6 +161,15 @@ declare_clippy_lint! {
/// [dependencies]
/// regex = "*"
/// ```
/// Use instead:
/// ```toml
/// [dependencies]
/// # allow patch updates, but not minor or major version changes
/// some_crate_1 = "~1.2.3"
///
/// # pin the version to a specific version
/// some_crate_2 = "=1.2.3"
/// ```
#[clippy::version = "1.32.0"]
pub WILDCARD_DEPENDENCIES,
cargo,

View file

@ -84,6 +84,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
diag
.note("`usize` and `isize` may be as small as 16 bits on some platforms")
.note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types");
};
}
});
}

View file

@ -205,7 +205,7 @@ fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
// - uncertain if there are any uncertain values (because they could be negative or positive),
Sign::Uncertain => return Sign::Uncertain,
Sign::ZeroOrPositive => (),
};
}
}
// A mul/div is:
@ -236,7 +236,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
// - uncertain if there are any uncertain values (because they could be negative or positive),
Sign::Uncertain => return Sign::Uncertain,
Sign::ZeroOrPositive => positive_count += 1,
};
}
}
// A sum is:

View file

@ -273,7 +273,7 @@ fn get_types_from_cast<'a>(
},
_ => {},
}
};
}
None
}

View file

@ -142,6 +142,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
crate::doc::DOC_MARKDOWN_INFO,
crate::doc::DOC_NESTED_REFDEFS_INFO,
crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO,
crate::doc::EMPTY_DOCS_INFO,
crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
@ -335,6 +336,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO,
crate::matches::MANUAL_FILTER_INFO,
crate::matches::MANUAL_MAP_INFO,
crate::matches::MANUAL_OK_ERR_INFO,
crate::matches::MANUAL_UNWRAP_OR_INFO,
crate::matches::MATCH_AS_REF_INFO,
crate::matches::MATCH_BOOL_INFO,
@ -419,6 +421,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
crate::methods::MANUAL_NEXT_BACK_INFO,
crate::methods::MANUAL_OK_OR_INFO,
crate::methods::MANUAL_REPEAT_N_INFO,
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
crate::methods::MANUAL_SPLIT_ONCE_INFO,
crate::methods::MANUAL_STR_REPEAT_INFO,
@ -466,6 +469,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
crate::methods::SKIP_WHILE_NEXT_INFO,
crate::methods::SLICED_STRING_AS_BYTES_INFO,
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
crate::methods::STRING_EXTEND_CHARS_INFO,
crate::methods::STRING_LIT_CHARS_ANY_INFO,
@ -495,6 +499,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::UNWRAP_OR_DEFAULT_INFO,
crate::methods::UNWRAP_USED_INFO,
crate::methods::USELESS_ASREF_INFO,
crate::methods::USELESS_NONZERO_NEW_UNCHECKED_INFO,
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
crate::methods::VERBOSE_FILE_READS_INFO,
crate::methods::WAKER_CLONE_WAKE_INFO,
@ -572,6 +577,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::non_expressive_names::SIMILAR_NAMES_INFO,
crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO,
crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO,
crate::non_std_lazy_statics::NON_STD_LAZY_STATICS_INFO,
crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO,
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
crate::octal_escapes::OCTAL_ESCAPES_INFO,
@ -753,8 +759,10 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO,
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO,
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
crate::unused_async::UNUSED_ASYNC_INFO,

View file

@ -80,6 +80,6 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
String::new(),
Applicability::MachineApplicable,
);
};
}
}
}

View file

@ -223,19 +223,17 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
}
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
match pat.kind {
PatKind::Expr(&PatExpr {
hir_id,
kind: PatExprKind::Lit { lit, .. },
..
}) => {
let ty = self.cx.typeck_results().node_type(hir_id);
self.check_lit(lit, ty, hir_id);
return;
},
_ => {},
if let PatKind::Expr(&PatExpr {
hir_id,
kind: PatExprKind::Lit { lit, .. },
..
}) = pat.kind
{
let ty = self.cx.typeck_results().node_type(hir_id);
self.check_lit(lit, ty, hir_id);
return;
}
walk_pat(self, pat)
walk_pat(self, pat);
}
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {

View file

@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
deref_count += 1;
},
None => break None,
};
}
};
let use_node = use_cx.use_node(cx);

View file

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use itertools::Itertools;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::{BytePos, Span};
use std::cmp::Ordering;
use std::ops::Range;
use super::DOC_LAZY_CONTINUATION;
use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS};
fn map_container_to_text(c: &super::Container) -> &'static str {
match c {
@ -28,12 +29,57 @@ pub(super) fn check(
return;
}
// Blockquote
let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
let blockquote_level = containers
.iter()
.filter(|c| matches!(c, super::Container::Blockquote))
.count();
let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count();
if ccount < blockquote_level {
span_lint_and_then(
cx,
DOC_LAZY_CONTINUATION,
span,
"doc quote line without `>` marker",
|diag| {
let mut doc_start_range = &doc[range];
let mut suggested = String::new();
for c in containers {
let text = map_container_to_text(c);
if doc_start_range.starts_with(text) {
doc_start_range = &doc_start_range[text.len()..];
span = span.with_lo(
span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")),
);
} else if matches!(c, super::Container::Blockquote)
&& let Some(i) = doc_start_range.find('>')
{
doc_start_range = &doc_start_range[i + 1..];
span = span
.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
} else {
suggested.push_str(text);
}
}
diag.span_suggestion_verbose(
span,
"add markers to start of line",
suggested,
Applicability::MachineApplicable,
);
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
},
);
return;
}
if ccount != 0 && blockquote_level != 0 {
// If this doc is a blockquote, we don't go further.
return;
}
// List
let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count();
let list_indentation = containers
.iter()
.map(|c| {
@ -44,50 +90,36 @@ pub(super) fn check(
}
})
.sum();
if ccount < blockquote_level || lcount < list_indentation {
let msg = if ccount < blockquote_level {
"doc quote line without `>` marker"
} else {
"doc list item without indentation"
};
span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
if ccount == 0 && blockquote_level == 0 {
match leading_spaces.cmp(&list_indentation) {
Ordering::Less => span_lint_and_then(
cx,
DOC_LAZY_CONTINUATION,
span,
"doc list item without indentation",
|diag| {
// simpler suggestion style for indentation
let indent = list_indentation - lcount;
let indent = list_indentation - leading_spaces;
diag.span_suggestion_verbose(
span.shrink_to_hi(),
"indent this line",
std::iter::repeat(" ").take(indent).join(""),
std::iter::repeat_n(" ", indent).join(""),
Applicability::MaybeIncorrect,
);
diag.help("if this is supposed to be its own paragraph, add a blank line");
return;
}
let mut doc_start_range = &doc[range];
let mut suggested = String::new();
for c in containers {
let text = map_container_to_text(c);
if doc_start_range.starts_with(text) {
doc_start_range = &doc_start_range[text.len()..];
span = span
.with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")));
} else if matches!(c, super::Container::Blockquote)
&& let Some(i) = doc_start_range.find('>')
{
doc_start_range = &doc_start_range[i + 1..];
span =
span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
} else {
suggested.push_str(text);
}
}
diag.span_suggestion_verbose(
},
),
Ordering::Greater => {
let sugg = std::iter::repeat_n(" ", list_indentation).join("");
span_lint_and_sugg(
cx,
DOC_OVERINDENTED_LIST_ITEMS,
span,
"add markers to start of line",
suggested,
Applicability::MachineApplicable,
"doc list item overindented",
format!("try using `{sugg}` ({list_indentation} spaces)"),
sugg,
Applicability::MaybeIncorrect,
);
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
});
},
Ordering::Equal => {},
}
}

View file

@ -428,6 +428,39 @@ declare_clippy_lint! {
"require every line of a paragraph to be indented and marked"
}
declare_clippy_lint! {
/// ### What it does
///
/// Detects overindented list items in doc comments where the continuation
/// lines are indented more than necessary.
///
/// ### Why is this bad?
///
/// Overindented list items in doc comments can lead to inconsistent and
/// poorly formatted documentation when rendered. Excessive indentation may
/// cause the text to be misinterpreted as a nested list item or code block,
/// affecting readability and the overall structure of the documentation.
///
/// ### Example
///
/// ```no_run
/// /// - This is the first item in a list
/// /// and this line is overindented.
/// # fn foo() {}
/// ```
///
/// Fixes this into:
/// ```no_run
/// /// - This is the first item in a list
/// /// and this line is overindented.
/// # fn foo() {}
/// ```
#[clippy::version = "1.80.0"]
pub DOC_OVERINDENTED_LIST_ITEMS,
style,
"ensure list items are not overindented"
}
declare_clippy_lint! {
/// ### What it does
/// Checks if the first paragraph in the documentation of items listed in the module page is too long.
@ -617,6 +650,7 @@ impl_lint_pass!(Documentation => [
SUSPICIOUS_DOC_COMMENTS,
EMPTY_DOCS,
DOC_LAZY_CONTINUATION,
DOC_OVERINDENTED_LIST_ITEMS,
EMPTY_LINE_AFTER_OUTER_ATTR,
EMPTY_LINE_AFTER_DOC_COMMENTS,
TOO_LONG_FIRST_DOC_PARAGRAPH,

View file

@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
var.span,
"C-like enum variant discriminant is not portable to 32-bit targets",
);
};
}
}
}
}

View file

@ -3,7 +3,9 @@ use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::get_type_diagnostic_name;
use clippy_utils::usage::{local_used_after_expr, local_used_in};
use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
use clippy_utils::{
get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
};
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
@ -101,19 +103,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
};
if body.value.span.from_expansion() {
if body.params.is_empty() {
if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
REDUNDANT_CLOSURE,
expr.span,
"redundant closure",
"replace the closure with `Vec::new`",
"std::vec::Vec::new".into(),
Applicability::MachineApplicable,
);
}
if body.params.is_empty()
&& let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value)
{
let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" };
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
REDUNDANT_CLOSURE,
expr.span,
"redundant closure",
"replace the closure with `Vec::new`",
format!("{vec_crate}::vec::Vec::new"),
Applicability::MachineApplicable,
);
}
// skip `foo(|| macro!())`
return;

View file

@ -183,7 +183,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
.collect()
};
self.emit_sugg(spans, msg, help);
};
}
}
}

View file

@ -45,7 +45,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body:
for param in generics.params {
if param.is_impl_trait() {
report(cx, param, generics);
};
}
}
}
}

View file

@ -160,7 +160,7 @@ fn check_needless_must_use(
&& !is_must_use_ty(cx, future_ty)
{
return;
};
}
}
span_lint_and_help(

View file

@ -120,7 +120,7 @@ fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, B
let ecx = ConstEvalCtxt::new(cx);
if let Some(Constant::Int(c)) = ecx.eval(r) {
return Some((c, op.node, l));
};
}
if let Some(Constant::Int(c)) = ecx.eval(l) {
return Some((c, invert_op(op.node)?, r));
}

View file

@ -350,7 +350,7 @@ fn check_with_condition<'tcx>(
if cx.typeck_results().expr_ty(cond_left).is_signed() {
} else {
print_lint_and_sugg(cx, var_name, expr);
};
}
}
},
ExprKind::Path(QPath::TypeRelative(_, name)) => {

View file

@ -65,6 +65,6 @@ impl LateLintPass<'_> for IterOverHashType {
expr.span,
"iteration over unordered hash-based type",
);
};
}
}
}

View file

@ -41,6 +41,6 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
Some(ty.span.with_lo(local.pat.span.hi())),
"remove the explicit type `_` declaration",
);
};
}
}
}

View file

@ -279,6 +279,7 @@ mod non_copy_const;
mod non_expressive_names;
mod non_octal_unix_permissions;
mod non_send_fields_in_send_ty;
mod non_std_lazy_statics;
mod non_zero_suggestions;
mod nonstandard_macro_braces;
mod octal_escapes;
@ -372,8 +373,10 @@ mod unnecessary_literal_bound;
mod unnecessary_map_on_constructor;
mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports;
mod unnecessary_semicolon;
mod unnecessary_struct_initialization;
mod unnecessary_wraps;
mod unneeded_struct_pattern;
mod unnested_or_patterns;
mod unsafe_removed_from_name;
mod unused_async;
@ -680,7 +683,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
store.register_late_pass(move |_| Box::new(loops::Loops::new(conf)));
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf)));
store.register_late_pass(|_| Box::new(entry::HashMapPass));
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
@ -970,5 +973,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default());
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -1,4 +1,6 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::trait_ref_of_method;
use itertools::Itertools;
use rustc_ast::visit::{try_visit, walk_list};
@ -20,7 +22,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter as middle_nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
use rustc_session::impl_lint_pass;
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{Ident, kw};
@ -91,7 +93,19 @@ declare_clippy_lint! {
"unused lifetimes in function definitions"
}
declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
pub struct Lifetimes {
msrv: Msrv,
}
impl Lifetimes {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
}
}
impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
..
} = item.kind
{
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true);
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv);
} else if let ItemKind::Impl(impl_) = item.kind {
if !item.span.from_expansion() {
report_extra_impl_lifetimes(cx, impl_);
@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
item.generics,
item.span,
report_extra_lifetimes,
&self.msrv,
);
}
}
@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
TraitFn::Required(sig) => (None, Some(sig)),
TraitFn::Provided(id) => (Some(id), None),
};
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true);
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv);
}
}
extract_msrv_attr!(LateContext);
}
#[allow(clippy::too_many_arguments)]
fn check_fn_inner<'tcx>(
cx: &LateContext<'tcx>,
sig: &'tcx FnSig<'_>,
@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>(
generics: &'tcx Generics<'_>,
span: Span,
report_extra_lifetimes: bool,
msrv: &Msrv,
) {
if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) {
return;
@ -195,7 +214,7 @@ fn check_fn_inner<'tcx>(
}
}
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) {
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) {
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
return;
}
@ -216,6 +235,7 @@ fn could_use_elision<'tcx>(
body: Option<BodyId>,
trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>],
msrv: &Msrv,
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
// There are two scenarios where elision works:
// * no output references, all input references have different LT
@ -249,17 +269,17 @@ fn could_use_elision<'tcx>(
let input_lts = input_visitor.lts;
let output_lts = output_visitor.lts;
if let Some(trait_sig) = trait_sig {
if explicit_self_type(cx, func, trait_sig.first().copied()) {
return None;
}
if let Some(trait_sig) = trait_sig
&& non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv)
{
return None;
}
if let Some(body_id) = body {
let body = cx.tcx.hir().body(body_id);
let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
if explicit_self_type(cx, func, first_ident) {
if non_elidable_self_type(cx, func, first_ident, msrv) {
return None;
}
@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefI
.collect()
}
// elision doesn't work for explicit self types, see rust-lang/rust#69064
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
if let Some(ident) = ident
// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064
fn non_elidable_self_type<'tcx>(
cx: &LateContext<'tcx>,
func: &FnDecl<'tcx>,
ident: Option<Ident>,
msrv: &Msrv,
) -> bool {
if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION)
&& let Some(ident) = ident
&& ident.name == kw::SelfLower
&& !func.implicit_self.has_implicit_self()
&& let Some(self_ty) = func.inputs.first()
@ -488,11 +514,13 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
false
}
#[allow(clippy::struct_excessive_bools)]
struct Usage {
lifetime: Lifetime,
in_where_predicate: bool,
in_bounded_ty: bool,
in_generics_arg: bool,
lifetime_elision_impossible: bool,
}
struct LifetimeChecker<'cx, 'tcx, F> {
@ -501,6 +529,7 @@ struct LifetimeChecker<'cx, 'tcx, F> {
where_predicate_depth: usize,
bounded_ty_depth: usize,
generic_args_depth: usize,
lifetime_elision_impossible: bool,
phantom: std::marker::PhantomData<F>,
}
@ -525,6 +554,7 @@ where
where_predicate_depth: 0,
bounded_ty_depth: 0,
generic_args_depth: 0,
lifetime_elision_impossible: false,
phantom: std::marker::PhantomData,
}
}
@ -566,6 +596,7 @@ where
in_where_predicate: self.where_predicate_depth != 0,
in_bounded_ty: self.bounded_ty_depth != 0,
in_generics_arg: self.generic_args_depth != 0,
lifetime_elision_impossible: self.lifetime_elision_impossible,
});
}
}
@ -592,11 +623,44 @@ where
self.generic_args_depth -= 1;
}
fn visit_fn_decl(&mut self, fd: &'tcx FnDecl<'tcx>) -> Self::Result {
self.lifetime_elision_impossible = !is_candidate_for_elision(fd);
walk_fn_decl(self, fd);
self.lifetime_elision_impossible = false;
}
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
}
/// Check if `fd` supports function elision with an anonymous (or elided) lifetime,
/// and has a lifetime somewhere in its output type.
fn is_candidate_for_elision(fd: &FnDecl<'_>) -> bool {
struct V;
impl Visitor<'_> for V {
type Result = ControlFlow<bool>;
fn visit_lifetime(&mut self, lifetime: &Lifetime) -> Self::Result {
ControlFlow::Break(lifetime.is_elided() || lifetime.is_anonymous())
}
}
if fd.lifetime_elision_allowed
&& let Return(ret_ty) = fd.output
&& walk_unambig_ty(&mut V, ret_ty).is_break()
{
// The first encountered input lifetime will either be one on `self`, or will be the only lifetime.
fd.inputs
.iter()
.find_map(|ty| walk_unambig_ty(&mut V, ty).break_value())
.unwrap()
} else {
false
}
}
fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, generics);
@ -662,6 +726,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
Usage {
lifetime,
in_where_predicate: false,
lifetime_elision_impossible: false,
..
},
] = usages.as_slice()
@ -719,7 +784,7 @@ fn report_elidable_lifetimes(
|diag| {
if !include_suggestions {
return;
};
}
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);

View file

@ -251,7 +251,7 @@ impl LiteralDigitGrouping {
);
if !consistent {
return Err(WarningType::InconsistentDigitGrouping);
};
}
}
Ok(())

View file

@ -29,9 +29,9 @@ declare_clippy_lint! {
/// let y = "hello";
/// x.expect(&format!("{y:?}"));
/// ```
#[clippy::version = "1.83.0"]
#[clippy::version = "1.85.0"]
pub LITERAL_STRING_WITH_FORMATTING_ARGS,
suspicious,
nursery,
"Checks if string literals have formatting arguments"
}

View file

@ -830,7 +830,7 @@ impl Loops {
for_kv_map::check(cx, pat, arg, body);
mut_range_bound::check(cx, arg, body);
single_element_loop::check(cx, pat, arg, body, expr);
same_item_push::check(cx, pat, arg, body, expr);
same_item_push::check(cx, pat, arg, body, expr, &self.msrv);
manual_flatten::check(cx, pat, arg, body, span);
manual_find::check(cx, pat, arg, body, span, expr);
unused_enumerate_index::check(cx, pat, arg, body);

View file

@ -1,8 +1,9 @@
use super::SAME_ITEM_PUSH;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::path_to_local;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::Msrv;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{msrvs, path_to_local, std_or_core};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
@ -19,19 +20,30 @@ pub(super) fn check<'tcx>(
_: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
msrv: &Msrv,
) {
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) {
fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext, msrv: &Msrv) {
let mut app = Applicability::Unspecified;
let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0;
let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0;
span_lint_and_help(
let secondary_help = if msrv.meets(msrvs::REPEAT_N)
&& let Some(std_or_core) = std_or_core(cx)
{
format!("or `{vec_str}.extend({std_or_core}::iter::repeat_n({item_str}, SIZE))`")
} else {
format!("or `{vec_str}.resize(NEW_SIZE, {item_str})`")
};
span_lint_and_then(
cx,
SAME_ITEM_PUSH,
vec.span,
"it looks like the same item is being pushed into this Vec",
None,
format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"),
"it looks like the same item is being pushed into this `Vec`",
|diag| {
diag.help(format!("consider using `vec![{item_str};SIZE]`"))
.help(secondary_help);
},
);
}
@ -67,11 +79,11 @@ pub(super) fn check<'tcx>(
{
match init.kind {
// immutable bindings that are initialized with literal
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv),
// immutable bindings that are initialized with constant
ExprKind::Path(ref path) => {
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
emit_lint(cx, vec, pushed_item, ctxt);
emit_lint(cx, vec, pushed_item, ctxt, msrv);
}
},
_ => {},
@ -79,11 +91,11 @@ pub(super) fn check<'tcx>(
}
},
// constant
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt),
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt, msrv),
_ => {},
}
},
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv),
_ => {},
}
}

View file

@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_context;
use rustc_ast::ast::LitKind;
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
@ -99,7 +99,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
&& let QPath::Resolved(_, count_func_path) = count_func_qpath
&& let Some(segment_zero) = count_func_path.segments.first()
&& let Some(args) = segment_zero.args
&& let Some(real_ty_span) = args.args.first().map(|arg| arg.span())
&& let Some(real_ty_span) = args.args.first().map(GenericArg::span)
&& let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
{

View file

@ -102,12 +102,48 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
{
build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability);
}
// (x + (Y - 1)) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, div_rhs) {
build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability);
}
// ((Y - 1) + x) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, div_rhs) {
build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability);
}
// (x - (-Y - 1)) / Y
if inner_op.node == BinOpKind::Sub
&& let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = div_rhs.kind
&& differ_by_one(abs_div_rhs, inner_rhs)
{
build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability);
}
}
}
extract_msrv_attr!(LateContext);
}
/// Checks if two expressions represent non-zero integer literals such that `small_expr + 1 ==
/// large_expr`.
fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool {
if let ExprKind::Lit(small) = small_expr.kind
&& let ExprKind::Lit(large) = large_expr.kind
&& let LitKind::Int(s, _) = small.node
&& let LitKind::Int(l, _) = large.node
{
Some(l.get()) == s.get().checked_add(1)
} else if let ExprKind::Unary(UnOp::Neg, small_inner_expr) = small_expr.kind
&& let ExprKind::Unary(UnOp::Neg, large_inner_expr) = large_expr.kind
{
differ_by_one(large_inner_expr, small_inner_expr)
} else {
false
}
}
fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
match expr_ty.peel_refs().kind() {

View file

@ -7,7 +7,7 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator
use rustc_ast::LitKind::{Byte, Char};
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit};
use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
@ -202,8 +202,14 @@ fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
}
fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange {
if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind
&& let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind
if let PatExprKind::Lit {
lit: start_lit,
negated: false,
} = &start.kind
&& let PatExprKind::Lit {
lit: end_lit,
negated: false,
} = &end.kind
{
check_lit_range(start_lit, end_lit)
} else {

View file

@ -106,7 +106,7 @@ impl<'tcx> QuestionMark {
emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body);
},
}
};
}
}
}
@ -295,7 +295,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo
PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..)
) {
return;
};
}
let ty = typeck_results.pat_ty(pat);
// Option and Result are allowed, everything else isn't.
if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) {

View file

@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
}
},
_ => return,
};
}
let mut app = Applicability::MachineApplicable;
let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0;

View file

@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
let target_res = cx.qpath_res(target_path, target_arg.hir_id);
if target_res == Res::Err {
return;
};
}
if let Res::Local(hir_id) = target_res
&& let Some(used_mutably) = mutated_variables(then, cx)

View file

@ -34,7 +34,7 @@ fn get_cond_expr<'tcx>(
needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the
* cond */
});
};
}
None
}
@ -45,7 +45,7 @@ fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>>
if block.stmts.is_empty() {
return block.expr;
}
};
}
None
}
@ -68,14 +68,14 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr:
&& is_res_lang_ctor(cx, path_res(cx, callee), OptionSome)
&& path_to_local_id(arg, target);
}
};
}
false
}
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone);
};
}
false
}

View 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,
);
}

View file

@ -109,7 +109,7 @@ where
}
},
None => return None,
};
}
let mut app = Applicability::MachineApplicable;

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_unit_expr;
use clippy_utils::source::{expr_block, snippet};
use clippy_utils::source::expr_block;
use clippy_utils::sugg::Sugg;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
@ -17,17 +17,28 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
cx,
MATCH_BOOL,
expr.span,
"you seem to be trying to match on a boolean expression",
"`match` on a boolean expression",
move |diag| {
if arms.len() == 2 {
// no guards
let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
let mut app = Applicability::MachineApplicable;
let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
let test = Sugg::hir_with_applicability(cx, scrutinee, "_", &mut app);
if let PatExprKind::Lit { lit, .. } = arm_bool.kind {
match lit.node {
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),
match &lit.node {
LitKind::Bool(true) => Some(test),
LitKind::Bool(false) => Some(!test),
_ => None,
}
.map(|test| {
if let Some(guard) = &arms[0]
.guard
.map(|g| Sugg::hir_with_applicability(cx, g, "_", &mut app))
{
test.and(guard)
} else {
test
}
})
} else {
None
}
@ -35,39 +46,31 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
None
};
if let Some((true_expr, false_expr)) = exprs {
let mut app = Applicability::HasPlaceholders;
if let Some(test_sugg) = test_sugg {
let ctxt = expr.span.ctxt();
let (true_expr, false_expr) = (arms[0].body, arms[1].body);
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => Some(format!(
"if {} {} else {}",
snippet(cx, scrutinee.span, "b"),
test_sugg,
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(false, true) => Some(format!(
"if {} {}",
snippet(cx, scrutinee.span, "b"),
test_sugg,
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, false) => {
let test = Sugg::hir(cx, scrutinee, "..");
Some(format!(
"if {} {}",
!test,
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
))
},
(true, false) => Some(format!(
"if {} {}",
!test_sugg,
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, true) => None,
};
if let Some(sugg) = sugg {
diag.span_suggestion(
expr.span,
"consider using an `if`/`else` expression",
sugg,
Applicability::HasPlaceholders,
);
diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app);
}
}
}

View file

@ -117,7 +117,7 @@ where
if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
ex_new = ex_inner;
}
};
}
span_lint_and_sugg(
cx,
MATCH_LIKE_MATCHES_MACRO,

View file

@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatExpr, PatExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Span;

View file

@ -170,7 +170,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
);
});
},
};
}
}
enum CommonPrefixSearcher<'a> {

View file

@ -2,6 +2,7 @@ mod collapsible_match;
mod infallible_destructuring_match;
mod manual_filter;
mod manual_map;
mod manual_ok_err;
mod manual_unwrap_or;
mod manual_utils;
mod match_as_ref;
@ -972,6 +973,40 @@ declare_clippy_lint! {
"checks for unnecessary guards in match expressions"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for manual implementation of `.ok()` or `.err()`
/// on `Result` values.
///
/// ### Why is this bad?
/// Using `.ok()` or `.err()` rather than a `match` or
/// `if let` is less complex and more readable.
///
/// ### Example
/// ```no_run
/// # fn func() -> Result<u32, &'static str> { Ok(0) }
/// let a = match func() {
/// Ok(v) => Some(v),
/// Err(_) => None,
/// };
/// let b = if let Err(v) = func() {
/// Some(v)
/// } else {
/// None
/// };
/// ```
/// Use instead:
/// ```no_run
/// # fn func() -> Result<u32, &'static str> { Ok(0) }
/// let a = func().ok();
/// let b = func().err();
/// ```
#[clippy::version = "1.86.0"]
pub MANUAL_OK_ERR,
complexity,
"find manual implementations of `.ok()` or `.err()` on `Result`"
}
pub struct Matches {
msrv: Msrv,
infallible_destructuring_match_linted: bool,
@ -1013,6 +1048,7 @@ impl_lint_pass!(Matches => [
MANUAL_MAP,
MANUAL_FILTER,
REDUNDANT_GUARDS,
MANUAL_OK_ERR,
]);
impl<'tcx> LateLintPass<'tcx> for Matches {
@ -1091,6 +1127,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
manual_unwrap_or::check_match(cx, expr, ex, arms);
manual_map::check_match(cx, expr, ex, arms);
manual_filter::check_match(cx, ex, arms, expr);
manual_ok_err::check_match(cx, expr, ex, arms);
}
if self.infallible_destructuring_match_linted {
@ -1134,6 +1171,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
if_let.if_then,
else_expr,
);
manual_ok_err::check_if_let(
cx,
expr,
if_let.let_pat,
if_let.let_expr,
if_let.if_then,
else_expr,
);
}
}
redundant_pattern_match::check_if_let(

View file

@ -2,7 +2,7 @@ use std::ops::ControlFlow;
use crate::FxHashSet;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{indent_of, snippet};
use clippy_utils::source::{first_line_of_span, indent_of, snippet};
use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
use clippy_utils::{get_attr, is_lint_allowed};
use itertools::Itertools;
@ -152,7 +152,7 @@ fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
diag.multipart_suggestion(
suggestion_message,
vec![
(expr.span.shrink_to_lo(), replacement),
(first_line_of_span(cx, expr.span).shrink_to_lo(), replacement),
(found.found_span, scrutinee_replacement),
],
Applicability::MaybeIncorrect,
@ -441,8 +441,9 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> {
let parent_expr_before = self.parent_expr.replace(ex);
match ex.kind {
// Skip blocks because values in blocks will be dropped as usual.
ExprKind::Block(..) => (),
// Skip blocks because values in blocks will be dropped as usual, and await
// desugaring because temporary insides the future will have been dropped.
ExprKind::Block(..) | ExprKind::Match(_, _, MatchSource::AwaitDesugar) => (),
_ => walk_expr(self, ex),
}

View file

@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
err_ty = ty;
} else {
return;
};
}
span_lint_and_then(
cx,

View file

@ -13,7 +13,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) {
&& has_non_exhaustive_attr(cx.tcx, *adt_def)
{
return;
};
}
for arm in arms {
if let PatKind::Or(fields) = arm.pat.kind {
// look for multiple fields in this arm that contains at least one Wild pattern

View file

@ -62,5 +62,5 @@ pub(super) fn check<'tcx>(
),
applicability,
);
};
}
}

View file

@ -32,5 +32,5 @@ pub(super) fn check<'tcx>(
),
applicability,
);
};
}
}

View file

@ -46,5 +46,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
format!("{receiver}.as_bytes().get({n}).copied()"),
applicability,
);
};
}
}

View file

@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span,
// &T where T: Copy
ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
_ => return,
};
}
span_lint_and_sugg(
cx,
CLONED_INSTEAD_OF_COPIED,

View file

@ -1,8 +1,8 @@
use crate::methods::DRAIN_COLLECT;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_range_full;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{is_range_full, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
use rustc_lint::LateContext;
@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re
.then_some("Vec")
.or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
.or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs))
&& let Some(exec_context) = std_or_core(cx)
{
let recv = snippet(cx, recv.span, "<expr>");
let sugg = if let ty::Ref(..) = recv_ty.kind() {
format!("std::mem::take({recv})")
format!("{exec_context}::mem::take({recv})")
} else {
format!("std::mem::take(&mut {recv})")
format!("{exec_context}::mem::take(&mut {recv})")
};
span_lint_and_sugg(

View file

@ -37,7 +37,7 @@ pub(super) fn check(
"expect_err".to_string(),
Applicability::MachineApplicable,
);
};
}
}
/// Given a `Result<T, E>` type, return its data (`T`).

View file

@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(
if ty.is_str() && can_be_static_str(cx, arg) {
return false;
}
};
}
true
}

View file

@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
"into_iter()".to_string(),
Applicability::MaybeIncorrect,
);
};
}
}

View 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,
);
}
}

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{eager_or_lazy, higher, usage};
use clippy_utils::{eager_or_lazy, higher, std_or_core, usage};
use rustc_ast::LitKind;
use rustc_ast::ast::RangeLimits;
use rustc_data_structures::packed::Pu128;
@ -75,6 +75,7 @@ pub(super) fn check(
} = body_hir
&& !usage::BindingUsageFinder::are_params_used(cx, body_hir)
&& let Some(count) = extract_count_with_applicability(cx, range, &mut applicability)
&& let Some(exec_context) = std_or_core(cx)
{
let method_to_use_name;
let new_span;
@ -105,7 +106,7 @@ pub(super) fn check(
let mut parts = vec![
(
receiver.span.to(method_call_span),
format!("std::iter::{method_to_use_name}"),
format!("{exec_context}::iter::{method_to_use_name}"),
),
new_span,
];

View file

@ -58,6 +58,7 @@ mod manual_inspect;
mod manual_is_variant_and;
mod manual_next_back;
mod manual_ok_or;
mod manual_repeat_n;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
mod manual_try_fold;
@ -101,6 +102,7 @@ mod single_char_add_str;
mod single_char_insert_string;
mod single_char_push_string;
mod skip_while_next;
mod sliced_string_as_bytes;
mod stable_sort_primitive;
mod str_split;
mod str_splitn;
@ -130,6 +132,7 @@ mod unnecessary_to_owned;
mod unused_enumerate_index;
mod unwrap_expect_used;
mod useless_asref;
mod useless_nonzero_new_unchecked;
mod utils;
mod vec_resize_to_zero;
mod verbose_file_reads;
@ -2416,14 +2419,14 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.then_some(..).unwrap_or(..)`
/// Checks for unnecessary method chains that can be simplified into `if .. else ..`.
///
/// ### Why is this bad?
/// This can be written more clearly with `if .. else ..`
///
/// ### Limitations
/// This lint currently only looks for usages of
/// `.then_some(..).unwrap_or(..)`, but will be expanded
/// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded
/// to account for similar patterns.
///
/// ### Example
@ -4311,6 +4314,84 @@ declare_clippy_lint! {
"using `Iterator::last` on a `DoubleEndedIterator`"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `NonZero*::new_unchecked()` being used in a `const` context.
///
/// ### Why is this bad?
///
/// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a
/// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical
/// runtime performances while not requiring `unsafe`.
///
/// ### Example
/// ```no_run
/// use std::num::NonZeroUsize;
/// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) };
/// ```
/// Use instead:
/// ```no_run
/// use std::num::NonZeroUsize;
/// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap();
/// ```
#[clippy::version = "1.86.0"]
pub USELESS_NONZERO_NEW_UNCHECKED,
complexity,
"using `NonZero::new_unchecked()` in a `const` context"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `repeat().take()` that can be replaced with `repeat_n()`.
///
/// ### Why is this bad?
///
/// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning.
///
/// ### Example
/// ```no_run
/// let _ = std::iter::repeat(10).take(3);
/// ```
/// Use instead:
/// ```no_run
/// let _ = std::iter::repeat_n(10, 3);
/// ```
#[clippy::version = "1.86.0"]
pub MANUAL_REPEAT_N,
style,
"detect `repeat().take()` that can be replaced with `repeat_n()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for string slices immediantly followed by `as_bytes`.
///
/// ### Why is this bad?
/// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic.
///
/// ### Known problems
/// In some cases, the UTF-8 validation and potential panic from string slicing may be required for
/// the code's correctness. If you need to ensure the slice boundaries fall on valid UTF-8 character
/// boundaries, the original form (`s[1..5].as_bytes()`) should be preferred.
///
/// ### Example
/// ```rust
/// let s = "Lorem ipsum";
/// s[1..5].as_bytes();
/// ```
/// Use instead:
/// ```rust
/// let s = "Lorem ipsum";
/// &s.as_bytes()[1..5];
/// ```
#[clippy::version = "1.86.0"]
pub SLICED_STRING_AS_BYTES,
perf,
"slicing a string and immediately calling as_bytes is less efficient and can lead to panics"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -4477,6 +4558,9 @@ impl_lint_pass!(Methods => [
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
UNNECESSARY_MAP_OR,
DOUBLE_ENDED_ITERATOR_LAST,
USELESS_NONZERO_NEW_UNCHECKED,
MANUAL_REPEAT_N,
SLICED_STRING_AS_BYTES,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4505,6 +4589,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
from_iter_instead_of_collect::check(cx, expr, args, func);
unnecessary_fallible_conversions::check_function(cx, expr, func);
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv);
},
ExprKind::MethodCall(method_call, receiver, args, _) => {
let method_span = method_call.ident.span;
@ -4743,6 +4828,7 @@ impl Methods {
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
redundant_as_str::check(cx, expr, recv, as_str_span, span);
}
sliced_string_as_bytes::check(cx, expr, recv);
},
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
@ -4924,8 +5010,8 @@ impl Methods {
},
("is_empty", []) => {
match method_call(recv) {
Some(("as_bytes", prev_recv, [], _, _)) => {
needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span);
Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => {
needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span);
},
Some(("as_str", recv, [], as_str_span, _)) => {
redundant_as_str::check(cx, expr, recv, as_str_span, span);
@ -4962,8 +5048,8 @@ impl Methods {
double_ended_iterator_last::check(cx, expr, recv, call_span);
},
("len", []) => {
if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span);
if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) {
needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span);
}
},
("lock", []) => {
@ -5015,7 +5101,7 @@ impl Methods {
option_map_or_none::check(cx, expr, recv, def, map);
manual_ok_or::check(cx, expr, recv, def, map);
option_map_or_err_ok::check(cx, expr, recv, def, map);
unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv);
unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv);
},
("map_or_else", [def, map]) => {
result_map_or_else_none::check(cx, expr, recv, def, map);
@ -5146,6 +5232,7 @@ impl Methods {
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", [arg]) => {
iter_out_of_bounds::check_take(cx, expr, recv, arg);
manual_repeat_n::check(cx, expr, recv, arg, &self.msrv);
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,
@ -5220,8 +5307,8 @@ impl Methods {
Some(("map", m_recv, [m_arg], span, _)) => {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
},
Some(("then_some", t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method);
},
_ => {},
}

View file

@ -8,18 +8,16 @@ use rustc_span::Span;
use super::NEEDLESS_AS_BYTES;
pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) {
if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
&& let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs()
&& (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
{
pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) {
let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs();
if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() {
let mut app = Applicability::MachineApplicable;
let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
span_lint_and_sugg(
cx,
NEEDLESS_AS_BYTES,
span,
"needless call to `as_bytes()`",
format!("needless call to `{prev_method}`"),
format!("`{method}()` can be called directly on strings"),
format!("{sugg}.{method}()"),
app,

View file

@ -1,8 +1,11 @@
use super::OBFUSCATED_IF_ELSE;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::ExprKind;
use rustc_lint::LateContext;
pub(super) fn check<'tcx>(
@ -11,19 +14,30 @@ pub(super) fn check<'tcx>(
then_recv: &'tcx hir::Expr<'_>,
then_arg: &'tcx hir::Expr<'_>,
unwrap_arg: &'tcx hir::Expr<'_>,
then_method_name: &str,
) {
// something.then_some(blah).unwrap_or(blah)
// ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
let recv_ty = cx.typeck_results().expr_ty(then_recv);
if recv_ty.is_bool() {
let mut applicability = Applicability::MachineApplicable;
let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
};
let if_then = match then_method_name {
"then" if let ExprKind::Closure(closure) = then_arg.kind => {
let body = cx.tcx.hir().body(closure.body);
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
},
"then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
_ => String::new().into(),
};
let sugg = format!(
"if {} {{ {} }} else {{ {} }}",
snippet_with_applicability(cx, then_recv.span, "..", &mut applicability),
snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability),
if_then,
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
);
@ -31,8 +45,7 @@ pub(super) fn check<'tcx>(
cx,
OBFUSCATED_IF_ELSE,
expr.span,
"use of `.then_some(..).unwrap_or(..)` can be written \
more clearly with `if .. else ..`",
"this method chain can be written more clearly with `if .. else ..`",
"try",
sugg,
applicability,

View file

@ -37,7 +37,7 @@ pub(super) fn check(
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
} else {
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
};
}
span_lint_and_sugg(
cx,

View 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,
);
}
}

View file

@ -87,7 +87,7 @@ pub fn check_for_loop_iter(
// skip lint
return true;
}
};
}
// the lint should not be executed if no violation happens
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind

View file

@ -1,9 +1,8 @@
use std::borrow::Cow;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::sugg::{Sugg, make_binop};
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
use clippy_utils::visitors::is_local_used;
@ -12,7 +11,7 @@ use rustc_ast::LitKind::Bool;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use rustc_span::{Span, sym};
use super::UNNECESSARY_MAP_OR;
@ -42,13 +41,14 @@ pub(super) fn check<'a>(
recv: &Expr<'_>,
def: &Expr<'_>,
map: &Expr<'_>,
method_span: Span,
msrv: &Msrv,
) {
let ExprKind::Lit(def_kind) = def.kind else {
return;
};
let recv_ty = cx.typeck_results().expr_ty(recv);
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
let Bool(def_bool) = def_kind.node else {
return;
@ -60,6 +60,8 @@ pub(super) fn check<'a>(
Some(_) | None => return,
};
let ext_def_span = def.span.until(map.span);
let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind
&& let closure_body = cx.tcx.hir().body(map_closure.body)
&& let closure_body_value = closure_body.value.peel_blocks()
@ -114,26 +116,17 @@ pub(super) fn check<'a>(
}
.into_string();
(sugg, "a standard comparison", app)
} else if !def_bool
&& msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
{
(vec![(expr.span, sugg)], "a standard comparison", app)
} else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) {
let suggested_name = variant.method_name();
(
format!("{recv_callsite}.{suggested_name}({span_callsite})",),
vec![(method_span, suggested_name.into()), (ext_def_span, String::default())],
suggested_name,
Applicability::MachineApplicable,
)
} else if def_bool
&& matches!(variant, Variant::Some)
&& msrv.meets(msrvs::IS_NONE_OR)
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
{
} else if def_bool && matches!(variant, Variant::Some) && msrv.meets(msrvs::IS_NONE_OR) {
(
format!("{recv_callsite}.is_none_or({span_callsite})"),
vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())],
"is_none_or",
Applicability::MachineApplicable,
)
@ -145,13 +138,13 @@ pub(super) fn check<'a>(
return;
}
span_lint_and_sugg(
span_lint_and_then(
cx,
UNNECESSARY_MAP_OR,
expr.span,
"this `map_or` can be simplified",
format!("use {method} instead"),
sugg,
applicability,
|diag| {
diag.multipart_suggestion_verbose(format!("use {method} instead"), sugg, applicability);
},
);
}

View file

@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind};
use rustc_middle::ty;
use rustc_middle::ty::GenericArgKind;
use rustc_span::sym;
use rustc_span::symbol::Ident;
use std::iter;
@ -43,8 +44,7 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
},
// The two exprs are method calls.
// Check to see that the function is the same and the arguments are mirrored
// This is enough because the receiver of the method is listed in the arguments
// Check to see that the function is the same and the arguments and receivers are mirrored
(
ExprKind::MethodCall(left_segment, left_receiver, left_args, _),
ExprKind::MethodCall(right_segment, right_receiver, right_args, _),

View file

@ -217,10 +217,13 @@ fn check_into_iter_call_arg(
&& implements_trait(cx, parent_ty, iterator_trait_id, &[])
&& let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
&& let Some(receiver_snippet) = receiver.span.get_source_text(cx)
// If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624.
&& !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow)
{
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
return true;
}
let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
"copied"
} else {

View 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");
});
}
}
}

View file

@ -214,11 +214,12 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
);
},
);
};
}
if let StmtKind::Semi(expr) = stmt.kind
&& let ExprKind::Binary(ref binop, a, b) = expr.kind
&& (binop.node == BinOpKind::And || binop.node == BinOpKind::Or)
&& let Some(sugg) = Sugg::hir_opt(cx, a)
&& let ExprKind::Binary(binop, a, b) = &expr.kind
&& matches!(binop.node, BinOpKind::And | BinOpKind::Or)
&& !stmt.span.from_expansion()
&& expr.span.eq_ctxt(stmt.span)
{
span_lint_hir_and_then(
cx,
@ -227,16 +228,14 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
stmt.span,
"boolean short circuit operator in statement may be clearer using an explicit test",
|diag| {
let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
diag.span_suggestion(
stmt.span,
"replace it with",
format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),),
Applicability::MachineApplicable, // snippet
);
let mut app = Applicability::MachineApplicable;
let test = Sugg::hir_with_context(cx, a, expr.span.ctxt(), "_", &mut app);
let test = if binop.node == BinOpKind::Or { !test } else { test };
let then = Sugg::hir_with_context(cx, b, expr.span.ctxt(), "_", &mut app);
diag.span_suggestion(stmt.span, "replace it with", format!("if {test} {{ {then}; }}"), app);
},
);
};
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {

View file

@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
}) => impl_params.push((path.segments[0].ident.to_string(), path.span)),
GenericArg::Type(_) => return,
_ => (),
};
}
}
// find the type that the Impl is for

View file

@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
| hir::ItemKind::GlobalAsm(..)
| hir::ItemKind::Impl { .. }
| hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span),
};
}
let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());

View file

@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
// this prevents ICEs such as when self is a type parameter or a primitive type
// (see #10887, #11063)
&& let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res
&& cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug])
&& cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id)
// don't trigger if this impl was derived
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
&& !item.span.from_expansion()

View file

@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
| hir::ItemKind::ForeignMod { .. }
| hir::ItemKind::Impl { .. }
| hir::ItemKind::Use(..) => {},
};
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {

View file

@ -56,10 +56,10 @@ impl EarlyLintPass for MultiAssignments {
if let ExprKind::Assign(target, source, _) = &expr.kind {
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
};
}
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
}
};
}
}
}

View file

@ -171,7 +171,7 @@ fn collect_unsafe_exprs<'tcx>(
},
_ => {},
};
}
Continue::<(), _>(Descend::Yes)
});

View file

@ -96,10 +96,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View file

@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex {
ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg),
_ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg),
};
}
}
}
}

View file

@ -107,6 +107,10 @@ struct LocalAssign {
impl LocalAssign {
fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
if expr.span.from_expansion() {
return None;
}
if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
if lhs.span.from_expansion() {
return None;
@ -336,7 +340,7 @@ fn check<'tcx>(
);
},
_ => {},
};
}
Some(())
}

View file

@ -37,7 +37,9 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]);
impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind {
if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind
&& !item.span.from_expansion()
{
let attrs = cx.tcx.hir().attrs(item.hir_id());
let mut app = Applicability::MaybeIncorrect;
let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app);

View file

@ -89,16 +89,6 @@ declare_clippy_lint! {
///
/// The `const` value should be stored inside a `static` item.
///
/// ### Known problems
/// When an enum has variants with interior mutability, use of its non
/// interior mutable variants can generate false positives. See issue
/// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
///
/// Types that have underlying or potential interior mutability trigger the lint whether
/// the interior mutable field is used or not. See issues
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
///
/// ### Example
/// ```no_run
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};

Some files were not shown because too many files have changed in this diff Show more