mirror of
https://github.com/rust-lang/rust-clippy
synced 2025-02-16 05:58:41 +00:00
Merge commit '26ac6aab023393c94edf42f38f6ad31196009643'
This commit is contained in:
parent
beeaee9785
commit
aa220c7ee7
157 changed files with 5167 additions and 820 deletions
2
.github/workflows/clippy.yml
vendored
2
.github/workflows/clippy.yml
vendored
|
@ -38,7 +38,7 @@ jobs:
|
||||||
github_token: "${{ secrets.github_token }}"
|
github_token: "${{ secrets.github_token }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
run: rustup show active-toolchain
|
run: rustup show active-toolchain
|
||||||
|
|
10
.github/workflows/clippy_bors.yml
vendored
10
.github/workflows/clippy_bors.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
github_token: "${{ secrets.github_token }}"
|
github_token: "${{ secrets.github_token }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.ref }}
|
ref: ${{ github.ref }}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ jobs:
|
||||||
github_token: "${{ secrets.github_token }}"
|
github_token: "${{ secrets.github_token }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install i686 dependencies
|
- name: Install i686 dependencies
|
||||||
if: matrix.host == 'i686-unknown-linux-gnu'
|
if: matrix.host == 'i686-unknown-linux-gnu'
|
||||||
|
@ -151,7 +151,7 @@ jobs:
|
||||||
github_token: "${{ secrets.github_token }}"
|
github_token: "${{ secrets.github_token }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
run: rustup show active-toolchain
|
run: rustup show active-toolchain
|
||||||
|
@ -175,7 +175,7 @@ jobs:
|
||||||
github_token: "${{ secrets.github_token }}"
|
github_token: "${{ secrets.github_token }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
run: rustup show active-toolchain
|
run: rustup show active-toolchain
|
||||||
|
@ -231,7 +231,7 @@ jobs:
|
||||||
github_token: "${{ secrets.github_token }}"
|
github_token: "${{ secrets.github_token }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install toolchain
|
- name: Install toolchain
|
||||||
run: rustup show active-toolchain
|
run: rustup show active-toolchain
|
||||||
|
|
2
.github/workflows/clippy_dev.yml
vendored
2
.github/workflows/clippy_dev.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
# Setup
|
# Setup
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|
4
.github/workflows/deploy.yml
vendored
4
.github/workflows/deploy.yml
vendored
|
@ -21,10 +21,10 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
# Setup
|
# Setup
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ env.TARGET_BRANCH }}
|
ref: ${{ env.TARGET_BRANCH }}
|
||||||
path: 'out'
|
path: 'out'
|
||||||
|
|
2
.github/workflows/remark.yml
vendored
2
.github/workflows/remark.yml
vendored
|
@ -16,7 +16,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
# Setup
|
# Setup
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
|
|
|
@ -5105,6 +5105,7 @@ Released 2018-09-13
|
||||||
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
|
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
|
||||||
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
|
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
|
||||||
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
|
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
|
||||||
|
[`empty_enum_variants_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum_variants_with_brackets
|
||||||
[`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
|
[`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
|
||||||
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
|
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
|
||||||
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
|
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
|
||||||
|
@ -5294,6 +5295,7 @@ Released 2018-09-13
|
||||||
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
|
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
|
||||||
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
|
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
|
||||||
[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
|
[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
|
||||||
|
[`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and
|
||||||
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
|
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
|
||||||
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
|
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
|
||||||
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
|
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
|
||||||
|
@ -5440,6 +5442,7 @@ Released 2018-09-13
|
||||||
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
|
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
|
||||||
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
|
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
|
||||||
[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
|
[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
|
||||||
|
[`option_as_ref_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_cloned
|
||||||
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
|
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
|
||||||
[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
|
[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
|
||||||
[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
|
[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
|
||||||
|
@ -5483,6 +5486,7 @@ Released 2018-09-13
|
||||||
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
|
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
|
||||||
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
|
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
|
||||||
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
|
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
|
||||||
|
[`pub_underscore_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields
|
||||||
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
|
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
|
||||||
[`pub_with_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_with_shorthand
|
[`pub_with_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_with_shorthand
|
||||||
[`pub_without_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_without_shorthand
|
[`pub_without_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_without_shorthand
|
||||||
|
@ -5580,6 +5584,7 @@ Released 2018-09-13
|
||||||
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
||||||
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
|
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
|
||||||
[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
|
[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
|
||||||
|
[`str_split_at_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_split_at_newline
|
||||||
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
|
[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
|
||||||
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
|
[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
|
||||||
[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
|
[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
|
||||||
|
@ -5612,6 +5617,7 @@ Released 2018-09-13
|
||||||
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
|
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
|
||||||
[`test_attr_in_doctest`]: https://rust-lang.github.io/rust-clippy/master/index.html#test_attr_in_doctest
|
[`test_attr_in_doctest`]: https://rust-lang.github.io/rust-clippy/master/index.html#test_attr_in_doctest
|
||||||
[`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module
|
[`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module
|
||||||
|
[`thread_local_initializer_can_be_made_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#thread_local_initializer_can_be_made_const
|
||||||
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
|
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
|
||||||
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
|
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
|
||||||
[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
|
[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
|
||||||
|
@ -5810,4 +5816,5 @@ Released 2018-09-13
|
||||||
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
|
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
|
||||||
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
|
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
|
||||||
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
|
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
|
||||||
|
[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
|
||||||
<!-- end autogenerated links to configuration documentation -->
|
<!-- end autogenerated links to configuration documentation -->
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// REUSE-IgnoreStart
|
// REUSE-IgnoreStart
|
||||||
|
|
||||||
Copyright 2014-2022 The Rust Project Developers
|
Copyright 2014-2024 The Rust Project Developers
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
|
|
@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
|
||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2014-2022 The Rust Project Developers
|
Copyright 2014-2024 The Rust Project Developers
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2014-2022 The Rust Project Developers
|
Copyright (c) 2014-2024 The Rust Project Developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Permission is hereby granted, free of charge, to any
|
||||||
person obtaining a copy of this software and associated
|
person obtaining a copy of this software and associated
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
|
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
|
||||||
|
|
||||||
[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||||
|
|
||||||
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
|
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
|
||||||
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
|
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
|
||||||
|
@ -278,7 +278,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
|
||||||
|
|
||||||
<!-- REUSE-IgnoreStart -->
|
<!-- REUSE-IgnoreStart -->
|
||||||
|
|
||||||
Copyright 2014-2023 The Rust Project Developers
|
Copyright 2014-2024 The Rust Project Developers
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
|
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
A collection of lints to catch common mistakes and improve your
|
A collection of lints to catch common mistakes and improve your
|
||||||
[Rust](https://github.com/rust-lang/rust) code.
|
[Rust](https://github.com/rust-lang/rust) code.
|
||||||
|
|
||||||
[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||||
|
|
||||||
Lints are divided into categories, each with a default [lint
|
Lints are divided into categories, each with a default [lint
|
||||||
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
|
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
- [Clippy's Lints](lints.md)
|
- [Clippy's Lints](lints.md)
|
||||||
- [Continuous Integration](continuous_integration/README.md)
|
- [Continuous Integration](continuous_integration/README.md)
|
||||||
- [GitHub Actions](continuous_integration/github_actions.md)
|
- [GitHub Actions](continuous_integration/github_actions.md)
|
||||||
|
- [GitLab CI](continuous_integration/gitlab.md)
|
||||||
- [Travis CI](continuous_integration/travis.md)
|
- [Travis CI](continuous_integration/travis.md)
|
||||||
- [Development](development/README.md)
|
- [Development](development/README.md)
|
||||||
- [Basics](development/basics.md)
|
- [Basics](development/basics.md)
|
||||||
|
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
clippy_check:
|
clippy_check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Run Clippy
|
- name: Run Clippy
|
||||||
run: cargo clippy --all-targets --all-features
|
run: cargo clippy --all-targets --all-features
|
||||||
```
|
```
|
||||||
|
|
16
book/src/continuous_integration/gitlab.md
Normal file
16
book/src/continuous_integration/gitlab.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# GitLab CI
|
||||||
|
|
||||||
|
You can add Clippy to GitLab CI by using the latest stable [rust docker image](https://hub.docker.com/_/rust),
|
||||||
|
as it is shown in the `.gitlab-ci.yml` CI configuration file below,
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# Make sure CI fails on all warnings, including Clippy lints
|
||||||
|
variables:
|
||||||
|
RUSTFLAGS: "-Dwarnings"
|
||||||
|
|
||||||
|
clippy_check:
|
||||||
|
image: rust:latest
|
||||||
|
script:
|
||||||
|
- rustup component add clippy
|
||||||
|
- cargo clippy --all-targets --all-features
|
||||||
|
```
|
|
@ -102,7 +102,7 @@ let x: Option<u32> = Some(42);
|
||||||
m!(x, x.unwrap());
|
m!(x, x.unwrap());
|
||||||
```
|
```
|
||||||
|
|
||||||
If the `m!(x, x.unwrapp());` line is expanded, we would get two expanded
|
If the `m!(x, x.unwrap());` line is expanded, we would get two expanded
|
||||||
expressions:
|
expressions:
|
||||||
|
|
||||||
- `x.is_some()` (from the `$a.is_some()` line in the `m` macro)
|
- `x.is_some()` (from the `$a.is_some()` line in the `m` macro)
|
||||||
|
|
|
@ -133,7 +133,7 @@ in this chapter:
|
||||||
- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
|
- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
|
||||||
- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
|
- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
|
||||||
|
|
||||||
[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Adt
|
[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Adt
|
||||||
[AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html
|
[AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html
|
||||||
[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
|
[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
|
||||||
[node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type
|
[node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type
|
||||||
|
@ -144,7 +144,7 @@ in this chapter:
|
||||||
[LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
|
[LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
|
||||||
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/typeck_results/struct.TypeckResults.html#method.pat_ty
|
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/typeck_results/struct.TypeckResults.html#method.pat_ty
|
||||||
[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
|
[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
|
||||||
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html
|
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html
|
||||||
[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
|
[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
|
||||||
[middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html
|
[middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html
|
||||||
[hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html
|
[hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html
|
||||||
|
|
|
@ -805,3 +805,13 @@ for _ in &mut *rmvec {}
|
||||||
* [`missing_errors_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc)
|
* [`missing_errors_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc)
|
||||||
|
|
||||||
|
|
||||||
|
## `pub-underscore-fields-behavior`
|
||||||
|
|
||||||
|
|
||||||
|
**Default Value:** `"PublicallyExported"`
|
||||||
|
|
||||||
|
---
|
||||||
|
**Affected lints:**
|
||||||
|
* [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::msrvs::Msrv;
|
use crate::msrvs::Msrv;
|
||||||
use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, Rename};
|
use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
|
||||||
use crate::ClippyConfiguration;
|
use crate::ClippyConfiguration;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
@ -547,6 +547,11 @@ define_Conf! {
|
||||||
///
|
///
|
||||||
/// Whether to also run the listed lints on private items.
|
/// Whether to also run the listed lints on private items.
|
||||||
(check_private_items: bool = false),
|
(check_private_items: bool = false),
|
||||||
|
/// Lint: PUB_UNDERSCORE_FIELDS
|
||||||
|
///
|
||||||
|
/// Lint "public" fields in a struct that are prefixed with an underscore based on their
|
||||||
|
/// exported visibility, or whether they are marked as "pub".
|
||||||
|
(pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PublicallyExported),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for the configuration file.
|
/// Search for the configuration file.
|
||||||
|
|
|
@ -96,6 +96,9 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
|
||||||
doc_comment.make_ascii_lowercase();
|
doc_comment.make_ascii_lowercase();
|
||||||
let lints: Vec<String> = doc_comment
|
let lints: Vec<String> = doc_comment
|
||||||
.split_off(DOC_START.len())
|
.split_off(DOC_START.len())
|
||||||
|
.lines()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
.split(", ")
|
.split(", ")
|
||||||
.map(str::to_string)
|
.map(str::to_string)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -17,7 +17,7 @@ macro_rules! msrv_aliases {
|
||||||
// names may refer to stabilized feature flags or library items
|
// names may refer to stabilized feature flags or library items
|
||||||
msrv_aliases! {
|
msrv_aliases! {
|
||||||
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
|
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
|
||||||
1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN }
|
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
|
||||||
1,68,0 { PATH_MAIN_SEPARATOR_STR }
|
1,68,0 { PATH_MAIN_SEPARATOR_STR }
|
||||||
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
|
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
|
||||||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
|
||||||
|
|
|
@ -126,3 +126,9 @@ unimplemented_serialize! {
|
||||||
Rename,
|
Rename,
|
||||||
MacroMatcher,
|
MacroMatcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
pub enum PubUnderscoreFieldsBehaviour {
|
||||||
|
PublicallyExported,
|
||||||
|
AllPubFields,
|
||||||
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ pub fn create(
|
||||||
if pass == "early" {
|
if pass == "early" {
|
||||||
println!(
|
println!(
|
||||||
"\n\
|
"\n\
|
||||||
NOTE: Use a late pass unless you need something specific from\
|
NOTE: Use a late pass unless you need something specific from\n\
|
||||||
an early pass, as they lack many features and utilities"
|
an early pass, as they lack many features and utilities"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use clippy_utils::consts::{constant, Constant};
|
use clippy_utils::consts::{constant, Constant};
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::{method_chain_args, sext};
|
use clippy_utils::{clip, method_chain_args, sext};
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty, UintTy};
|
||||||
|
|
||||||
use super::CAST_SIGN_LOSS;
|
use super::CAST_SIGN_LOSS;
|
||||||
|
|
||||||
|
const METHODS_RET_POSITIVE: &[&str] = &["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||||
if should_lint(cx, cast_op, cast_from, cast_to) {
|
if should_lint(cx, cast_op, cast_from, cast_to) {
|
||||||
span_lint(
|
span_lint(
|
||||||
|
@ -25,33 +27,28 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't lint for positive constants.
|
// Don't lint if `cast_op` is known to be positive.
|
||||||
let const_val = constant(cx, cx.typeck_results(), cast_op);
|
if let Sign::ZeroOrPositive = expr_sign(cx, cast_op, cast_from) {
|
||||||
if let Some(Constant::Int(n)) = const_val
|
|
||||||
&& let ty::Int(ity) = *cast_from.kind()
|
|
||||||
&& sext(cx.tcx, n, ity) >= 0
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't lint for the result of methods that always return non-negative values.
|
let (mut uncertain_count, mut negative_count) = (0, 0);
|
||||||
if let ExprKind::MethodCall(path, ..) = cast_op.kind {
|
// Peel off possible binary expressions, e.g. x * x * y => [x, x, y]
|
||||||
let mut method_name = path.ident.name.as_str();
|
let Some(exprs) = exprs_with_selected_binop_peeled(cast_op) else {
|
||||||
let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
|
// Assume cast sign lose if we cannot determine the sign of `cast_op`
|
||||||
|
return true;
|
||||||
if method_name == "unwrap"
|
};
|
||||||
&& let Some(arglist) = method_chain_args(cast_op, &["unwrap"])
|
for expr in exprs {
|
||||||
&& let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind
|
let ty = cx.typeck_results().expr_ty(expr);
|
||||||
{
|
match expr_sign(cx, expr, ty) {
|
||||||
method_name = inner_path.ident.name.as_str();
|
Sign::Negative => negative_count += 1,
|
||||||
}
|
Sign::Uncertain => uncertain_count += 1,
|
||||||
|
Sign::ZeroOrPositive => (),
|
||||||
if allowed_methods.iter().any(|&name| method_name == name) {
|
};
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
// Lint if there are odd number of uncertain or negative results
|
||||||
|
uncertain_count % 2 == 1 || negative_count % 2 == 1
|
||||||
},
|
},
|
||||||
|
|
||||||
(false, true) => !cast_to.is_signed(),
|
(false, true) => !cast_to.is_signed(),
|
||||||
|
@ -59,3 +56,97 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
|
||||||
(_, _) => false,
|
(_, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_const_int_eval(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Option<i128> {
|
||||||
|
if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
|
||||||
|
&& let ty::Int(ity) = *ty.kind()
|
||||||
|
{
|
||||||
|
return Some(sext(cx.tcx, n, ity));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Sign {
|
||||||
|
ZeroOrPositive,
|
||||||
|
Negative,
|
||||||
|
Uncertain,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign {
|
||||||
|
// Try evaluate this expr first to see if it's positive
|
||||||
|
if let Some(val) = get_const_int_eval(cx, expr, ty) {
|
||||||
|
return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative };
|
||||||
|
}
|
||||||
|
// Calling on methods that always return non-negative values.
|
||||||
|
if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind {
|
||||||
|
let mut method_name = path.ident.name.as_str();
|
||||||
|
|
||||||
|
if method_name == "unwrap"
|
||||||
|
&& let Some(arglist) = method_chain_args(expr, &["unwrap"])
|
||||||
|
&& let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind
|
||||||
|
{
|
||||||
|
method_name = inner_path.ident.name.as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
if method_name == "pow"
|
||||||
|
&& let [arg] = args
|
||||||
|
{
|
||||||
|
return pow_call_result_sign(cx, caller, arg);
|
||||||
|
} else if METHODS_RET_POSITIVE.iter().any(|&name| method_name == name) {
|
||||||
|
return Sign::ZeroOrPositive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Sign::Uncertain
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the sign of the `pow` call's result.
|
||||||
|
///
|
||||||
|
/// If the caller is a positive number, the result is always positive,
|
||||||
|
/// If the `power_of` is a even number, the result is always positive as well,
|
||||||
|
/// Otherwise a [`Sign::Uncertain`] will be returned.
|
||||||
|
fn pow_call_result_sign(cx: &LateContext<'_>, caller: &Expr<'_>, power_of: &Expr<'_>) -> Sign {
|
||||||
|
let caller_ty = cx.typeck_results().expr_ty(caller);
|
||||||
|
if let Some(caller_val) = get_const_int_eval(cx, caller, caller_ty)
|
||||||
|
&& caller_val >= 0
|
||||||
|
{
|
||||||
|
return Sign::ZeroOrPositive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), power_of)
|
||||||
|
&& clip(cx.tcx, n, UintTy::U32) % 2 == 0
|
||||||
|
{
|
||||||
|
return Sign::ZeroOrPositive;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sign::Uncertain
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
|
||||||
|
/// which the result could always be positive under certain condition.
|
||||||
|
///
|
||||||
|
/// Other operators such as `+`/`-` causing the result's sign hard to determine, which we will
|
||||||
|
/// return `None`
|
||||||
|
fn exprs_with_selected_binop_peeled<'a>(expr: &'a Expr<'_>) -> Option<Vec<&'a Expr<'a>>> {
|
||||||
|
#[inline]
|
||||||
|
fn collect_operands<'a>(expr: &'a Expr<'a>, operands: &mut Vec<&'a Expr<'a>>) -> Option<()> {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Binary(op, lhs, rhs) => {
|
||||||
|
if matches!(op.node, BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem) {
|
||||||
|
collect_operands(lhs, operands);
|
||||||
|
operands.push(rhs);
|
||||||
|
} else {
|
||||||
|
// Things are complicated when there are other binary ops exist,
|
||||||
|
// abort checking by returning `None` for now.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => operands.push(expr),
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = vec![];
|
||||||
|
collect_operands(expr, &mut res)?;
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
|
@ -150,7 +150,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
|
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
|
||||||
crate::empty_drop::EMPTY_DROP_INFO,
|
crate::empty_drop::EMPTY_DROP_INFO,
|
||||||
crate::empty_enum::EMPTY_ENUM_INFO,
|
crate::empty_enum::EMPTY_ENUM_INFO,
|
||||||
crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
|
crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO,
|
||||||
|
crate::empty_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
|
||||||
crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,
|
crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,
|
||||||
crate::endian_bytes::HOST_ENDIAN_BYTES_INFO,
|
crate::endian_bytes::HOST_ENDIAN_BYTES_INFO,
|
||||||
crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO,
|
crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO,
|
||||||
|
@ -385,6 +386,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
|
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
|
||||||
crate::methods::MANUAL_FILTER_MAP_INFO,
|
crate::methods::MANUAL_FILTER_MAP_INFO,
|
||||||
crate::methods::MANUAL_FIND_MAP_INFO,
|
crate::methods::MANUAL_FIND_MAP_INFO,
|
||||||
|
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
|
||||||
crate::methods::MANUAL_NEXT_BACK_INFO,
|
crate::methods::MANUAL_NEXT_BACK_INFO,
|
||||||
crate::methods::MANUAL_OK_OR_INFO,
|
crate::methods::MANUAL_OK_OR_INFO,
|
||||||
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
|
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
|
||||||
|
@ -408,6 +410,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::methods::NO_EFFECT_REPLACE_INFO,
|
crate::methods::NO_EFFECT_REPLACE_INFO,
|
||||||
crate::methods::OBFUSCATED_IF_ELSE_INFO,
|
crate::methods::OBFUSCATED_IF_ELSE_INFO,
|
||||||
crate::methods::OK_EXPECT_INFO,
|
crate::methods::OK_EXPECT_INFO,
|
||||||
|
crate::methods::OPTION_AS_REF_CLONED_INFO,
|
||||||
crate::methods::OPTION_AS_REF_DEREF_INFO,
|
crate::methods::OPTION_AS_REF_DEREF_INFO,
|
||||||
crate::methods::OPTION_FILTER_MAP_INFO,
|
crate::methods::OPTION_FILTER_MAP_INFO,
|
||||||
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
|
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
|
||||||
|
@ -433,6 +436,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
|
||||||
crate::methods::STRING_EXTEND_CHARS_INFO,
|
crate::methods::STRING_EXTEND_CHARS_INFO,
|
||||||
crate::methods::STRING_LIT_CHARS_ANY_INFO,
|
crate::methods::STRING_LIT_CHARS_ANY_INFO,
|
||||||
|
crate::methods::STR_SPLIT_AT_NEWLINE_INFO,
|
||||||
crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO,
|
crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO,
|
||||||
crate::methods::SUSPICIOUS_MAP_INFO,
|
crate::methods::SUSPICIOUS_MAP_INFO,
|
||||||
crate::methods::SUSPICIOUS_SPLITN_INFO,
|
crate::methods::SUSPICIOUS_SPLITN_INFO,
|
||||||
|
@ -576,6 +580,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::ptr::MUT_FROM_REF_INFO,
|
crate::ptr::MUT_FROM_REF_INFO,
|
||||||
crate::ptr::PTR_ARG_INFO,
|
crate::ptr::PTR_ARG_INFO,
|
||||||
crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
|
crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
|
||||||
|
crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO,
|
||||||
crate::pub_use::PUB_USE_INFO,
|
crate::pub_use::PUB_USE_INFO,
|
||||||
crate::question_mark::QUESTION_MARK_INFO,
|
crate::question_mark::QUESTION_MARK_INFO,
|
||||||
crate::question_mark_used::QUESTION_MARK_USED_INFO,
|
crate::question_mark_used::QUESTION_MARK_USED_INFO,
|
||||||
|
@ -648,6 +653,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
||||||
crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
|
crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
|
||||||
crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
|
crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
|
||||||
crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
|
crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
|
||||||
|
crate::thread_local_initializer_can_be_made_const::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST_INFO,
|
||||||
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
|
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
|
||||||
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
|
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
|
||||||
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
|
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::{get_parent_node, numeric_literal};
|
||||||
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
|
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||||
use rustc_hir::{Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind};
|
use rustc_hir::{Block, Body, Expr, ExprKind, FnRetTy, HirId, ItemKind, Lit, Node, Stmt, StmtKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
|
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
|
||||||
|
@ -122,13 +122,42 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
|
ExprKind::Block(
|
||||||
|
Block {
|
||||||
|
stmts, expr: Some(_), ..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
) => {
|
||||||
|
if let Some(parent) = self.cx.tcx.hir().find_parent(expr.hir_id)
|
||||||
|
&& let Some(fn_sig) = parent.fn_sig()
|
||||||
|
&& let FnRetTy::Return(_ty) = fn_sig.decl.output
|
||||||
|
{
|
||||||
|
// We cannot check the exact type since it's a `hir::Ty`` which does not implement `is_numeric`
|
||||||
|
self.ty_bounds.push(ExplicitTyBound(true));
|
||||||
|
for stmt in *stmts {
|
||||||
|
self.visit_stmt(stmt);
|
||||||
|
}
|
||||||
|
self.ty_bounds.pop();
|
||||||
|
// Ignore return expr since we know its type was inferred from return ty
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Ignore return expr since we know its type was inferred from return ty
|
||||||
|
ExprKind::Ret(_) => return,
|
||||||
|
|
||||||
ExprKind::Call(func, args) => {
|
ExprKind::Call(func, args) => {
|
||||||
if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
|
if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
|
||||||
for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) {
|
for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) {
|
||||||
// Push found arg type, then visit arg.
|
// If is from macro, try to use last bound type (typically pushed when visiting stmt),
|
||||||
self.ty_bounds.push((*bound).into());
|
// otherwise push found arg type, then visit arg,
|
||||||
self.visit_expr(expr);
|
if expr.span.from_expansion() {
|
||||||
self.ty_bounds.pop();
|
self.visit_expr(expr);
|
||||||
|
} else {
|
||||||
|
self.ty_bounds.push((*bound).into());
|
||||||
|
self.visit_expr(expr);
|
||||||
|
self.ty_bounds.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
||||||
ExprKind::MethodCall(_, receiver, args, _) => {
|
ExprKind::MethodCall(_, receiver, args, _) => {
|
||||||
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||||
let fn_sig = self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
|
let fn_sig = self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
|
||||||
for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
|
for (expr, bound) in iter::zip(iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
|
||||||
self.ty_bounds.push((*bound).into());
|
self.ty_bounds.push((*bound).into());
|
||||||
self.visit_expr(expr);
|
self.visit_expr(expr);
|
||||||
self.ty_bounds.pop();
|
self.ty_bounds.pop();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use rustc_ast::ast::{Item, ItemKind, VariantData};
|
use rustc_ast::ast::{Item, ItemKind, Variant, VariantData};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_lexer::TokenKind;
|
use rustc_lexer::TokenKind;
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||||
|
@ -27,9 +27,38 @@ declare_clippy_lint! {
|
||||||
restriction,
|
restriction,
|
||||||
"finds struct declarations with empty brackets"
|
"finds struct declarations with empty brackets"
|
||||||
}
|
}
|
||||||
declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]);
|
|
||||||
|
|
||||||
impl EarlyLintPass for EmptyStructsWithBrackets {
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Finds enum variants without fields that are declared with empty brackets.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Empty brackets while defining enum variants are redundant and can be omitted.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// enum MyEnum {
|
||||||
|
/// HasData(u8),
|
||||||
|
/// HasNoData(), // redundant parentheses
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// enum MyEnum {
|
||||||
|
/// HasData(u8),
|
||||||
|
/// HasNoData,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.77.0"]
|
||||||
|
pub EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
|
||||||
|
restriction,
|
||||||
|
"finds enum variants with empty brackets"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]);
|
||||||
|
|
||||||
|
impl EarlyLintPass for EmptyWithBrackets {
|
||||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||||
let span_after_ident = item.span.with_lo(item.ident.span.hi());
|
let span_after_ident = item.span.with_lo(item.ident.span.hi());
|
||||||
|
|
||||||
|
@ -53,6 +82,27 @@ impl EarlyLintPass for EmptyStructsWithBrackets {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &Variant) {
|
||||||
|
let span_after_ident = variant.span.with_lo(variant.ident.span.hi());
|
||||||
|
|
||||||
|
if has_brackets(&variant.data) && has_no_fields(cx, &variant.data, span_after_ident) {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
|
||||||
|
span_after_ident,
|
||||||
|
"enum variant has empty brackets",
|
||||||
|
|diagnostic| {
|
||||||
|
diagnostic.span_suggestion_hidden(
|
||||||
|
span_after_ident,
|
||||||
|
"remove the brackets",
|
||||||
|
"",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_no_ident_token(braces_span_str: &str) -> bool {
|
fn has_no_ident_token(braces_span_str: &str) -> bool {
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||||
use clippy_utils::source::snippet_with_context;
|
use clippy_utils::source::snippet_with_context;
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
|
use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||||
use rustc_hir::{Expr, ExprKind};
|
use rustc_hir::{Expr, ExprKind};
|
||||||
|
@ -74,6 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `bool::then()` and `bool::then_some()` are not const
|
||||||
|
if in_constant(cx, expr.hir_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let ctxt = expr.span.ctxt();
|
let ctxt = expr.span.ctxt();
|
||||||
|
|
||||||
if let Some(higher::If {
|
if let Some(higher::If {
|
||||||
|
|
|
@ -40,7 +40,7 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Lints subtraction between an [`Instant`] and a [`Duration`].
|
/// Lints subtraction between an `Instant` and a `Duration`.
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// Unchecked subtraction could cause underflow on certain platforms, leading to
|
/// Unchecked subtraction could cause underflow on certain platforms, leading to
|
||||||
|
@ -57,9 +57,6 @@ declare_clippy_lint! {
|
||||||
/// # use std::time::{Instant, Duration};
|
/// # use std::time::{Instant, Duration};
|
||||||
/// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
|
/// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// [`Duration`]: std::time::Duration
|
|
||||||
/// [`Instant::now()`]: std::time::Instant::now;
|
|
||||||
#[clippy::version = "1.67.0"]
|
#[clippy::version = "1.67.0"]
|
||||||
pub UNCHECKED_DURATION_SUBTRACTION,
|
pub UNCHECKED_DURATION_SUBTRACTION,
|
||||||
pedantic,
|
pedantic,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! lint on enum variants that are prefixed or suffixed by the same characters
|
//! lint on enum variants that are prefixed or suffixed by the same characters
|
||||||
|
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
|
||||||
|
use clippy_utils::is_bool;
|
||||||
use clippy_utils::macros::span_is_local;
|
use clippy_utils::macros::span_is_local;
|
||||||
use clippy_utils::source::is_present_in_source;
|
use clippy_utils::source::is_present_in_source;
|
||||||
use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case};
|
use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case};
|
||||||
|
@ -231,6 +232,10 @@ fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: &
|
||||||
(false, _) => ("pre", prefix),
|
(false, _) => ("pre", prefix),
|
||||||
(true, false) => ("post", postfix),
|
(true, false) => ("post", postfix),
|
||||||
};
|
};
|
||||||
|
if fields.iter().all(|field| is_bool(field.ty)) {
|
||||||
|
// If all fields are booleans, we don't want to emit this lint.
|
||||||
|
return;
|
||||||
|
}
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
STRUCT_FIELD_NAMES,
|
STRUCT_FIELD_NAMES,
|
||||||
|
|
|
@ -5,7 +5,8 @@ use clippy_utils::ty::{implements_trait, make_normalized_projection};
|
||||||
use rustc_ast::Mutability;
|
use rustc_ast::Mutability;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
|
use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::{sym, Symbol};
|
use rustc_span::{sym, Symbol};
|
||||||
|
@ -152,7 +153,8 @@ fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol
|
||||||
|
|
||||||
impl LateLintPass<'_> for IterWithoutIntoIter {
|
impl LateLintPass<'_> for IterWithoutIntoIter {
|
||||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
|
||||||
if let ItemKind::Impl(imp) = item.kind
|
if !in_external_macro(cx.sess(), item.span)
|
||||||
|
&& let ItemKind::Impl(imp) = item.kind
|
||||||
&& let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
|
&& let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
|
||||||
&& let Some(trait_ref) = imp.of_trait
|
&& let Some(trait_ref) = imp.of_trait
|
||||||
&& trait_ref
|
&& trait_ref
|
||||||
|
@ -219,7 +221,8 @@ impl {self_ty_without_ref} {{
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let ImplItemKind::Fn(sig, _) = item.kind
|
if !in_external_macro(cx.sess(), item.span)
|
||||||
|
&& let ImplItemKind::Fn(sig, _) = item.kind
|
||||||
&& let FnRetTy::Return(ret) = sig.decl.output
|
&& let FnRetTy::Return(ret) = sig.decl.output
|
||||||
&& is_nameable_in_impl_trait(ret)
|
&& is_nameable_in_impl_trait(ret)
|
||||||
&& cx.tcx.generics_of(item_did).params.is_empty()
|
&& cx.tcx.generics_of(item_did).params.is_empty()
|
||||||
|
|
|
@ -115,7 +115,7 @@ mod duplicate_mod;
|
||||||
mod else_if_without_else;
|
mod else_if_without_else;
|
||||||
mod empty_drop;
|
mod empty_drop;
|
||||||
mod empty_enum;
|
mod empty_enum;
|
||||||
mod empty_structs_with_brackets;
|
mod empty_with_brackets;
|
||||||
mod endian_bytes;
|
mod endian_bytes;
|
||||||
mod entry;
|
mod entry;
|
||||||
mod enum_clike;
|
mod enum_clike;
|
||||||
|
@ -272,6 +272,7 @@ mod permissions_set_readonly_false;
|
||||||
mod precedence;
|
mod precedence;
|
||||||
mod ptr;
|
mod ptr;
|
||||||
mod ptr_offset_with_cast;
|
mod ptr_offset_with_cast;
|
||||||
|
mod pub_underscore_fields;
|
||||||
mod pub_use;
|
mod pub_use;
|
||||||
mod question_mark;
|
mod question_mark;
|
||||||
mod question_mark_used;
|
mod question_mark_used;
|
||||||
|
@ -322,6 +323,7 @@ mod swap_ptr_to_ref;
|
||||||
mod tabs_in_doc_comments;
|
mod tabs_in_doc_comments;
|
||||||
mod temporary_assignment;
|
mod temporary_assignment;
|
||||||
mod tests_outside_test_module;
|
mod tests_outside_test_module;
|
||||||
|
mod thread_local_initializer_can_be_made_const;
|
||||||
mod to_digit_is_some;
|
mod to_digit_is_some;
|
||||||
mod trailing_empty_array;
|
mod trailing_empty_array;
|
||||||
mod trait_bounds;
|
mod trait_bounds;
|
||||||
|
@ -571,6 +573,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
verbose_bit_mask_threshold,
|
verbose_bit_mask_threshold,
|
||||||
warn_on_all_wildcard_imports,
|
warn_on_all_wildcard_imports,
|
||||||
check_private_items,
|
check_private_items,
|
||||||
|
pub_underscore_fields_behavior,
|
||||||
|
|
||||||
blacklisted_names: _,
|
blacklisted_names: _,
|
||||||
cyclomatic_complexity_threshold: _,
|
cyclomatic_complexity_threshold: _,
|
||||||
|
@ -947,7 +950,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
|
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
|
||||||
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
|
store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets));
|
||||||
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
|
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
|
||||||
store.register_early_pass(|| Box::new(pub_use::PubUse));
|
store.register_early_pass(|| Box::new(pub_use::PubUse));
|
||||||
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
|
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
|
||||||
|
@ -1080,7 +1083,15 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity));
|
store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity));
|
||||||
store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences));
|
store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences));
|
||||||
store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions));
|
store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions));
|
||||||
store.register_late_pass(|_| Box::new(unconditional_recursion::UnconditionalRecursion));
|
store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default());
|
||||||
|
store.register_late_pass(move |_| {
|
||||||
|
Box::new(pub_underscore_fields::PubUnderscoreFields {
|
||||||
|
behavior: pub_underscore_fields_behavior,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
store.register_late_pass(move |_| {
|
||||||
|
Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv()))
|
||||||
|
});
|
||||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,8 +96,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo
|
||||||
ExprKind::Path(qpath) => cx
|
ExprKind::Path(qpath) => cx
|
||||||
.qpath_res(qpath, fm_arg.hir_id)
|
.qpath_res(qpath, fm_arg.hir_id)
|
||||||
.opt_def_id()
|
.opt_def_id()
|
||||||
.map(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD))
|
.is_some_and(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)),
|
||||||
.unwrap_or_default(),
|
|
||||||
// Detect `|x| x.ok()`
|
// Detect `|x| x.ok()`
|
||||||
ExprKind::Closure(Closure { body, .. }) => {
|
ExprKind::Closure(Closure { body, .. }) => {
|
||||||
if let Body {
|
if let Body {
|
||||||
|
|
|
@ -26,13 +26,14 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) ->
|
||||||
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||||
let body = cx.tcx.hir().body(body);
|
let body = cx.tcx.hir().body(body);
|
||||||
let closure_expr = peel_blocks(body.value);
|
let closure_expr = peel_blocks(body.value);
|
||||||
let arg_id = body.params[0].pat.hir_id;
|
|
||||||
match closure_expr.kind {
|
match closure_expr.kind {
|
||||||
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
|
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
|
||||||
if ident.name == method_name
|
if ident.name == method_name
|
||||||
&& let hir::ExprKind::Path(path) = &receiver.kind
|
&& let hir::ExprKind::Path(path) = &receiver.kind
|
||||||
&& let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id)
|
&& let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id)
|
||||||
|
&& !body.params.is_empty()
|
||||||
{
|
{
|
||||||
|
let arg_id = body.params[0].pat.hir_id;
|
||||||
return arg_id == *local;
|
return arg_id == *local;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
|
@ -1,80 +1,181 @@
|
||||||
|
use clippy_utils::ty::get_iterator_item_ty;
|
||||||
|
use hir::ExprKind;
|
||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
|
|
||||||
use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME};
|
use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME};
|
||||||
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::{indent_of, reindent_multiline};
|
use clippy_utils::source::{indent_of, reindent_multiline};
|
||||||
use clippy_utils::{is_trait_method, peel_blocks, span_contains_comment};
|
use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::Res;
|
|
||||||
use rustc_hir::QPath;
|
use rustc_hir::QPath;
|
||||||
use rustc_span::symbol::{sym, Symbol};
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
|
///
|
||||||
match &expr.kind {
|
/// Returns true if the expression is a method call to `method_name`
|
||||||
hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
|
/// e.g. `a.method_name()` or `Option::method_name`.
|
||||||
hir::ExprKind::Path(QPath::Resolved(_, segments)) => {
|
///
|
||||||
segments.segments.last().unwrap().ident.name == method_name
|
/// The type-checker verifies for us that the method accepts the right kind of items
|
||||||
|
/// (e.g. `Option::is_some` accepts `Option<_>`), so we don't need to check that.
|
||||||
|
///
|
||||||
|
/// How to capture each case:
|
||||||
|
///
|
||||||
|
/// `.filter(|a| { std::option::Option::is_some(a) })`
|
||||||
|
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a closure, getting unwrapped and
|
||||||
|
/// recursively checked.
|
||||||
|
/// `std::option::Option::is_some(a)`
|
||||||
|
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a call. It unwraps to a path with
|
||||||
|
/// `QPath::TypeRelative`. Since this is a type relative path, we need to check the method name, the
|
||||||
|
/// type, and that the parameter of the closure is passed in the call. This part is the dual of
|
||||||
|
/// `receiver.method_name()` below.
|
||||||
|
///
|
||||||
|
/// `filter(std::option::Option::is_some);`
|
||||||
|
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a type relative path, like above, we check the
|
||||||
|
/// type and the method name.
|
||||||
|
///
|
||||||
|
/// `filter(|a| a.is_some());`
|
||||||
|
/// ^^^^^^^^^^^^^^^ <- this is a method call inside a closure,
|
||||||
|
/// we check that the parameter of the closure is the receiver of the method call and don't allow
|
||||||
|
/// any other parameters.
|
||||||
|
fn is_method(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &hir::Expr<'_>,
|
||||||
|
type_symbol: Symbol,
|
||||||
|
method_name: Symbol,
|
||||||
|
params: &[&hir::Pat<'_>],
|
||||||
|
) -> bool {
|
||||||
|
fn pat_is_recv(ident: Ident, param: &hir::Pat<'_>) -> bool {
|
||||||
|
match param.kind {
|
||||||
|
hir::PatKind::Binding(_, _, other, _) => ident == other,
|
||||||
|
hir::PatKind::Ref(pat, _) => pat_is_recv(ident, pat),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match expr.kind {
|
||||||
|
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, recv, ..) => {
|
||||||
|
// compare the identifier of the receiver to the parameter
|
||||||
|
// we are in a filter => closure has a single parameter and a single, non-block
|
||||||
|
// expression, this means that the parameter shadows all outside variables with
|
||||||
|
// the same name => avoid FPs. If the parameter is not the receiver, then this hits
|
||||||
|
// outside variables => avoid FP
|
||||||
|
if ident.name == method_name
|
||||||
|
&& let ExprKind::Path(QPath::Resolved(None, path)) = recv.kind
|
||||||
|
&& let &[seg] = path.segments
|
||||||
|
&& params.iter().any(|p| pat_is_recv(seg.ident, p))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
// This is used to check for complete paths via `|a| std::option::Option::is_some(a)`
|
||||||
|
// this then unwraps to a path with `QPath::TypeRelative`
|
||||||
|
// we pass the params as they've been passed to the current call through the closure
|
||||||
|
hir::ExprKind::Call(expr, [param]) => {
|
||||||
|
// this will hit the `QPath::TypeRelative` case and check that the method name is correct
|
||||||
|
if is_method(cx, expr, type_symbol, method_name, params)
|
||||||
|
// we then check that this is indeed passing the parameter of the closure
|
||||||
|
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.kind
|
||||||
|
&& let &[seg] = path.segments
|
||||||
|
&& params.iter().any(|p| pat_is_recv(seg.ident, p))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
hir::ExprKind::Path(QPath::TypeRelative(ty, mname)) => {
|
||||||
|
let ty = cx.typeck_results().node_type(ty.hir_id);
|
||||||
|
if let Some(did) = cx.tcx.get_diagnostic_item(type_symbol)
|
||||||
|
&& ty.ty_adt_def() == cx.tcx.type_of(did).skip_binder().ty_adt_def()
|
||||||
|
{
|
||||||
|
return mname.ident.name == method_name;
|
||||||
|
}
|
||||||
|
false
|
||||||
},
|
},
|
||||||
hir::ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name,
|
|
||||||
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||||
let body = cx.tcx.hir().body(body);
|
let body = cx.tcx.hir().body(body);
|
||||||
let closure_expr = peel_blocks(body.value);
|
let closure_expr = peel_blocks(body.value);
|
||||||
let arg_id = body.params[0].pat.hir_id;
|
let params = body.params.iter().map(|param| param.pat).collect::<Vec<_>>();
|
||||||
match closure_expr.kind {
|
is_method(cx, closure_expr, type_symbol, method_name, params.as_slice())
|
||||||
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
|
|
||||||
if ident.name == method_name
|
|
||||||
&& let hir::ExprKind::Path(path) = &receiver.kind
|
|
||||||
&& let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id)
|
|
||||||
{
|
|
||||||
return arg_id == *local;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||||
if let hir::Node::Expr(parent_expr) = cx.tcx.hir().get_parent(expr.hir_id) {
|
if let Some(expr) = get_parent_expr(cx, expr)
|
||||||
is_method(cx, parent_expr, rustc_span::sym::map)
|
&& is_trait_method(cx, expr, sym::Iterator)
|
||||||
} else {
|
&& let hir::ExprKind::MethodCall(path, _, _, _) = expr.kind
|
||||||
false
|
&& path.ident.name == rustc_span::sym::map
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
enum FilterType {
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, filter_span: Span) {
|
IsSome,
|
||||||
let is_iterator = is_trait_method(cx, expr, sym::Iterator);
|
IsOk,
|
||||||
let parent_is_not_map = !parent_is_map(cx, expr);
|
}
|
||||||
|
|
||||||
if is_iterator
|
/// Returns the `FilterType` of the expression if it is a filter over an Iter<Option> or
|
||||||
&& parent_is_not_map
|
/// Iter<Result> with the parent expression not being a map, and not having a comment in the span of
|
||||||
&& is_method(cx, filter_arg, sym!(is_some))
|
/// the filter. If it is not a filter over an Iter<Option> or Iter<Result> then it returns None
|
||||||
&& !span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi()))
|
///
|
||||||
|
/// How this is done:
|
||||||
|
/// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs`
|
||||||
|
/// 2. we check that we are in a trait method. Therefore we are in an
|
||||||
|
/// `(x as Iterator).filter({filter_arg})` method call.
|
||||||
|
/// 3. we check that the parent expression is not a map. This is because we don't want to lint
|
||||||
|
/// twice, and we already have a specialized lint for that.
|
||||||
|
/// 4. we check that the span of the filter does not contain a comment.
|
||||||
|
/// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and
|
||||||
|
/// Result.
|
||||||
|
/// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or
|
||||||
|
/// `is_ok`.
|
||||||
|
/// 7. if all of the above are true, then we return the `FilterType`
|
||||||
|
fn expression_type(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &hir::Expr<'_>,
|
||||||
|
filter_arg: &hir::Expr<'_>,
|
||||||
|
filter_span: Span,
|
||||||
|
) -> Option<FilterType> {
|
||||||
|
if !is_trait_method(cx, expr, sym::Iterator)
|
||||||
|
|| parent_is_map(cx, expr)
|
||||||
|
|| span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi()))
|
||||||
{
|
{
|
||||||
span_lint_and_sugg(
|
return None;
|
||||||
cx,
|
|
||||||
ITER_FILTER_IS_SOME,
|
|
||||||
filter_span.with_hi(expr.span.hi()),
|
|
||||||
"`filter` for `is_some` on iterator over `Option`",
|
|
||||||
"consider using `flatten` instead",
|
|
||||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
|
||||||
Applicability::HasPlaceholders,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if is_iterator
|
if let hir::ExprKind::MethodCall(_, receiver, _, _) = expr.kind
|
||||||
&& parent_is_not_map
|
&& let receiver_ty = cx.typeck_results().expr_ty(receiver)
|
||||||
&& is_method(cx, filter_arg, sym!(is_ok))
|
&& let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty)
|
||||||
&& !span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi()))
|
|
||||||
{
|
{
|
||||||
span_lint_and_sugg(
|
if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Option)
|
||||||
|
&& let opt_ty = cx.tcx.type_of(opt_defid).skip_binder()
|
||||||
|
&& iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def()
|
||||||
|
&& is_method(cx, filter_arg, sym::Option, sym!(is_some), &[])
|
||||||
|
{
|
||||||
|
return Some(FilterType::IsSome);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Result)
|
||||||
|
&& let opt_ty = cx.tcx.type_of(opt_defid).skip_binder()
|
||||||
|
&& iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def()
|
||||||
|
&& is_method(cx, filter_arg, sym::Result, sym!(is_ok), &[])
|
||||||
|
{
|
||||||
|
return Some(FilterType::IsOk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, filter_span: Span) {
|
||||||
|
// we are in a filter inside an iterator
|
||||||
|
match expression_type(cx, expr, filter_arg, filter_span) {
|
||||||
|
None => (),
|
||||||
|
Some(FilterType::IsOk) => span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
ITER_FILTER_IS_OK,
|
ITER_FILTER_IS_OK,
|
||||||
filter_span.with_hi(expr.span.hi()),
|
filter_span.with_hi(expr.span.hi()),
|
||||||
|
@ -82,6 +183,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir
|
||||||
"consider using `flatten` instead",
|
"consider using `flatten` instead",
|
||||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
||||||
Applicability::HasPlaceholders,
|
Applicability::HasPlaceholders,
|
||||||
);
|
),
|
||||||
|
Some(FilterType::IsSome) => span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
ITER_FILTER_IS_SOME,
|
||||||
|
filter_span.with_hi(expr.span.hi()),
|
||||||
|
"`filter` for `is_some` on iterator over `Option`",
|
||||||
|
"consider using `flatten` instead",
|
||||||
|
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
||||||
|
Applicability::HasPlaceholders,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
59
clippy_lints/src/methods/manual_is_variant_and.rs
Normal file
59
clippy_lints/src/methods/manual_is_variant_and.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use clippy_config::msrvs::{Msrv, OPTION_RESULT_IS_VARIANT_AND};
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::source::snippet;
|
||||||
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
|
use super::MANUAL_IS_VARIANT_AND;
|
||||||
|
|
||||||
|
pub(super) fn check<'tcx>(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &'tcx rustc_hir::Expr<'_>,
|
||||||
|
map_recv: &'tcx rustc_hir::Expr<'_>,
|
||||||
|
map_arg: &'tcx rustc_hir::Expr<'_>,
|
||||||
|
map_span: Span,
|
||||||
|
msrv: &Msrv,
|
||||||
|
) {
|
||||||
|
// Don't lint if:
|
||||||
|
|
||||||
|
// 1. the `expr` is generated by a macro
|
||||||
|
if expr.span.from_expansion() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. the caller of `map()` is neither `Option` nor `Result`
|
||||||
|
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option);
|
||||||
|
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result);
|
||||||
|
if !is_option && !is_result {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. the caller of `unwrap_or_default` is neither `Option<bool>` nor `Result<bool, _>`
|
||||||
|
if !cx.typeck_results().expr_ty(expr).is_bool() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. msrv doesn't meet `OPTION_RESULT_IS_VARIANT_AND`
|
||||||
|
if !msrv.meets(OPTION_RESULT_IS_VARIANT_AND) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lint_msg = if is_option {
|
||||||
|
"called `map(<f>).unwrap_or_default()` on an `Option` value"
|
||||||
|
} else {
|
||||||
|
"called `map(<f>).unwrap_or_default()` on a `Result` value"
|
||||||
|
};
|
||||||
|
let suggestion = if is_option { "is_some_and" } else { "is_ok_and" };
|
||||||
|
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
MANUAL_IS_VARIANT_AND,
|
||||||
|
expr.span.with_lo(map_span.lo()),
|
||||||
|
lint_msg,
|
||||||
|
"use",
|
||||||
|
format!("{}({})", suggestion, snippet(cx, map_arg.span, "..")),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
|
@ -2,9 +2,10 @@ use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||||
use clippy_utils::{is_diag_trait_item, peel_blocks};
|
use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::mir::Mutability;
|
use rustc_middle::mir::Mutability;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
|
@ -14,60 +15,110 @@ use rustc_span::{sym, Span};
|
||||||
|
|
||||||
use super::MAP_CLONE;
|
use super::MAP_CLONE;
|
||||||
|
|
||||||
|
// If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't
|
||||||
|
// run this lint because it would overlap with `useless_asref` which provides a better suggestion
|
||||||
|
// in this case.
|
||||||
|
fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool {
|
||||||
|
if is_diag_trait_item(cx, method_id, sym::Iterator) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// We check if it's an `Option` or a `Result`.
|
||||||
|
if let Some(id) = cx.tcx.impl_of_method(method_id) {
|
||||||
|
let identity = cx.tcx.type_of(id).instantiate_identity();
|
||||||
|
if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// We check if the previous method call is `as_ref`.
|
||||||
|
if let hir::ExprKind::MethodCall(path1, receiver, _, _) = &e.kind
|
||||||
|
&& let hir::ExprKind::MethodCall(path2, _, _, _) = &receiver.kind
|
||||||
|
{
|
||||||
|
return path2.ident.name != sym::as_ref || path1.ident.name != sym::map;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
|
pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) {
|
||||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||||
&& (cx.tcx.impl_of_method(method_id).map_or(false, |id| {
|
&& should_run_lint(cx, e, method_id)
|
||||||
is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option)
|
|
||||||
}) || is_diag_trait_item(cx, method_id, sym::Iterator))
|
|
||||||
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind
|
|
||||||
{
|
{
|
||||||
let closure_body = cx.tcx.hir().body(body);
|
match arg.kind {
|
||||||
let closure_expr = peel_blocks(closure_body.value);
|
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||||
match closure_body.params[0].pat.kind {
|
let closure_body = cx.tcx.hir().body(body);
|
||||||
hir::PatKind::Ref(inner, hir::Mutability::Not) => {
|
let closure_expr = peel_blocks(closure_body.value);
|
||||||
if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind {
|
match closure_body.params[0].pat.kind {
|
||||||
if ident_eq(name, closure_expr) {
|
hir::PatKind::Ref(inner, hir::Mutability::Not) => {
|
||||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind {
|
||||||
}
|
if ident_eq(name, closure_expr) {
|
||||||
}
|
|
||||||
},
|
|
||||||
hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => {
|
|
||||||
match closure_expr.kind {
|
|
||||||
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
|
||||||
if ident_eq(name, inner) {
|
|
||||||
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
|
||||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hir::ExprKind::MethodCall(method, obj, [], _) => {
|
hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => {
|
||||||
if ident_eq(name, obj) && method.ident.name == sym::clone
|
match closure_expr.kind {
|
||||||
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
|
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
||||||
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
if ident_eq(name, inner) {
|
||||||
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
|
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
||||||
// no autoderefs
|
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||||
&& !cx.typeck_results().expr_adjustments(obj).iter()
|
}
|
||||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
|
|
||||||
{
|
|
||||||
let obj_ty = cx.typeck_results().expr_ty(obj);
|
|
||||||
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
|
|
||||||
if matches!(mutability, Mutability::Not) {
|
|
||||||
let copy = is_copy(cx, *ty);
|
|
||||||
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
|
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
lint_needless_cloning(cx, e.span, recv.span);
|
hir::ExprKind::MethodCall(method, obj, [], _) => {
|
||||||
}
|
if ident_eq(name, obj) && method.ident.name == sym::clone
|
||||||
|
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
|
||||||
|
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||||
|
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
|
||||||
|
// no autoderefs
|
||||||
|
&& !cx.typeck_results().expr_adjustments(obj).iter()
|
||||||
|
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
|
||||||
|
{
|
||||||
|
let obj_ty = cx.typeck_results().expr_ty(obj);
|
||||||
|
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
|
||||||
|
if matches!(mutability, Mutability::Not) {
|
||||||
|
let copy = is_copy(cx, *ty);
|
||||||
|
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lint_needless_cloning(cx, e.span, recv.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hir::ExprKind::Call(call, [_]) => {
|
||||||
|
if let hir::ExprKind::Path(qpath) = call.kind {
|
||||||
|
handle_path(cx, call, &qpath, e, recv);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
hir::ExprKind::Path(qpath) => handle_path(cx, arg, &qpath, e, recv),
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_path(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
arg: &hir::Expr<'_>,
|
||||||
|
qpath: &hir::QPath<'_>,
|
||||||
|
e: &hir::Expr<'_>,
|
||||||
|
recv: &hir::Expr<'_>,
|
||||||
|
) {
|
||||||
|
if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
|
||||||
|
&& match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD)
|
||||||
|
{
|
||||||
|
// FIXME: It would be better to infer the type to check if it's copyable or not
|
||||||
|
// to suggest to use `.copied()` instead of `.cloned()` where applicable.
|
||||||
|
lint_path(cx, e.span, recv.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
|
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
|
||||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
|
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
|
||||||
path.segments.len() == 1 && path.segments[0].ident == name
|
path.segments.len() == 1 && path.segments[0].ident == name
|
||||||
|
@ -88,6 +139,23 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span) {
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
MAP_CLONE,
|
||||||
|
replace,
|
||||||
|
"you are explicitly cloning with `.map()`",
|
||||||
|
"consider calling the dedicated `cloned` method",
|
||||||
|
format!(
|
||||||
|
"{}.cloned()",
|
||||||
|
snippet_with_applicability(cx, root, "..", &mut applicability),
|
||||||
|
),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
|
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ mod iter_skip_zero;
|
||||||
mod iter_with_drain;
|
mod iter_with_drain;
|
||||||
mod iterator_step_by_zero;
|
mod iterator_step_by_zero;
|
||||||
mod join_absolute_paths;
|
mod join_absolute_paths;
|
||||||
|
mod manual_is_variant_and;
|
||||||
mod manual_next_back;
|
mod manual_next_back;
|
||||||
mod manual_ok_or;
|
mod manual_ok_or;
|
||||||
mod manual_saturating_arithmetic;
|
mod manual_saturating_arithmetic;
|
||||||
|
@ -70,6 +71,7 @@ mod no_effect_replace;
|
||||||
mod obfuscated_if_else;
|
mod obfuscated_if_else;
|
||||||
mod ok_expect;
|
mod ok_expect;
|
||||||
mod open_options;
|
mod open_options;
|
||||||
|
mod option_as_ref_cloned;
|
||||||
mod option_as_ref_deref;
|
mod option_as_ref_deref;
|
||||||
mod option_map_or_err_ok;
|
mod option_map_or_err_ok;
|
||||||
mod option_map_or_none;
|
mod option_map_or_none;
|
||||||
|
@ -93,6 +95,7 @@ mod single_char_pattern;
|
||||||
mod single_char_push_string;
|
mod single_char_push_string;
|
||||||
mod skip_while_next;
|
mod skip_while_next;
|
||||||
mod stable_sort_primitive;
|
mod stable_sort_primitive;
|
||||||
|
mod str_split;
|
||||||
mod str_splitn;
|
mod str_splitn;
|
||||||
mod string_extend_chars;
|
mod string_extend_chars;
|
||||||
mod string_lit_chars_any;
|
mod string_lit_chars_any;
|
||||||
|
@ -2589,11 +2592,11 @@ declare_clippy_lint! {
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for usage of `x.get(0)` instead of
|
/// Checks for usage of `x.get(0)` instead of
|
||||||
/// `x.first()`.
|
/// `x.first()` or `x.front()`.
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// Using `x.first()` is easier to read and has the same
|
/// Using `x.first()` for `Vec`s and slices or `x.front()`
|
||||||
/// result.
|
/// for `VecDeque`s is easier to read and has the same result.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -2609,7 +2612,7 @@ declare_clippy_lint! {
|
||||||
#[clippy::version = "1.63.0"]
|
#[clippy::version = "1.63.0"]
|
||||||
pub GET_FIRST,
|
pub GET_FIRST,
|
||||||
style,
|
style,
|
||||||
"Using `x.get(0)` when `x.first()` is simpler"
|
"Using `x.get(0)` when `x.first()` or `x.front()` is simpler"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
|
@ -3784,7 +3787,7 @@ declare_clippy_lint! {
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// This pattern is often followed by manual unwrapping of the `Option`. The simplification
|
/// This pattern is often followed by manual unwrapping of the `Option`. The simplification
|
||||||
/// results in more readable and succint code without the need for manual unwrapping.
|
/// results in more readable and succinct code without the need for manual unwrapping.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -3810,7 +3813,7 @@ declare_clippy_lint! {
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// This pattern is often followed by manual unwrapping of `Result`. The simplification
|
/// This pattern is often followed by manual unwrapping of `Result`. The simplification
|
||||||
/// results in more readable and succint code without the need for manual unwrapping.
|
/// results in more readable and succinct code without the need for manual unwrapping.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -3829,6 +3832,87 @@ declare_clippy_lint! {
|
||||||
"filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`"
|
"filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where f is a function or closure that returns the `bool` type.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Readability. These can be written more concisely as `option.is_some_and(f)` and `result.is_ok_and(f)`.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// # let option = Some(1);
|
||||||
|
/// # let result: Result<usize, ()> = Ok(1);
|
||||||
|
/// option.map(|a| a > 10).unwrap_or_default();
|
||||||
|
/// result.map(|a| a > 10).unwrap_or_default();
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// # let option = Some(1);
|
||||||
|
/// # let result: Result<usize, ()> = Ok(1);
|
||||||
|
/// option.is_some_and(|a| a > 10);
|
||||||
|
/// result.is_ok_and(|a| a > 10);
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.76.0"]
|
||||||
|
pub MANUAL_IS_VARIANT_AND,
|
||||||
|
pedantic,
|
||||||
|
"using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
///
|
||||||
|
/// Checks for usages of `str.trim().split("\n")` and `str.trim().split("\r\n")`.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
///
|
||||||
|
/// Hard-coding the line endings makes the code less compatible. `str.lines` should be used instead.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// "some\ntext\nwith\nnewlines\n".trim().split('\n');
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// "some\ntext\nwith\nnewlines\n".lines();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Known Problems
|
||||||
|
///
|
||||||
|
/// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or
|
||||||
|
/// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is
|
||||||
|
/// valid.
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.76.0"]
|
||||||
|
pub STR_SPLIT_AT_NEWLINE,
|
||||||
|
pedantic,
|
||||||
|
"splitting a trimmed string at hard-coded newlines"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for usage of `.as_ref().cloned()` and `.as_mut().cloned()` on `Option`s
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// This can be written more concisely by cloning the `Option` directly.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
|
||||||
|
/// bar.as_ref().cloned()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> {
|
||||||
|
/// bar.clone()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.77.0"]
|
||||||
|
pub OPTION_AS_REF_CLONED,
|
||||||
|
pedantic,
|
||||||
|
"cloning an `Option` via `as_ref().cloned()`"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Methods {
|
pub struct Methods {
|
||||||
avoid_breaking_exported_api: bool,
|
avoid_breaking_exported_api: bool,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
|
@ -3983,6 +4067,9 @@ impl_lint_pass!(Methods => [
|
||||||
RESULT_FILTER_MAP,
|
RESULT_FILTER_MAP,
|
||||||
ITER_FILTER_IS_SOME,
|
ITER_FILTER_IS_SOME,
|
||||||
ITER_FILTER_IS_OK,
|
ITER_FILTER_IS_OK,
|
||||||
|
MANUAL_IS_VARIANT_AND,
|
||||||
|
STR_SPLIT_AT_NEWLINE,
|
||||||
|
OPTION_AS_REF_CLONED,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Extracts a method call name, args, and `Span` of the method name.
|
/// Extracts a method call name, args, and `Span` of the method name.
|
||||||
|
@ -4230,7 +4317,10 @@ impl Methods {
|
||||||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||||
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv),
|
("cloned", []) => {
|
||||||
|
cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv);
|
||||||
|
option_as_ref_cloned::check(cx, recv, span);
|
||||||
|
},
|
||||||
("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
|
("collect", []) if is_trait_method(cx, expr, sym::Iterator) => {
|
||||||
needless_collect::check(cx, span, expr, recv, call_span);
|
needless_collect::check(cx, span, expr, recv, call_span);
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
|
@ -4569,6 +4659,9 @@ impl Methods {
|
||||||
("sort_unstable_by", [arg]) => {
|
("sort_unstable_by", [arg]) => {
|
||||||
unnecessary_sort_by::check(cx, expr, recv, arg, true);
|
unnecessary_sort_by::check(cx, expr, recv, arg, true);
|
||||||
},
|
},
|
||||||
|
("split", [arg]) => {
|
||||||
|
str_split::check(cx, expr, recv, arg);
|
||||||
|
},
|
||||||
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
||||||
if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) {
|
if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||||
|
@ -4664,7 +4757,13 @@ impl Methods {
|
||||||
}
|
}
|
||||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||||
},
|
},
|
||||||
("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
|
("unwrap_or_default", []) => {
|
||||||
|
if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) {
|
||||||
|
manual_is_variant_and::check(cx, expr, m_recv, arg, span, &self.msrv);
|
||||||
|
}
|
||||||
|
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||||
|
},
|
||||||
|
("unwrap_unchecked" | "unwrap_err_unchecked", []) => {
|
||||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||||
},
|
},
|
||||||
("unwrap_or_else", [u_arg]) => {
|
("unwrap_or_else", [u_arg]) => {
|
||||||
|
|
24
clippy_lints/src/methods/option_as_ref_cloned.rs
Normal file
24
clippy_lints/src/methods/option_as_ref_cloned.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::Expr;
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
|
use super::{method_call, OPTION_AS_REF_CLONED};
|
||||||
|
|
||||||
|
pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) {
|
||||||
|
if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv)
|
||||||
|
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option)
|
||||||
|
{
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
OPTION_AS_REF_CLONED,
|
||||||
|
as_ref_ident_span.to(cloned_ident_span),
|
||||||
|
&format!("cloning an `Option<_>` using `.{method}().cloned()`"),
|
||||||
|
"this can be written more concisely by cloning the `Option<_>` directly",
|
||||||
|
"clone".into(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ pub(super) fn check<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
|
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
|
||||||
let suggest_is_some_and = msrv.meets(msrvs::OPTION_IS_SOME_AND)
|
let suggest_is_some_and = msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
|
||||||
&& matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
|
&& matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
|
||||||
if matches!(lit.node, rustc_ast::LitKind::Bool(false)));
|
if matches!(lit.node, rustc_ast::LitKind::Bool(false)));
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub(super) fn check(
|
||||||
&& path.chars().all(char::is_alphanumeric)
|
&& path.chars().all(char::is_alphanumeric)
|
||||||
{
|
{
|
||||||
let mut sugg = snippet(cx, recv.span, "..").into_owned();
|
let mut sugg = snippet(cx, recv.span, "..").into_owned();
|
||||||
if msrv.meets(msrvs::OPTION_IS_SOME_AND) {
|
if msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) {
|
||||||
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
|
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
|
||||||
} else {
|
} else {
|
||||||
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
|
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
|
||||||
|
|
|
@ -13,7 +13,11 @@ pub(super) fn check(
|
||||||
as_str_span: Span,
|
as_str_span: Span,
|
||||||
other_method_span: Span,
|
other_method_span: Span,
|
||||||
) {
|
) {
|
||||||
if cx.typeck_results().expr_ty(recv).ty_adt_def().is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string())
|
if cx
|
||||||
|
.typeck_results()
|
||||||
|
.expr_ty(recv)
|
||||||
|
.ty_adt_def()
|
||||||
|
.is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string())
|
||||||
{
|
{
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
|
|
38
clippy_lints/src/methods/str_split.rs
Normal file
38
clippy_lints/src/methods/str_split.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::source::snippet_with_context;
|
||||||
|
use clippy_utils::visitors::is_const_evaluatable;
|
||||||
|
use rustc_ast::ast::LitKind;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{Expr, ExprKind};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
|
||||||
|
use super::STR_SPLIT_AT_NEWLINE;
|
||||||
|
|
||||||
|
pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &'a Expr<'_>, split_arg: &'_ Expr<'_>) {
|
||||||
|
// We're looking for `A.trim().split(B)`, where the adjusted type of `A` is `&str` (e.g. an
|
||||||
|
// expression returning `String`), and `B` is a `Pattern` that hard-codes a newline (either `"\n"`
|
||||||
|
// or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most
|
||||||
|
// basic ones: a `'\n'`, `"\n"`, and `"\r\n"`.
|
||||||
|
if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind
|
||||||
|
&& trim_method_name.ident.as_str() == "trim"
|
||||||
|
&& cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str()
|
||||||
|
&& !is_const_evaluatable(cx, trim_recv)
|
||||||
|
&& let ExprKind::Lit(split_lit) = split_arg.kind
|
||||||
|
&& (matches!(split_lit.node, LitKind::Char('\n'))
|
||||||
|
|| matches!(split_lit.node, LitKind::Str(sym, _) if (sym.as_str() == "\n" || sym.as_str() == "\r\n")))
|
||||||
|
{
|
||||||
|
let mut app = Applicability::MaybeIncorrect;
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
STR_SPLIT_AT_NEWLINE,
|
||||||
|
expr.span,
|
||||||
|
"using `str.trim().split()` with hard-coded newlines",
|
||||||
|
"use `str.lines()` instead",
|
||||||
|
format!(
|
||||||
|
"{}.lines()",
|
||||||
|
snippet_with_context(cx, trim_recv.span, expr.span.ctxt(), "..", &mut app).0
|
||||||
|
),
|
||||||
|
app,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,13 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
|
||||||
use clippy_config::msrvs::{self, Msrv};
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
|
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_mid_ty_refs};
|
||||||
use clippy_utils::visitors::find_all_ret_expressions;
|
use clippy_utils::visitors::find_all_ret_expressions;
|
||||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
|
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node};
|
use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node};
|
||||||
use rustc_hir_typeck::{FnCtxt, Inherited};
|
use rustc_hir_typeck::{FnCtxt, Inherited};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -246,6 +246,19 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
|
||||||
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
|
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
|
||||||
&& let Some(arg_snippet) = snippet_opt(cx, argument_expr.span)
|
&& let Some(arg_snippet) = snippet_opt(cx, argument_expr.span)
|
||||||
{
|
{
|
||||||
|
// We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x`
|
||||||
|
// implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to
|
||||||
|
// add `.as_ref()` to the suggestion.
|
||||||
|
let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String)
|
||||||
|
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
|
||||||
|
&& cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, "Target")
|
||||||
|
!= Some(cx.tcx.types.str_)
|
||||||
|
{
|
||||||
|
".as_ref()"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
// The next suggestion may be incorrect because the removal of the `to_owned`-like
|
// The next suggestion may be incorrect because the removal of the `to_owned`-like
|
||||||
// function could cause the iterator to hold a reference to a resource that is used
|
// function could cause the iterator to hold a reference to a resource that is used
|
||||||
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
|
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
|
||||||
|
@ -255,7 +268,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
|
||||||
parent.span,
|
parent.span,
|
||||||
&format!("unnecessary use of `{method_name}`"),
|
&format!("unnecessary use of `{method_name}`"),
|
||||||
"use",
|
"use",
|
||||||
format!("{receiver_snippet}.split({arg_snippet})"),
|
format!("{receiver_snippet}{as_ref}.split({arg_snippet})"),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
|
@ -445,7 +458,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
|
||||||
{
|
{
|
||||||
let bound_fn_sig = cx.tcx.fn_sig(callee_def_id);
|
let bound_fn_sig = cx.tcx.fn_sig(callee_def_id);
|
||||||
let fn_sig = bound_fn_sig.skip_binder();
|
let fn_sig = bound_fn_sig.skip_binder();
|
||||||
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
|
if let Some(arg_index) = recv
|
||||||
|
.into_iter()
|
||||||
|
.chain(call_args)
|
||||||
|
.position(|arg| arg.hir_id == expr.hir_id)
|
||||||
&& let param_ty = fn_sig.input(arg_index).skip_binder()
|
&& let param_ty = fn_sig.input(arg_index).skip_binder()
|
||||||
&& let ty::Param(ParamTy { index: param_index , ..}) = *param_ty.kind()
|
&& let ty::Param(ParamTy { index: param_index , ..}) = *param_ty.kind()
|
||||||
// https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021
|
// https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021
|
||||||
|
|
|
@ -1,19 +1,52 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::walk_ptrs_ty_depth;
|
use clippy_utils::ty::walk_ptrs_ty_depth;
|
||||||
use clippy_utils::{get_parent_expr, is_trait_method};
|
use clippy_utils::{get_parent_expr, is_diag_trait_item, match_def_path, paths, peel_blocks};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::sym;
|
use rustc_middle::ty::adjustment::Adjust;
|
||||||
|
use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
|
||||||
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
|
use core::ops::ControlFlow;
|
||||||
|
|
||||||
use super::USELESS_ASREF;
|
use super::USELESS_ASREF;
|
||||||
|
|
||||||
|
/// Returns the first type inside the `Option`/`Result` type passed as argument.
|
||||||
|
fn get_enum_ty(enum_ty: Ty<'_>) -> Option<Ty<'_>> {
|
||||||
|
struct ContainsTyVisitor {
|
||||||
|
level: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTyVisitor {
|
||||||
|
type BreakTy = Ty<'tcx>;
|
||||||
|
|
||||||
|
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||||
|
self.level += 1;
|
||||||
|
if self.level == 1 {
|
||||||
|
t.super_visit_with(self)
|
||||||
|
} else {
|
||||||
|
ControlFlow::Break(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match enum_ty.visit_with(&mut ContainsTyVisitor { level: 0 }) {
|
||||||
|
ControlFlow::Break(ty) => Some(ty),
|
||||||
|
ControlFlow::Continue(()) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks for the `USELESS_ASREF` lint.
|
/// Checks for the `USELESS_ASREF` lint.
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) {
|
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) {
|
||||||
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
|
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
|
||||||
// check if the call is to the actual `AsRef` or `AsMut` trait
|
// check if the call is to the actual `AsRef` or `AsMut` trait
|
||||||
if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, sym::AsMut) {
|
let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) {
|
||||||
// check if the type after `as_ref` or `as_mut` is the same as before
|
// check if the type after `as_ref` or `as_mut` is the same as before
|
||||||
let rcv_ty = cx.typeck_results().expr_ty(recvr);
|
let rcv_ty = cx.typeck_results().expr_ty(recvr);
|
||||||
let res_ty = cx.typeck_results().expr_ty(expr);
|
let res_ty = cx.typeck_results().expr_ty(expr);
|
||||||
|
@ -39,5 +72,89 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if match_def_path(cx, def_id, &["core", "option", "Option", call_name])
|
||||||
|
|| match_def_path(cx, def_id, &["core", "result", "Result", call_name])
|
||||||
|
{
|
||||||
|
let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs();
|
||||||
|
let res_ty = cx.typeck_results().expr_ty(expr).peel_refs();
|
||||||
|
|
||||||
|
if let Some(rcv_ty) = get_enum_ty(rcv_ty)
|
||||||
|
&& let Some(res_ty) = get_enum_ty(res_ty)
|
||||||
|
// If the only thing the `as_mut`/`as_ref` call is doing is adding references and not
|
||||||
|
// changing the type, then we can move forward.
|
||||||
|
&& rcv_ty.peel_refs() == res_ty.peel_refs()
|
||||||
|
&& let Some(parent) = get_parent_expr(cx, expr)
|
||||||
|
&& let hir::ExprKind::MethodCall(segment, _, args, _) = parent.kind
|
||||||
|
&& segment.ident.span != expr.span
|
||||||
|
// We check that the called method name is `map`.
|
||||||
|
&& segment.ident.name == sym::map
|
||||||
|
// And that it only has one argument.
|
||||||
|
&& let [arg] = args
|
||||||
|
&& is_calling_clone(cx, arg)
|
||||||
|
{
|
||||||
|
lint_as_ref_clone(cx, expr.span.with_hi(parent.span.hi()), recvr, call_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) -> bool {
|
||||||
|
// We check it's calling the `clone` method of the `Clone` trait.
|
||||||
|
if let Some(path_def_id) = cx.qpath_res(&qpath, hir_id).opt_def_id() {
|
||||||
|
match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
|
||||||
|
match arg.kind {
|
||||||
|
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||||
|
// If it's a closure, we need to check what is called.
|
||||||
|
let closure_body = cx.tcx.hir().body(body);
|
||||||
|
let closure_expr = peel_blocks(closure_body.value);
|
||||||
|
match closure_expr.kind {
|
||||||
|
hir::ExprKind::MethodCall(method, obj, [], _) => {
|
||||||
|
if method.ident.name == sym::clone
|
||||||
|
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
|
||||||
|
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||||
|
// We check it's the `Clone` trait.
|
||||||
|
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
|
||||||
|
// no autoderefs
|
||||||
|
&& !cx.typeck_results().expr_adjustments(obj).iter()
|
||||||
|
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hir::ExprKind::Call(call, [_]) => {
|
||||||
|
if let hir::ExprKind::Path(qpath) = call.kind {
|
||||||
|
check_qpath(cx, qpath, call.hir_id)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hir::ExprKind::Path(qpath) => check_qpath(cx, qpath, arg.hir_id),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: &str) {
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
USELESS_ASREF,
|
||||||
|
span,
|
||||||
|
&format!("this call to `{call_name}.map(...)` does nothing"),
|
||||||
|
"try",
|
||||||
|
format!(
|
||||||
|
"{}.clone()",
|
||||||
|
snippet_with_applicability(cx, recvr.span, "..", &mut applicability)
|
||||||
|
),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@ pub(super) fn get_hint_if_single_char_arg(
|
||||||
match ch {
|
match ch {
|
||||||
"'" => "\\'",
|
"'" => "\\'",
|
||||||
r"\" => "\\\\",
|
r"\" => "\\\\",
|
||||||
|
"\\\"" => "\"", // no need to escape `"` in `'"'`
|
||||||
_ => ch,
|
_ => ch,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -331,7 +331,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
|
||||||
slice,
|
slice,
|
||||||
} if indexes.len() > 1 => {
|
} if indexes.len() > 1 => {
|
||||||
// if we have found an `assert!`, let's also check that it's actually right
|
// if we have found an `assert!`, let's also check that it's actually right
|
||||||
// and if it convers the highest index and if not, suggest the correct length
|
// and if it covers the highest index and if not, suggest the correct length
|
||||||
let sugg = match comparison {
|
let sugg = match comparison {
|
||||||
// `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks.
|
// `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks.
|
||||||
// The user probably meant `v.len() > 5`
|
// The user probably meant `v.len() > 5`
|
||||||
|
|
|
@ -13,21 +13,23 @@ use rustc_span::Symbol;
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for imports that do not rename the item as specified
|
/// Checks for imports that do not rename the item as specified
|
||||||
/// in the `enforce-import-renames` config option.
|
/// in the `enforced-import-renames` config option.
|
||||||
///
|
///
|
||||||
/// Note: Even though this lint is warn-by-default, it will only trigger if
|
/// Note: Even though this lint is warn-by-default, it will only trigger if
|
||||||
/// import renames are defined in the clippy.toml file.
|
/// import renames are defined in the `clippy.toml` file.
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// Consistency is important, if a project has defined import
|
/// Consistency is important; if a project has defined import renames, then they should be
|
||||||
/// renames they should be followed. More practically, some item names are too
|
/// followed. More practically, some item names are too vague outside of their defining scope,
|
||||||
/// vague outside of their defining scope this can enforce a more meaningful naming.
|
/// in which case this can enforce a more meaningful naming.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// An example clippy.toml configuration:
|
/// An example clippy.toml configuration:
|
||||||
/// ```toml
|
/// ```toml
|
||||||
/// # clippy.toml
|
/// # clippy.toml
|
||||||
/// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }]
|
/// enforced-import-renames = [
|
||||||
|
/// { path = "serde_json::Value", rename = "JsonValue" },
|
||||||
|
/// ]
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
|
|
|
@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, IntTy, Ty, UintTy};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
|
||||||
|
@ -105,8 +105,28 @@ impl<'tcx> LateLintPass<'tcx> for Mutex {
|
||||||
fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
|
fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::Bool => Some("AtomicBool"),
|
ty::Bool => Some("AtomicBool"),
|
||||||
ty::Uint(_) => Some("AtomicUsize"),
|
ty::Uint(uint_ty) => {
|
||||||
ty::Int(_) => Some("AtomicIsize"),
|
match uint_ty {
|
||||||
|
UintTy::U8 => Some("AtomicU8"),
|
||||||
|
UintTy::U16 => Some("AtomicU16"),
|
||||||
|
UintTy::U32 => Some("AtomicU32"),
|
||||||
|
UintTy::U64 => Some("AtomicU64"),
|
||||||
|
UintTy::Usize => Some("AtomicUsize"),
|
||||||
|
// There's no `AtomicU128`.
|
||||||
|
UintTy::U128 => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ty::Int(int_ty) => {
|
||||||
|
match int_ty {
|
||||||
|
IntTy::I8 => Some("AtomicI8"),
|
||||||
|
IntTy::I16 => Some("AtomicI16"),
|
||||||
|
IntTy::I32 => Some("AtomicI32"),
|
||||||
|
IntTy::I64 => Some("AtomicI64"),
|
||||||
|
IntTy::Isize => Some("AtomicIsize"),
|
||||||
|
// There's no `AtomicI128`.
|
||||||
|
IntTy::I128 => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
ty::RawPtr(_) => Some("AtomicPtr"),
|
ty::RawPtr(_) => Some("AtomicPtr"),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
// It's very hard or impossable to check whether overridden operator have side-effect this lint.
|
// It's very hard or impossible to check whether overridden operator have side-effect this lint.
|
||||||
// So, this function assume user-defined operator is overridden with an side-effect.
|
// So, this function assume user-defined operator is overridden with an side-effect.
|
||||||
// The definition of user-defined structure here is ADT-type,
|
// The definition of user-defined structure here is ADT-type,
|
||||||
// Althrough this will weaken the ability of this lint, less error lint-fix happen.
|
// Althrough this will weaken the ability of this lint, less error lint-fix happen.
|
||||||
|
|
|
@ -53,14 +53,10 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|
||||||
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
|
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
|
||||||
&& let ExprKind::Lit(_) = param.kind
|
&& let ExprKind::Lit(_) = param.kind
|
||||||
&& param.span.eq_ctxt(expr.span)
|
&& param.span.eq_ctxt(expr.span)
|
||||||
|
&& let Some(snip) = snippet_opt(cx, param.span)
|
||||||
|
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
|
||||||
{
|
{
|
||||||
let Some(snip) = snippet_opt(cx, param.span) else {
|
show_error(cx, param);
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !snip.starts_with("0o") {
|
|
||||||
show_error(cx, param);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprKind::Call(func, [param]) => {
|
ExprKind::Call(func, [param]) => {
|
||||||
|
@ -70,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|
||||||
&& let ExprKind::Lit(_) = param.kind
|
&& let ExprKind::Lit(_) = param.kind
|
||||||
&& param.span.eq_ctxt(expr.span)
|
&& param.span.eq_ctxt(expr.span)
|
||||||
&& let Some(snip) = snippet_opt(cx, param.span)
|
&& let Some(snip) = snippet_opt(cx, param.span)
|
||||||
&& !snip.starts_with("0o")
|
&& !(snip.starts_with("0o") || snip.starts_with("0b"))
|
||||||
{
|
{
|
||||||
show_error(cx, param);
|
show_error(cx, param);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,82 +18,118 @@ pub(crate) fn check<'tcx>(
|
||||||
right: &'tcx Expr<'_>,
|
right: &'tcx Expr<'_>,
|
||||||
) {
|
) {
|
||||||
if !is_allowed(cx, op, left, right) {
|
if !is_allowed(cx, op, left, right) {
|
||||||
match op {
|
return;
|
||||||
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
}
|
||||||
check_op(
|
|
||||||
cx,
|
// we need to know whether a ref is coerced to a value
|
||||||
left,
|
// if a ref is coerced, then the suggested lint must deref it
|
||||||
0,
|
// e.g. `let _: i32 = x+0` with `x: &i32` should be replaced with `let _: i32 = *x`.
|
||||||
expr.span,
|
// we do this by checking the _kind_ of the type of the expression
|
||||||
peel_hir_expr_refs(right).0.span,
|
// if it's a ref, we then check whether it is erased, and that's it.
|
||||||
needs_parenthesis(cx, expr, right),
|
let (peeled_left_span, left_is_coerced_to_value) = {
|
||||||
);
|
let expr = peel_hir_expr_refs(left).0;
|
||||||
check_op(
|
let span = expr.span;
|
||||||
cx,
|
let is_coerced = expr_is_erased_ref(cx, expr);
|
||||||
right,
|
(span, is_coerced)
|
||||||
0,
|
};
|
||||||
expr.span,
|
|
||||||
peel_hir_expr_refs(left).0.span,
|
let (peeled_right_span, right_is_coerced_to_value) = {
|
||||||
Parens::Unneeded,
|
let expr = peel_hir_expr_refs(right).0;
|
||||||
);
|
let span = expr.span;
|
||||||
},
|
let is_coerced = expr_is_erased_ref(cx, expr);
|
||||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
|
(span, is_coerced)
|
||||||
check_op(
|
};
|
||||||
cx,
|
|
||||||
right,
|
match op {
|
||||||
0,
|
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
||||||
expr.span,
|
check_op(
|
||||||
peel_hir_expr_refs(left).0.span,
|
cx,
|
||||||
Parens::Unneeded,
|
left,
|
||||||
);
|
0,
|
||||||
},
|
expr.span,
|
||||||
BinOpKind::Mul => {
|
peeled_right_span,
|
||||||
check_op(
|
needs_parenthesis(cx, expr, right),
|
||||||
cx,
|
right_is_coerced_to_value,
|
||||||
left,
|
);
|
||||||
1,
|
check_op(
|
||||||
expr.span,
|
cx,
|
||||||
peel_hir_expr_refs(right).0.span,
|
right,
|
||||||
needs_parenthesis(cx, expr, right),
|
0,
|
||||||
);
|
expr.span,
|
||||||
check_op(
|
peeled_left_span,
|
||||||
cx,
|
Parens::Unneeded,
|
||||||
right,
|
left_is_coerced_to_value,
|
||||||
1,
|
);
|
||||||
expr.span,
|
},
|
||||||
peel_hir_expr_refs(left).0.span,
|
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
|
||||||
Parens::Unneeded,
|
check_op(
|
||||||
);
|
cx,
|
||||||
},
|
right,
|
||||||
BinOpKind::Div => check_op(
|
0,
|
||||||
|
expr.span,
|
||||||
|
peeled_left_span,
|
||||||
|
Parens::Unneeded,
|
||||||
|
left_is_coerced_to_value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
BinOpKind::Mul => {
|
||||||
|
check_op(
|
||||||
|
cx,
|
||||||
|
left,
|
||||||
|
1,
|
||||||
|
expr.span,
|
||||||
|
peeled_right_span,
|
||||||
|
needs_parenthesis(cx, expr, right),
|
||||||
|
right_is_coerced_to_value,
|
||||||
|
);
|
||||||
|
check_op(
|
||||||
cx,
|
cx,
|
||||||
right,
|
right,
|
||||||
1,
|
1,
|
||||||
expr.span,
|
expr.span,
|
||||||
peel_hir_expr_refs(left).0.span,
|
peeled_left_span,
|
||||||
Parens::Unneeded,
|
Parens::Unneeded,
|
||||||
),
|
left_is_coerced_to_value,
|
||||||
BinOpKind::BitAnd => {
|
);
|
||||||
check_op(
|
},
|
||||||
cx,
|
BinOpKind::Div => check_op(
|
||||||
left,
|
cx,
|
||||||
-1,
|
right,
|
||||||
expr.span,
|
1,
|
||||||
peel_hir_expr_refs(right).0.span,
|
expr.span,
|
||||||
needs_parenthesis(cx, expr, right),
|
peeled_left_span,
|
||||||
);
|
Parens::Unneeded,
|
||||||
check_op(
|
left_is_coerced_to_value,
|
||||||
cx,
|
),
|
||||||
right,
|
BinOpKind::BitAnd => {
|
||||||
-1,
|
check_op(
|
||||||
expr.span,
|
cx,
|
||||||
peel_hir_expr_refs(left).0.span,
|
left,
|
||||||
Parens::Unneeded,
|
-1,
|
||||||
);
|
expr.span,
|
||||||
},
|
peeled_right_span,
|
||||||
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
|
needs_parenthesis(cx, expr, right),
|
||||||
_ => (),
|
right_is_coerced_to_value,
|
||||||
}
|
);
|
||||||
|
check_op(
|
||||||
|
cx,
|
||||||
|
right,
|
||||||
|
-1,
|
||||||
|
expr.span,
|
||||||
|
peeled_left_span,
|
||||||
|
Parens::Unneeded,
|
||||||
|
left_is_coerced_to_value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_is_erased_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
match cx.typeck_results().expr_ty(expr).kind() {
|
||||||
|
ty::Ref(r, ..) => r.is_erased(),
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,11 +180,11 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||||
// This lint applies to integers
|
// This lint applies to integers and their references
|
||||||
!cx.typeck_results().expr_ty(left).peel_refs().is_integral()
|
cx.typeck_results().expr_ty(left).peel_refs().is_integral()
|
||||||
|| !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
|
&& cx.typeck_results().expr_ty(right).peel_refs().is_integral()
|
||||||
// `1 << 0` is a common pattern in bit manipulation code
|
// `1 << 0` is a common pattern in bit manipulation code
|
||||||
|| (cmp == BinOpKind::Shl
|
&& !(cmp == BinOpKind::Shl
|
||||||
&& constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
|
&& constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
|
||||||
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
|
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
|
||||||
}
|
}
|
||||||
|
@ -161,11 +197,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
|
||||||
(Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
|
(Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
|
||||||
_ => return,
|
_ => return,
|
||||||
} {
|
} {
|
||||||
span_ineffective_operation(cx, span, arg, Parens::Unneeded);
|
span_ineffective_operation(cx, span, arg, Parens::Unneeded, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
|
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) {
|
||||||
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
|
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
|
||||||
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
|
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
|
||||||
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
|
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
|
||||||
|
@ -178,18 +214,28 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
|
||||||
1 => v == 1,
|
1 => v == 1,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
} {
|
} {
|
||||||
span_ineffective_operation(cx, span, arg, parens);
|
span_ineffective_operation(cx, span, arg, parens, is_erased);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) {
|
fn span_ineffective_operation(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
span: Span,
|
||||||
|
arg: Span,
|
||||||
|
parens: Parens,
|
||||||
|
is_ref_coerced_to_val: bool,
|
||||||
|
) {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability);
|
let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability);
|
||||||
|
let expr_snippet = if is_ref_coerced_to_val {
|
||||||
|
format!("*{expr_snippet}")
|
||||||
|
} else {
|
||||||
|
expr_snippet.into_owned()
|
||||||
|
};
|
||||||
let suggestion = match parens {
|
let suggestion = match parens {
|
||||||
Parens::Needed => format!("({expr_snippet})"),
|
Parens::Needed => format!("({expr_snippet})"),
|
||||||
Parens::Unneeded => expr_snippet.into_owned(),
|
Parens::Unneeded => expr_snippet,
|
||||||
};
|
};
|
||||||
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
|
|
83
clippy_lints/src/pub_underscore_fields.rs
Normal file
83
clippy_lints/src/pub_underscore_fields.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use clippy_config::types::PubUnderscoreFieldsBehaviour;
|
||||||
|
use clippy_utils::attrs::is_doc_hidden;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
|
use clippy_utils::is_path_lang_item;
|
||||||
|
use rustc_hir::{FieldDef, Item, ItemKind, LangItem};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_session::impl_lint_pass;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks whether any field of the struct is prefixed with an `_` (underscore) and also marked
|
||||||
|
/// `pub` (public)
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Fields prefixed with an `_` are inferred as unused, which suggests it should not be marked
|
||||||
|
/// as `pub`, because marking it as `pub` infers it will be used.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// struct FileHandle {
|
||||||
|
/// pub _descriptor: usize,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// struct FileHandle {
|
||||||
|
/// _descriptor: usize,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// OR
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// struct FileHandle {
|
||||||
|
/// pub descriptor: usize,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.77.0"]
|
||||||
|
pub PUB_UNDERSCORE_FIELDS,
|
||||||
|
pedantic,
|
||||||
|
"struct field prefixed with underscore and marked public"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PubUnderscoreFields {
|
||||||
|
pub behavior: PubUnderscoreFieldsBehaviour,
|
||||||
|
}
|
||||||
|
impl_lint_pass!(PubUnderscoreFields => [PUB_UNDERSCORE_FIELDS]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields {
|
||||||
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||||
|
// This lint only pertains to structs.
|
||||||
|
let ItemKind::Struct(variant_data, _) = &item.kind else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_visible = |field: &FieldDef<'_>| match self.behavior {
|
||||||
|
PubUnderscoreFieldsBehaviour::PublicallyExported => cx.effective_visibilities.is_reachable(field.def_id),
|
||||||
|
PubUnderscoreFieldsBehaviour::AllPubFields => {
|
||||||
|
// If there is a visibility span then the field is marked pub in some way.
|
||||||
|
!field.vis_span.is_empty()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for field in variant_data.fields() {
|
||||||
|
// Only pertains to fields that start with an underscore, and are public.
|
||||||
|
if field.ident.as_str().starts_with('_') && is_visible(field)
|
||||||
|
// We ignore fields that have `#[doc(hidden)]`.
|
||||||
|
&& !is_doc_hidden(cx.tcx.hir().attrs(field.hir_id))
|
||||||
|
// We ignore fields that are `PhantomData`.
|
||||||
|
&& !is_path_lang_item(cx, field.ty, LangItem::PhantomData)
|
||||||
|
{
|
||||||
|
span_lint_and_help(
|
||||||
|
cx,
|
||||||
|
PUB_UNDERSCORE_FIELDS,
|
||||||
|
field.vis_span.to(field.ident.span),
|
||||||
|
"field marked as public but also inferred as unused because it's prefixed with `_`",
|
||||||
|
None,
|
||||||
|
"consider removing the underscore, or making the field private",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,12 +75,7 @@ enum IfBlockType<'hir> {
|
||||||
/// An `if x.is_xxx() { a } else { b } ` expression.
|
/// An `if x.is_xxx() { a } else { b } ` expression.
|
||||||
///
|
///
|
||||||
/// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
|
/// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
|
||||||
IfIs(
|
IfIs(&'hir Expr<'hir>, Ty<'hir>, Symbol, &'hir Expr<'hir>),
|
||||||
&'hir Expr<'hir>,
|
|
||||||
Ty<'hir>,
|
|
||||||
Symbol,
|
|
||||||
&'hir Expr<'hir>,
|
|
||||||
),
|
|
||||||
/// An `if let Xxx(a) = b { c } else { d }` expression.
|
/// An `if let Xxx(a) = b { c } else { d }` expression.
|
||||||
///
|
///
|
||||||
/// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
|
/// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for mis-uses of the serde API.
|
/// Checks for misuses of the serde API.
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// Serde is very finnicky about how its API should be
|
/// Serde is very finnicky about how its API should be
|
||||||
|
|
102
clippy_lints/src/thread_local_initializer_can_be_made_const.rs
Normal file
102
clippy_lints/src/thread_local_initializer_can_be_made_const.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use clippy_config::msrvs::Msrv;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::fn_has_unsatisfiable_preds;
|
||||||
|
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
|
||||||
|
use clippy_utils::source::snippet;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{intravisit, ExprKind};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::lint::in_external_macro;
|
||||||
|
use rustc_session::impl_lint_pass;
|
||||||
|
use rustc_span::sym::thread_local_macro;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Suggests to use `const` in `thread_local!` macro if possible.
|
||||||
|
/// ### Why is this bad?
|
||||||
|
///
|
||||||
|
/// The `thread_local!` macro wraps static declarations and makes them thread-local.
|
||||||
|
/// It supports using a `const` keyword that may be used for declarations that can
|
||||||
|
/// be evaluated as a constant expression. This can enable a more efficient thread
|
||||||
|
/// local implementation that can avoid lazy initialization. For types that do not
|
||||||
|
/// need to be dropped, this can enable an even more efficient implementation that
|
||||||
|
/// does not need to track any additional state.
|
||||||
|
///
|
||||||
|
/// https://doc.rust-lang.org/std/macro.thread_local.html
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// // example code where clippy issues a warning
|
||||||
|
/// thread_local! {
|
||||||
|
/// static BUF: String = String::new();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// // example code which does not raise clippy warning
|
||||||
|
/// thread_local! {
|
||||||
|
/// static BUF: String = const { String::new() };
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.76.0"]
|
||||||
|
pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
|
||||||
|
perf,
|
||||||
|
"suggest using `const` in `thread_local!` macro"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ThreadLocalInitializerCanBeMadeConst {
|
||||||
|
msrv: Msrv,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThreadLocalInitializerCanBeMadeConst {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(msrv: Msrv) -> Self {
|
||||||
|
Self { msrv }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
|
||||||
|
fn check_fn(
|
||||||
|
&mut self,
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
fn_kind: rustc_hir::intravisit::FnKind<'tcx>,
|
||||||
|
_: &'tcx rustc_hir::FnDecl<'tcx>,
|
||||||
|
body: &'tcx rustc_hir::Body<'tcx>,
|
||||||
|
span: rustc_span::Span,
|
||||||
|
defid: rustc_span::def_id::LocalDefId,
|
||||||
|
) {
|
||||||
|
if in_external_macro(cx.sess(), span)
|
||||||
|
&& let Some(callee) = span.source_callee()
|
||||||
|
&& let Some(macro_def_id) = callee.macro_def_id
|
||||||
|
&& cx.tcx.is_diagnostic_item(thread_local_macro, macro_def_id)
|
||||||
|
&& let intravisit::FnKind::ItemFn(..) = fn_kind
|
||||||
|
// Building MIR for `fn`s with unsatisfiable preds results in ICE.
|
||||||
|
&& !fn_has_unsatisfiable_preds(cx, defid.to_def_id())
|
||||||
|
&& let mir = cx.tcx.optimized_mir(defid.to_def_id())
|
||||||
|
&& let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv)
|
||||||
|
// this is the `__init` function emitted by the `thread_local!` macro
|
||||||
|
// when the `const` keyword is not used. We avoid checking the `__init` directly
|
||||||
|
// as that is not a public API.
|
||||||
|
// we know that the function is const-qualifiable, so now we need only to get the
|
||||||
|
// initializer expression to span-lint it.
|
||||||
|
&& let ExprKind::Block(block, _) = body.value.kind
|
||||||
|
&& let Some(ret_expr) = block.expr
|
||||||
|
&& let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }")
|
||||||
|
&& initializer_snippet != "thread_local! { ... }"
|
||||||
|
{
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
|
||||||
|
ret_expr.span,
|
||||||
|
"initializer for `thread_local` value can be made `const`",
|
||||||
|
"replace with",
|
||||||
|
format!("const {{ {initializer_snippet} }}"),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_msrv_attr!(LateContext);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::ty::is_normalizable;
|
use clippy_utils::ty::is_normalizable;
|
||||||
use clippy_utils::{path_to_local, path_to_local_id};
|
use clippy_utils::{eq_expr_value, path_to_local};
|
||||||
use rustc_abi::WrappingRange;
|
use rustc_abi::WrappingRange;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind, Node};
|
use rustc_hir::{Expr, ExprKind, Node};
|
||||||
|
@ -25,6 +25,52 @@ fn range_fully_contained(from: WrappingRange, to: WrappingRange) -> bool {
|
||||||
to.contains(from.start) && to.contains(from.end)
|
to.contains(from.start) && to.contains(from.end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a given expression is a binary operation involving a local variable or is made up of
|
||||||
|
/// other (nested) binary expressions involving the local. There must be at least one local
|
||||||
|
/// reference that is the same as `local_expr`.
|
||||||
|
///
|
||||||
|
/// This is used as a heuristic to detect if a variable
|
||||||
|
/// is checked to be within the valid range of a transmuted type.
|
||||||
|
/// All of these would return true:
|
||||||
|
/// * `x < 4`
|
||||||
|
/// * `x < 4 && x > 1`
|
||||||
|
/// * `x.field < 4 && x.field > 1` (given `x.field`)
|
||||||
|
/// * `x.field < 4 && unrelated()`
|
||||||
|
/// * `(1..=3).contains(&x)`
|
||||||
|
fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Binary(_, lhs, rhs) => {
|
||||||
|
binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs)
|
||||||
|
},
|
||||||
|
ExprKind::MethodCall(path, receiver, [arg], _)
|
||||||
|
if path.ident.name == sym!(contains)
|
||||||
|
// ... `contains` called on some kind of range
|
||||||
|
&& let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def()
|
||||||
|
&& let lang_items = cx.tcx.lang_items()
|
||||||
|
&& [
|
||||||
|
lang_items.range_from_struct(),
|
||||||
|
lang_items.range_inclusive_struct(),
|
||||||
|
lang_items.range_struct(),
|
||||||
|
lang_items.range_to_inclusive_struct(),
|
||||||
|
lang_items.range_to_struct()
|
||||||
|
].into_iter().any(|did| did == Some(receiver_adt.did())) =>
|
||||||
|
{
|
||||||
|
eq_expr_value(cx, local_expr, arg.peel_borrows())
|
||||||
|
},
|
||||||
|
_ => eq_expr_value(cx, local_expr, expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if an expression is a path to a local variable (with optional projections), e.g.
|
||||||
|
/// `x.field[0].field2` would return true.
|
||||||
|
fn is_local_with_projections(expr: &Expr<'_>) -> bool {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Path(_) => path_to_local(expr).is_some(),
|
||||||
|
ExprKind::Field(expr, _) | ExprKind::Index(expr, ..) => is_local_with_projections(expr),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn check<'tcx>(
|
pub(super) fn check<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
expr: &'tcx Expr<'tcx>,
|
expr: &'tcx Expr<'tcx>,
|
||||||
|
@ -36,9 +82,8 @@ pub(super) fn check<'tcx>(
|
||||||
&& let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind
|
&& let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind
|
||||||
&& cx.typeck_results().expr_ty(receiver).is_bool()
|
&& cx.typeck_results().expr_ty(receiver).is_bool()
|
||||||
&& path.ident.name == sym!(then_some)
|
&& path.ident.name == sym!(then_some)
|
||||||
&& let ExprKind::Binary(_, lhs, rhs) = receiver.kind
|
&& is_local_with_projections(transmutable)
|
||||||
&& let Some(local_id) = path_to_local(transmutable)
|
&& binops_with_local(cx, transmutable, receiver)
|
||||||
&& (path_to_local_id(lhs, local_id) || path_to_local_id(rhs, local_id))
|
|
||||||
&& is_normalizable(cx, cx.param_env, from_ty)
|
&& is_normalizable(cx, cx.param_env, from_ty)
|
||||||
&& is_normalizable(cx, cx.param_env, to_ty)
|
&& is_normalizable(cx, cx.param_env, to_ty)
|
||||||
// we only want to lint if the target type has a niche that is larger than the one of the source type
|
// we only want to lint if the target type has a niche that is larger than the one of the source type
|
||||||
|
|
|
@ -37,9 +37,7 @@ pub(super) fn check<'tcx>(
|
||||||
to_ty = to_sub_ty;
|
to_ty = to_sub_ty;
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
(ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty)))
|
(ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => {
|
||||||
if reduced_tys.from_fat_ptr =>
|
|
||||||
{
|
|
||||||
from_ty = from_sub_ty;
|
from_ty = from_sub_ty;
|
||||||
to_ty = to_sub_ty;
|
to_ty = to_sub_ty;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::{get_trait_def_id, path_res};
|
use clippy_utils::{expr_or_init, get_trait_def_id, path_def_id};
|
||||||
use rustc_ast::BinOpKind;
|
use rustc_ast::BinOpKind;
|
||||||
use rustc_hir::def::Res;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::{walk_body, walk_expr, FnKind, Visitor};
|
||||||
use rustc_hir::{Body, Expr, ExprKind, FnDecl, Item, ItemKind, Node};
|
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind};
|
||||||
|
use rustc_hir_analysis::hir_ty_to_ty;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::hir::map::Map;
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_middle::hir::nested_filter;
|
||||||
|
use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt};
|
||||||
|
use rustc_session::impl_lint_pass;
|
||||||
|
use rustc_span::symbol::{kw, Ident};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -41,7 +48,26 @@ declare_clippy_lint! {
|
||||||
"detect unconditional recursion in some traits implementation"
|
"detect unconditional recursion in some traits implementation"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(UnconditionalRecursion => [UNCONDITIONAL_RECURSION]);
|
#[derive(Default)]
|
||||||
|
pub struct UnconditionalRecursion {
|
||||||
|
/// The key is the `DefId` of the type implementing the `Default` trait and the value is the
|
||||||
|
/// `DefId` of the return call.
|
||||||
|
default_impl_for_type: FxHashMap<DefId, DefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(UnconditionalRecursion => [UNCONDITIONAL_RECURSION]);
|
||||||
|
|
||||||
|
fn span_error(cx: &LateContext<'_>, method_span: Span, expr: &Expr<'_>) {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
UNCONDITIONAL_RECURSION,
|
||||||
|
method_span,
|
||||||
|
"function cannot return without recursing",
|
||||||
|
|diag| {
|
||||||
|
diag.span_note(expr.span, "recursive call site");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
|
fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
|
||||||
match ty.peel_refs().kind() {
|
match ty.peel_refs().kind() {
|
||||||
|
@ -51,84 +77,329 @@ fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_local(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
fn get_hir_ty_def_id(tcx: TyCtxt<'_>, hir_ty: rustc_hir::Ty<'_>) -> Option<DefId> {
|
||||||
matches!(path_res(cx, expr), Res::Local(_))
|
let TyKind::Path(qpath) = hir_ty.kind else { return None };
|
||||||
|
match qpath {
|
||||||
|
QPath::Resolved(_, path) => path.res.opt_def_id(),
|
||||||
|
QPath::TypeRelative(_, _) => {
|
||||||
|
let ty = hir_ty_to_ty(tcx, &hir_ty);
|
||||||
|
|
||||||
|
match ty.kind() {
|
||||||
|
ty::Alias(ty::Projection, proj) => {
|
||||||
|
Res::<HirId>::Def(DefKind::Trait, proj.trait_ref(tcx).def_id).opt_def_id()
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
QPath::LangItem(..) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_return_calls_in_body<'tcx>(body: &'tcx Body<'tcx>) -> Vec<&'tcx Expr<'tcx>> {
|
||||||
|
let mut visitor = ReturnsVisitor::default();
|
||||||
|
|
||||||
|
visitor.visit_body(body);
|
||||||
|
visitor.returns
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_conditional_return(body: &Body<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
match get_return_calls_in_body(body).as_slice() {
|
||||||
|
[] => false,
|
||||||
|
[return_expr] => return_expr.hir_id != expr.hir_id,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Option<DefId> {
|
||||||
|
let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id);
|
||||||
|
if let Some((
|
||||||
|
_,
|
||||||
|
Node::Item(Item {
|
||||||
|
kind: ItemKind::Impl(impl_),
|
||||||
|
owner_id,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
)) = cx.tcx.hir().parent_iter(hir_id).next()
|
||||||
|
// We exclude `impl` blocks generated from rustc's proc macros.
|
||||||
|
&& !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
|
||||||
|
// It is a implementation of a trait.
|
||||||
|
&& let Some(trait_) = impl_.of_trait
|
||||||
|
{
|
||||||
|
trait_.trait_def_id()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_def_path)]
|
||||||
|
fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) {
|
||||||
|
let args = cx
|
||||||
|
.tcx
|
||||||
|
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder())
|
||||||
|
.inputs();
|
||||||
|
// That has two arguments.
|
||||||
|
if let [self_arg, other_arg] = args
|
||||||
|
&& let Some(self_arg) = get_ty_def_id(*self_arg)
|
||||||
|
&& let Some(other_arg) = get_ty_def_id(*other_arg)
|
||||||
|
// The two arguments are of the same type.
|
||||||
|
&& self_arg == other_arg
|
||||||
|
&& let Some(trait_def_id) = get_impl_trait_def_id(cx, method_def_id)
|
||||||
|
// The trait is `PartialEq`.
|
||||||
|
&& Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"])
|
||||||
|
{
|
||||||
|
let to_check_op = if name.name == sym::eq {
|
||||||
|
BinOpKind::Eq
|
||||||
|
} else {
|
||||||
|
BinOpKind::Ne
|
||||||
|
};
|
||||||
|
let is_bad = match expr.kind {
|
||||||
|
ExprKind::Binary(op, left, right) if op.node == to_check_op => {
|
||||||
|
// Then we check if the left-hand element is of the same type as `self`.
|
||||||
|
if let Some(left_ty) = cx.typeck_results().expr_ty_opt(left)
|
||||||
|
&& let Some(left_id) = get_ty_def_id(left_ty)
|
||||||
|
&& self_arg == left_id
|
||||||
|
&& let Some(right_ty) = cx.typeck_results().expr_ty_opt(right)
|
||||||
|
&& let Some(right_id) = get_ty_def_id(right_ty)
|
||||||
|
&& other_arg == right_id
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => {
|
||||||
|
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||||
|
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||||
|
&& trait_id == trait_def_id
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if is_bad {
|
||||||
|
span_error(cx, method_span, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_def_path)]
|
||||||
|
fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) {
|
||||||
|
let args = cx
|
||||||
|
.tcx
|
||||||
|
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder())
|
||||||
|
.inputs();
|
||||||
|
// That has one argument.
|
||||||
|
if let [_self_arg] = args
|
||||||
|
&& let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id)
|
||||||
|
&& let Some((
|
||||||
|
_,
|
||||||
|
Node::Item(Item {
|
||||||
|
kind: ItemKind::Impl(impl_),
|
||||||
|
owner_id,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
)) = cx.tcx.hir().parent_iter(hir_id).next()
|
||||||
|
// We exclude `impl` blocks generated from rustc's proc macros.
|
||||||
|
&& !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
|
||||||
|
// It is a implementation of a trait.
|
||||||
|
&& let Some(trait_) = impl_.of_trait
|
||||||
|
&& let Some(trait_def_id) = trait_.trait_def_id()
|
||||||
|
// The trait is `ToString`.
|
||||||
|
&& Some(trait_def_id) == get_trait_def_id(cx, &["alloc", "string", "ToString"])
|
||||||
|
{
|
||||||
|
let is_bad = match expr.kind {
|
||||||
|
ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => {
|
||||||
|
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||||
|
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||||
|
&& trait_id == trait_def_id
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if is_bad {
|
||||||
|
span_error(cx, method_span, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_default_method_on_current_ty(tcx: TyCtxt<'_>, qpath: QPath<'_>, implemented_ty_id: DefId) -> bool {
|
||||||
|
match qpath {
|
||||||
|
QPath::Resolved(_, path) => match path.segments {
|
||||||
|
[first, .., last] => last.ident.name == kw::Default && first.res.opt_def_id() == Some(implemented_ty_id),
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
QPath::TypeRelative(ty, segment) => {
|
||||||
|
if segment.ident.name != kw::Default {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if matches!(
|
||||||
|
ty.kind,
|
||||||
|
TyKind::Path(QPath::Resolved(
|
||||||
|
_,
|
||||||
|
hir::Path {
|
||||||
|
res: Res::SelfTyAlias { .. },
|
||||||
|
..
|
||||||
|
},
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
get_hir_ty_def_id(tcx, *ty) == Some(implemented_ty_id)
|
||||||
|
},
|
||||||
|
QPath::LangItem(..) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CheckCalls<'a, 'tcx> {
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
map: Map<'tcx>,
|
||||||
|
implemented_ty_id: DefId,
|
||||||
|
found_default_call: bool,
|
||||||
|
method_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> Visitor<'tcx> for CheckCalls<'a, 'tcx>
|
||||||
|
where
|
||||||
|
'tcx: 'a,
|
||||||
|
{
|
||||||
|
type NestedFilter = nested_filter::OnlyBodies;
|
||||||
|
|
||||||
|
fn nested_visit_map(&mut self) -> Self::Map {
|
||||||
|
self.map
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unnecessary_def_path)]
|
||||||
|
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||||
|
if self.found_default_call {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
walk_expr(self, expr);
|
||||||
|
|
||||||
|
if let ExprKind::Call(f, _) = expr.kind
|
||||||
|
&& let ExprKind::Path(qpath) = f.kind
|
||||||
|
&& is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id)
|
||||||
|
&& let Some(method_def_id) = path_def_id(self.cx, f)
|
||||||
|
&& let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id)
|
||||||
|
&& Some(trait_def_id) == get_trait_def_id(self.cx, &["core", "default", "Default"])
|
||||||
|
{
|
||||||
|
self.found_default_call = true;
|
||||||
|
span_error(self.cx, self.method_span, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnconditionalRecursion {
|
||||||
|
#[allow(clippy::unnecessary_def_path)]
|
||||||
|
fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) {
|
||||||
|
if self.default_impl_for_type.is_empty()
|
||||||
|
&& let Some(default_trait_id) = get_trait_def_id(cx, &["core", "default", "Default"])
|
||||||
|
{
|
||||||
|
let impls = cx.tcx.trait_impls_of(default_trait_id);
|
||||||
|
for (ty, impl_def_ids) in impls.non_blanket_impls() {
|
||||||
|
let Some(self_def_id) = ty.def() else { continue };
|
||||||
|
for impl_def_id in impl_def_ids {
|
||||||
|
if !cx.tcx.has_attr(*impl_def_id, sym::automatically_derived) &&
|
||||||
|
let Some(assoc_item) = cx
|
||||||
|
.tcx
|
||||||
|
.associated_items(impl_def_id)
|
||||||
|
.in_definition_order()
|
||||||
|
// We're not interested in foreign implementations of the `Default` trait.
|
||||||
|
.find(|item| {
|
||||||
|
item.kind == AssocKind::Fn && item.def_id.is_local() && item.name == kw::Default
|
||||||
|
})
|
||||||
|
&& let Some(body_node) = cx.tcx.hir().get_if_local(assoc_item.def_id)
|
||||||
|
&& let Some(body_id) = body_node.body_id()
|
||||||
|
&& let body = cx.tcx.hir().body(body_id)
|
||||||
|
// We don't want to keep it if it has conditional return.
|
||||||
|
&& let [return_expr] = get_return_calls_in_body(body).as_slice()
|
||||||
|
&& let ExprKind::Call(call_expr, _) = return_expr.kind
|
||||||
|
// We need to use typeck here to infer the actual function being called.
|
||||||
|
&& let body_def_id = cx.tcx.hir().enclosing_body_owner(call_expr.hir_id)
|
||||||
|
&& let Some(body_owner) = cx.tcx.hir().maybe_body_owned_by(body_def_id)
|
||||||
|
&& let typeck = cx.tcx.typeck_body(body_owner)
|
||||||
|
&& let Some(call_def_id) = typeck.type_dependent_def_id(call_expr.hir_id)
|
||||||
|
{
|
||||||
|
self.default_impl_for_type.insert(self_def_id, call_def_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_default_new<'tcx>(
|
||||||
|
&mut self,
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
decl: &FnDecl<'tcx>,
|
||||||
|
body: &'tcx Body<'tcx>,
|
||||||
|
method_span: Span,
|
||||||
|
method_def_id: LocalDefId,
|
||||||
|
) {
|
||||||
|
// We're only interested into static methods.
|
||||||
|
if decl.implicit_self.has_implicit_self() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We don't check trait implementations.
|
||||||
|
if get_impl_trait_def_id(cx, method_def_id).is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id);
|
||||||
|
if let Some((
|
||||||
|
_,
|
||||||
|
Node::Item(Item {
|
||||||
|
kind: ItemKind::Impl(impl_),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
)) = cx.tcx.hir().parent_iter(hir_id).next()
|
||||||
|
&& let Some(implemented_ty_id) = get_hir_ty_def_id(cx.tcx, *impl_.self_ty)
|
||||||
|
&& {
|
||||||
|
self.init_default_impl_for_type_if_needed(cx);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
&& let Some(return_def_id) = self.default_impl_for_type.get(&implemented_ty_id)
|
||||||
|
&& method_def_id.to_def_id() == *return_def_id
|
||||||
|
{
|
||||||
|
let mut c = CheckCalls {
|
||||||
|
cx,
|
||||||
|
map: cx.tcx.hir(),
|
||||||
|
implemented_ty_id,
|
||||||
|
found_default_call: false,
|
||||||
|
method_span,
|
||||||
|
};
|
||||||
|
walk_body(&mut c, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
|
impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
|
||||||
#[allow(clippy::unnecessary_def_path)]
|
|
||||||
fn check_fn(
|
fn check_fn(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
kind: FnKind<'tcx>,
|
kind: FnKind<'tcx>,
|
||||||
_decl: &'tcx FnDecl<'tcx>,
|
decl: &'tcx FnDecl<'tcx>,
|
||||||
body: &'tcx Body<'tcx>,
|
body: &'tcx Body<'tcx>,
|
||||||
method_span: Span,
|
method_span: Span,
|
||||||
def_id: LocalDefId,
|
method_def_id: LocalDefId,
|
||||||
) {
|
) {
|
||||||
// If the function is a method...
|
// If the function is a method...
|
||||||
if let FnKind::Method(name, _) = kind
|
if let FnKind::Method(name, _) = kind
|
||||||
// That has two arguments.
|
&& let expr = expr_or_init(cx, body.value).peel_blocks()
|
||||||
&& let [self_arg, other_arg] = cx
|
// Doesn't have a conditional return.
|
||||||
.tcx
|
&& !has_conditional_return(body, expr)
|
||||||
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder())
|
|
||||||
.inputs()
|
|
||||||
&& let Some(self_arg) = get_ty_def_id(*self_arg)
|
|
||||||
&& let Some(other_arg) = get_ty_def_id(*other_arg)
|
|
||||||
// The two arguments are of the same type.
|
|
||||||
&& self_arg == other_arg
|
|
||||||
&& let hir_id = cx.tcx.local_def_id_to_hir_id(def_id)
|
|
||||||
&& let Some((
|
|
||||||
_,
|
|
||||||
Node::Item(Item {
|
|
||||||
kind: ItemKind::Impl(impl_),
|
|
||||||
owner_id,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
)) = cx.tcx.hir().parent_iter(hir_id).next()
|
|
||||||
// We exclude `impl` blocks generated from rustc's proc macros.
|
|
||||||
&& !cx.tcx.has_attr(*owner_id, sym::automatically_derived)
|
|
||||||
// It is a implementation of a trait.
|
|
||||||
&& let Some(trait_) = impl_.of_trait
|
|
||||||
&& let Some(trait_def_id) = trait_.trait_def_id()
|
|
||||||
// The trait is `PartialEq`.
|
|
||||||
&& Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"])
|
|
||||||
{
|
{
|
||||||
let to_check_op = if name.name == sym::eq {
|
if name.name == sym::eq || name.name == sym::ne {
|
||||||
BinOpKind::Eq
|
check_partial_eq(cx, method_span, method_def_id, name, expr);
|
||||||
} else {
|
} else if name.name == sym::to_string {
|
||||||
BinOpKind::Ne
|
check_to_string(cx, method_span, method_def_id, name, expr);
|
||||||
};
|
|
||||||
let expr = body.value.peel_blocks();
|
|
||||||
let is_bad = match expr.kind {
|
|
||||||
ExprKind::Binary(op, left, right) if op.node == to_check_op => {
|
|
||||||
is_local(cx, left) && is_local(cx, right)
|
|
||||||
},
|
|
||||||
ExprKind::MethodCall(segment, receiver, &[arg], _) if segment.ident.name == name.name => {
|
|
||||||
if is_local(cx, receiver)
|
|
||||||
&& is_local(cx, &arg)
|
|
||||||
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
|
||||||
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
|
||||||
&& trait_id == trait_def_id
|
|
||||||
{
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
if is_bad {
|
|
||||||
span_lint_and_then(
|
|
||||||
cx,
|
|
||||||
UNCONDITIONAL_RECURSION,
|
|
||||||
method_span,
|
|
||||||
"function cannot return without recursing",
|
|
||||||
|diag| {
|
|
||||||
diag.span_note(expr.span, "recursive call site");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
self.check_default_new(cx, decl, body, method_span, method_def_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,11 +681,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos
|
||||||
.filter(|(_, text)| !text.is_empty());
|
.filter(|(_, text)| !text.is_empty());
|
||||||
|
|
||||||
let (line_start, line) = lines.next()?;
|
let (line_start, line) = lines.next()?;
|
||||||
|
let mut in_codeblock = false;
|
||||||
// Check for a sequence of line comments.
|
// Check for a sequence of line comments.
|
||||||
if line.starts_with("//") {
|
if line.starts_with("//") {
|
||||||
let (mut line, mut line_start) = (line, line_start);
|
let (mut line, mut line_start) = (line, line_start);
|
||||||
loop {
|
loop {
|
||||||
if line.to_ascii_uppercase().contains("SAFETY:") {
|
// Don't lint if the safety comment is part of a codeblock in a doc comment.
|
||||||
|
// It may or may not be required, and we can't very easily check it (and we shouldn't, since
|
||||||
|
// the safety comment isn't referring to the node we're currently checking)
|
||||||
|
if line.trim_start_matches("///").trim_start().starts_with("```") {
|
||||||
|
in_codeblock = !in_codeblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.to_ascii_uppercase().contains("SAFETY:") && !in_codeblock {
|
||||||
return Some(start_pos + BytePos(u32::try_from(line_start).unwrap()));
|
return Some(start_pos + BytePos(u32::try_from(line_start).unwrap()));
|
||||||
}
|
}
|
||||||
match lines.next() {
|
match lines.next() {
|
||||||
|
|
|
@ -13,12 +13,31 @@ use rustc_middle::ty;
|
||||||
use super::LET_UNIT_VALUE;
|
use super::LET_UNIT_VALUE;
|
||||||
|
|
||||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
||||||
|
// skip `let () = { ... }`
|
||||||
|
if let PatKind::Tuple(fields, ..) = local.pat.kind
|
||||||
|
&& fields.is_empty()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(init) = local.init
|
if let Some(init) = local.init
|
||||||
&& !local.pat.span.from_expansion()
|
&& !local.pat.span.from_expansion()
|
||||||
&& !in_external_macro(cx.sess(), local.span)
|
&& !in_external_macro(cx.sess(), local.span)
|
||||||
&& !is_from_async_await(local.span)
|
&& !is_from_async_await(local.span)
|
||||||
&& cx.typeck_results().pat_ty(local.pat).is_unit()
|
&& cx.typeck_results().pat_ty(local.pat).is_unit()
|
||||||
{
|
{
|
||||||
|
// skip `let awa = ()`
|
||||||
|
if let ExprKind::Tup([]) = init.kind {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip `let _: () = { ... }`
|
||||||
|
if let Some(ty) = local.ty
|
||||||
|
&& let TyKind::Tup([]) = ty.kind
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
|
if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
|
||||||
|| matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()))
|
|| matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()))
|
||||||
&& expr_needs_inferred_result(cx, init)
|
&& expr_needs_inferred_result(cx, init)
|
||||||
|
@ -34,7 +53,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
local.pat.span,
|
local.pat.span,
|
||||||
"use a wild (`_`) binding",
|
"use a wildcard binding",
|
||||||
"_",
|
"_",
|
||||||
Applicability::MaybeIncorrect, // snippet
|
Applicability::MaybeIncorrect, // snippet
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,8 +11,8 @@ use clippy_utils::{get_parent_expr, higher, is_trait_method};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Node, PatKind};
|
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Node, PatKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::ty;
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
use rustc_middle::ty::{self, Ty};
|
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::{sym, DesugaringKind, Span};
|
use rustc_span::{sym, DesugaringKind, Span};
|
||||||
|
|
||||||
|
@ -79,7 +79,6 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
|
||||||
// this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
|
// this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
|
||||||
&& local.ty.is_none()
|
&& local.ty.is_none()
|
||||||
&& let PatKind::Binding(_, id, ..) = local.pat.kind
|
&& let PatKind::Binding(_, id, ..) = local.pat.kind
|
||||||
&& is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr.peel_borrows())))
|
|
||||||
{
|
{
|
||||||
let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
|
let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
|
||||||
// allow indexing into a vec and some set of allowed method calls that exist on slices, too
|
// allow indexing into a vec and some set of allowed method calls that exist on slices, too
|
||||||
|
@ -185,6 +184,11 @@ impl UselessVec {
|
||||||
let snippet = match *vec_args {
|
let snippet = match *vec_args {
|
||||||
higher::VecArgs::Repeat(elem, len) => {
|
higher::VecArgs::Repeat(elem, len) => {
|
||||||
if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
|
if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
|
||||||
|
// vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also
|
||||||
|
if !is_copy(cx, cx.typeck_results().expr_ty(elem)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#[expect(clippy::cast_possible_truncation)]
|
#[expect(clippy::cast_possible_truncation)]
|
||||||
if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
|
if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
|
||||||
return;
|
return;
|
||||||
|
@ -241,12 +245,3 @@ fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
|
||||||
let ty = cx.typeck_results().expr_ty_adjusted(expr);
|
let ty = cx.typeck_results().expr_ty_adjusted(expr);
|
||||||
cx.layout_of(ty).map_or(0, |l| l.size.bytes())
|
cx.layout_of(ty).map_or(0, |l| l.size.bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).
|
|
||||||
fn vec_type(ty: Ty<'_>) -> Ty<'_> {
|
|
||||||
if let ty::Adt(_, args) = ty.kind() {
|
|
||||||
args.type_at(0)
|
|
||||||
} else {
|
|
||||||
panic!("The type of `vec!` is a not a struct?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -142,7 +142,7 @@ impl LateLintPass<'_> for WildcardImports {
|
||||||
} else {
|
} else {
|
||||||
// In this case, the `use_path.span` ends right before the `::*`, so we need to
|
// In this case, the `use_path.span` ends right before the `::*`, so we need to
|
||||||
// extend it up to the `*`. Since it is hard to find the `*` in weird
|
// extend it up to the `*`. Since it is hard to find the `*` in weird
|
||||||
// formattings like `use _ :: *;`, we extend it up to, but not including the
|
// formatting like `use _ :: *;`, we extend it up to, but not including the
|
||||||
// `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
|
// `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we
|
||||||
// can just use the end of the item span
|
// can just use the end of the item span
|
||||||
let mut span = use_path.span.with_hi(item.span.hi());
|
let mut span = use_path.span.with_hi(item.span.hi());
|
||||||
|
|
|
@ -99,7 +99,10 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
|
||||||
}
|
}
|
||||||
|
|
||||||
fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||||
if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
|
if let Res::Def(DefKind::Ctor(..) | DefKind::Variant | DefKind::Enum | DefKind::Struct, _)
|
||||||
|
| Res::SelfCtor(_)
|
||||||
|
| Res::SelfTyAlias { .. } = res
|
||||||
|
{
|
||||||
cx.typeck_results()
|
cx.typeck_results()
|
||||||
.expr_ty(e)
|
.expr_ty(e)
|
||||||
.has_significant_drop(cx.tcx, cx.param_env)
|
.has_significant_drop(cx.tcx, cx.param_env)
|
||||||
|
@ -173,6 +176,13 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||||
self.eagerness |= NoChange;
|
self.eagerness |= NoChange;
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
#[expect(clippy::match_same_arms)] // arm pattern can't be merged due to `ref`, see rust#105778
|
||||||
|
ExprKind::Struct(path, ..) => {
|
||||||
|
if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
|
||||||
|
self.eagerness = ForceNoChange;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
ExprKind::Path(ref path) => {
|
ExprKind::Path(ref path) => {
|
||||||
if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
|
if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
|
||||||
self.eagerness = ForceNoChange;
|
self.eagerness = ForceNoChange;
|
||||||
|
@ -291,7 +301,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
|
||||||
| ExprKind::Closure { .. }
|
| ExprKind::Closure { .. }
|
||||||
| ExprKind::Field(..)
|
| ExprKind::Field(..)
|
||||||
| ExprKind::AddrOf(..)
|
| ExprKind::AddrOf(..)
|
||||||
| ExprKind::Struct(..)
|
|
||||||
| ExprKind::Repeat(..)
|
| ExprKind::Repeat(..)
|
||||||
| ExprKind::Block(Block { stmts: [], .. }, _)
|
| ExprKind::Block(Block { stmts: [], .. }, _)
|
||||||
| ExprKind::OffsetOf(..) => (),
|
| ExprKind::OffsetOf(..) => (),
|
||||||
|
|
|
@ -865,7 +865,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
||||||
|
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
self.hash_pat(arm.pat);
|
self.hash_pat(arm.pat);
|
||||||
if let Some(ref e) = arm.guard {
|
if let Some(e) = arm.guard {
|
||||||
self.hash_expr(e);
|
self.hash_expr(e);
|
||||||
}
|
}
|
||||||
self.hash_expr(arm.body);
|
self.hash_expr(arm.body);
|
||||||
|
|
|
@ -420,7 +420,7 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId)
|
||||||
ast_format_args
|
ast_format_args
|
||||||
.get()?
|
.get()?
|
||||||
.get(&format_args_expr.span.with_parent(None))
|
.get(&format_args_expr.span.with_parent(None))
|
||||||
.map(Rc::clone)
|
.cloned()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
|
||||||
|
|
||||||
ExprKind::Call(callee, args) => {
|
ExprKind::Call(callee, args) => {
|
||||||
let lhs = expr_type_certainty(cx, callee);
|
let lhs = expr_type_certainty(cx, callee);
|
||||||
let rhs = if type_is_inferrable_from_arguments(cx, expr) {
|
let rhs = if type_is_inferable_from_arguments(cx, expr) {
|
||||||
meet(args.iter().map(|arg| expr_type_certainty(cx, arg)))
|
meet(args.iter().map(|arg| expr_type_certainty(cx, arg)))
|
||||||
} else {
|
} else {
|
||||||
Certainty::Uncertain
|
Certainty::Uncertain
|
||||||
|
@ -57,7 +57,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
|
||||||
receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id);
|
receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id);
|
||||||
};
|
};
|
||||||
let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false);
|
let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false);
|
||||||
let rhs = if type_is_inferrable_from_arguments(cx, expr) {
|
let rhs = if type_is_inferable_from_arguments(cx, expr) {
|
||||||
meet(
|
meet(
|
||||||
std::iter::once(receiver_type_certainty).chain(args.iter().map(|arg| expr_type_certainty(cx, arg))),
|
std::iter::once(receiver_type_certainty).chain(args.iter().map(|arg| expr_type_certainty(cx, arg))),
|
||||||
)
|
)
|
||||||
|
@ -279,7 +279,7 @@ fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
let Some(callee_def_id) = (match expr.kind {
|
let Some(callee_def_id) = (match expr.kind {
|
||||||
ExprKind::Call(callee, _) => {
|
ExprKind::Call(callee, _) => {
|
||||||
let callee_ty = cx.typeck_results().expr_ty(callee);
|
let callee_ty = cx.typeck_results().expr_ty(callee);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2023-12-28"
|
channel = "nightly-2024-01-11"
|
||||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||||
|
|
|
@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under:
|
||||||
|
|
||||||
<!-- REUSE-IgnoreStart -->
|
<!-- REUSE-IgnoreStart -->
|
||||||
|
|
||||||
Copyright 2014-2022 The Rust Project Developers
|
Copyright 2014-2024 The Rust Project Developers
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pub-underscore-fields-behavior = "AllPubFields"
|
1
tests/ui-toml/pub_underscore_fields/exported/clippy.toml
Normal file
1
tests/ui-toml/pub_underscore_fields/exported/clippy.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub-underscore-fields-behavior = "PublicallyExported"
|
|
@ -0,0 +1,60 @@
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:15:9
|
||||||
|
|
|
||||||
|
LL | pub _b: u8,
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
= note: `-D clippy::pub-underscore-fields` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]`
|
||||||
|
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:23:13
|
||||||
|
|
|
||||||
|
LL | pub(in crate::inner) _f: Option<()>,
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:27:13
|
||||||
|
|
|
||||||
|
LL | pub _g: String,
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:34:9
|
||||||
|
|
|
||||||
|
LL | pub _a: usize,
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:41:9
|
||||||
|
|
|
||||||
|
LL | pub _c: i64,
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:44:9
|
||||||
|
|
|
||||||
|
LL | pub _e: Option<u8>,
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:57:9
|
||||||
|
|
|
||||||
|
LL | pub(crate) _b: Option<String>,
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
error: field marked as public but also inferred as unused because it's prefixed with `_`
|
||||||
|
--> $DIR/pub_underscore_fields.rs:15:9
|
||||||
|
|
|
||||||
|
LL | pub _b: u8,
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: consider removing the underscore, or making the field private
|
||||||
|
= note: `-D clippy::pub-underscore-fields` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
66
tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs
Normal file
66
tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
//@revisions: exported all_pub_fields
|
||||||
|
//@[all_pub_fields] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/pub_underscore_fields/all_pub_fields
|
||||||
|
//@[exported] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/pub_underscore_fields/exported
|
||||||
|
|
||||||
|
#![allow(unused)]
|
||||||
|
#![warn(clippy::pub_underscore_fields)]
|
||||||
|
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub mod inner {
|
||||||
|
use std::marker;
|
||||||
|
|
||||||
|
pub struct PubSuper {
|
||||||
|
pub(super) a: usize,
|
||||||
|
pub _b: u8,
|
||||||
|
_c: i32,
|
||||||
|
pub _mark: marker::PhantomData<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod inner2 {
|
||||||
|
pub struct PubModVisibility {
|
||||||
|
pub(in crate::inner) e: bool,
|
||||||
|
pub(in crate::inner) _f: Option<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrivateStructPubField {
|
||||||
|
pub _g: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
pub struct StructWithOneViolation {
|
||||||
|
pub _a: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// should handle structs with multiple violations
|
||||||
|
pub struct StructWithMultipleViolations {
|
||||||
|
a: u8,
|
||||||
|
_b: usize,
|
||||||
|
pub _c: i64,
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub d: String,
|
||||||
|
pub _e: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't warn on anonymous fields
|
||||||
|
pub struct AnonymousFields(pub usize, i32);
|
||||||
|
|
||||||
|
// don't warn on empty structs
|
||||||
|
pub struct Empty1;
|
||||||
|
pub struct Empty2();
|
||||||
|
pub struct Empty3 {};
|
||||||
|
|
||||||
|
pub struct PubCrate {
|
||||||
|
pub(crate) a: String,
|
||||||
|
pub(crate) _b: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't warn on fields named pub
|
||||||
|
pub struct NamedPub {
|
||||||
|
r#pub: bool,
|
||||||
|
_pub: String,
|
||||||
|
pub(crate) _mark: PhantomData<u8>,
|
||||||
|
}
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
||||||
missing-docs-in-crate-items
|
missing-docs-in-crate-items
|
||||||
msrv
|
msrv
|
||||||
pass-by-value-size-limit
|
pass-by-value-size-limit
|
||||||
|
pub-underscore-fields-behavior
|
||||||
semicolon-inside-block-ignore-singleline
|
semicolon-inside-block-ignore-singleline
|
||||||
semicolon-outside-block-ignore-multiline
|
semicolon-outside-block-ignore-multiline
|
||||||
single-char-binding-names-threshold
|
single-char-binding-names-threshold
|
||||||
|
@ -124,6 +125,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
||||||
missing-docs-in-crate-items
|
missing-docs-in-crate-items
|
||||||
msrv
|
msrv
|
||||||
pass-by-value-size-limit
|
pass-by-value-size-limit
|
||||||
|
pub-underscore-fields-behavior
|
||||||
semicolon-inside-block-ignore-singleline
|
semicolon-inside-block-ignore-singleline
|
||||||
semicolon-outside-block-ignore-multiline
|
semicolon-outside-block-ignore-multiline
|
||||||
single-char-binding-names-threshold
|
single-char-binding-names-threshold
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn main() {
|
||||||
with_span!(
|
with_span!(
|
||||||
span
|
span
|
||||||
|
|
||||||
fn coverting() {
|
fn converting() {
|
||||||
let x = 0u32 as u64;
|
let x = 0u32 as u64;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -365,3 +365,52 @@ fn avoid_subtract_overflow(q: u32) {
|
||||||
fn issue11426() {
|
fn issue11426() {
|
||||||
(&42u8 >> 0xa9008fb6c9d81e42_0e25730562a601c8_u128) as usize;
|
(&42u8 >> 0xa9008fb6c9d81e42_0e25730562a601c8_u128) as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue11642() {
|
||||||
|
fn square(x: i16) -> u32 {
|
||||||
|
let x = x as i32;
|
||||||
|
(x * x) as u32;
|
||||||
|
x.pow(2) as u32;
|
||||||
|
(-2_i32).pow(2) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
let _a = |x: i32| -> u32 { (x * x * x * x) as u32 };
|
||||||
|
|
||||||
|
(-2_i32).pow(3) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
|
||||||
|
let x: i32 = 10;
|
||||||
|
(x * x) as u32;
|
||||||
|
(x * x * x) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
|
||||||
|
let y: i16 = -2;
|
||||||
|
(y * y * y * y * -2) as u16;
|
||||||
|
//~^ ERROR: casting `i16` to `u16` may lose the sign of the value
|
||||||
|
(y * y * y * y * 2) as u16;
|
||||||
|
(y * y * y * 2) as u16;
|
||||||
|
//~^ ERROR: casting `i16` to `u16` may lose the sign of the value
|
||||||
|
(y * y * y * -2) as u16;
|
||||||
|
//~^ ERROR: casting `i16` to `u16` may lose the sign of the value
|
||||||
|
|
||||||
|
fn foo(a: i32, b: i32, c: i32) -> u32 {
|
||||||
|
(a * a * b * b * c * c) as u32;
|
||||||
|
(a * b * c) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
(a * -b * c) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
(a * b * c * c) as u32;
|
||||||
|
(a * -2) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
(a * b * c * -2) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
(a / b) as u32;
|
||||||
|
(a / b * c) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
(a / b + b * c) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
a.pow(3) as u32;
|
||||||
|
//~^ ERROR: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
(a.abs() * b.pow(2) / c.abs()) as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -444,5 +444,77 @@ help: ... or use `try_from` and handle the error accordingly
|
||||||
LL | let c = u8::try_from(q / 1000);
|
LL | let c = u8::try_from(q / 1000);
|
||||||
| ~~~~~~~~~~~~~~~~~~~~~~
|
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
error: aborting due to 51 previous errors
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:379:5
|
||||||
|
|
|
||||||
|
LL | (-2_i32).pow(3) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:384:5
|
||||||
|
|
|
||||||
|
LL | (x * x * x) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i16` to `u16` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:388:5
|
||||||
|
|
|
||||||
|
LL | (y * y * y * y * -2) as u16;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i16` to `u16` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:391:5
|
||||||
|
|
|
||||||
|
LL | (y * y * y * 2) as u16;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i16` to `u16` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:393:5
|
||||||
|
|
|
||||||
|
LL | (y * y * y * -2) as u16;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:398:9
|
||||||
|
|
|
||||||
|
LL | (a * b * c) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:400:9
|
||||||
|
|
|
||||||
|
LL | (a * -b * c) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:403:9
|
||||||
|
|
|
||||||
|
LL | (a * -2) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:405:9
|
||||||
|
|
|
||||||
|
LL | (a * b * c * -2) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:408:9
|
||||||
|
|
|
||||||
|
LL | (a / b * c) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:410:9
|
||||||
|
|
|
||||||
|
LL | (a / b + b * c) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: casting `i32` to `u32` may lose the sign of the value
|
||||||
|
--> $DIR/cast.rs:412:9
|
||||||
|
|
|
||||||
|
LL | a.pow(3) as u32;
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 63 previous errors
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
#![warn(clippy::let_unit_value)]
|
|
||||||
|
|
||||||
fn f() {}
|
|
||||||
static FN: fn() = f;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
FN();
|
|
||||||
//~^ ERROR: this let-binding has unit value
|
|
||||||
//~| NOTE: `-D clippy::let-unit-value` implied by `-D warnings`
|
|
||||||
}
|
|
|
@ -5,6 +5,4 @@ static FN: fn() = f;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _: () = FN();
|
let _: () = FN();
|
||||||
//~^ ERROR: this let-binding has unit value
|
|
||||||
//~| NOTE: `-D clippy::let-unit-value` implied by `-D warnings`
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
error: this let-binding has unit value
|
|
||||||
--> $DIR/ice-8821.rs:7:5
|
|
||||||
|
|
|
||||||
LL | let _: () = FN();
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();`
|
|
||||||
|
|
|
||||||
= note: `-D clippy::let-unit-value` implied by `-D warnings`
|
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]`
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
|
||||||
|
|
|
@ -73,9 +73,7 @@ mod nested_local {
|
||||||
|
|
||||||
mod function_def {
|
mod function_def {
|
||||||
fn ret_f64() -> f64 {
|
fn ret_f64() -> f64 {
|
||||||
// Even though the output type is specified,
|
1.
|
||||||
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
|
|
||||||
1.0_f64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
|
|
@ -73,8 +73,6 @@ mod nested_local {
|
||||||
|
|
||||||
mod function_def {
|
mod function_def {
|
||||||
fn ret_f64() -> f64 {
|
fn ret_f64() -> f64 {
|
||||||
// Even though the output type is specified,
|
|
||||||
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
|
|
||||||
1.
|
1.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,66 +86,60 @@ LL | let y = 1.;
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:78:9
|
--> $DIR/default_numeric_fallback_f64.rs:82:27
|
||||||
|
|
|
||||||
LL | 1.
|
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:84:27
|
|
||||||
|
|
|
|
||||||
LL | let f = || -> _ { 1. };
|
LL | let f = || -> _ { 1. };
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:88:29
|
--> $DIR/default_numeric_fallback_f64.rs:86:29
|
||||||
|
|
|
|
||||||
LL | let f = || -> f64 { 1. };
|
LL | let f = || -> f64 { 1. };
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:102:21
|
--> $DIR/default_numeric_fallback_f64.rs:100:21
|
||||||
|
|
|
|
||||||
LL | generic_arg(1.);
|
LL | generic_arg(1.);
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:105:32
|
--> $DIR/default_numeric_fallback_f64.rs:103:32
|
||||||
|
|
|
|
||||||
LL | let x: _ = generic_arg(1.);
|
LL | let x: _ = generic_arg(1.);
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:123:28
|
--> $DIR/default_numeric_fallback_f64.rs:121:28
|
||||||
|
|
|
|
||||||
LL | GenericStruct { x: 1. };
|
LL | GenericStruct { x: 1. };
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:126:36
|
--> $DIR/default_numeric_fallback_f64.rs:124:36
|
||||||
|
|
|
|
||||||
LL | let _ = GenericStruct { x: 1. };
|
LL | let _ = GenericStruct { x: 1. };
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:144:24
|
--> $DIR/default_numeric_fallback_f64.rs:142:24
|
||||||
|
|
|
|
||||||
LL | GenericEnum::X(1.);
|
LL | GenericEnum::X(1.);
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:164:23
|
--> $DIR/default_numeric_fallback_f64.rs:162:23
|
||||||
|
|
|
|
||||||
LL | s.generic_arg(1.);
|
LL | s.generic_arg(1.);
|
||||||
| ^^ help: consider adding suffix: `1.0_f64`
|
| ^^ help: consider adding suffix: `1.0_f64`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_f64.rs:174:25
|
--> $DIR/default_numeric_fallback_f64.rs:172:25
|
||||||
|
|
|
|
||||||
LL | inline!(let x = 22.;);
|
LL | inline!(let x = 22.;);
|
||||||
| ^^^ help: consider adding suffix: `22.0_f64`
|
| ^^^ help: consider adding suffix: `22.0_f64`
|
||||||
|
|
|
|
||||||
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to 24 previous errors
|
error: aborting due to 23 previous errors
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,7 @@ mod nested_local {
|
||||||
|
|
||||||
mod function_def {
|
mod function_def {
|
||||||
fn ret_i32() -> i32 {
|
fn ret_i32() -> i32 {
|
||||||
// Even though the output type is specified,
|
1
|
||||||
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
|
|
||||||
1_i32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test() {
|
fn test() {
|
||||||
|
@ -186,4 +184,36 @@ fn check_expect_suppression() {
|
||||||
let x = 21;
|
let x = 21;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod type_already_inferred {
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_i32() -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_if_i32(b: bool) -> i32 {
|
||||||
|
if b { 100 } else { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_i32_tuple() -> (i32, i32) {
|
||||||
|
(0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_stmt(b: bool) -> (i32, i32) {
|
||||||
|
if b {
|
||||||
|
return (0, 1);
|
||||||
|
}
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::useless_vec)]
|
||||||
|
fn vec_macro() {
|
||||||
|
// Should NOT lint in `vec!` call if the type was already stated
|
||||||
|
let data_i32: Vec<i32> = vec![1, 2, 3];
|
||||||
|
let data_i32 = vec![1_i32, 2_i32, 3_i32];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -74,8 +74,6 @@ mod nested_local {
|
||||||
|
|
||||||
mod function_def {
|
mod function_def {
|
||||||
fn ret_i32() -> i32 {
|
fn ret_i32() -> i32 {
|
||||||
// Even though the output type is specified,
|
|
||||||
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
|
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,4 +184,36 @@ fn check_expect_suppression() {
|
||||||
let x = 21;
|
let x = 21;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod type_already_inferred {
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_i32() -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_if_i32(b: bool) -> i32 {
|
||||||
|
if b { 100 } else { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_i32_tuple() -> (i32, i32) {
|
||||||
|
(0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT lint if bound to return type
|
||||||
|
fn ret_stmt(b: bool) -> (i32, i32) {
|
||||||
|
if b {
|
||||||
|
return (0, 1);
|
||||||
|
}
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::useless_vec)]
|
||||||
|
fn vec_macro() {
|
||||||
|
// Should NOT lint in `vec!` call if the type was already stated
|
||||||
|
let data_i32: Vec<i32> = vec![1, 2, 3];
|
||||||
|
let data_i32 = vec![1, 2, 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -98,66 +98,78 @@ LL | let y = 1;
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:79:9
|
--> $DIR/default_numeric_fallback_i32.rs:83:27
|
||||||
|
|
|
||||||
LL | 1
|
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:85:27
|
|
||||||
|
|
|
|
||||||
LL | let f = || -> _ { 1 };
|
LL | let f = || -> _ { 1 };
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:89:29
|
--> $DIR/default_numeric_fallback_i32.rs:87:29
|
||||||
|
|
|
|
||||||
LL | let f = || -> i32 { 1 };
|
LL | let f = || -> i32 { 1 };
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:103:21
|
--> $DIR/default_numeric_fallback_i32.rs:101:21
|
||||||
|
|
|
|
||||||
LL | generic_arg(1);
|
LL | generic_arg(1);
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:106:32
|
--> $DIR/default_numeric_fallback_i32.rs:104:32
|
||||||
|
|
|
|
||||||
LL | let x: _ = generic_arg(1);
|
LL | let x: _ = generic_arg(1);
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:124:28
|
--> $DIR/default_numeric_fallback_i32.rs:122:28
|
||||||
|
|
|
|
||||||
LL | GenericStruct { x: 1 };
|
LL | GenericStruct { x: 1 };
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:127:36
|
--> $DIR/default_numeric_fallback_i32.rs:125:36
|
||||||
|
|
|
|
||||||
LL | let _ = GenericStruct { x: 1 };
|
LL | let _ = GenericStruct { x: 1 };
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:145:24
|
--> $DIR/default_numeric_fallback_i32.rs:143:24
|
||||||
|
|
|
|
||||||
LL | GenericEnum::X(1);
|
LL | GenericEnum::X(1);
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:165:23
|
--> $DIR/default_numeric_fallback_i32.rs:163:23
|
||||||
|
|
|
|
||||||
LL | s.generic_arg(1);
|
LL | s.generic_arg(1);
|
||||||
| ^ help: consider adding suffix: `1_i32`
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
error: default numeric fallback might occur
|
error: default numeric fallback might occur
|
||||||
--> $DIR/default_numeric_fallback_i32.rs:175:25
|
--> $DIR/default_numeric_fallback_i32.rs:173:25
|
||||||
|
|
|
|
||||||
LL | inline!(let x = 22;);
|
LL | inline!(let x = 22;);
|
||||||
| ^^ help: consider adding suffix: `22_i32`
|
| ^^ help: consider adding suffix: `22_i32`
|
||||||
|
|
|
|
||||||
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to 26 previous errors
|
error: default numeric fallback might occur
|
||||||
|
--> $DIR/default_numeric_fallback_i32.rs:215:29
|
||||||
|
|
|
||||||
|
LL | let data_i32 = vec![1, 2, 3];
|
||||||
|
| ^ help: consider adding suffix: `1_i32`
|
||||||
|
|
||||||
|
error: default numeric fallback might occur
|
||||||
|
--> $DIR/default_numeric_fallback_i32.rs:215:32
|
||||||
|
|
|
||||||
|
LL | let data_i32 = vec![1, 2, 3];
|
||||||
|
| ^ help: consider adding suffix: `2_i32`
|
||||||
|
|
||||||
|
error: default numeric fallback might occur
|
||||||
|
--> $DIR/default_numeric_fallback_i32.rs:215:35
|
||||||
|
|
|
||||||
|
LL | let data_i32 = vec![1, 2, 3];
|
||||||
|
| ^ help: consider adding suffix: `3_i32`
|
||||||
|
|
||||||
|
error: aborting due to 28 previous errors
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,49 @@ enum Opcode {
|
||||||
Div = 3,
|
Div = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
foo: &'static [u8],
|
||||||
|
bar: &'static [u8],
|
||||||
|
}
|
||||||
|
|
||||||
fn int_to_opcode(op: u8) -> Option<Opcode> {
|
fn int_to_opcode(op: u8) -> Option<Opcode> {
|
||||||
(op < 4).then(|| unsafe { std::mem::transmute(op) })
|
(op < 4).then(|| unsafe { std::mem::transmute(op) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn f(op: u8, unrelated: u8) {
|
fn f(op: u8, op2: Data, unrelated: u8) {
|
||||||
true.then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
true.then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(unrelated < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(unrelated < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
|
|
||||||
|
let _: Option<Opcode> = (op > 0 && op < 10).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
|
||||||
|
// lint even when the transmutable goes through field/array accesses
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| unsafe { std::mem::transmute(op2.foo[0]) });
|
||||||
|
|
||||||
|
// don't lint: wrong index used in the transmute
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[1]) });
|
||||||
|
|
||||||
|
// don't lint: no check for the transmutable in the condition
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op2.bar[0]) });
|
||||||
|
|
||||||
|
// don't lint: wrong variable
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
|
||||||
|
// range contains checks
|
||||||
|
let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
|
||||||
|
// unrelated binding in contains
|
||||||
|
let _: Option<Opcode> = (1..=3)
|
||||||
|
.contains(&unrelated)
|
||||||
|
.then_some(unsafe { std::mem::transmute(op) });
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn f2(op: u8) {
|
unsafe fn f2(op: u8) {
|
||||||
|
|
|
@ -12,16 +12,49 @@ enum Opcode {
|
||||||
Div = 3,
|
Div = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
foo: &'static [u8],
|
||||||
|
bar: &'static [u8],
|
||||||
|
}
|
||||||
|
|
||||||
fn int_to_opcode(op: u8) -> Option<Opcode> {
|
fn int_to_opcode(op: u8) -> Option<Opcode> {
|
||||||
(op < 4).then_some(unsafe { std::mem::transmute(op) })
|
(op < 4).then_some(unsafe { std::mem::transmute(op) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn f(op: u8, unrelated: u8) {
|
fn f(op: u8, op2: Data, unrelated: u8) {
|
||||||
true.then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
true.then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(unrelated < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(unrelated < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
(op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
(op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
|
|
||||||
|
let _: Option<Opcode> = (op > 0 && op < 10).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
|
||||||
|
// lint even when the transmutable goes through field/array accesses
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[0]) });
|
||||||
|
|
||||||
|
// don't lint: wrong index used in the transmute
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[1]) });
|
||||||
|
|
||||||
|
// don't lint: no check for the transmutable in the condition
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op2.bar[0]) });
|
||||||
|
|
||||||
|
// don't lint: wrong variable
|
||||||
|
let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
|
||||||
|
// range contains checks
|
||||||
|
let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
|
||||||
|
// unrelated binding in contains
|
||||||
|
let _: Option<Opcode> = (1..=3)
|
||||||
|
.contains(&unrelated)
|
||||||
|
.then_some(unsafe { std::mem::transmute(op) });
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn f2(op: u8) {
|
unsafe fn f2(op: u8) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:16:33
|
--> $DIR/eager_transmute.rs:21:33
|
||||||
|
|
|
|
||||||
LL | (op < 4).then_some(unsafe { std::mem::transmute(op) })
|
LL | (op < 4).then_some(unsafe { std::mem::transmute(op) })
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -12,7 +12,7 @@ LL | (op < 4).then(|| unsafe { std::mem::transmute(op) })
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:22:33
|
--> $DIR/eager_transmute.rs:27:33
|
||||||
|
|
|
|
||||||
LL | (op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
LL | (op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -23,7 +23,7 @@ LL | (op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:23:33
|
--> $DIR/eager_transmute.rs:28:33
|
||||||
|
|
|
|
||||||
LL | (op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
LL | (op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -34,7 +34,7 @@ LL | (op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:24:34
|
--> $DIR/eager_transmute.rs:29:34
|
||||||
|
|
|
|
||||||
LL | (op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
LL | (op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -45,7 +45,106 @@ LL | (op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) });
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:28:24
|
--> $DIR/eager_transmute.rs:31:68
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (op > 0 && op < 10).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (op > 0 && op < 10).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:32:86
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:35:84
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[0]) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| unsafe { std::mem::transmute(op2.foo[0]) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:47:70
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:48:83
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:49:69
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:50:68
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:51:68
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:52:69
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider using `bool::then` to only transmute if the condition holds
|
||||||
|
|
|
||||||
|
LL | let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) });
|
||||||
|
| ~~~~ ++
|
||||||
|
|
||||||
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
|
--> $DIR/eager_transmute.rs:61:24
|
||||||
|
|
|
|
||||||
LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op));
|
LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op));
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -56,7 +155,7 @@ LL | (op < 4).then(|| std::mem::transmute::<_, Opcode>(op));
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:57:60
|
--> $DIR/eager_transmute.rs:90:60
|
||||||
|
|
|
|
||||||
LL | let _: Option<NonZeroU8> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) });
|
LL | let _: Option<NonZeroU8> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) });
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -67,7 +166,7 @@ LL | let _: Option<NonZeroU8> = (v1 > 0).then(|| unsafe { std::mem::transmut
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:63:86
|
--> $DIR/eager_transmute.rs:96:86
|
||||||
|
|
|
|
||||||
LL | let _: Option<NonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
|
LL | let _: Option<NonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -78,7 +177,7 @@ LL | let _: Option<NonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then(|| u
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: this transmute is always evaluated eagerly, even if the condition is false
|
error: this transmute is always evaluated eagerly, even if the condition is false
|
||||||
--> $DIR/eager_transmute.rs:69:93
|
--> $DIR/eager_transmute.rs:102:93
|
||||||
|
|
|
|
||||||
LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
|
LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) });
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -88,5 +187,5 @@ help: consider using `bool::then` to only transmute if the condition holds
|
||||||
LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) });
|
LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) });
|
||||||
| ~~~~ ++
|
| ~~~~ ++
|
||||||
|
|
||||||
error: aborting due to 8 previous errors
|
error: aborting due to 17 previous errors
|
||||||
|
|
||||||
|
|
27
tests/ui/empty_enum_variants_with_brackets.fixed
Normal file
27
tests/ui/empty_enum_variants_with_brackets.fixed
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#![warn(clippy::empty_enum_variants_with_brackets)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
pub enum PublicTestEnum {
|
||||||
|
NonEmptyBraces { x: i32, y: i32 }, // No error
|
||||||
|
NonEmptyParentheses(i32, i32), // No error
|
||||||
|
EmptyBraces, //~ ERROR: enum variant has empty brackets
|
||||||
|
EmptyParentheses, //~ ERROR: enum variant has empty brackets
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestEnum {
|
||||||
|
NonEmptyBraces { x: i32, y: i32 }, // No error
|
||||||
|
NonEmptyParentheses(i32, i32), // No error
|
||||||
|
EmptyBraces, //~ ERROR: enum variant has empty brackets
|
||||||
|
EmptyParentheses, //~ ERROR: enum variant has empty brackets
|
||||||
|
AnotherEnum, // No error
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestEnumWithFeatures {
|
||||||
|
NonEmptyBraces {
|
||||||
|
#[cfg(feature = "thisisneverenabled")]
|
||||||
|
x: i32,
|
||||||
|
}, // No error
|
||||||
|
NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
27
tests/ui/empty_enum_variants_with_brackets.rs
Normal file
27
tests/ui/empty_enum_variants_with_brackets.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#![warn(clippy::empty_enum_variants_with_brackets)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
pub enum PublicTestEnum {
|
||||||
|
NonEmptyBraces { x: i32, y: i32 }, // No error
|
||||||
|
NonEmptyParentheses(i32, i32), // No error
|
||||||
|
EmptyBraces {}, //~ ERROR: enum variant has empty brackets
|
||||||
|
EmptyParentheses(), //~ ERROR: enum variant has empty brackets
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestEnum {
|
||||||
|
NonEmptyBraces { x: i32, y: i32 }, // No error
|
||||||
|
NonEmptyParentheses(i32, i32), // No error
|
||||||
|
EmptyBraces {}, //~ ERROR: enum variant has empty brackets
|
||||||
|
EmptyParentheses(), //~ ERROR: enum variant has empty brackets
|
||||||
|
AnotherEnum, // No error
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestEnumWithFeatures {
|
||||||
|
NonEmptyBraces {
|
||||||
|
#[cfg(feature = "thisisneverenabled")]
|
||||||
|
x: i32,
|
||||||
|
}, // No error
|
||||||
|
NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
36
tests/ui/empty_enum_variants_with_brackets.stderr
Normal file
36
tests/ui/empty_enum_variants_with_brackets.stderr
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
error: enum variant has empty brackets
|
||||||
|
--> $DIR/empty_enum_variants_with_brackets.rs:7:16
|
||||||
|
|
|
||||||
|
LL | EmptyBraces {},
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::empty-enum-variants-with-brackets` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::empty_enum_variants_with_brackets)]`
|
||||||
|
= help: remove the brackets
|
||||||
|
|
||||||
|
error: enum variant has empty brackets
|
||||||
|
--> $DIR/empty_enum_variants_with_brackets.rs:8:21
|
||||||
|
|
|
||||||
|
LL | EmptyParentheses(),
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: remove the brackets
|
||||||
|
|
||||||
|
error: enum variant has empty brackets
|
||||||
|
--> $DIR/empty_enum_variants_with_brackets.rs:14:16
|
||||||
|
|
|
||||||
|
LL | EmptyBraces {},
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: remove the brackets
|
||||||
|
|
||||||
|
error: enum variant has empty brackets
|
||||||
|
--> $DIR/empty_enum_variants_with_brackets.rs:15:21
|
||||||
|
|
|
||||||
|
LL | EmptyParentheses(),
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: remove the brackets
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
clippy::unnecessary_operation,
|
clippy::unnecessary_operation,
|
||||||
clippy::op_ref,
|
clippy::op_ref,
|
||||||
clippy::double_parens,
|
clippy::double_parens,
|
||||||
clippy::uninlined_format_args
|
clippy::uninlined_format_args,
|
||||||
|
clippy::borrow_deref_ref,
|
||||||
|
clippy::deref_addrof
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
@ -40,32 +42,45 @@ fn main() {
|
||||||
let x = 0;
|
let x = 0;
|
||||||
|
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x + 1;
|
x + 1;
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1 + x;
|
1 + x;
|
||||||
x - ZERO; //no error, as we skip lookups (for now)
|
x - ZERO; //no error, as we skip lookups (for now)
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
((ZERO)) | x; //no error, as we skip lookups (for now)
|
((ZERO)) | x; //no error, as we skip lookups (for now)
|
||||||
|
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x / ONE; //no error, as we skip lookups (for now)
|
x / ONE; //no error, as we skip lookups (for now)
|
||||||
|
|
||||||
x / 2; //no false positive
|
x / 2; //no false positive
|
||||||
|
|
||||||
x & NEG_ONE; //no error, as we skip lookups (for now)
|
x & NEG_ONE; //no error, as we skip lookups (for now)
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
let u: u8 = 0;
|
let u: u8 = 0;
|
||||||
u;
|
u;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
1 << 0; // no error, this case is allowed, see issue 3430
|
1 << 0; // no error, this case is allowed, see issue 3430
|
||||||
42;
|
42;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1;
|
1;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
42;
|
42;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x;
|
x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
let mut a = A(String::new());
|
let mut a = A(String::new());
|
||||||
let b = a << 0; // no error: non-integer
|
let b = a << 0; // no error: non-integer
|
||||||
|
@ -73,10 +88,15 @@ fn main() {
|
||||||
1 * Meter; // no error: non-integer
|
1 * Meter; // no error: non-integer
|
||||||
|
|
||||||
2;
|
2;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
-2;
|
-2;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
2 + x;
|
2 + x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
-2 + x;
|
-2 + x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x + 1;
|
x + 1;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(x + 1) % 3; // no error
|
(x + 1) % 3; // no error
|
||||||
4 % 3; // no error
|
4 % 3; // no error
|
||||||
4 % -3; // no error
|
4 % -3; // no error
|
||||||
|
@ -85,38 +105,110 @@ fn main() {
|
||||||
let a = 0;
|
let a = 0;
|
||||||
let b = true;
|
let b = true;
|
||||||
(if b { 1 } else { 2 });
|
(if b { 1 } else { 2 });
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(if b { 1 } else { 2 }) + if b { 3 } else { 4 };
|
(if b { 1 } else { 2 }) + if b { 3 } else { 4 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(match a { 0 => 10, _ => 20 });
|
(match a { 0 => 10, _ => 20 });
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 };
|
(match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 };
|
(if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
|
(match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(if b { 1 } else { 2 });
|
(if b { 1 } else { 2 });
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
({ a }) + 3;
|
({ a }) + 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
({ a } * 2);
|
({ a } * 2);
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 };
|
(loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
fn f(_: i32) {
|
fn f(_: i32) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
f(a + { 8 * 5 });
|
f(a + { 8 * 5 });
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
f(if b { 1 } else { 2 } + 3);
|
f(if b { 1 } else { 2 } + 3);
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
const _: i32 = { 2 * 4 } + 3;
|
const _: i32 = { 2 * 4 } + 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
const _: i32 = { 1 + 2 * 3 } + 3;
|
const _: i32 = { 1 + 2 * 3 } + 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
a as usize;
|
a as usize;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
let _ = a as usize;
|
let _ = a as usize;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
({ a } as usize);
|
({ a } as usize);
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
2 * { a };
|
2 * { a };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(({ a } + 4));
|
(({ a } + 4));
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1;
|
1;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
// Issue #9904
|
// Issue #9904
|
||||||
let x = 0i32;
|
let x = 0i32;
|
||||||
let _: i32 = x;
|
let _: i32 = x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decide(a: bool, b: bool) -> u32 {
|
pub fn decide(a: bool, b: bool) -> u32 {
|
||||||
(if a { 1 } else { 2 }) + if b { 3 } else { 5 }
|
(if a { 1 } else { 2 }) + if b { 3 } else { 5 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The following tests are from / for issue #12050
|
||||||
|
/// In short, the lint didn't work for coerced references,
|
||||||
|
/// e.g. let x = &0; let y = x + 0;
|
||||||
|
/// because the suggested fix was `let y = x;` but
|
||||||
|
/// it should have been `let y = *x;`
|
||||||
|
fn issue_12050() {
|
||||||
|
{
|
||||||
|
let x = &0i32;
|
||||||
|
let _: i32 = *x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = *x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let x = &&0i32;
|
||||||
|
let _: i32 = **x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let x = &&0i32;
|
||||||
|
let _: i32 = **x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// this is just silly
|
||||||
|
let x = &&&0i32;
|
||||||
|
let _: i32 = ***x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = ***x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let x = 0i32;
|
||||||
|
let _: i32 = *&x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = **&&x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = *&*&x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = **&&*&x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// this is getting ridiculous, but we should still see the same
|
||||||
|
// error message so let's just keep going
|
||||||
|
let x = &0i32;
|
||||||
|
let _: i32 = ***&&*&x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = ***&&*&x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
clippy::unnecessary_operation,
|
clippy::unnecessary_operation,
|
||||||
clippy::op_ref,
|
clippy::op_ref,
|
||||||
clippy::double_parens,
|
clippy::double_parens,
|
||||||
clippy::uninlined_format_args
|
clippy::uninlined_format_args,
|
||||||
|
clippy::borrow_deref_ref,
|
||||||
|
clippy::deref_addrof
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
@ -40,32 +42,45 @@ fn main() {
|
||||||
let x = 0;
|
let x = 0;
|
||||||
|
|
||||||
x + 0;
|
x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x + (1 - 1);
|
x + (1 - 1);
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x + 1;
|
x + 1;
|
||||||
0 + x;
|
0 + x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1 + x;
|
1 + x;
|
||||||
x - ZERO; //no error, as we skip lookups (for now)
|
x - ZERO; //no error, as we skip lookups (for now)
|
||||||
x | (0);
|
x | (0);
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
((ZERO)) | x; //no error, as we skip lookups (for now)
|
((ZERO)) | x; //no error, as we skip lookups (for now)
|
||||||
|
|
||||||
x * 1;
|
x * 1;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1 * x;
|
1 * x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x / ONE; //no error, as we skip lookups (for now)
|
x / ONE; //no error, as we skip lookups (for now)
|
||||||
|
|
||||||
x / 2; //no false positive
|
x / 2; //no false positive
|
||||||
|
|
||||||
x & NEG_ONE; //no error, as we skip lookups (for now)
|
x & NEG_ONE; //no error, as we skip lookups (for now)
|
||||||
-1 & x;
|
-1 & x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
let u: u8 = 0;
|
let u: u8 = 0;
|
||||||
u & 255;
|
u & 255;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
1 << 0; // no error, this case is allowed, see issue 3430
|
1 << 0; // no error, this case is allowed, see issue 3430
|
||||||
42 << 0;
|
42 << 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1 >> 0;
|
1 >> 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
42 >> 0;
|
42 >> 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
&x >> 0;
|
&x >> 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x >> &0;
|
x >> &0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
let mut a = A(String::new());
|
let mut a = A(String::new());
|
||||||
let b = a << 0; // no error: non-integer
|
let b = a << 0; // no error: non-integer
|
||||||
|
@ -73,10 +88,15 @@ fn main() {
|
||||||
1 * Meter; // no error: non-integer
|
1 * Meter; // no error: non-integer
|
||||||
|
|
||||||
2 % 3;
|
2 % 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
-2 % 3;
|
-2 % 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
2 % -3 + x;
|
2 % -3 + x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
-2 % -3 + x;
|
-2 % -3 + x;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
x + 1 % 3;
|
x + 1 % 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(x + 1) % 3; // no error
|
(x + 1) % 3; // no error
|
||||||
4 % 3; // no error
|
4 % 3; // no error
|
||||||
4 % -3; // no error
|
4 % -3; // no error
|
||||||
|
@ -85,38 +105,110 @@ fn main() {
|
||||||
let a = 0;
|
let a = 0;
|
||||||
let b = true;
|
let b = true;
|
||||||
0 + if b { 1 } else { 2 };
|
0 + if b { 1 } else { 2 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
|
0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + match a { 0 => 10, _ => 20 };
|
0 + match a { 0 => 10, _ => 20 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
|
0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
|
0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
|
0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
(if b { 1 } else { 2 }) + 0;
|
(if b { 1 } else { 2 }) + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
0 + { a } + 3;
|
0 + { a } + 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + { a } * 2;
|
0 + { a } * 2;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
|
0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
fn f(_: i32) {
|
fn f(_: i32) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
f(1 * a + { 8 * 5 });
|
f(1 * a + { 8 * 5 });
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
f(0 + if b { 1 } else { 2 } + 3);
|
f(0 + if b { 1 } else { 2 } + 3);
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
const _: i32 = { 2 * 4 } + 0 + 3;
|
const _: i32 = { 2 * 4 } + 0 + 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
const _: i32 = 0 + { 1 + 2 * 3 } + 3;
|
const _: i32 = 0 + { 1 + 2 * 3 } + 3;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
0 + a as usize;
|
0 + a as usize;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
let _ = 0 + a as usize;
|
let _ = 0 + a as usize;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
0 + { a } as usize;
|
0 + { a } as usize;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
2 * (0 + { a });
|
2 * (0 + { a });
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1 * ({ a } + 4);
|
1 * ({ a } + 4);
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
1 * 1;
|
1 * 1;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
|
||||||
// Issue #9904
|
// Issue #9904
|
||||||
let x = 0i32;
|
let x = 0i32;
|
||||||
let _: i32 = &x + 0;
|
let _: i32 = &x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decide(a: bool, b: bool) -> u32 {
|
pub fn decide(a: bool, b: bool) -> u32 {
|
||||||
0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
|
0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The following tests are from / for issue #12050
|
||||||
|
/// In short, the lint didn't work for coerced references,
|
||||||
|
/// e.g. let x = &0; let y = x + 0;
|
||||||
|
/// because the suggested fix was `let y = x;` but
|
||||||
|
/// it should have been `let y = *x;`
|
||||||
|
fn issue_12050() {
|
||||||
|
{
|
||||||
|
let x = &0i32;
|
||||||
|
let _: i32 = *x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let x = &&0i32;
|
||||||
|
let _: i32 = **x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let x = &&0i32;
|
||||||
|
let _: i32 = *x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// this is just silly
|
||||||
|
let x = &&&0i32;
|
||||||
|
let _: i32 = ***x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = **x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let x = 0i32;
|
||||||
|
let _: i32 = *&x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = **&&x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = *&*&x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = **&&*&x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// this is getting ridiculous, but we should still see the same
|
||||||
|
// error message so let's just keep going
|
||||||
|
let x = &0i32;
|
||||||
|
let _: i32 = **&&*&x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
let _: i32 = **&&*&x + 0;
|
||||||
|
//~^ ERROR: this operation has no effect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:42:5
|
--> $DIR/identity_op.rs:44:5
|
||||||
|
|
|
|
||||||
LL | x + 0;
|
LL | x + 0;
|
||||||
| ^^^^^ help: consider reducing it to: `x`
|
| ^^^^^ help: consider reducing it to: `x`
|
||||||
|
@ -8,238 +8,310 @@ LL | x + 0;
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::identity_op)]`
|
= help: to override `-D warnings` add `#[allow(clippy::identity_op)]`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:43:5
|
--> $DIR/identity_op.rs:46:5
|
||||||
|
|
|
|
||||||
LL | x + (1 - 1);
|
LL | x + (1 - 1);
|
||||||
| ^^^^^^^^^^^ help: consider reducing it to: `x`
|
| ^^^^^^^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:45:5
|
--> $DIR/identity_op.rs:49:5
|
||||||
|
|
|
|
||||||
LL | 0 + x;
|
LL | 0 + x;
|
||||||
| ^^^^^ help: consider reducing it to: `x`
|
| ^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:48:5
|
--> $DIR/identity_op.rs:53:5
|
||||||
|
|
|
|
||||||
LL | x | (0);
|
LL | x | (0);
|
||||||
| ^^^^^^^ help: consider reducing it to: `x`
|
| ^^^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:51:5
|
--> $DIR/identity_op.rs:57:5
|
||||||
|
|
|
|
||||||
LL | x * 1;
|
LL | x * 1;
|
||||||
| ^^^^^ help: consider reducing it to: `x`
|
| ^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:52:5
|
--> $DIR/identity_op.rs:59:5
|
||||||
|
|
|
|
||||||
LL | 1 * x;
|
LL | 1 * x;
|
||||||
| ^^^^^ help: consider reducing it to: `x`
|
| ^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:58:5
|
--> $DIR/identity_op.rs:66:5
|
||||||
|
|
|
|
||||||
LL | -1 & x;
|
LL | -1 & x;
|
||||||
| ^^^^^^ help: consider reducing it to: `x`
|
| ^^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:61:5
|
--> $DIR/identity_op.rs:70:5
|
||||||
|
|
|
|
||||||
LL | u & 255;
|
LL | u & 255;
|
||||||
| ^^^^^^^ help: consider reducing it to: `u`
|
| ^^^^^^^ help: consider reducing it to: `u`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:64:5
|
--> $DIR/identity_op.rs:74:5
|
||||||
|
|
|
|
||||||
LL | 42 << 0;
|
LL | 42 << 0;
|
||||||
| ^^^^^^^ help: consider reducing it to: `42`
|
| ^^^^^^^ help: consider reducing it to: `42`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:65:5
|
--> $DIR/identity_op.rs:76:5
|
||||||
|
|
|
|
||||||
LL | 1 >> 0;
|
LL | 1 >> 0;
|
||||||
| ^^^^^^ help: consider reducing it to: `1`
|
| ^^^^^^ help: consider reducing it to: `1`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:66:5
|
--> $DIR/identity_op.rs:78:5
|
||||||
|
|
|
|
||||||
LL | 42 >> 0;
|
LL | 42 >> 0;
|
||||||
| ^^^^^^^ help: consider reducing it to: `42`
|
| ^^^^^^^ help: consider reducing it to: `42`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:67:5
|
--> $DIR/identity_op.rs:80:5
|
||||||
|
|
|
|
||||||
LL | &x >> 0;
|
LL | &x >> 0;
|
||||||
| ^^^^^^^ help: consider reducing it to: `x`
|
| ^^^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:68:5
|
--> $DIR/identity_op.rs:82:5
|
||||||
|
|
|
|
||||||
LL | x >> &0;
|
LL | x >> &0;
|
||||||
| ^^^^^^^ help: consider reducing it to: `x`
|
| ^^^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:75:5
|
--> $DIR/identity_op.rs:90:5
|
||||||
|
|
|
|
||||||
LL | 2 % 3;
|
LL | 2 % 3;
|
||||||
| ^^^^^ help: consider reducing it to: `2`
|
| ^^^^^ help: consider reducing it to: `2`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:76:5
|
--> $DIR/identity_op.rs:92:5
|
||||||
|
|
|
|
||||||
LL | -2 % 3;
|
LL | -2 % 3;
|
||||||
| ^^^^^^ help: consider reducing it to: `-2`
|
| ^^^^^^ help: consider reducing it to: `-2`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:77:5
|
--> $DIR/identity_op.rs:94:5
|
||||||
|
|
|
|
||||||
LL | 2 % -3 + x;
|
LL | 2 % -3 + x;
|
||||||
| ^^^^^^ help: consider reducing it to: `2`
|
| ^^^^^^ help: consider reducing it to: `2`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:78:5
|
--> $DIR/identity_op.rs:96:5
|
||||||
|
|
|
|
||||||
LL | -2 % -3 + x;
|
LL | -2 % -3 + x;
|
||||||
| ^^^^^^^ help: consider reducing it to: `-2`
|
| ^^^^^^^ help: consider reducing it to: `-2`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:79:9
|
--> $DIR/identity_op.rs:98:9
|
||||||
|
|
|
|
||||||
LL | x + 1 % 3;
|
LL | x + 1 % 3;
|
||||||
| ^^^^^ help: consider reducing it to: `1`
|
| ^^^^^ help: consider reducing it to: `1`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:87:5
|
--> $DIR/identity_op.rs:107:5
|
||||||
|
|
|
|
||||||
LL | 0 + if b { 1 } else { 2 };
|
LL | 0 + if b { 1 } else { 2 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:88:5
|
--> $DIR/identity_op.rs:109:5
|
||||||
|
|
|
|
||||||
LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
|
LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:89:5
|
--> $DIR/identity_op.rs:111:5
|
||||||
|
|
|
|
||||||
LL | 0 + match a { 0 => 10, _ => 20 };
|
LL | 0 + match a { 0 => 10, _ => 20 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:90:5
|
--> $DIR/identity_op.rs:113:5
|
||||||
|
|
|
|
||||||
LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
|
LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:91:5
|
--> $DIR/identity_op.rs:115:5
|
||||||
|
|
|
|
||||||
LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
|
LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:92:5
|
--> $DIR/identity_op.rs:117:5
|
||||||
|
|
|
|
||||||
LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
|
LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:93:5
|
--> $DIR/identity_op.rs:119:5
|
||||||
|
|
|
|
||||||
LL | (if b { 1 } else { 2 }) + 0;
|
LL | (if b { 1 } else { 2 }) + 0;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:95:5
|
--> $DIR/identity_op.rs:122:5
|
||||||
|
|
|
|
||||||
LL | 0 + { a } + 3;
|
LL | 0 + { a } + 3;
|
||||||
| ^^^^^^^^^ help: consider reducing it to: `({ a })`
|
| ^^^^^^^^^ help: consider reducing it to: `({ a })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:96:5
|
--> $DIR/identity_op.rs:124:5
|
||||||
|
|
|
|
||||||
LL | 0 + { a } * 2;
|
LL | 0 + { a } * 2;
|
||||||
| ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)`
|
| ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:97:5
|
--> $DIR/identity_op.rs:126:5
|
||||||
|
|
|
|
||||||
LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
|
LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:102:7
|
--> $DIR/identity_op.rs:133:7
|
||||||
|
|
|
|
||||||
LL | f(1 * a + { 8 * 5 });
|
LL | f(1 * a + { 8 * 5 });
|
||||||
| ^^^^^ help: consider reducing it to: `a`
|
| ^^^^^ help: consider reducing it to: `a`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:103:7
|
--> $DIR/identity_op.rs:135:7
|
||||||
|
|
|
|
||||||
LL | f(0 + if b { 1 } else { 2 } + 3);
|
LL | f(0 + if b { 1 } else { 2 } + 3);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:104:20
|
--> $DIR/identity_op.rs:138:20
|
||||||
|
|
|
|
||||||
LL | const _: i32 = { 2 * 4 } + 0 + 3;
|
LL | const _: i32 = { 2 * 4 } + 0 + 3;
|
||||||
| ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }`
|
| ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:105:20
|
--> $DIR/identity_op.rs:140:20
|
||||||
|
|
|
|
||||||
LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3;
|
LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3;
|
||||||
| ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }`
|
| ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:107:5
|
--> $DIR/identity_op.rs:143:5
|
||||||
|
|
|
|
||||||
LL | 0 + a as usize;
|
LL | 0 + a as usize;
|
||||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
|
| ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:108:13
|
--> $DIR/identity_op.rs:145:13
|
||||||
|
|
|
|
||||||
LL | let _ = 0 + a as usize;
|
LL | let _ = 0 + a as usize;
|
||||||
| ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
|
| ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:109:5
|
--> $DIR/identity_op.rs:147:5
|
||||||
|
|
|
|
||||||
LL | 0 + { a } as usize;
|
LL | 0 + { a } as usize;
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)`
|
| ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:111:9
|
--> $DIR/identity_op.rs:150:9
|
||||||
|
|
|
|
||||||
LL | 2 * (0 + { a });
|
LL | 2 * (0 + { a });
|
||||||
| ^^^^^^^^^^^ help: consider reducing it to: `{ a }`
|
| ^^^^^^^^^^^ help: consider reducing it to: `{ a }`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:112:5
|
--> $DIR/identity_op.rs:152:5
|
||||||
|
|
|
|
||||||
LL | 1 * ({ a } + 4);
|
LL | 1 * ({ a } + 4);
|
||||||
| ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))`
|
| ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:113:5
|
--> $DIR/identity_op.rs:154:5
|
||||||
|
|
|
|
||||||
LL | 1 * 1;
|
LL | 1 * 1;
|
||||||
| ^^^^^ help: consider reducing it to: `1`
|
| ^^^^^ help: consider reducing it to: `1`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:117:18
|
--> $DIR/identity_op.rs:159:18
|
||||||
|
|
|
|
||||||
LL | let _: i32 = &x + 0;
|
LL | let _: i32 = &x + 0;
|
||||||
| ^^^^^^ help: consider reducing it to: `x`
|
| ^^^^^^ help: consider reducing it to: `x`
|
||||||
|
|
||||||
error: this operation has no effect
|
error: this operation has no effect
|
||||||
--> $DIR/identity_op.rs:121:5
|
--> $DIR/identity_op.rs:164:5
|
||||||
|
|
|
|
||||||
LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
|
LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })`
|
||||||
|
|
||||||
error: aborting due to 40 previous errors
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:175:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = *x + 0;
|
||||||
|
| ^^^^^^ help: consider reducing it to: `*x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:177:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = x + 0;
|
||||||
|
| ^^^^^ help: consider reducing it to: `*x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:182:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = **x + 0;
|
||||||
|
| ^^^^^^^ help: consider reducing it to: `**x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:185:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = *x + 0;
|
||||||
|
| ^^^^^^ help: consider reducing it to: `**x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:191:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = ***x + 0;
|
||||||
|
| ^^^^^^^^ help: consider reducing it to: `***x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:193:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = **x + 0;
|
||||||
|
| ^^^^^^^ help: consider reducing it to: `***x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:196:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = *&x + 0;
|
||||||
|
| ^^^^^^^ help: consider reducing it to: `*&x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:198:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = **&&x + 0;
|
||||||
|
| ^^^^^^^^^ help: consider reducing it to: `**&&x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:200:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = *&*&x + 0;
|
||||||
|
| ^^^^^^^^^ help: consider reducing it to: `*&*&x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:202:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = **&&*&x + 0;
|
||||||
|
| ^^^^^^^^^^^ help: consider reducing it to: `**&&*&x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:209:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = **&&*&x + 0;
|
||||||
|
| ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x`
|
||||||
|
|
||||||
|
error: this operation has no effect
|
||||||
|
--> $DIR/identity_op.rs:211:22
|
||||||
|
|
|
||||||
|
LL | let _: i32 = **&&*&x + 0;
|
||||||
|
| ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x`
|
||||||
|
|
||||||
|
error: aborting due to 52 previous errors
|
||||||
|
|
||||||
|
|
|
@ -130,3 +130,8 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn issue12103(x: u32) -> Option<u32> {
|
||||||
|
// Should not issue an error in `const` context
|
||||||
|
if x > 42 { Some(150) } else { None }
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//@no-rustfix
|
//@no-rustfix
|
||||||
|
//@aux-build:proc_macros.rs
|
||||||
#![warn(clippy::into_iter_without_iter)]
|
#![warn(clippy::into_iter_without_iter)]
|
||||||
|
extern crate proc_macros;
|
||||||
|
|
||||||
use std::iter::IntoIterator;
|
use std::iter::IntoIterator;
|
||||||
|
|
||||||
|
@ -111,6 +113,43 @@ impl IntoIterator for &Alias {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fine to lint, the impls comes from a local macro.
|
||||||
|
pub struct Issue12037;
|
||||||
|
macro_rules! generate_impl {
|
||||||
|
() => {
|
||||||
|
impl<'a> IntoIterator for &'a Issue12037 {
|
||||||
|
type IntoIter = std::slice::Iter<'a, u8>;
|
||||||
|
type Item = &'a u8;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
generate_impl!();
|
||||||
|
|
||||||
|
// Impl comes from an external crate
|
||||||
|
proc_macros::external! {
|
||||||
|
pub struct ImplWithForeignSpan;
|
||||||
|
impl<'a> IntoIterator for &'a ImplWithForeignSpan {
|
||||||
|
type IntoIter = std::slice::Iter<'a, u8>;
|
||||||
|
type Item = &'a u8;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Allowed;
|
||||||
|
#[allow(clippy::into_iter_without_iter)]
|
||||||
|
impl<'a> IntoIterator for &'a Allowed {
|
||||||
|
type IntoIter = std::slice::Iter<'a, u8>;
|
||||||
|
type Item = &'a u8;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
pub mod issue11635 {
|
pub mod issue11635 {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: `IntoIterator` implemented for a reference type without an `iter` method
|
error: `IntoIterator` implemented for a reference type without an `iter` method
|
||||||
--> $DIR/into_iter_without_iter.rs:7:1
|
--> $DIR/into_iter_without_iter.rs:9:1
|
||||||
|
|
|
|
||||||
LL | / impl<'a> IntoIterator for &'a S1 {
|
LL | / impl<'a> IntoIterator for &'a S1 {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -23,7 +23,7 @@ LL + }
|
||||||
|
|
|
|
||||||
|
|
||||||
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
|
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
|
||||||
--> $DIR/into_iter_without_iter.rs:15:1
|
--> $DIR/into_iter_without_iter.rs:17:1
|
||||||
|
|
|
|
||||||
LL | / impl<'a> IntoIterator for &'a mut S1 {
|
LL | / impl<'a> IntoIterator for &'a mut S1 {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -45,7 +45,7 @@ LL + }
|
||||||
|
|
|
|
||||||
|
|
||||||
error: `IntoIterator` implemented for a reference type without an `iter` method
|
error: `IntoIterator` implemented for a reference type without an `iter` method
|
||||||
--> $DIR/into_iter_without_iter.rs:25:1
|
--> $DIR/into_iter_without_iter.rs:27:1
|
||||||
|
|
|
|
||||||
LL | / impl<'a, T> IntoIterator for &'a S2<T> {
|
LL | / impl<'a, T> IntoIterator for &'a S2<T> {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -67,7 +67,7 @@ LL + }
|
||||||
|
|
|
|
||||||
|
|
||||||
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
|
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
|
||||||
--> $DIR/into_iter_without_iter.rs:33:1
|
--> $DIR/into_iter_without_iter.rs:35:1
|
||||||
|
|
|
|
||||||
LL | / impl<'a, T> IntoIterator for &'a mut S2<T> {
|
LL | / impl<'a, T> IntoIterator for &'a mut S2<T> {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -89,7 +89,7 @@ LL + }
|
||||||
|
|
|
|
||||||
|
|
||||||
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
|
error: `IntoIterator` implemented for a reference type without an `iter_mut` method
|
||||||
--> $DIR/into_iter_without_iter.rs:84:1
|
--> $DIR/into_iter_without_iter.rs:86:1
|
||||||
|
|
|
|
||||||
LL | / impl<'a, T> IntoIterator for &mut S4<'a, T> {
|
LL | / impl<'a, T> IntoIterator for &mut S4<'a, T> {
|
||||||
LL | |
|
LL | |
|
||||||
|
@ -110,5 +110,31 @@ LL + }
|
||||||
LL + }
|
LL + }
|
||||||
|
|
|
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
error: `IntoIterator` implemented for a reference type without an `iter` method
|
||||||
|
--> $DIR/into_iter_without_iter.rs:120:9
|
||||||
|
|
|
||||||
|
LL | / impl<'a> IntoIterator for &'a Issue12037 {
|
||||||
|
LL | | type IntoIter = std::slice::Iter<'a, u8>;
|
||||||
|
LL | | type Item = &'a u8;
|
||||||
|
LL | | fn into_iter(self) -> Self::IntoIter {
|
||||||
|
LL | | todo!()
|
||||||
|
LL | | }
|
||||||
|
LL | | }
|
||||||
|
| |_________^
|
||||||
|
...
|
||||||
|
LL | generate_impl!();
|
||||||
|
| ---------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `generate_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
help: consider implementing `iter`
|
||||||
|
|
|
||||||
|
LL ~
|
||||||
|
LL + impl Issue12037 {
|
||||||
|
LL + fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||||
|
LL + <&Self as IntoIterator>::into_iter(self)
|
||||||
|
LL + }
|
||||||
|
LL + }
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,72 @@
|
||||||
#![warn(clippy::iter_filter_is_ok)]
|
#![warn(clippy::iter_filter_is_ok)]
|
||||||
|
#![allow(
|
||||||
|
clippy::map_identity,
|
||||||
|
clippy::result_filter_map,
|
||||||
|
clippy::needless_borrow,
|
||||||
|
clippy::redundant_closure
|
||||||
|
)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
{
|
||||||
//~^ HELP: consider using `flatten` instead
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
||||||
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
//~^ HELP: consider using `flatten` instead
|
||||||
//~^ HELP: consider using `flatten` instead
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
{
|
||||||
let _ = vec![Ok(1), Err(2)].into_iter().flatten();
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
||||||
//~^ HELP: consider using `flatten` instead
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
.flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
.flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2)].into_iter().flatten();
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_linting_when_filter_has_side_effects() {
|
||||||
// Don't lint below
|
// Don't lint below
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
o.is_ok()
|
o.is_ok()
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_linting_when_commented() {
|
||||||
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
||||||
// Roses are red,
|
// Roses are red,
|
||||||
// Violets are blue,
|
// Violets are blue,
|
||||||
|
@ -24,3 +75,131 @@ fn main() {
|
||||||
o.is_ok()
|
o.is_ok()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ice_12058() {
|
||||||
|
// check that checking the parent node doesn't cause an ICE
|
||||||
|
// by indexing the parameters of a closure without parameters
|
||||||
|
Some(1).or_else(|| {
|
||||||
|
vec![Ok(1), Err(())].into_iter().filter(|z| *z != Ok(2));
|
||||||
|
None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_linting_map() {
|
||||||
|
// should not lint
|
||||||
|
let _ = vec![Ok(1), Err(())]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|o| o.is_ok())
|
||||||
|
.map(|o| o.unwrap());
|
||||||
|
|
||||||
|
// should not lint
|
||||||
|
let _ = vec![Ok(1), Err(())].into_iter().filter(|o| o.is_ok()).map(|o| o);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_false_positive_due_to_is_ok_and_iterator_impl() {
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
struct Foo {}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn is_ok(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Foo {
|
||||||
|
type Item = Foo;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
Some(Foo::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = vec![Foo::default()];
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(Foo::is_ok);
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(|f| f.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_false_positive_due_to_is_ok_and_into_iterator_impl() {
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
struct Foo {}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn is_ok(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = vec![Foo::default()];
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(Foo::is_ok);
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(|f| f.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_for_trivial() {
|
||||||
|
let _ = vec![Ok(1), Err(()), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| (Err(()) as Result<i32, ()>).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_false_positive_due_to_method_name() {
|
||||||
|
fn is_ok(x: &Result<i32, i32>) -> bool {
|
||||||
|
x.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![Ok(1), Err(2), Ok(3)].into_iter().filter(is_ok);
|
||||||
|
// should not lint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_due_to_trait_type() {
|
||||||
|
struct Foo {
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
impl Foo {
|
||||||
|
fn is_ok(obj: &Result<i32, i32>) -> bool {
|
||||||
|
obj.is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Foo::is_ok);
|
||||||
|
// should not lint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_with_call_to_outside_var() {
|
||||||
|
let outside: Result<i32, ()> = Ok(1);
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| outside.is_ok());
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| Result::is_ok(&outside));
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| std::result::Result::is_ok(&outside));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_with_call_to_outside_var_mix_match_types() {
|
||||||
|
let outside = Some(1);
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| outside.is_some());
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| Option::is_some(&outside));
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| std::option::Option::is_some(&outside));
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,72 @@
|
||||||
#![warn(clippy::iter_filter_is_ok)]
|
#![warn(clippy::iter_filter_is_ok)]
|
||||||
|
#![allow(
|
||||||
|
clippy::map_identity,
|
||||||
|
clippy::result_filter_map,
|
||||||
|
clippy::needless_borrow,
|
||||||
|
clippy::redundant_closure
|
||||||
|
)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok);
|
{
|
||||||
//~^ HELP: consider using `flatten` instead
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok);
|
||||||
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok());
|
//~^ HELP: consider using `flatten` instead
|
||||||
//~^ HELP: consider using `flatten` instead
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok());
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() });
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
{
|
||||||
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() });
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok());
|
||||||
//~^ HELP: consider using `flatten` instead
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok());
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2)].into_iter().filter(|&o| { o.is_ok() });
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
.filter(std::result::Result::is_ok);
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|a| std::result::Result::is_ok(a));
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| { std::result::Result::is_ok(a) });
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok());
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok());
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let _ = vec![Ok(1), Err(2)].into_iter().filter(|ref o| { o.is_ok() });
|
||||||
|
//~^ HELP: consider using `flatten` instead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_linting_when_filter_has_side_effects() {
|
||||||
// Don't lint below
|
// Don't lint below
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
o.is_ok()
|
o.is_ok()
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_linting_when_commented() {
|
||||||
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| {
|
||||||
// Roses are red,
|
// Roses are red,
|
||||||
// Violets are blue,
|
// Violets are blue,
|
||||||
|
@ -24,3 +75,131 @@ fn main() {
|
||||||
o.is_ok()
|
o.is_ok()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ice_12058() {
|
||||||
|
// check that checking the parent node doesn't cause an ICE
|
||||||
|
// by indexing the parameters of a closure without parameters
|
||||||
|
Some(1).or_else(|| {
|
||||||
|
vec![Ok(1), Err(())].into_iter().filter(|z| *z != Ok(2));
|
||||||
|
None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_linting_map() {
|
||||||
|
// should not lint
|
||||||
|
let _ = vec![Ok(1), Err(())]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|o| o.is_ok())
|
||||||
|
.map(|o| o.unwrap());
|
||||||
|
|
||||||
|
// should not lint
|
||||||
|
let _ = vec![Ok(1), Err(())].into_iter().filter(|o| o.is_ok()).map(|o| o);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_false_positive_due_to_is_ok_and_iterator_impl() {
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
struct Foo {}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn is_ok(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Foo {
|
||||||
|
type Item = Foo;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
Some(Foo::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = vec![Foo::default()];
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(Foo::is_ok);
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(|f| f.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_false_positive_due_to_is_ok_and_into_iterator_impl() {
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
struct Foo {}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn is_ok(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = vec![Foo::default()];
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(Foo::is_ok);
|
||||||
|
// should not lint
|
||||||
|
let _ = data.clone().into_iter().filter(|f| f.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_for_trivial() {
|
||||||
|
let _ = vec![Ok(1), Err(()), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| (Err(()) as Result<i32, ()>).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_false_positive_due_to_method_name() {
|
||||||
|
fn is_ok(x: &Result<i32, i32>) -> bool {
|
||||||
|
x.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![Ok(1), Err(2), Ok(3)].into_iter().filter(is_ok);
|
||||||
|
// should not lint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_due_to_trait_type() {
|
||||||
|
struct Foo {
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
impl Foo {
|
||||||
|
fn is_ok(obj: &Result<i32, i32>) -> bool {
|
||||||
|
obj.is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Foo::is_ok);
|
||||||
|
// should not lint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_with_call_to_outside_var() {
|
||||||
|
let outside: Result<i32, ()> = Ok(1);
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| outside.is_ok());
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| Result::is_ok(&outside));
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| std::result::Result::is_ok(&outside));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn avoid_fp_with_call_to_outside_var_mix_match_types() {
|
||||||
|
let outside = Some(1);
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| outside.is_some());
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| Option::is_some(&outside));
|
||||||
|
|
||||||
|
let _ = vec![Ok(1), Err(2), Ok(3)]
|
||||||
|
.into_iter()
|
||||||
|
// should not lint
|
||||||
|
.filter(|o| std::option::Option::is_some(&outside));
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue