Merge commit '9e3e9649cb' into clippy-subtree-update
This commit is contained in:
parent
d196db781e
commit
19f0c81b3f
97 changed files with 4089 additions and 1111 deletions
83
CHANGELOG.md
83
CHANGELOG.md
|
|
@ -6,7 +6,83 @@ document.
|
||||||
|
|
||||||
## Unreleased / Beta / In Rust Nightly
|
## Unreleased / Beta / In Rust Nightly
|
||||||
|
|
||||||
[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master)
|
[d9fb15c...master](https://github.com/rust-lang/rust-clippy/compare/d9fb15c...master)
|
||||||
|
|
||||||
|
## Rust 1.92
|
||||||
|
|
||||||
|
Current stable, released 2025-12-11
|
||||||
|
|
||||||
|
[View all 124 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-09-05T18%3A24%3A03Z..2025-10-16T14%3A13%3A43Z+base%3Amaster)
|
||||||
|
|
||||||
|
### New Lints
|
||||||
|
|
||||||
|
* Added [`unnecessary_option_map_or_else`] to `suspicious`
|
||||||
|
[#14662](https://github.com/rust-lang/rust-clippy/pull/14662)
|
||||||
|
* Added [`replace_box`] to `perf`
|
||||||
|
[#14953](https://github.com/rust-lang/rust-clippy/pull/14953)
|
||||||
|
* Added [`volatile_composites`] to `nursery`
|
||||||
|
[#15686](https://github.com/rust-lang/rust-clippy/pull/15686)
|
||||||
|
* Added [`self_only_used_in_recursion`] to `pedantic`
|
||||||
|
[#14787](https://github.com/rust-lang/rust-clippy/pull/14787)
|
||||||
|
* Added [`redundant_iter_cloned`] to `perf`
|
||||||
|
[#15277](https://github.com/rust-lang/rust-clippy/pull/15277)
|
||||||
|
|
||||||
|
### Moves and Deprecations
|
||||||
|
|
||||||
|
* Renamed [`unchecked_duration_subtraction`] to [`unchecked_time_subtraction`]
|
||||||
|
[#13800](https://github.com/rust-lang/rust-clippy/pull/13800)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [`mutex_atomic`] and [`mutex_integer`] overhauled to only lint definitions, not uses; added suggestions
|
||||||
|
and better help messages
|
||||||
|
[#15632](https://github.com/rust-lang/rust-clippy/pull/15632)
|
||||||
|
* [`manual_rotate`] now recognizes non-const rotation amounts
|
||||||
|
[#15402](https://github.com/rust-lang/rust-clippy/pull/15402)
|
||||||
|
* [`multiple_inherent_impl`] added `inherent-impl-lint-scope` config option (`module`, `file`,
|
||||||
|
or `crate`)
|
||||||
|
[#15843](https://github.com/rust-lang/rust-clippy/pull/15843)
|
||||||
|
* [`use_self`] now checks structs and enums
|
||||||
|
[#15566](https://github.com/rust-lang/rust-clippy/pull/15566)
|
||||||
|
* [`while_let_loop`] extended to lint on `loop { let else }`
|
||||||
|
[#15701](https://github.com/rust-lang/rust-clippy/pull/15701)
|
||||||
|
* [`mut_mut`] overhauled with structured suggestions and improved documentation
|
||||||
|
[#15417](https://github.com/rust-lang/rust-clippy/pull/15417)
|
||||||
|
* [`nonstandard_macro_braces`] now suggests trailing semicolon when needed
|
||||||
|
[#15593](https://github.com/rust-lang/rust-clippy/pull/15593)
|
||||||
|
* [`ptr_offset_with_cast`] now respects MSRV when suggesting fix, and lints more cases
|
||||||
|
[#15613](https://github.com/rust-lang/rust-clippy/pull/15613)
|
||||||
|
* [`cast_sign_loss`] and [`cast_possible_wrap`] added suggestions using `cast_{un,}signed()` methods
|
||||||
|
(MSRV 1.87+)
|
||||||
|
[#15384](https://github.com/rust-lang/rust-clippy/pull/15384)
|
||||||
|
* [`unchecked_time_subtraction`] extended to include `Duration - Duration` operations
|
||||||
|
[#13800](https://github.com/rust-lang/rust-clippy/pull/13800)
|
||||||
|
* [`filter_next`] now suggests replacing `filter().next_back()` with `rfind()` for
|
||||||
|
`DoubleEndedIterator`
|
||||||
|
[#15748](https://github.com/rust-lang/rust-clippy/pull/15748)
|
||||||
|
|
||||||
|
### False Positive Fixes
|
||||||
|
|
||||||
|
* [`unnecessary_safety_comment`] fixed FPs with comments above attributes
|
||||||
|
[#15678](https://github.com/rust-lang/rust-clippy/pull/15678)
|
||||||
|
* [`manual_unwrap_or`] fixed FP edge case
|
||||||
|
[#15812](https://github.com/rust-lang/rust-clippy/pull/15812)
|
||||||
|
* [`needless_continue`] fixed FP when match type is not unit or never
|
||||||
|
[#15547](https://github.com/rust-lang/rust-clippy/pull/15547)
|
||||||
|
* [`if_then_some_else_none`] fixed FP when return exists in block expr
|
||||||
|
[#15783](https://github.com/rust-lang/rust-clippy/pull/15783)
|
||||||
|
* [`new_without_default`] fixed to copy `#[cfg]` onto `impl Default` and fixed FP on private type
|
||||||
|
with trait impl
|
||||||
|
[#15720](https://github.com/rust-lang/rust-clippy/pull/15720)
|
||||||
|
[#15782](https://github.com/rust-lang/rust-clippy/pull/15782)
|
||||||
|
* [`question_mark`] fixed FP on variables used after
|
||||||
|
[#15644](https://github.com/rust-lang/rust-clippy/pull/15644)
|
||||||
|
* [`needless_return`] fixed FP with `cfg`d code after `return`
|
||||||
|
[#15669](https://github.com/rust-lang/rust-clippy/pull/15669)
|
||||||
|
* [`useless_attribute`] fixed FP on `deprecated_in_future`
|
||||||
|
[#15645](https://github.com/rust-lang/rust-clippy/pull/15645)
|
||||||
|
* [`double_parens`] fixed FP when macros are involved
|
||||||
|
[#15420](https://github.com/rust-lang/rust-clippy/pull/15420)
|
||||||
|
|
||||||
## Rust 1.91
|
## Rust 1.91
|
||||||
|
|
||||||
|
|
@ -6280,6 +6356,7 @@ Released 2018-09-13
|
||||||
[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
|
[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
|
||||||
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
|
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
|
||||||
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
|
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
|
||||||
|
[`decimal_bitwise_operands`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_bitwise_operands
|
||||||
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
|
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
|
||||||
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
|
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
|
||||||
[`default_constructed_unit_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_constructed_unit_structs
|
[`default_constructed_unit_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_constructed_unit_structs
|
||||||
|
|
@ -6541,6 +6618,7 @@ Released 2018-09-13
|
||||||
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
|
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
|
||||||
[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one
|
[`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one
|
||||||
[`manual_ignore_case_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ignore_case_cmp
|
[`manual_ignore_case_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ignore_case_cmp
|
||||||
|
[`manual_ilog2`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ilog2
|
||||||
[`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect
|
[`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect
|
||||||
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
|
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
|
||||||
[`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
|
||||||
|
|
@ -6686,6 +6764,7 @@ Released 2018-09-13
|
||||||
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
|
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
|
||||||
[`needless_return_with_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_question_mark
|
[`needless_return_with_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_question_mark
|
||||||
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
|
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
|
||||||
|
[`needless_type_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_type_cast
|
||||||
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
|
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
|
||||||
[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
|
[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
|
||||||
[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
|
[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
|
||||||
|
|
@ -6765,6 +6844,7 @@ Released 2018-09-13
|
||||||
[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
|
[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
|
||||||
[`ptr_cast_constness`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness
|
[`ptr_cast_constness`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness
|
||||||
[`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_by_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_by_literal
|
||||||
[`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_underscore_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields
|
||||||
|
|
@ -7081,6 +7161,7 @@ Released 2018-09-13
|
||||||
[`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts
|
[`allow-expect-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-consts
|
||||||
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
|
[`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
|
||||||
[`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests
|
[`allow-indexing-slicing-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-indexing-slicing-in-tests
|
||||||
|
[`allow-large-stack-frames-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-large-stack-frames-in-tests
|
||||||
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
|
[`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
|
||||||
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
|
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
|
||||||
[`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests
|
[`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
version = "0.1.93"
|
version = "0.1.94"
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,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 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
[There are over 800 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.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,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 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
[There are over 800 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
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,16 @@ Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]`
|
||||||
* [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
|
* [`indexing_slicing`](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
|
||||||
|
|
||||||
|
|
||||||
|
## `allow-large-stack-frames-in-tests`
|
||||||
|
Whether functions inside `#[cfg(test)]` modules or test functions should be checked.
|
||||||
|
|
||||||
|
**Default Value:** `true`
|
||||||
|
|
||||||
|
---
|
||||||
|
**Affected lints:**
|
||||||
|
* [`large_stack_frames`](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames)
|
||||||
|
|
||||||
|
|
||||||
## `allow-mixed-uninlined-format-args`
|
## `allow-mixed-uninlined-format-args`
|
||||||
Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
|
Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_config"
|
name = "clippy_config"
|
||||||
version = "0.1.93"
|
version = "0.1.94"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -373,6 +373,9 @@ define_Conf! {
|
||||||
/// Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]`
|
/// Whether `indexing_slicing` should be allowed in test functions or `#[cfg(test)]`
|
||||||
#[lints(indexing_slicing)]
|
#[lints(indexing_slicing)]
|
||||||
allow_indexing_slicing_in_tests: bool = false,
|
allow_indexing_slicing_in_tests: bool = false,
|
||||||
|
/// Whether functions inside `#[cfg(test)]` modules or test functions should be checked.
|
||||||
|
#[lints(large_stack_frames)]
|
||||||
|
allow_large_stack_frames_in_tests: bool = true,
|
||||||
/// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
|
/// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
|
||||||
#[lints(uninlined_format_args)]
|
#[lints(uninlined_format_args)]
|
||||||
allow_mixed_uninlined_format_args: bool = true,
|
allow_mixed_uninlined_format_args: bool = true,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_lints"
|
name = "clippy_lints"
|
||||||
version = "0.1.93"
|
version = "0.1.94"
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ mod fn_to_numeric_cast;
|
||||||
mod fn_to_numeric_cast_any;
|
mod fn_to_numeric_cast_any;
|
||||||
mod fn_to_numeric_cast_with_truncation;
|
mod fn_to_numeric_cast_with_truncation;
|
||||||
mod manual_dangling_ptr;
|
mod manual_dangling_ptr;
|
||||||
|
mod needless_type_cast;
|
||||||
mod ptr_as_ptr;
|
mod ptr_as_ptr;
|
||||||
mod ptr_cast_constness;
|
mod ptr_cast_constness;
|
||||||
mod ref_as_ptr;
|
mod ref_as_ptr;
|
||||||
|
|
@ -813,6 +814,32 @@ declare_clippy_lint! {
|
||||||
"casting a primitive method pointer to any integer type"
|
"casting a primitive method pointer to any integer type"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for bindings (constants, statics, or let bindings) that are defined
|
||||||
|
/// with one numeric type but are consistently cast to a different type in all usages.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// If a binding is always cast to a different type when used, it would be clearer
|
||||||
|
/// and more efficient to define it with the target type from the start.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// const SIZE: u16 = 15;
|
||||||
|
/// let arr: [u8; SIZE as usize] = [0; SIZE as usize];
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// const SIZE: usize = 15;
|
||||||
|
/// let arr: [u8; SIZE] = [0; SIZE];
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.93.0"]
|
||||||
|
pub NEEDLESS_TYPE_CAST,
|
||||||
|
pedantic,
|
||||||
|
"binding defined with one type but always cast to another"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Casts {
|
pub struct Casts {
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
@ -851,6 +878,7 @@ impl_lint_pass!(Casts => [
|
||||||
AS_POINTER_UNDERSCORE,
|
AS_POINTER_UNDERSCORE,
|
||||||
MANUAL_DANGLING_PTR,
|
MANUAL_DANGLING_PTR,
|
||||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||||
|
NEEDLESS_TYPE_CAST,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||||
|
|
@ -920,4 +948,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||||
cast_slice_different_sizes::check(cx, expr, self.msrv);
|
cast_slice_different_sizes::check(cx, expr, self.msrv);
|
||||||
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
|
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &rustc_hir::Body<'tcx>) {
|
||||||
|
needless_type_cast::check(cx, body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
289
clippy_lints/src/casts/needless_type_cast.rs
Normal file
289
clippy_lints/src/casts/needless_type_cast.rs
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::visitors::{Descend, for_each_expr, for_each_expr_without_closures};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::def::{DefKind, Res};
|
||||||
|
use rustc_hir::{BlockCheckMode, Body, Expr, ExprKind, HirId, LetStmt, PatKind, StmtKind, UnsafeSource};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty::{Ty, TypeVisitableExt};
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
use super::NEEDLESS_TYPE_CAST;
|
||||||
|
|
||||||
|
struct BindingInfo<'a> {
|
||||||
|
source_ty: Ty<'a>,
|
||||||
|
ty_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UsageInfo<'a> {
|
||||||
|
cast_to: Option<Ty<'a>>,
|
||||||
|
in_generic_context: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn check<'a>(cx: &LateContext<'a>, body: &Body<'a>) {
|
||||||
|
let mut bindings: FxHashMap<HirId, BindingInfo<'a>> = FxHashMap::default();
|
||||||
|
|
||||||
|
for_each_expr_without_closures(body.value, |expr| {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Block(block, _) => {
|
||||||
|
for stmt in block.stmts {
|
||||||
|
if let StmtKind::Let(let_stmt) = stmt.kind {
|
||||||
|
collect_binding_from_local(cx, let_stmt, &mut bindings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExprKind::Let(let_expr) => {
|
||||||
|
collect_binding_from_let(cx, let_expr, &mut bindings);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
ControlFlow::<()>::Continue(())
|
||||||
|
});
|
||||||
|
|
||||||
|
#[allow(rustc::potential_query_instability)]
|
||||||
|
let mut binding_vec: Vec<_> = bindings.into_iter().collect();
|
||||||
|
binding_vec.sort_by_key(|(_, info)| info.ty_span.lo());
|
||||||
|
|
||||||
|
for (hir_id, binding_info) in binding_vec {
|
||||||
|
check_binding_usages(cx, body, hir_id, &binding_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_binding_from_let<'a>(
|
||||||
|
cx: &LateContext<'a>,
|
||||||
|
let_expr: &rustc_hir::LetExpr<'a>,
|
||||||
|
bindings: &mut FxHashMap<HirId, BindingInfo<'a>>,
|
||||||
|
) {
|
||||||
|
if let_expr.ty.is_none()
|
||||||
|
|| let_expr.span.from_expansion()
|
||||||
|
|| has_generic_return_type(cx, let_expr.init)
|
||||||
|
|| contains_unsafe(let_expr.init)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let PatKind::Binding(_, hir_id, _, _) = let_expr.pat.kind
|
||||||
|
&& let Some(ty_hir) = let_expr.ty
|
||||||
|
{
|
||||||
|
let ty = cx.typeck_results().pat_ty(let_expr.pat);
|
||||||
|
if ty.is_numeric() {
|
||||||
|
bindings.insert(
|
||||||
|
hir_id,
|
||||||
|
BindingInfo {
|
||||||
|
source_ty: ty,
|
||||||
|
ty_span: ty_hir.span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_binding_from_local<'a>(
|
||||||
|
cx: &LateContext<'a>,
|
||||||
|
let_stmt: &LetStmt<'a>,
|
||||||
|
bindings: &mut FxHashMap<HirId, BindingInfo<'a>>,
|
||||||
|
) {
|
||||||
|
if let_stmt.ty.is_none()
|
||||||
|
|| let_stmt.span.from_expansion()
|
||||||
|
|| let_stmt
|
||||||
|
.init
|
||||||
|
.is_some_and(|init| has_generic_return_type(cx, init) || contains_unsafe(init))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let PatKind::Binding(_, hir_id, _, _) = let_stmt.pat.kind
|
||||||
|
&& let Some(ty_hir) = let_stmt.ty
|
||||||
|
{
|
||||||
|
let ty = cx.typeck_results().pat_ty(let_stmt.pat);
|
||||||
|
if ty.is_numeric() {
|
||||||
|
bindings.insert(
|
||||||
|
hir_id,
|
||||||
|
BindingInfo {
|
||||||
|
source_ty: ty,
|
||||||
|
ty_span: ty_hir.span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_unsafe(expr: &Expr<'_>) -> bool {
|
||||||
|
for_each_expr_without_closures(expr, |e| {
|
||||||
|
if let ExprKind::Block(block, _) = e.kind
|
||||||
|
&& let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules
|
||||||
|
{
|
||||||
|
return ControlFlow::Break(());
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
})
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_generic_return_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
|
match &expr.kind {
|
||||||
|
ExprKind::Block(block, _) => {
|
||||||
|
if let Some(tail_expr) = block.expr {
|
||||||
|
return has_generic_return_type(cx, tail_expr);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
ExprKind::If(_, then_block, else_expr) => {
|
||||||
|
has_generic_return_type(cx, then_block) || else_expr.is_some_and(|e| has_generic_return_type(cx, e))
|
||||||
|
},
|
||||||
|
ExprKind::Match(_, arms, _) => arms.iter().any(|arm| has_generic_return_type(cx, arm.body)),
|
||||||
|
ExprKind::Loop(block, label, ..) => for_each_expr_without_closures(*block, |e| {
|
||||||
|
match e.kind {
|
||||||
|
ExprKind::Loop(..) => {
|
||||||
|
// Unlabeled breaks inside nested loops target the inner loop, not ours
|
||||||
|
return ControlFlow::Continue(Descend::No);
|
||||||
|
},
|
||||||
|
ExprKind::Break(dest, Some(break_expr)) => {
|
||||||
|
let targets_this_loop =
|
||||||
|
dest.label.is_none() || dest.label.map(|l| l.ident) == label.map(|l| l.ident);
|
||||||
|
if targets_this_loop && has_generic_return_type(cx, break_expr) {
|
||||||
|
return ControlFlow::Break(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(Descend::Yes)
|
||||||
|
})
|
||||||
|
.is_some(),
|
||||||
|
ExprKind::MethodCall(..) => {
|
||||||
|
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||||
|
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
|
||||||
|
let ret_ty = sig.output().skip_binder();
|
||||||
|
return ret_ty.has_param();
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
ExprKind::Call(callee, _) => {
|
||||||
|
if let ExprKind::Path(qpath) = &callee.kind {
|
||||||
|
let res = cx.qpath_res(qpath, callee.hir_id);
|
||||||
|
if let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) = res {
|
||||||
|
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
|
||||||
|
let ret_ty = sig.output().skip_binder();
|
||||||
|
return ret_ty.has_param();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_generic_res(cx: &LateContext<'_>, res: Res) -> bool {
|
||||||
|
let has_type_params = |def_id| {
|
||||||
|
cx.tcx
|
||||||
|
.generics_of(def_id)
|
||||||
|
.own_params
|
||||||
|
.iter()
|
||||||
|
.any(|p| p.kind.is_ty_or_const())
|
||||||
|
};
|
||||||
|
match res {
|
||||||
|
Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => has_type_params(def_id),
|
||||||
|
// Ctor → Variant → ADT: constructor's parent is variant, variant's parent is the ADT
|
||||||
|
Res::Def(DefKind::Ctor(..), def_id) => has_type_params(cx.tcx.parent(cx.tcx.parent(def_id))),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_cast_in_generic_context<'a>(cx: &LateContext<'a>, cast_expr: &Expr<'a>) -> bool {
|
||||||
|
let mut current_id = cast_expr.hir_id;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let parent_id = cx.tcx.parent_hir_id(current_id);
|
||||||
|
if parent_id == current_id {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent = cx.tcx.hir_node(parent_id);
|
||||||
|
|
||||||
|
match parent {
|
||||||
|
rustc_hir::Node::Expr(parent_expr) => {
|
||||||
|
match &parent_expr.kind {
|
||||||
|
ExprKind::Closure(_) => return false,
|
||||||
|
ExprKind::Call(callee, _) => {
|
||||||
|
if let ExprKind::Path(qpath) = &callee.kind {
|
||||||
|
let res = cx.qpath_res(qpath, callee.hir_id);
|
||||||
|
if is_generic_res(cx, res) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExprKind::MethodCall(..) => {
|
||||||
|
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id)
|
||||||
|
&& cx
|
||||||
|
.tcx
|
||||||
|
.generics_of(def_id)
|
||||||
|
.own_params
|
||||||
|
.iter()
|
||||||
|
.any(|p| p.kind.is_ty_or_const())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
current_id = parent_id;
|
||||||
|
},
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_binding_usages<'a>(cx: &LateContext<'a>, body: &Body<'a>, hir_id: HirId, binding_info: &BindingInfo<'a>) {
|
||||||
|
let mut usages = Vec::new();
|
||||||
|
|
||||||
|
for_each_expr(cx, body.value, |expr| {
|
||||||
|
if let ExprKind::Path(ref qpath) = expr.kind
|
||||||
|
&& !expr.span.from_expansion()
|
||||||
|
&& let Res::Local(id) = cx.qpath_res(qpath, expr.hir_id)
|
||||||
|
&& id == hir_id
|
||||||
|
{
|
||||||
|
let parent_id = cx.tcx.parent_hir_id(expr.hir_id);
|
||||||
|
let parent = cx.tcx.hir_node(parent_id);
|
||||||
|
|
||||||
|
let usage = if let rustc_hir::Node::Expr(parent_expr) = parent
|
||||||
|
&& let ExprKind::Cast(..) = parent_expr.kind
|
||||||
|
&& !parent_expr.span.from_expansion()
|
||||||
|
{
|
||||||
|
UsageInfo {
|
||||||
|
cast_to: Some(cx.typeck_results().expr_ty(parent_expr)),
|
||||||
|
in_generic_context: is_cast_in_generic_context(cx, parent_expr),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
UsageInfo {
|
||||||
|
cast_to: None,
|
||||||
|
in_generic_context: false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
usages.push(usage);
|
||||||
|
}
|
||||||
|
ControlFlow::<()>::Continue(())
|
||||||
|
});
|
||||||
|
|
||||||
|
let Some(first_target) = usages
|
||||||
|
.first()
|
||||||
|
.and_then(|u| u.cast_to)
|
||||||
|
.filter(|&t| t != binding_info.source_ty)
|
||||||
|
.filter(|&t| usages.iter().all(|u| u.cast_to == Some(t) && !u.in_generic_context))
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
NEEDLESS_TYPE_CAST,
|
||||||
|
binding_info.ty_span,
|
||||||
|
format!(
|
||||||
|
"this binding is defined as `{}` but is always cast to `{}`",
|
||||||
|
binding_info.source_ty, first_target
|
||||||
|
),
|
||||||
|
"consider defining it as",
|
||||||
|
first_target.to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -70,6 +70,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
||||||
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
|
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
|
||||||
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
|
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
|
||||||
crate::casts::MANUAL_DANGLING_PTR_INFO,
|
crate::casts::MANUAL_DANGLING_PTR_INFO,
|
||||||
|
crate::casts::NEEDLESS_TYPE_CAST_INFO,
|
||||||
crate::casts::PTR_AS_PTR_INFO,
|
crate::casts::PTR_AS_PTR_INFO,
|
||||||
crate::casts::PTR_CAST_CONSTNESS_INFO,
|
crate::casts::PTR_CAST_CONSTNESS_INFO,
|
||||||
crate::casts::REF_AS_PTR_INFO,
|
crate::casts::REF_AS_PTR_INFO,
|
||||||
|
|
@ -245,8 +246,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
||||||
crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
|
crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
|
||||||
crate::large_stack_frames::LARGE_STACK_FRAMES_INFO,
|
crate::large_stack_frames::LARGE_STACK_FRAMES_INFO,
|
||||||
crate::legacy_numeric_constants::LEGACY_NUMERIC_CONSTANTS_INFO,
|
crate::legacy_numeric_constants::LEGACY_NUMERIC_CONSTANTS_INFO,
|
||||||
|
crate::len_without_is_empty::LEN_WITHOUT_IS_EMPTY_INFO,
|
||||||
crate::len_zero::COMPARISON_TO_EMPTY_INFO,
|
crate::len_zero::COMPARISON_TO_EMPTY_INFO,
|
||||||
crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
|
|
||||||
crate::len_zero::LEN_ZERO_INFO,
|
crate::len_zero::LEN_ZERO_INFO,
|
||||||
crate::let_if_seq::USELESS_LET_IF_SEQ_INFO,
|
crate::let_if_seq::USELESS_LET_IF_SEQ_INFO,
|
||||||
crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO,
|
crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO,
|
||||||
|
|
@ -300,6 +301,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
||||||
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
|
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
|
||||||
crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
|
crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
|
||||||
crate::manual_ignore_case_cmp::MANUAL_IGNORE_CASE_CMP_INFO,
|
crate::manual_ignore_case_cmp::MANUAL_IGNORE_CASE_CMP_INFO,
|
||||||
|
crate::manual_ilog2::MANUAL_ILOG2_INFO,
|
||||||
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
|
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
|
||||||
crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO,
|
crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO,
|
||||||
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
|
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
|
||||||
|
|
@ -444,6 +446,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
||||||
crate::methods::OR_THEN_UNWRAP_INFO,
|
crate::methods::OR_THEN_UNWRAP_INFO,
|
||||||
crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
|
crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
|
||||||
crate::methods::PATH_ENDS_WITH_EXT_INFO,
|
crate::methods::PATH_ENDS_WITH_EXT_INFO,
|
||||||
|
crate::methods::PTR_OFFSET_BY_LITERAL_INFO,
|
||||||
crate::methods::PTR_OFFSET_WITH_CAST_INFO,
|
crate::methods::PTR_OFFSET_WITH_CAST_INFO,
|
||||||
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
|
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
|
||||||
crate::methods::READONLY_WRITE_LOCK_INFO,
|
crate::methods::READONLY_WRITE_LOCK_INFO,
|
||||||
|
|
@ -578,6 +581,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
||||||
crate::operators::ASSIGN_OP_PATTERN_INFO,
|
crate::operators::ASSIGN_OP_PATTERN_INFO,
|
||||||
crate::operators::BAD_BIT_MASK_INFO,
|
crate::operators::BAD_BIT_MASK_INFO,
|
||||||
crate::operators::CMP_OWNED_INFO,
|
crate::operators::CMP_OWNED_INFO,
|
||||||
|
crate::operators::DECIMAL_BITWISE_OPERANDS_INFO,
|
||||||
crate::operators::DOUBLE_COMPARISONS_INFO,
|
crate::operators::DOUBLE_COMPARISONS_INFO,
|
||||||
crate::operators::DURATION_SUBSEC_INFO,
|
crate::operators::DURATION_SUBSEC_INFO,
|
||||||
crate::operators::EQ_OP_INFO,
|
crate::operators::EQ_OP_INFO,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [
|
||||||
#[clippy::version = "pre 1.29.0"]
|
#[clippy::version = "pre 1.29.0"]
|
||||||
("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"),
|
("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"),
|
||||||
#[clippy::version = "1.91.0"]
|
#[clippy::version = "1.91.0"]
|
||||||
("clippy::string_to_string", "`clippy:implicit_clone` covers those cases"),
|
("clippy::string_to_string", "`clippy::implicit_clone` covers those cases"),
|
||||||
#[clippy::version = "pre 1.29.0"]
|
#[clippy::version = "pre 1.29.0"]
|
||||||
("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"),
|
("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"),
|
||||||
#[clippy::version = "pre 1.29.0"]
|
#[clippy::version = "pre 1.29.0"]
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_appl
|
||||||
use clippy_utils::ty::is_copy;
|
use clippy_utils::ty::is_copy;
|
||||||
use clippy_utils::visitors::for_each_expr;
|
use clippy_utils::visitors::for_each_expr;
|
||||||
use clippy_utils::{
|
use clippy_utils::{
|
||||||
SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified,
|
SpanlessEq, can_move_expr_to_closure_no_visit, desugar_await, higher, is_expr_final_block_expr,
|
||||||
peel_hir_expr_while,
|
is_expr_used_or_unified, paths, peel_hir_expr_while,
|
||||||
};
|
};
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::hir_id::HirIdSet;
|
use rustc_hir::hir_id::HirIdSet;
|
||||||
use rustc_hir::intravisit::{Visitor, walk_body, walk_expr};
|
use rustc_hir::intravisit::{Visitor, walk_body, walk_expr};
|
||||||
use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp};
|
use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp};
|
||||||
|
|
@ -382,6 +383,8 @@ struct InsertSearcher<'cx, 'tcx> {
|
||||||
loops: Vec<HirId>,
|
loops: Vec<HirId>,
|
||||||
/// Local variables created in the expression. These don't need to be captured.
|
/// Local variables created in the expression. These don't need to be captured.
|
||||||
locals: HirIdSet,
|
locals: HirIdSet,
|
||||||
|
/// Whether the map is a non-async-aware `MutexGuard`.
|
||||||
|
map_is_mutex_guard: bool,
|
||||||
}
|
}
|
||||||
impl<'tcx> InsertSearcher<'_, 'tcx> {
|
impl<'tcx> InsertSearcher<'_, 'tcx> {
|
||||||
/// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
|
/// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
|
||||||
|
|
@ -524,15 +527,22 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
||||||
ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
|
ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
|
||||||
self.is_single_insert = false;
|
self.is_single_insert = false;
|
||||||
self.visit_non_tail_expr(cond_expr);
|
self.visit_non_tail_expr(cond_expr);
|
||||||
// Each branch may contain it's own insert expression.
|
// Each branch may contain its own insert expression.
|
||||||
let mut is_map_used = self.visit_cond_arm(then_expr);
|
let mut is_map_used = self.visit_cond_arm(then_expr);
|
||||||
is_map_used |= self.visit_cond_arm(else_expr);
|
is_map_used |= self.visit_cond_arm(else_expr);
|
||||||
self.is_map_used = is_map_used;
|
self.is_map_used = is_map_used;
|
||||||
},
|
},
|
||||||
ExprKind::Match(scrutinee_expr, arms, _) => {
|
ExprKind::Match(scrutinee_expr, arms, _) => {
|
||||||
|
// If the map is a non-async-aware `MutexGuard` and
|
||||||
|
// `.await` expression appears alongside map insertion in the same `then` or `else` block,
|
||||||
|
// we cannot suggest using `entry()` because it would hold the lock across the await point,
|
||||||
|
// triggering `await_holding_lock` and risking deadlock.
|
||||||
|
if self.map_is_mutex_guard && desugar_await(expr).is_some() {
|
||||||
|
self.can_use_entry = false;
|
||||||
|
}
|
||||||
self.is_single_insert = false;
|
self.is_single_insert = false;
|
||||||
self.visit_non_tail_expr(scrutinee_expr);
|
self.visit_non_tail_expr(scrutinee_expr);
|
||||||
// Each branch may contain it's own insert expression.
|
// Each branch may contain its own insert expression.
|
||||||
let mut is_map_used = self.is_map_used;
|
let mut is_map_used = self.is_map_used;
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
self.visit_pat(arm.pat);
|
self.visit_pat(arm.pat);
|
||||||
|
|
@ -725,16 +735,32 @@ fn find_insert_calls<'tcx>(
|
||||||
edits: Vec::new(),
|
edits: Vec::new(),
|
||||||
loops: Vec::new(),
|
loops: Vec::new(),
|
||||||
locals: HirIdSet::default(),
|
locals: HirIdSet::default(),
|
||||||
|
map_is_mutex_guard: false,
|
||||||
};
|
};
|
||||||
|
// Check if the map is a non-async-aware `MutexGuard`
|
||||||
|
if let rustc_middle::ty::Adt(adt, _) = cx.typeck_results().expr_ty(contains_expr.map).kind()
|
||||||
|
&& is_mutex_guard(cx, adt.did())
|
||||||
|
{
|
||||||
|
s.map_is_mutex_guard = true;
|
||||||
|
}
|
||||||
|
|
||||||
s.visit_expr(expr);
|
s.visit_expr(expr);
|
||||||
let allow_insert_closure = s.allow_insert_closure;
|
if !s.can_use_entry {
|
||||||
let is_single_insert = s.is_single_insert;
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let is_key_used_and_no_copy = s.is_key_used && !is_copy(cx, cx.typeck_results().expr_ty(contains_expr.key));
|
let is_key_used_and_no_copy = s.is_key_used && !is_copy(cx, cx.typeck_results().expr_ty(contains_expr.key));
|
||||||
let edits = s.edits;
|
Some(InsertSearchResults {
|
||||||
s.can_use_entry.then_some(InsertSearchResults {
|
edits: s.edits,
|
||||||
edits,
|
allow_insert_closure: s.allow_insert_closure,
|
||||||
allow_insert_closure,
|
is_single_insert: s.is_single_insert,
|
||||||
is_single_insert,
|
|
||||||
is_key_used_and_no_copy,
|
is_key_used_and_no_copy,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||||
|
match cx.tcx.get_diagnostic_name(def_id) {
|
||||||
|
Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard),
|
||||||
|
None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,16 @@ use std::{fmt, ops};
|
||||||
|
|
||||||
use clippy_config::Conf;
|
use clippy_config::Conf;
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::fn_has_unsatisfiable_preds;
|
use clippy_utils::source::{HasSession, SpanRangeExt};
|
||||||
use clippy_utils::source::SpanRangeExt;
|
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_in_test};
|
||||||
|
use rustc_errors::Diag;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_hir::{Body, FnDecl};
|
use rustc_hir::{Body, FnDecl};
|
||||||
use rustc_lexer::is_ident;
|
use rustc_lexer::is_ident;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::Span;
|
use rustc_span::{Span, SyntaxContext};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
|
@ -83,12 +84,14 @@ declare_clippy_lint! {
|
||||||
|
|
||||||
pub struct LargeStackFrames {
|
pub struct LargeStackFrames {
|
||||||
maximum_allowed_size: u64,
|
maximum_allowed_size: u64,
|
||||||
|
allow_large_stack_frames_in_tests: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LargeStackFrames {
|
impl LargeStackFrames {
|
||||||
pub fn new(conf: &'static Conf) -> Self {
|
pub fn new(conf: &'static Conf) -> Self {
|
||||||
Self {
|
Self {
|
||||||
maximum_allowed_size: conf.stack_size_threshold,
|
maximum_allowed_size: conf.stack_size_threshold,
|
||||||
|
allow_large_stack_frames_in_tests: conf.allow_large_stack_frames_in_tests,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,67 +155,122 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
|
||||||
let mir = cx.tcx.optimized_mir(def_id);
|
let mir = cx.tcx.optimized_mir(def_id);
|
||||||
let typing_env = mir.typing_env(cx.tcx);
|
let typing_env = mir.typing_env(cx.tcx);
|
||||||
|
|
||||||
let sizes_of_locals = || {
|
let sizes_of_locals = mir
|
||||||
mir.local_decls.iter().filter_map(|local| {
|
.local_decls
|
||||||
|
.iter()
|
||||||
|
.filter_map(|local| {
|
||||||
let layout = cx.tcx.layout_of(typing_env.as_query_input(local.ty)).ok()?;
|
let layout = cx.tcx.layout_of(typing_env.as_query_input(local.ty)).ok()?;
|
||||||
Some((local, layout.size.bytes()))
|
Some((local, layout.size.bytes()))
|
||||||
})
|
})
|
||||||
};
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let frame_size = sizes_of_locals().fold(Space::Used(0), |sum, (_, size)| sum + size);
|
let frame_size = sizes_of_locals
|
||||||
|
.iter()
|
||||||
|
.fold(Space::Used(0), |sum, (_, size)| sum + *size);
|
||||||
|
|
||||||
let limit = self.maximum_allowed_size;
|
let limit = self.maximum_allowed_size;
|
||||||
if frame_size.exceeds_limit(limit) {
|
if frame_size.exceeds_limit(limit) {
|
||||||
// Point at just the function name if possible, because lints that span
|
// Point at just the function name if possible, because lints that span
|
||||||
// the entire body and don't have to are less legible.
|
// the entire body and don't have to are less legible.
|
||||||
let fn_span = match fn_kind {
|
let (fn_span, fn_name) = match fn_kind {
|
||||||
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
|
FnKind::ItemFn(ident, _, _) => (ident.span, format!("function `{}`", ident.name)),
|
||||||
FnKind::Closure => entire_fn_span,
|
FnKind::Method(ident, _) => (ident.span, format!("method `{}`", ident.name)),
|
||||||
|
FnKind::Closure => (entire_fn_span, "closure".to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Don't lint inside tests if configured to not do so.
|
||||||
|
if self.allow_large_stack_frames_in_tests && is_in_test(cx.tcx, cx.tcx.local_def_id_to_hir_id(local_def_id))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let explain_lint = |diag: &mut Diag<'_, ()>, ctxt: SyntaxContext| {
|
||||||
|
// Point out the largest individual contribution to this size, because
|
||||||
|
// it is the most likely to be unintentionally large.
|
||||||
|
if let Some((local, size)) = sizes_of_locals.iter().max_by_key(|&(_, size)| size)
|
||||||
|
&& let local_span = local.source_info.span
|
||||||
|
&& local_span.ctxt() == ctxt
|
||||||
|
{
|
||||||
|
let size = Space::Used(*size); // pluralizes for us
|
||||||
|
let ty = local.ty;
|
||||||
|
|
||||||
|
// TODO: Is there a cleaner, robust way to ask this question?
|
||||||
|
// The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data",
|
||||||
|
// and that doesn't get us the true name in scope rather than the span text either.
|
||||||
|
if let Some(name) = local_span.get_source_text(cx)
|
||||||
|
&& is_ident(&name)
|
||||||
|
{
|
||||||
|
// If the local is an ordinary named variable,
|
||||||
|
// print its name rather than relying solely on the span.
|
||||||
|
diag.span_label(
|
||||||
|
local_span,
|
||||||
|
format!("`{name}` is the largest part, at {size} for type `{ty}`"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
diag.span_label(
|
||||||
|
local_span,
|
||||||
|
format!("this is the largest part, at {size} for type `{ty}`"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explain why we are linting this and not other functions.
|
||||||
|
diag.note(format!(
|
||||||
|
"{frame_size} is larger than Clippy's configured `stack-size-threshold` of {limit}"
|
||||||
|
));
|
||||||
|
|
||||||
|
// Explain why the user should care, briefly.
|
||||||
|
diag.note_once(
|
||||||
|
"allocating large amounts of stack space can overflow the stack \
|
||||||
|
and cause the program to abort",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if fn_span.from_expansion() {
|
||||||
|
// Don't lint on the main function generated by `--test` target
|
||||||
|
if cx.sess().is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_from_external_macro = fn_span.in_external_macro(cx.sess().source_map());
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
LARGE_STACK_FRAMES,
|
||||||
|
fn_span.source_callsite(),
|
||||||
|
format!(
|
||||||
|
"{} generated by this macro may allocate a lot of stack space",
|
||||||
|
if is_from_external_macro {
|
||||||
|
cx.tcx.def_descr(local_def_id.into())
|
||||||
|
} else {
|
||||||
|
fn_name.as_str()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|diag| {
|
||||||
|
if is_from_external_macro {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
diag.span_label(
|
||||||
|
fn_span,
|
||||||
|
format!(
|
||||||
|
"this {} has a stack frame size of {frame_size}",
|
||||||
|
cx.tcx.def_descr(local_def_id.into())
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
explain_lint(diag, fn_span.ctxt());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
LARGE_STACK_FRAMES,
|
LARGE_STACK_FRAMES,
|
||||||
fn_span,
|
fn_span,
|
||||||
format!("this function may allocate {frame_size} on the stack"),
|
format!("this function may allocate {frame_size} on the stack"),
|
||||||
|diag| {
|
|diag| {
|
||||||
// Point out the largest individual contribution to this size, because
|
explain_lint(diag, SyntaxContext::root());
|
||||||
// it is the most likely to be unintentionally large.
|
|
||||||
if let Some((local, size)) = sizes_of_locals().max_by_key(|&(_, size)| size) {
|
|
||||||
let local_span: Span = local.source_info.span;
|
|
||||||
let size = Space::Used(size); // pluralizes for us
|
|
||||||
let ty = local.ty;
|
|
||||||
|
|
||||||
// TODO: Is there a cleaner, robust way to ask this question?
|
|
||||||
// The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data",
|
|
||||||
// and that doesn't get us the true name in scope rather than the span text either.
|
|
||||||
if let Some(name) = local_span.get_source_text(cx)
|
|
||||||
&& is_ident(&name)
|
|
||||||
{
|
|
||||||
// If the local is an ordinary named variable,
|
|
||||||
// print its name rather than relying solely on the span.
|
|
||||||
diag.span_label(
|
|
||||||
local_span,
|
|
||||||
format!("`{name}` is the largest part, at {size} for type `{ty}`"),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
diag.span_label(
|
|
||||||
local_span,
|
|
||||||
format!("this is the largest part, at {size} for type `{ty}`"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explain why we are linting this and not other functions.
|
|
||||||
diag.note(format!(
|
|
||||||
"{frame_size} is larger than Clippy's configured `stack-size-threshold` of {limit}"
|
|
||||||
));
|
|
||||||
|
|
||||||
// Explain why the user should care, briefly.
|
|
||||||
diag.note_once(
|
|
||||||
"allocating large amounts of stack space can overflow the stack \
|
|
||||||
and cause the program to abort",
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
342
clippy_lints/src/len_without_is_empty.rs
Normal file
342
clippy_lints/src/len_without_is_empty.rs
Normal file
|
|
@ -0,0 +1,342 @@
|
||||||
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||||
|
use clippy_utils::res::MaybeDef;
|
||||||
|
use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, sym};
|
||||||
|
use rustc_hir::def::Res;
|
||||||
|
use rustc_hir::def_id::{DefId, DefIdSet};
|
||||||
|
use rustc_hir::{
|
||||||
|
FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind, ImplicitSelfKind, Item, ItemKind, Mutability,
|
||||||
|
Node, OpaqueTyOrigin, PathSegment, PrimTy, QPath, TraitItemId, TyKind,
|
||||||
|
};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::ty::{self, FnSig, Ty};
|
||||||
|
use rustc_session::declare_lint_pass;
|
||||||
|
use rustc_span::symbol::kw;
|
||||||
|
use rustc_span::{Ident, Span, Symbol};
|
||||||
|
use rustc_trait_selection::traits::supertrait_def_ids;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for items that implement `.len()` but not
|
||||||
|
/// `.is_empty()`.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// It is good custom to have both methods, because for
|
||||||
|
/// some data structures, asking about the length will be a costly operation,
|
||||||
|
/// whereas `.is_empty()` can usually answer in constant time. Also it used to
|
||||||
|
/// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
|
||||||
|
/// lint will ignore such entities.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```ignore
|
||||||
|
/// impl X {
|
||||||
|
/// pub fn len(&self) -> usize {
|
||||||
|
/// ..
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "pre 1.29.0"]
|
||||||
|
pub LEN_WITHOUT_IS_EMPTY,
|
||||||
|
style,
|
||||||
|
"traits or impls with a public `len` method but no corresponding `is_empty` method"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(LenWithoutIsEmpty => [LEN_WITHOUT_IS_EMPTY]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for LenWithoutIsEmpty {
|
||||||
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||||
|
if let ItemKind::Trait(_, _, _, ident, _, _, trait_items) = item.kind
|
||||||
|
&& !item.span.from_expansion()
|
||||||
|
{
|
||||||
|
check_trait_items(cx, item, ident, trait_items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
|
||||||
|
if item.ident.name == sym::len
|
||||||
|
&& let ImplItemKind::Fn(sig, _) = &item.kind
|
||||||
|
&& sig.decl.implicit_self.has_implicit_self()
|
||||||
|
&& sig.decl.inputs.len() == 1
|
||||||
|
&& cx.effective_visibilities.is_exported(item.owner_id.def_id)
|
||||||
|
&& matches!(sig.decl.output, FnRetTy::Return(_))
|
||||||
|
&& let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id())
|
||||||
|
&& imp.of_trait.is_none()
|
||||||
|
&& let TyKind::Path(ty_path) = &imp.self_ty.kind
|
||||||
|
&& let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id()
|
||||||
|
&& let Some(local_id) = ty_id.as_local()
|
||||||
|
&& let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_id)
|
||||||
|
&& let Some(output) = LenOutput::new(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder())
|
||||||
|
{
|
||||||
|
let (name, kind) = match cx.tcx.hir_node(ty_hir_id) {
|
||||||
|
Node::ForeignItem(x) => (x.ident.name, "extern type"),
|
||||||
|
Node::Item(x) => match x.kind {
|
||||||
|
ItemKind::Struct(ident, ..) => (ident.name, "struct"),
|
||||||
|
ItemKind::Enum(ident, ..) => (ident.name, "enum"),
|
||||||
|
ItemKind::Union(ident, ..) => (ident.name, "union"),
|
||||||
|
_ => (x.kind.ident().unwrap().name, "type"),
|
||||||
|
},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
check_for_is_empty(
|
||||||
|
cx,
|
||||||
|
sig.span,
|
||||||
|
sig.decl.implicit_self,
|
||||||
|
output,
|
||||||
|
ty_id,
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
item.hir_id(),
|
||||||
|
ty_hir_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Ident, trait_items: &[TraitItemId]) {
|
||||||
|
fn is_named_self(cx: &LateContext<'_>, item: TraitItemId, name: Symbol) -> bool {
|
||||||
|
cx.tcx.item_name(item.owner_id) == name
|
||||||
|
&& matches!(
|
||||||
|
cx.tcx.fn_arg_idents(item.owner_id),
|
||||||
|
[Some(Ident {
|
||||||
|
name: kw::SelfLower,
|
||||||
|
..
|
||||||
|
})],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the set with current and super traits
|
||||||
|
fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
|
||||||
|
if set.insert(traitt) {
|
||||||
|
for supertrait in supertrait_def_ids(cx.tcx, traitt) {
|
||||||
|
fill_trait_set(supertrait, set, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cx.effective_visibilities.is_exported(visited_trait.owner_id.def_id)
|
||||||
|
&& trait_items.iter().any(|&i| is_named_self(cx, i, sym::len))
|
||||||
|
{
|
||||||
|
let mut current_and_super_traits = DefIdSet::default();
|
||||||
|
fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx);
|
||||||
|
let is_empty_method_found = current_and_super_traits
|
||||||
|
.items()
|
||||||
|
.flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(sym::is_empty))
|
||||||
|
.any(|i| i.is_method() && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1);
|
||||||
|
|
||||||
|
if !is_empty_method_found {
|
||||||
|
span_lint(
|
||||||
|
cx,
|
||||||
|
LEN_WITHOUT_IS_EMPTY,
|
||||||
|
visited_trait.span,
|
||||||
|
format!(
|
||||||
|
"trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
|
||||||
|
ident.name
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
||||||
|
if let ty::Alias(_, alias_ty) = ty.kind()
|
||||||
|
&& let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(alias_ty.def_id)
|
||||||
|
&& let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin
|
||||||
|
&& let [GenericBound::Trait(trait_ref)] = &opaque.bounds
|
||||||
|
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
|
||||||
|
&& let Some(generic_args) = segment.args
|
||||||
|
&& let [constraint] = generic_args.constraints
|
||||||
|
&& let Some(ty) = constraint.ty()
|
||||||
|
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
|
||||||
|
&& let [segment] = path.segments
|
||||||
|
{
|
||||||
|
return Some(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
|
||||||
|
if let Some(generic_args) = segment.args
|
||||||
|
&& let [GenericArg::Type(ty), ..] = &generic_args.args
|
||||||
|
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
|
||||||
|
&& let [segment, ..] = &path.segments
|
||||||
|
&& matches!(segment.res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_)))
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum LenOutput {
|
||||||
|
Integral,
|
||||||
|
Option(DefId),
|
||||||
|
Result(DefId),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LenOutput {
|
||||||
|
fn new<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<Self> {
|
||||||
|
if let Some(segment) = extract_future_output(cx, sig.output()) {
|
||||||
|
let res = segment.res;
|
||||||
|
|
||||||
|
if matches!(res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) {
|
||||||
|
return Some(Self::Integral);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Res::Def(_, def_id) = res
|
||||||
|
&& let Some(res) = match cx.tcx.get_diagnostic_name(def_id) {
|
||||||
|
Some(sym::Option) => Some(Self::Option(def_id)),
|
||||||
|
Some(sym::Result) => Some(Self::Result(def_id)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
&& is_first_generic_integral(segment)
|
||||||
|
{
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match *sig.output().kind() {
|
||||||
|
ty::Int(_) | ty::Uint(_) => Some(Self::Integral),
|
||||||
|
ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) {
|
||||||
|
Some(sym::Option) => subs.type_at(0).is_integral().then(|| Self::Option(adt.did())),
|
||||||
|
Some(sym::Result) => subs.type_at(0).is_integral().then(|| Self::Result(adt.did())),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, is_empty_output: Ty<'tcx>) -> bool {
|
||||||
|
if let Some(segment) = extract_future_output(cx, is_empty_output) {
|
||||||
|
return match (self, segment.res) {
|
||||||
|
(_, Res::PrimTy(PrimTy::Bool)) => true,
|
||||||
|
(Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true,
|
||||||
|
(Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
match (self, is_empty_output.kind()) {
|
||||||
|
(_, &ty::Bool) => true,
|
||||||
|
(Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
|
||||||
|
(Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The expected signature of `is_empty`, based on that of `len`
|
||||||
|
fn expected_is_empty_sig(len_output: LenOutput, len_self_kind: ImplicitSelfKind) -> String {
|
||||||
|
let self_ref = match len_self_kind {
|
||||||
|
ImplicitSelfKind::RefImm => "&",
|
||||||
|
ImplicitSelfKind::RefMut => "&(mut) ",
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
match len_output {
|
||||||
|
LenOutput::Integral => format!("expected signature: `({self_ref}self) -> bool`"),
|
||||||
|
LenOutput::Option(_) => {
|
||||||
|
format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option<bool>")
|
||||||
|
},
|
||||||
|
LenOutput::Result(..) => {
|
||||||
|
format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result<bool>")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given signature matches the expectations for `is_empty`
|
||||||
|
fn check_is_empty_sig<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
is_empty_sig: FnSig<'tcx>,
|
||||||
|
len_self_kind: ImplicitSelfKind,
|
||||||
|
len_output: LenOutput,
|
||||||
|
) -> bool {
|
||||||
|
if let [is_empty_self_arg, is_empty_output] = &**is_empty_sig.inputs_and_output
|
||||||
|
&& len_output.matches_is_empty_output(cx, *is_empty_output)
|
||||||
|
{
|
||||||
|
match (is_empty_self_arg.kind(), len_self_kind) {
|
||||||
|
// if `len` takes `&self`, `is_empty` should do so as well
|
||||||
|
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm)
|
||||||
|
// if `len` takes `&mut self`, `is_empty` may take that _or_ `&self` (#16190)
|
||||||
|
| (ty::Ref(_, _, Mutability::Mut | Mutability::Not), ImplicitSelfKind::RefMut) => true,
|
||||||
|
// if len takes `self`, `is_empty` should do so as well
|
||||||
|
// XXX: we might want to relax this to allow `&self` and `&mut self`
|
||||||
|
(_, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut) if !is_empty_self_arg.is_ref() => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given type has an `is_empty` method with the appropriate signature.
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
fn check_for_is_empty(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
len_span: Span,
|
||||||
|
len_self_kind: ImplicitSelfKind,
|
||||||
|
len_output: LenOutput,
|
||||||
|
impl_ty: DefId,
|
||||||
|
item_name: Symbol,
|
||||||
|
item_kind: &str,
|
||||||
|
len_method_hir_id: HirId,
|
||||||
|
ty_decl_hir_id: HirId,
|
||||||
|
) {
|
||||||
|
// Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to
|
||||||
|
// find the correct inherent impls.
|
||||||
|
let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() {
|
||||||
|
adt.did()
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_empty = cx
|
||||||
|
.tcx
|
||||||
|
.inherent_impls(impl_ty)
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty))
|
||||||
|
.find(|item| item.is_fn());
|
||||||
|
|
||||||
|
let (msg, is_empty_span, is_empty_expected_sig) = match is_empty {
|
||||||
|
None => (
|
||||||
|
format!("{item_kind} `{item_name}` has a public `len` method, but no `is_empty` method"),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Some(is_empty) if !cx.effective_visibilities.is_exported(is_empty.def_id.expect_local()) => (
|
||||||
|
format!("{item_kind} `{item_name}` has a public `len` method, but a private `is_empty` method"),
|
||||||
|
Some(cx.tcx.def_span(is_empty.def_id)),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
Some(is_empty)
|
||||||
|
if !(is_empty.is_method()
|
||||||
|
&& check_is_empty_sig(
|
||||||
|
cx,
|
||||||
|
cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(),
|
||||||
|
len_self_kind,
|
||||||
|
len_output,
|
||||||
|
)) =>
|
||||||
|
{
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"{item_kind} `{item_name}` has a public `len` method, but the `is_empty` method has an unexpected signature",
|
||||||
|
),
|
||||||
|
Some(cx.tcx.def_span(is_empty.def_id)),
|
||||||
|
Some(expected_is_empty_sig(len_output, len_self_kind)),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Some(_) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !fulfill_or_allowed(cx, LEN_WITHOUT_IS_EMPTY, [len_method_hir_id, ty_decl_hir_id]) {
|
||||||
|
span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, len_span, msg, |db| {
|
||||||
|
if let Some(span) = is_empty_span {
|
||||||
|
db.span_note(span, "`is_empty` defined here");
|
||||||
|
}
|
||||||
|
if let Some(expected_sig) = is_empty_expected_sig {
|
||||||
|
db.note(expected_sig);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,27 +1,20 @@
|
||||||
use clippy_config::Conf;
|
use clippy_config::Conf;
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::msrvs::Msrv;
|
use clippy_utils::msrvs::Msrv;
|
||||||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||||
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
|
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
|
||||||
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
|
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::ty::implements_trait;
|
||||||
use clippy_utils::{fulfill_or_allowed, get_parent_as_impl, parent_item_name, peel_ref_operators, sym};
|
use clippy_utils::{parent_item_name, peel_ref_operators, sym};
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::def_id::{DefId, DefIdSet};
|
use rustc_hir::{BinOpKind, Expr, ExprKind, PatExprKind, PatKind, RustcVersion, StabilityLevel, StableSince};
|
||||||
use rustc_hir::{
|
|
||||||
BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind, ImplicitSelfKind,
|
|
||||||
Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy, QPath, RustcVersion,
|
|
||||||
StabilityLevel, StableSince, TraitItemId, TyKind,
|
|
||||||
};
|
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{self, FnSig, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::source_map::Spanned;
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::symbol::kw;
|
use rustc_span::{Span, Symbol};
|
||||||
use rustc_span::{Ident, Span, Symbol};
|
|
||||||
use rustc_trait_selection::traits::supertrait_def_ids;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
|
@ -58,32 +51,6 @@ declare_clippy_lint! {
|
||||||
"checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
|
"checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
|
||||||
/// ### What it does
|
|
||||||
/// Checks for items that implement `.len()` but not
|
|
||||||
/// `.is_empty()`.
|
|
||||||
///
|
|
||||||
/// ### Why is this bad?
|
|
||||||
/// It is good custom to have both methods, because for
|
|
||||||
/// some data structures, asking about the length will be a costly operation,
|
|
||||||
/// whereas `.is_empty()` can usually answer in constant time. Also it used to
|
|
||||||
/// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
|
|
||||||
/// lint will ignore such entities.
|
|
||||||
///
|
|
||||||
/// ### Example
|
|
||||||
/// ```ignore
|
|
||||||
/// impl X {
|
|
||||||
/// pub fn len(&self) -> usize {
|
|
||||||
/// ..
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[clippy::version = "pre 1.29.0"]
|
|
||||||
pub LEN_WITHOUT_IS_EMPTY,
|
|
||||||
style,
|
|
||||||
"traits or impls with a public `len` method but no corresponding `is_empty` method"
|
|
||||||
}
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for comparing to an empty slice such as `""` or `[]`,
|
/// Checks for comparing to an empty slice such as `""` or `[]`,
|
||||||
|
|
@ -126,7 +93,7 @@ pub struct LenZero {
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
|
impl_lint_pass!(LenZero => [LEN_ZERO, COMPARISON_TO_EMPTY]);
|
||||||
|
|
||||||
impl LenZero {
|
impl LenZero {
|
||||||
pub fn new(conf: &'static Conf) -> Self {
|
pub fn new(conf: &'static Conf) -> Self {
|
||||||
|
|
@ -135,54 +102,6 @@ impl LenZero {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for LenZero {
|
impl<'tcx> LateLintPass<'tcx> for LenZero {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
|
||||||
if let ItemKind::Trait(_, _, _, ident, _, _, trait_items) = item.kind
|
|
||||||
&& !item.span.from_expansion()
|
|
||||||
{
|
|
||||||
check_trait_items(cx, item, ident, trait_items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
|
|
||||||
if item.ident.name == sym::len
|
|
||||||
&& let ImplItemKind::Fn(sig, _) = &item.kind
|
|
||||||
&& sig.decl.implicit_self.has_implicit_self()
|
|
||||||
&& sig.decl.inputs.len() == 1
|
|
||||||
&& cx.effective_visibilities.is_exported(item.owner_id.def_id)
|
|
||||||
&& matches!(sig.decl.output, FnRetTy::Return(_))
|
|
||||||
&& let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id())
|
|
||||||
&& imp.of_trait.is_none()
|
|
||||||
&& let TyKind::Path(ty_path) = &imp.self_ty.kind
|
|
||||||
&& let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id()
|
|
||||||
&& let Some(local_id) = ty_id.as_local()
|
|
||||||
&& let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_id)
|
|
||||||
&& let Some(output) =
|
|
||||||
parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder())
|
|
||||||
{
|
|
||||||
let (name, kind) = match cx.tcx.hir_node(ty_hir_id) {
|
|
||||||
Node::ForeignItem(x) => (x.ident.name, "extern type"),
|
|
||||||
Node::Item(x) => match x.kind {
|
|
||||||
ItemKind::Struct(ident, ..) => (ident.name, "struct"),
|
|
||||||
ItemKind::Enum(ident, ..) => (ident.name, "enum"),
|
|
||||||
ItemKind::Union(ident, ..) => (ident.name, "union"),
|
|
||||||
_ => (x.kind.ident().unwrap().name, "type"),
|
|
||||||
},
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
check_for_is_empty(
|
|
||||||
cx,
|
|
||||||
sig.span,
|
|
||||||
sig.decl.implicit_self,
|
|
||||||
output,
|
|
||||||
ty_id,
|
|
||||||
name,
|
|
||||||
kind,
|
|
||||||
item.hir_id(),
|
|
||||||
ty_hir_id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if let ExprKind::Let(lt) = expr.kind
|
if let ExprKind::Let(lt) = expr.kind
|
||||||
&& match lt.pat.kind {
|
&& match lt.pat.kind {
|
||||||
|
|
@ -356,256 +275,6 @@ fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Ident, trait_items: &[TraitItemId]) {
|
|
||||||
fn is_named_self(cx: &LateContext<'_>, item: TraitItemId, name: Symbol) -> bool {
|
|
||||||
cx.tcx.item_name(item.owner_id) == name
|
|
||||||
&& matches!(
|
|
||||||
cx.tcx.fn_arg_idents(item.owner_id),
|
|
||||||
[Some(Ident {
|
|
||||||
name: kw::SelfLower,
|
|
||||||
..
|
|
||||||
})],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill the set with current and super traits
|
|
||||||
fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
|
|
||||||
if set.insert(traitt) {
|
|
||||||
for supertrait in supertrait_def_ids(cx.tcx, traitt) {
|
|
||||||
fill_trait_set(supertrait, set, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cx.effective_visibilities.is_exported(visited_trait.owner_id.def_id)
|
|
||||||
&& trait_items.iter().any(|&i| is_named_self(cx, i, sym::len))
|
|
||||||
{
|
|
||||||
let mut current_and_super_traits = DefIdSet::default();
|
|
||||||
fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx);
|
|
||||||
let is_empty_method_found = current_and_super_traits
|
|
||||||
.items()
|
|
||||||
.flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(sym::is_empty))
|
|
||||||
.any(|i| i.is_method() && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1);
|
|
||||||
|
|
||||||
if !is_empty_method_found {
|
|
||||||
span_lint(
|
|
||||||
cx,
|
|
||||||
LEN_WITHOUT_IS_EMPTY,
|
|
||||||
visited_trait.span,
|
|
||||||
format!(
|
|
||||||
"trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
|
|
||||||
ident.name
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
enum LenOutput {
|
|
||||||
Integral,
|
|
||||||
Option(DefId),
|
|
||||||
Result(DefId),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
|
||||||
if let ty::Alias(_, alias_ty) = ty.kind()
|
|
||||||
&& let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir_get_if_local(alias_ty.def_id)
|
|
||||||
&& let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin
|
|
||||||
&& let [GenericBound::Trait(trait_ref)] = &opaque.bounds
|
|
||||||
&& let Some(segment) = trait_ref.trait_ref.path.segments.last()
|
|
||||||
&& let Some(generic_args) = segment.args
|
|
||||||
&& let [constraint] = generic_args.constraints
|
|
||||||
&& let Some(ty) = constraint.ty()
|
|
||||||
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
|
|
||||||
&& let [segment] = path.segments
|
|
||||||
{
|
|
||||||
return Some(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
|
|
||||||
if let Some(generic_args) = segment.args
|
|
||||||
&& let [GenericArg::Type(ty), ..] = &generic_args.args
|
|
||||||
&& let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
|
|
||||||
&& let [segment, ..] = &path.segments
|
|
||||||
&& matches!(segment.res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_)))
|
|
||||||
{
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<LenOutput> {
|
|
||||||
if let Some(segment) = extract_future_output(cx, sig.output()) {
|
|
||||||
let res = segment.res;
|
|
||||||
|
|
||||||
if matches!(res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) {
|
|
||||||
return Some(LenOutput::Integral);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Res::Def(_, def_id) = res
|
|
||||||
&& let Some(res) = match cx.tcx.get_diagnostic_name(def_id) {
|
|
||||||
Some(sym::Option) => Some(LenOutput::Option(def_id)),
|
|
||||||
Some(sym::Result) => Some(LenOutput::Result(def_id)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
&& is_first_generic_integral(segment)
|
|
||||||
{
|
|
||||||
return Some(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match *sig.output().kind() {
|
|
||||||
ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
|
|
||||||
ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) {
|
|
||||||
Some(sym::Option) => subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())),
|
|
||||||
Some(sym::Result) => subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LenOutput {
|
|
||||||
fn matches_is_empty_output<'tcx>(self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
|
||||||
if let Some(segment) = extract_future_output(cx, ty) {
|
|
||||||
return match (self, segment.res) {
|
|
||||||
(_, Res::PrimTy(PrimTy::Bool)) => true,
|
|
||||||
(Self::Option(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Option, def_id) => true,
|
|
||||||
(Self::Result(_), Res::Def(_, def_id)) if cx.tcx.is_diagnostic_item(sym::Result, def_id) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match (self, ty.kind()) {
|
|
||||||
(_, &ty::Bool) => true,
|
|
||||||
(Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
|
|
||||||
(Self::Result(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
|
|
||||||
let self_ref = match self_kind {
|
|
||||||
ImplicitSelfKind::RefImm => "&",
|
|
||||||
ImplicitSelfKind::RefMut => "&mut ",
|
|
||||||
_ => "",
|
|
||||||
};
|
|
||||||
match self {
|
|
||||||
Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"),
|
|
||||||
Self::Option(_) => {
|
|
||||||
format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option<bool>")
|
|
||||||
},
|
|
||||||
Self::Result(..) => {
|
|
||||||
format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result<bool>")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the given signature matches the expectations for `is_empty`
|
|
||||||
fn check_is_empty_sig<'tcx>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
sig: FnSig<'tcx>,
|
|
||||||
self_kind: ImplicitSelfKind,
|
|
||||||
len_output: LenOutput,
|
|
||||||
) -> bool {
|
|
||||||
match &**sig.inputs_and_output {
|
|
||||||
[arg, res] if len_output.matches_is_empty_output(cx, *res) => {
|
|
||||||
matches!(
|
|
||||||
(arg.kind(), self_kind),
|
|
||||||
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::RefImm)
|
|
||||||
| (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::RefMut)
|
|
||||||
) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut))
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the given type has an `is_empty` method with the appropriate signature.
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
fn check_for_is_empty(
|
|
||||||
cx: &LateContext<'_>,
|
|
||||||
span: Span,
|
|
||||||
self_kind: ImplicitSelfKind,
|
|
||||||
output: LenOutput,
|
|
||||||
impl_ty: DefId,
|
|
||||||
item_name: Symbol,
|
|
||||||
item_kind: &str,
|
|
||||||
len_method_hir_id: HirId,
|
|
||||||
ty_decl_hir_id: HirId,
|
|
||||||
) {
|
|
||||||
// Implementor may be a type alias, in which case we need to get the `DefId` of the aliased type to
|
|
||||||
// find the correct inherent impls.
|
|
||||||
let impl_ty = if let Some(adt) = cx.tcx.type_of(impl_ty).skip_binder().ty_adt_def() {
|
|
||||||
adt.did()
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_empty = cx
|
|
||||||
.tcx
|
|
||||||
.inherent_impls(impl_ty)
|
|
||||||
.iter()
|
|
||||||
.flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(sym::is_empty))
|
|
||||||
.find(|item| item.is_fn());
|
|
||||||
|
|
||||||
let (msg, is_empty_span, self_kind) = match is_empty {
|
|
||||||
None => (
|
|
||||||
format!(
|
|
||||||
"{item_kind} `{}` has a public `len` method, but no `is_empty` method",
|
|
||||||
item_name.as_str(),
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
Some(is_empty) if !cx.effective_visibilities.is_exported(is_empty.def_id.expect_local()) => (
|
|
||||||
format!(
|
|
||||||
"{item_kind} `{}` has a public `len` method, but a private `is_empty` method",
|
|
||||||
item_name.as_str(),
|
|
||||||
),
|
|
||||||
Some(cx.tcx.def_span(is_empty.def_id)),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
Some(is_empty)
|
|
||||||
if !(is_empty.is_method()
|
|
||||||
&& check_is_empty_sig(
|
|
||||||
cx,
|
|
||||||
cx.tcx.fn_sig(is_empty.def_id).instantiate_identity().skip_binder(),
|
|
||||||
self_kind,
|
|
||||||
output,
|
|
||||||
)) =>
|
|
||||||
{
|
|
||||||
(
|
|
||||||
format!(
|
|
||||||
"{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
|
|
||||||
item_name.as_str(),
|
|
||||||
),
|
|
||||||
Some(cx.tcx.def_span(is_empty.def_id)),
|
|
||||||
Some(self_kind),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
Some(_) => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !fulfill_or_allowed(cx, LEN_WITHOUT_IS_EMPTY, [len_method_hir_id, ty_decl_hir_id]) {
|
|
||||||
span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, msg, |db| {
|
|
||||||
if let Some(span) = is_empty_span {
|
|
||||||
db.span_note(span, "`is_empty` defined here");
|
|
||||||
}
|
|
||||||
if let Some(self_kind) = self_kind {
|
|
||||||
db.note(output.expected_sig(self_kind));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty_string(expr: &Expr<'_>) -> bool {
|
fn is_empty_string(expr: &Expr<'_>) -> bool {
|
||||||
if let ExprKind::Lit(lit) = expr.kind
|
if let ExprKind::Lit(lit) = expr.kind
|
||||||
&& let LitKind::Str(lit, _) = lit.node
|
&& let LitKind::Str(lit, _) = lit.node
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,7 @@ mod large_include_file;
|
||||||
mod large_stack_arrays;
|
mod large_stack_arrays;
|
||||||
mod large_stack_frames;
|
mod large_stack_frames;
|
||||||
mod legacy_numeric_constants;
|
mod legacy_numeric_constants;
|
||||||
|
mod len_without_is_empty;
|
||||||
mod len_zero;
|
mod len_zero;
|
||||||
mod let_if_seq;
|
mod let_if_seq;
|
||||||
mod let_underscore;
|
mod let_underscore;
|
||||||
|
|
@ -201,6 +202,7 @@ mod manual_clamp;
|
||||||
mod manual_float_methods;
|
mod manual_float_methods;
|
||||||
mod manual_hash_one;
|
mod manual_hash_one;
|
||||||
mod manual_ignore_case_cmp;
|
mod manual_ignore_case_cmp;
|
||||||
|
mod manual_ilog2;
|
||||||
mod manual_is_ascii_check;
|
mod manual_is_ascii_check;
|
||||||
mod manual_is_power_of_two;
|
mod manual_is_power_of_two;
|
||||||
mod manual_let_else;
|
mod manual_let_else;
|
||||||
|
|
@ -538,6 +540,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
||||||
Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)),
|
Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)),
|
||||||
Box::new(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()),
|
Box::new(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()),
|
||||||
Box::new(move |_| Box::new(len_zero::LenZero::new(conf))),
|
Box::new(move |_| Box::new(len_zero::LenZero::new(conf))),
|
||||||
|
Box::new(|_| Box::new(len_without_is_empty::LenWithoutIsEmpty)),
|
||||||
Box::new(move |_| Box::new(attrs::Attributes::new(conf))),
|
Box::new(move |_| Box::new(attrs::Attributes::new(conf))),
|
||||||
Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)),
|
Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)),
|
||||||
Box::new(|_| Box::new(unicode::Unicode)),
|
Box::new(|_| Box::new(unicode::Unicode)),
|
||||||
|
|
@ -848,6 +851,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
||||||
Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)),
|
Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)),
|
||||||
Box::new(|_| Box::new(volatile_composites::VolatileComposites)),
|
Box::new(|_| Box::new(volatile_composites::VolatileComposites)),
|
||||||
Box::new(|_| Box::<replace_box::ReplaceBox>::default()),
|
Box::new(|_| Box::<replace_box::ReplaceBox>::default()),
|
||||||
|
Box::new(move |_| Box::new(manual_ilog2::ManualIlog2::new(conf))),
|
||||||
// add late passes here, used by `cargo dev new_lint`
|
// add late passes here, used by `cargo dev new_lint`
|
||||||
];
|
];
|
||||||
store.late_passes.extend(late_lints);
|
store.late_passes.extend(late_lints);
|
||||||
|
|
|
||||||
|
|
@ -43,26 +43,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
|
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
|
||||||
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
|
// passed by reference. TODO: If the struct can be partially moved from and the struct isn't used
|
||||||
// afterwards a mutable borrow of a field isn't necessary.
|
// afterwards a mutable borrow of a field isn't necessary.
|
||||||
let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
|
let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
|
||||||
|
let iterator_by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
|
||||||
|| !iter_expr_struct.can_move
|
|| !iter_expr_struct.can_move
|
||||||
|| !iter_expr_struct.fields.is_empty()
|
|| !iter_expr_struct.fields.is_empty()
|
||||||
|| needs_mutable_borrow(cx, &iter_expr_struct, expr)
|
|| needs_mutable_borrow(cx, &iter_expr_struct, expr)
|
||||||
{
|
{
|
||||||
".by_ref()"
|
make_iterator_snippet(cx, iter_expr, &iterator)
|
||||||
} else {
|
} else {
|
||||||
""
|
iterator.into_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
WHILE_LET_ON_ITERATOR,
|
WHILE_LET_ON_ITERATOR,
|
||||||
expr.span.with_hi(let_expr.span.hi()),
|
expr.span.with_hi(let_expr.span.hi()),
|
||||||
"this loop could be written as a `for` loop",
|
"this loop could be written as a `for` loop",
|
||||||
"try",
|
"try",
|
||||||
format!("{loop_label}for {loop_var} in {iterator}{by_ref}"),
|
format!("{loop_label}for {loop_var} in {iterator_by_ref}"),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -355,3 +355,22 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
|
||||||
.is_break()
|
.is_break()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs the transformed iterator expression for the suggestion.
|
||||||
|
/// Returns `iterator.by_ref()` unless the last deref adjustment targets an unsized type,
|
||||||
|
/// in which case it applies all derefs (e.g., `&mut **iterator` or `&mut ***iterator`).
|
||||||
|
fn make_iterator_snippet<'tcx>(cx: &LateContext<'tcx>, iter_expr: &Expr<'tcx>, iterator: &str) -> String {
|
||||||
|
if let Some((n, adjust)) = cx
|
||||||
|
.typeck_results()
|
||||||
|
.expr_adjustments(iter_expr)
|
||||||
|
.iter()
|
||||||
|
.take_while(|x| matches!(x.kind, Adjust::Deref(_)))
|
||||||
|
.enumerate()
|
||||||
|
.last()
|
||||||
|
&& !adjust.target.is_sized(cx.tcx, cx.typing_env())
|
||||||
|
{
|
||||||
|
format!("&mut {:*<n$}{iterator}", '*', n = n + 1)
|
||||||
|
} else {
|
||||||
|
format!("{iterator}.by_ref()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
115
clippy_lints/src/manual_ilog2.rs
Normal file
115
clippy_lints/src/manual_ilog2.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
use clippy_config::Conf;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
|
use clippy_utils::{is_from_proc_macro, sym};
|
||||||
|
use rustc_ast::LitKind;
|
||||||
|
use rustc_data_structures::packed::Pu128;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::ty;
|
||||||
|
use rustc_session::impl_lint_pass;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for expressions like `N - x.leading_zeros()` (where `N` is one less than bit width
|
||||||
|
/// of `x`) or `x.ilog(2)`, which are manual reimplementations of `x.ilog2()`
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Manual reimplementations of `ilog2` increase code complexity for little benefit.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// let x: u32 = 5;
|
||||||
|
/// let log = 31 - x.leading_zeros();
|
||||||
|
/// let log = x.ilog(2);
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```no_run
|
||||||
|
/// let x: u32 = 5;
|
||||||
|
/// let log = x.ilog2();
|
||||||
|
/// let log = x.ilog2();
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.93.0"]
|
||||||
|
pub MANUAL_ILOG2,
|
||||||
|
pedantic,
|
||||||
|
"manually reimplementing `ilog2`"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ManualIlog2 {
|
||||||
|
msrv: Msrv,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManualIlog2 {
|
||||||
|
pub fn new(conf: &Conf) -> Self {
|
||||||
|
Self { msrv: conf.msrv }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(ManualIlog2 => [MANUAL_ILOG2]);
|
||||||
|
|
||||||
|
impl LateLintPass<'_> for ManualIlog2 {
|
||||||
|
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||||
|
if expr.span.in_external_macro(cx.sess().source_map()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match expr.kind {
|
||||||
|
// `BIT_WIDTH - 1 - n.leading_zeros()`
|
||||||
|
ExprKind::Binary(op, left, right)
|
||||||
|
if left.span.eq_ctxt(right.span)
|
||||||
|
&& op.node == BinOpKind::Sub
|
||||||
|
&& let ExprKind::Lit(lit) = left.kind
|
||||||
|
&& let LitKind::Int(Pu128(val), _) = lit.node
|
||||||
|
&& let ExprKind::MethodCall(leading_zeros, recv, [], _) = right.kind
|
||||||
|
&& leading_zeros.ident.name == sym::leading_zeros
|
||||||
|
&& let ty = cx.typeck_results().expr_ty(recv)
|
||||||
|
&& let Some(bit_width) = match ty.kind() {
|
||||||
|
ty::Uint(uint_ty) => uint_ty.bit_width(),
|
||||||
|
ty::Int(_) => {
|
||||||
|
// On non-positive integers, `ilog2` would panic, which might be a sign that the author does
|
||||||
|
// in fact want to calculate something different, so stay on the safer side and don't
|
||||||
|
// suggest anything.
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
_ => return,
|
||||||
|
}
|
||||||
|
&& val == u128::from(bit_width) - 1
|
||||||
|
&& self.msrv.meets(cx, msrvs::ILOG2)
|
||||||
|
&& !is_from_proc_macro(cx, expr) =>
|
||||||
|
{
|
||||||
|
emit(cx, recv, expr);
|
||||||
|
},
|
||||||
|
|
||||||
|
// `n.ilog(2)`
|
||||||
|
ExprKind::MethodCall(ilog, recv, [two], _)
|
||||||
|
if expr.span.eq_ctxt(two.span)
|
||||||
|
&& ilog.ident.name == sym::ilog
|
||||||
|
&& let ExprKind::Lit(lit) = two.kind
|
||||||
|
&& let LitKind::Int(Pu128(2), _) = lit.node
|
||||||
|
&& cx.typeck_results().expr_ty_adjusted(recv).is_integral()
|
||||||
|
/* no need to check MSRV here, as `ilog` and `ilog2` were introduced simultaneously */
|
||||||
|
&& !is_from_proc_macro(cx, expr) =>
|
||||||
|
{
|
||||||
|
emit(cx, recv, expr);
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit(cx: &LateContext<'_>, recv: &Expr<'_>, full_expr: &Expr<'_>) {
|
||||||
|
let mut app = Applicability::MachineApplicable;
|
||||||
|
let recv = snippet_with_applicability(cx, recv.span, "_", &mut app);
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
MANUAL_ILOG2,
|
||||||
|
full_expr.span,
|
||||||
|
"manually reimplementing `ilog2`",
|
||||||
|
"try",
|
||||||
|
format!("{recv}.ilog2()"),
|
||||||
|
app,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
//! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
|
//! Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
|
||||||
|
|
||||||
use super::REDUNDANT_PATTERN_MATCHING;
|
use super::REDUNDANT_PATTERN_MATCHING;
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::higher::has_let_expr;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment};
|
use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment};
|
||||||
use rustc_ast::LitKind;
|
use rustc_ast::LitKind;
|
||||||
|
|
@ -43,18 +44,23 @@ pub(crate) fn check_if_let<'tcx>(
|
||||||
{
|
{
|
||||||
ex_new = ex_inner;
|
ex_new = ex_inner;
|
||||||
}
|
}
|
||||||
span_lint_and_sugg(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
MATCH_LIKE_MATCHES_MACRO,
|
MATCH_LIKE_MATCHES_MACRO,
|
||||||
expr.span,
|
expr.span,
|
||||||
"if let .. else expression looks like `matches!` macro",
|
"`if let .. else` expression looks like `matches!` macro",
|
||||||
"try",
|
|diag| {
|
||||||
format!(
|
diag.span_suggestion_verbose(
|
||||||
"{}matches!({}, {pat})",
|
expr.span,
|
||||||
if b0 { "" } else { "!" },
|
"use `matches!` directly",
|
||||||
snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
|
format!(
|
||||||
),
|
"{}matches!({}, {pat})",
|
||||||
applicability,
|
if b0 { "" } else { "!" },
|
||||||
|
snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
|
||||||
|
),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +93,10 @@ pub(super) fn check_match<'tcx>(
|
||||||
// ```rs
|
// ```rs
|
||||||
// matches!(e, Either::Left $(if $guard)|+)
|
// matches!(e, Either::Left $(if $guard)|+)
|
||||||
// ```
|
// ```
|
||||||
middle_arms.is_empty()
|
//
|
||||||
|
// But if the guard _is_ present, it may not be an `if-let` guard, as `matches!` doesn't
|
||||||
|
// support these (currently?)
|
||||||
|
(middle_arms.is_empty() && first_arm.guard.is_none_or(|g| !has_let_expr(g)))
|
||||||
|
|
||||||
// - (added in #6216) There are middle arms
|
// - (added in #6216) There are middle arms
|
||||||
//
|
//
|
||||||
|
|
@ -169,18 +178,23 @@ pub(super) fn check_match<'tcx>(
|
||||||
{
|
{
|
||||||
ex_new = ex_inner;
|
ex_new = ex_inner;
|
||||||
}
|
}
|
||||||
span_lint_and_sugg(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
MATCH_LIKE_MATCHES_MACRO,
|
MATCH_LIKE_MATCHES_MACRO,
|
||||||
e.span,
|
e.span,
|
||||||
"match expression looks like `matches!` macro",
|
"match expression looks like `matches!` macro",
|
||||||
"try",
|
|diag| {
|
||||||
format!(
|
diag.span_suggestion_verbose(
|
||||||
"{}matches!({}, {pat_and_guard})",
|
e.span,
|
||||||
if b0 { "" } else { "!" },
|
"use `matches!` directly",
|
||||||
snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
|
format!(
|
||||||
),
|
"{}matches!({}, {pat_and_guard})",
|
||||||
applicability,
|
if b0 { "" } else { "!" },
|
||||||
|
snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
|
||||||
|
),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,20 @@ use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::sym;
|
use clippy_utils::sym;
|
||||||
use rustc_ast::ast;
|
use rustc_ast::ast;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
|
use rustc_hir::{self as hir, Expr};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
|
use rustc_span::Symbol;
|
||||||
|
|
||||||
pub fn check(
|
pub fn check_unwrap_or(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
expr: &hir::Expr<'_>,
|
expr: &Expr<'_>,
|
||||||
arith_lhs: &hir::Expr<'_>,
|
arith_lhs: &Expr<'_>,
|
||||||
arith_rhs: &hir::Expr<'_>,
|
arith_rhs: &Expr<'_>,
|
||||||
unwrap_arg: &hir::Expr<'_>,
|
unwrap_arg: &Expr<'_>,
|
||||||
arith: &str,
|
arith: Symbol,
|
||||||
) {
|
) {
|
||||||
let ty = cx.typeck_results().expr_ty(arith_lhs);
|
let ty = cx.typeck_results().expr_ty(arith_lhs);
|
||||||
if !ty.is_integral() {
|
if !ty.is_integral() {
|
||||||
|
|
@ -26,35 +28,75 @@ pub fn check(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if ty.is_signed() {
|
let Some(checked_arith) = CheckedArith::new(arith) else {
|
||||||
use self::MinMax::{Max, Min};
|
return;
|
||||||
use self::Sign::{Neg, Pos};
|
};
|
||||||
|
|
||||||
|
check(cx, expr, arith_lhs, arith_rhs, ty, mm, checked_arith);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn check_sub_unwrap_or_default(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
arith_lhs: &Expr<'_>,
|
||||||
|
arith_rhs: &Expr<'_>,
|
||||||
|
) {
|
||||||
|
let ty = cx.typeck_results().expr_ty(arith_lhs);
|
||||||
|
if !ty.is_integral() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mm = if ty.is_signed() {
|
||||||
|
return; // iN::default() is 0, which is neither MIN nor MAX
|
||||||
|
} else {
|
||||||
|
MinMax::Min // uN::default() is 0, which is also the MIN
|
||||||
|
};
|
||||||
|
|
||||||
|
let checked_arith = CheckedArith::Sub;
|
||||||
|
|
||||||
|
check(cx, expr, arith_lhs, arith_rhs, ty, mm, checked_arith);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
arith_lhs: &Expr<'_>,
|
||||||
|
arith_rhs: &Expr<'_>,
|
||||||
|
ty: Ty<'_>,
|
||||||
|
mm: MinMax,
|
||||||
|
checked_arith: CheckedArith,
|
||||||
|
) {
|
||||||
|
use self::MinMax::{Max, Min};
|
||||||
|
use self::Sign::{Neg, Pos};
|
||||||
|
use CheckedArith::{Add, Mul, Sub};
|
||||||
|
|
||||||
|
if ty.is_signed() {
|
||||||
let Some(sign) = lit_sign(arith_rhs) else {
|
let Some(sign) = lit_sign(arith_rhs) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match (arith, sign, mm) {
|
match (checked_arith, sign, mm) {
|
||||||
("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
|
(Add, Pos, Max) | (Add, Neg, Min) | (Sub, Neg, Max) | (Sub, Pos, Min) => (),
|
||||||
// "mul" is omitted because lhs can be negative.
|
// "mul" is omitted because lhs can be negative.
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match (mm, arith) {
|
match (mm, checked_arith) {
|
||||||
(MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
|
(Max, Add | Mul) | (Min, Sub) => (),
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
let saturating_arith = checked_arith.as_saturating();
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
super::MANUAL_SATURATING_ARITHMETIC,
|
super::MANUAL_SATURATING_ARITHMETIC,
|
||||||
expr.span,
|
expr.span,
|
||||||
"manual saturating arithmetic",
|
"manual saturating arithmetic",
|
||||||
format!("consider using `saturating_{arith}`"),
|
format!("consider using `{saturating_arith}`"),
|
||||||
format!(
|
format!(
|
||||||
"{}.saturating_{arith}({})",
|
"{}.{saturating_arith}({})",
|
||||||
snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
|
snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
|
||||||
snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
|
snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
|
||||||
),
|
),
|
||||||
|
|
@ -62,13 +104,40 @@ pub fn check(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum CheckedArith {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckedArith {
|
||||||
|
fn new(sym: Symbol) -> Option<Self> {
|
||||||
|
let res = match sym {
|
||||||
|
sym::checked_add => Self::Add,
|
||||||
|
sym::checked_sub => Self::Sub,
|
||||||
|
sym::checked_mul => Self::Mul,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_saturating(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Add => "saturating_add",
|
||||||
|
Self::Sub => "saturating_sub",
|
||||||
|
Self::Mul => "saturating_mul",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
enum MinMax {
|
enum MinMax {
|
||||||
Min,
|
Min,
|
||||||
Max,
|
Max,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
|
fn is_min_or_max(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<MinMax> {
|
||||||
// `T::max_value()` `T::min_value()` inherent methods
|
// `T::max_value()` `T::min_value()` inherent methods
|
||||||
if let hir::ExprKind::Call(func, []) = &expr.kind
|
if let hir::ExprKind::Call(func, []) = &expr.kind
|
||||||
&& let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind
|
&& let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind
|
||||||
|
|
@ -106,7 +175,7 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
|
||||||
(0, if bits == 128 { !0 } else { (1 << bits) - 1 })
|
(0, if bits == 128 { !0 } else { (1 << bits) - 1 })
|
||||||
};
|
};
|
||||||
|
|
||||||
let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
|
let check_lit = |expr: &Expr<'_>, check_min: bool| {
|
||||||
if let hir::ExprKind::Lit(lit) = &expr.kind
|
if let hir::ExprKind::Lit(lit) = &expr.kind
|
||||||
&& let ast::LitKind::Int(value, _) = lit.node
|
&& let ast::LitKind::Int(value, _) = lit.node
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +210,7 @@ enum Sign {
|
||||||
Neg,
|
Neg,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
|
fn lit_sign(expr: &Expr<'_>) -> Option<Sign> {
|
||||||
if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
|
if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
|
||||||
if let hir::ExprKind::Lit(..) = &inner.kind {
|
if let hir::ExprKind::Lit(..) = &inner.kind {
|
||||||
return Some(Sign::Neg);
|
return Some(Sign::Neg);
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ mod or_fun_call;
|
||||||
mod or_then_unwrap;
|
mod or_then_unwrap;
|
||||||
mod path_buf_push_overwrite;
|
mod path_buf_push_overwrite;
|
||||||
mod path_ends_with_ext;
|
mod path_ends_with_ext;
|
||||||
|
mod ptr_offset_by_literal;
|
||||||
mod ptr_offset_with_cast;
|
mod ptr_offset_with_cast;
|
||||||
mod range_zip_with_len;
|
mod range_zip_with_len;
|
||||||
mod read_line_without_trim;
|
mod read_line_without_trim;
|
||||||
|
|
@ -1728,6 +1729,40 @@ declare_clippy_lint! {
|
||||||
"Check for offset calculations on raw pointers to zero-sized types"
|
"Check for offset calculations on raw pointers to zero-sized types"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for usage of the `offset` pointer method with an integer
|
||||||
|
/// literal.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// The `add` and `sub` methods more accurately express the intent.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```no_run
|
||||||
|
/// let vec = vec![b'a', b'b', b'c'];
|
||||||
|
/// let ptr = vec.as_ptr();
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// ptr.offset(-8);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Could be written:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// let vec = vec![b'a', b'b', b'c'];
|
||||||
|
/// let ptr = vec.as_ptr();
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// ptr.sub(8);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.92.0"]
|
||||||
|
pub PTR_OFFSET_BY_LITERAL,
|
||||||
|
pedantic,
|
||||||
|
"unneeded pointer offset"
|
||||||
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for usage of the `offset` pointer method with a `usize` casted to an
|
/// Checks for usage of the `offset` pointer method with a `usize` casted to an
|
||||||
|
|
@ -4635,7 +4670,7 @@ declare_clippy_lint! {
|
||||||
/// let x = vec![String::new()];
|
/// let x = vec![String::new()];
|
||||||
/// let _ = x.iter().map(|x| x.len());
|
/// let _ = x.iter().map(|x| x.len());
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.90.0"]
|
#[clippy::version = "1.92.0"]
|
||||||
pub REDUNDANT_ITER_CLONED,
|
pub REDUNDANT_ITER_CLONED,
|
||||||
perf,
|
perf,
|
||||||
"detects redundant calls to `Iterator::cloned`"
|
"detects redundant calls to `Iterator::cloned`"
|
||||||
|
|
@ -4659,7 +4694,7 @@ declare_clippy_lint! {
|
||||||
/// let x: Option<u32> = Some(4);
|
/// let x: Option<u32> = Some(4);
|
||||||
/// let y = x.unwrap_or_else(|| 2 * k);
|
/// let y = x.unwrap_or_else(|| 2 * k);
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.88.0"]
|
#[clippy::version = "1.92.0"]
|
||||||
pub UNNECESSARY_OPTION_MAP_OR_ELSE,
|
pub UNNECESSARY_OPTION_MAP_OR_ELSE,
|
||||||
suspicious,
|
suspicious,
|
||||||
"making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`"
|
"making no use of the \"map closure\" when calling `.map_or_else(|| 2 * k, |n| n)`"
|
||||||
|
|
@ -4803,6 +4838,7 @@ impl_lint_pass!(Methods => [
|
||||||
UNINIT_ASSUMED_INIT,
|
UNINIT_ASSUMED_INIT,
|
||||||
MANUAL_SATURATING_ARITHMETIC,
|
MANUAL_SATURATING_ARITHMETIC,
|
||||||
ZST_OFFSET,
|
ZST_OFFSET,
|
||||||
|
PTR_OFFSET_BY_LITERAL,
|
||||||
PTR_OFFSET_WITH_CAST,
|
PTR_OFFSET_WITH_CAST,
|
||||||
FILETYPE_IS_FILE,
|
FILETYPE_IS_FILE,
|
||||||
OPTION_AS_REF_DEREF,
|
OPTION_AS_REF_DEREF,
|
||||||
|
|
@ -5426,6 +5462,7 @@ impl Methods {
|
||||||
zst_offset::check(cx, expr, recv);
|
zst_offset::check(cx, expr, recv);
|
||||||
|
|
||||||
ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv);
|
ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv);
|
||||||
|
ptr_offset_by_literal::check(cx, expr, self.msrv);
|
||||||
},
|
},
|
||||||
(sym::ok_or_else, [arg]) => {
|
(sym::ok_or_else, [arg]) => {
|
||||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or");
|
unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or");
|
||||||
|
|
@ -5567,14 +5604,7 @@ impl Methods {
|
||||||
(sym::unwrap_or, [u_arg]) => {
|
(sym::unwrap_or, [u_arg]) => {
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => {
|
Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => {
|
||||||
manual_saturating_arithmetic::check(
|
manual_saturating_arithmetic::check_unwrap_or(cx, expr, lhs, rhs, u_arg, arith);
|
||||||
cx,
|
|
||||||
expr,
|
|
||||||
lhs,
|
|
||||||
rhs,
|
|
||||||
u_arg,
|
|
||||||
&arith.as_str()[const { "checked_".len() }..],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
Some((sym::map, m_recv, [m_arg], span, _)) => {
|
Some((sym::map, m_recv, [m_arg], span, _)) => {
|
||||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
|
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
|
||||||
|
|
@ -5595,6 +5625,9 @@ impl Methods {
|
||||||
},
|
},
|
||||||
(sym::unwrap_or_default, []) => {
|
(sym::unwrap_or_default, []) => {
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
|
Some((sym::checked_sub, lhs, [rhs], _, _)) => {
|
||||||
|
manual_saturating_arithmetic::check_sub_unwrap_or_default(cx, expr, lhs, rhs);
|
||||||
|
},
|
||||||
Some((sym::map, m_recv, [arg], span, _)) => {
|
Some((sym::map, m_recv, [arg], span, _)) => {
|
||||||
manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv);
|
manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
138
clippy_lints/src/methods/ptr_offset_by_literal.rs
Normal file
138
clippy_lints/src/methods/ptr_offset_by_literal.rs
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
|
use clippy_utils::source::SpanRangeExt;
|
||||||
|
use clippy_utils::sym;
|
||||||
|
use rustc_ast::LitKind;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::{Expr, ExprKind, Lit, UnOp};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::PTR_OFFSET_BY_LITERAL;
|
||||||
|
|
||||||
|
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Msrv) {
|
||||||
|
// `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions
|
||||||
|
// became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable.
|
||||||
|
if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ExprKind::MethodCall(method_name, recv, [arg_expr], _) = expr.kind else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let method = match method_name.ident.name {
|
||||||
|
sym::offset => Method::Offset,
|
||||||
|
sym::wrapping_offset => Method::WrappingOffset,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the argument to the method call is a (negated) literal.
|
||||||
|
let Some((literal, literal_text)) = expr_as_literal(cx, arg_expr) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match method.suggestion(literal) {
|
||||||
|
None => {
|
||||||
|
let msg = format!("use of `{method}` with zero");
|
||||||
|
span_lint_and_then(cx, PTR_OFFSET_BY_LITERAL, expr.span, msg, |diag| {
|
||||||
|
diag.span_suggestion(
|
||||||
|
expr.span.with_lo(recv.span.hi()),
|
||||||
|
format!("remove the call to `{method}`"),
|
||||||
|
String::new(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Some(method_suggestion) => {
|
||||||
|
let msg = format!("use of `{method}` with a literal");
|
||||||
|
span_lint_and_then(cx, PTR_OFFSET_BY_LITERAL, expr.span, msg, |diag| {
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
format!("use `{method_suggestion}` instead"),
|
||||||
|
vec![
|
||||||
|
(method_name.ident.span, method_suggestion.to_string()),
|
||||||
|
(arg_expr.span, literal_text),
|
||||||
|
],
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_literal_bits<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<u128> {
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Lit(Lit {
|
||||||
|
node: LitKind::Int(packed_u128, _),
|
||||||
|
..
|
||||||
|
}) => Some(packed_u128.get()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the given expression is a (negated) literal, return its value.
|
||||||
|
fn expr_as_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<(i128, String)> {
|
||||||
|
if let Some(literal_bits) = get_literal_bits(expr) {
|
||||||
|
// The value must fit in a isize, so we can't have overflow here.
|
||||||
|
return Some((literal_bits.cast_signed(), format_isize_literal(cx, expr)?));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ExprKind::Unary(UnOp::Neg, inner) = expr.kind
|
||||||
|
&& let Some(literal_bits) = get_literal_bits(inner)
|
||||||
|
{
|
||||||
|
return Some((-(literal_bits.cast_signed()), format_isize_literal(cx, inner)?));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_isize_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<String> {
|
||||||
|
let text = expr.span.get_source_text(cx)?;
|
||||||
|
let text = peel_parens_str(&text);
|
||||||
|
Some(text.trim_end_matches("isize").trim_end_matches('_').to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peel_parens_str(snippet: &str) -> &str {
|
||||||
|
let mut s = snippet.trim();
|
||||||
|
while let Some(next) = s.strip_prefix("(").and_then(|suf| suf.strip_suffix(")")) {
|
||||||
|
s = next.trim();
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum Method {
|
||||||
|
Offset,
|
||||||
|
WrappingOffset,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Method {
|
||||||
|
fn suggestion(self, literal: i128) -> Option<&'static str> {
|
||||||
|
match Ord::cmp(&literal, &0) {
|
||||||
|
Ordering::Greater => match self {
|
||||||
|
Method::Offset => Some("add"),
|
||||||
|
Method::WrappingOffset => Some("wrapping_add"),
|
||||||
|
},
|
||||||
|
// `ptr.offset(0)` is equivalent to `ptr`, so no adjustment is needed
|
||||||
|
Ordering::Equal => None,
|
||||||
|
Ordering::Less => match self {
|
||||||
|
Method::Offset => Some("sub"),
|
||||||
|
Method::WrappingOffset => Some("wrapping_sub"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Method {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Offset => write!(f, "offset"),
|
||||||
|
Self::WrappingOffset => write!(f, "wrapping_offset"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ use clippy_utils::comparisons::{Rel, normalize_comparison};
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::higher::{If, Range};
|
use clippy_utils::higher::{If, Range};
|
||||||
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace, root_macro_call};
|
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace, root_macro_call};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||||
use clippy_utils::{eq_expr_value, hash_expr};
|
use clippy_utils::{eq_expr_value, hash_expr};
|
||||||
use rustc_ast::{BinOpKind, LitKind, RangeLimits};
|
use rustc_ast::{BinOpKind, LitKind, RangeLimits};
|
||||||
|
|
@ -67,16 +67,13 @@ declare_clippy_lint! {
|
||||||
}
|
}
|
||||||
declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]);
|
declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]);
|
||||||
|
|
||||||
fn report_lint<F>(cx: &LateContext<'_>, full_span: Span, msg: &'static str, indexes: &[Span], f: F)
|
fn report_lint<F>(cx: &LateContext<'_>, index_spans: Vec<Span>, msg: &'static str, f: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Diag<'_, ()>),
|
F: FnOnce(&mut Diag<'_, ()>),
|
||||||
{
|
{
|
||||||
span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, full_span, msg, |diag| {
|
span_lint_and_then(cx, MISSING_ASSERTS_FOR_INDEXING, index_spans, msg, |diag| {
|
||||||
f(diag);
|
f(diag);
|
||||||
for span in indexes {
|
diag.note_once("asserting the length before indexing will elide bounds checks");
|
||||||
diag.span_note(*span, "slice indexed here");
|
|
||||||
}
|
|
||||||
diag.note("asserting the length before indexing will elide bounds checks");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,15 +210,6 @@ impl<'hir> IndexEntry<'hir> {
|
||||||
| IndexEntry::IndexWithoutAssert { slice, .. } => slice,
|
| IndexEntry::IndexWithoutAssert { slice, .. } => slice,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_spans(&self) -> Option<&[Span]> {
|
|
||||||
match self {
|
|
||||||
IndexEntry::StrayAssert { .. } => None,
|
|
||||||
IndexEntry::AssertWithIndex { indexes, .. } | IndexEntry::IndexWithoutAssert { indexes, .. } => {
|
|
||||||
Some(indexes)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extracts the upper index of a slice indexing expression.
|
/// Extracts the upper index of a slice indexing expression.
|
||||||
|
|
@ -354,63 +342,47 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
|
||||||
/// Inspects indexes and reports lints.
|
/// Inspects indexes and reports lints.
|
||||||
///
|
///
|
||||||
/// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
|
/// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
|
||||||
fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>>>) {
|
fn report_indexes(cx: &LateContext<'_>, map: UnindexMap<u64, Vec<IndexEntry<'_>>>) {
|
||||||
for bucket in map.values() {
|
for bucket in map.into_values() {
|
||||||
for entry in bucket {
|
for entry in bucket {
|
||||||
let Some(full_span) = entry
|
match entry {
|
||||||
.index_spans()
|
|
||||||
.and_then(|spans| spans.first().zip(spans.last()))
|
|
||||||
.map(|(low, &high)| low.to(high))
|
|
||||||
else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
match *entry {
|
|
||||||
IndexEntry::AssertWithIndex {
|
IndexEntry::AssertWithIndex {
|
||||||
highest_index,
|
highest_index,
|
||||||
is_first_highest,
|
is_first_highest,
|
||||||
asserted_len,
|
asserted_len,
|
||||||
ref indexes,
|
indexes,
|
||||||
comparison,
|
comparison,
|
||||||
assert_span,
|
assert_span,
|
||||||
slice,
|
slice,
|
||||||
macro_call,
|
macro_call,
|
||||||
} if indexes.len() > 1 && !is_first_highest => {
|
} if indexes.len() > 1 && !is_first_highest => {
|
||||||
|
let mut app = Applicability::MachineApplicable;
|
||||||
|
let slice_str = snippet_with_applicability(cx, slice.span, "_", &mut app);
|
||||||
// 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 covers 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`
|
||||||
LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => Some(
|
LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => {
|
||||||
format!("assert!({}.len() > {highest_index})", snippet(cx, slice.span, "..")),
|
Some(format!("assert!({slice_str}.len() > {highest_index})",))
|
||||||
),
|
},
|
||||||
// `5 < v.len()` == `v.len() > 5`
|
// `5 < v.len()` == `v.len() > 5`
|
||||||
LengthComparison::IntLessThanLength if asserted_len < highest_index => Some(format!(
|
LengthComparison::IntLessThanLength if asserted_len < highest_index => {
|
||||||
"assert!({}.len() > {highest_index})",
|
Some(format!("assert!({slice_str}.len() > {highest_index})",))
|
||||||
snippet(cx, slice.span, "..")
|
},
|
||||||
)),
|
|
||||||
// `5 <= v.len() == `v.len() >= 5`
|
// `5 <= v.len() == `v.len() >= 5`
|
||||||
LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => Some(format!(
|
LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => {
|
||||||
"assert!({}.len() > {highest_index})",
|
Some(format!("assert!({slice_str}.len() > {highest_index})",))
|
||||||
snippet(cx, slice.span, "..")
|
},
|
||||||
)),
|
|
||||||
// `highest_index` here is rather a length, so we need to add 1 to it
|
// `highest_index` here is rather a length, so we need to add 1 to it
|
||||||
LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call {
|
LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call {
|
||||||
sym::assert_eq_macro => Some(format!(
|
sym::assert_eq_macro => {
|
||||||
"assert_eq!({}.len(), {})",
|
Some(format!("assert_eq!({slice_str}.len(), {})", highest_index + 1))
|
||||||
snippet(cx, slice.span, ".."),
|
},
|
||||||
highest_index + 1
|
sym::debug_assert_eq_macro => {
|
||||||
)),
|
Some(format!("debug_assert_eq!({slice_str}.len(), {})", highest_index + 1))
|
||||||
sym::debug_assert_eq_macro => Some(format!(
|
},
|
||||||
"debug_assert_eq!({}.len(), {})",
|
_ => Some(format!("assert!({slice_str}.len() == {})", highest_index + 1)),
|
||||||
snippet(cx, slice.span, ".."),
|
|
||||||
highest_index + 1
|
|
||||||
)),
|
|
||||||
_ => Some(format!(
|
|
||||||
"assert!({}.len() == {})",
|
|
||||||
snippet(cx, slice.span, ".."),
|
|
||||||
highest_index + 1
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
@ -418,22 +390,21 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
|
||||||
if let Some(sugg) = sugg {
|
if let Some(sugg) = sugg {
|
||||||
report_lint(
|
report_lint(
|
||||||
cx,
|
cx,
|
||||||
full_span,
|
|
||||||
"indexing into a slice multiple times with an `assert` that does not cover the highest index",
|
|
||||||
indexes,
|
indexes,
|
||||||
|
"indexing into a slice multiple times with an `assert` that does not cover the highest index",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion_verbose(
|
||||||
assert_span,
|
assert_span,
|
||||||
"provide the highest index that is indexed with",
|
"provide the highest index that is indexed with",
|
||||||
sugg,
|
sugg,
|
||||||
Applicability::MachineApplicable,
|
app,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
IndexEntry::IndexWithoutAssert {
|
IndexEntry::IndexWithoutAssert {
|
||||||
ref indexes,
|
indexes,
|
||||||
highest_index,
|
highest_index,
|
||||||
is_first_highest,
|
is_first_highest,
|
||||||
slice,
|
slice,
|
||||||
|
|
@ -442,9 +413,8 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
|
||||||
// adding an `assert!` that covers the highest index
|
// adding an `assert!` that covers the highest index
|
||||||
report_lint(
|
report_lint(
|
||||||
cx,
|
cx,
|
||||||
full_span,
|
|
||||||
"indexing into a slice multiple times without an `assert`",
|
|
||||||
indexes,
|
indexes,
|
||||||
|
"indexing into a slice multiple times without an `assert`",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.help(format!(
|
diag.help(format!(
|
||||||
"consider asserting the length before indexing: `assert!({}.len() > {highest_index});`",
|
"consider asserting the length before indexing: `assert!({}.len() > {highest_index});`",
|
||||||
|
|
@ -469,6 +439,6 @@ impl LateLintPass<'_> for MissingAssertsForIndexing {
|
||||||
ControlFlow::<!, ()>::Continue(())
|
ControlFlow::<!, ()>::Continue(())
|
||||||
});
|
});
|
||||||
|
|
||||||
report_indexes(cx, &map);
|
report_indexes(cx, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,35 +115,41 @@ impl EarlyLintPass for MacroBraces {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBraces) -> Option<MacroInfo> {
|
fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBraces) -> Option<MacroInfo> {
|
||||||
let unnested_or_local = || {
|
let unnested_or_local = |span: Span| {
|
||||||
!span.ctxt().outer_expn_data().call_site.from_expansion()
|
!span.from_expansion()
|
||||||
|| span
|
|| span
|
||||||
.macro_backtrace()
|
.macro_backtrace()
|
||||||
.last()
|
.last()
|
||||||
.is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local))
|
.is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local))
|
||||||
};
|
};
|
||||||
let callsite_span = span.ctxt().outer_expn_data().call_site;
|
|
||||||
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind
|
let mut ctxt = span.ctxt();
|
||||||
|
while !ctxt.is_root() {
|
||||||
|
let expn_data = ctxt.outer_expn_data();
|
||||||
|
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = expn_data.kind
|
||||||
&& let name = mac_name.as_str()
|
&& let name = mac_name.as_str()
|
||||||
&& let Some(&braces) = mac_braces.macro_braces.get(name)
|
&& let Some(&braces) = mac_braces.macro_braces.get(name)
|
||||||
&& let Some(snip) = callsite_span.get_source_text(cx)
|
&& let Some(snip) = expn_data.call_site.get_source_text(cx)
|
||||||
// we must check only invocation sites
|
// we must check only invocation sites
|
||||||
// https://github.com/rust-lang/rust-clippy/issues/7422
|
// https://github.com/rust-lang/rust-clippy/issues/7422
|
||||||
&& let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!'))
|
&& let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!'))
|
||||||
&& let Some(old_open_brace @ ('{' | '(' | '[')) = macro_args_str.trim_start().chars().next()
|
&& let Some(old_open_brace @ ('{' | '(' | '[')) = macro_args_str.trim_start().chars().next()
|
||||||
&& old_open_brace != braces.0
|
&& old_open_brace != braces.0
|
||||||
&& unnested_or_local()
|
&& unnested_or_local(expn_data.call_site)
|
||||||
&& !mac_braces.done.contains(&callsite_span)
|
&& !mac_braces.done.contains(&expn_data.call_site)
|
||||||
{
|
{
|
||||||
Some(MacroInfo {
|
return Some(MacroInfo {
|
||||||
callsite_span,
|
callsite_span: expn_data.call_site,
|
||||||
callsite_snippet: snip,
|
callsite_snippet: snip,
|
||||||
old_open_brace,
|
old_open_brace,
|
||||||
braces,
|
braces,
|
||||||
})
|
});
|
||||||
} else {
|
}
|
||||||
None
|
|
||||||
|
ctxt = expn_data.call_site.ctxt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span, add_semi: bool) {
|
fn emit_help(cx: &EarlyContext<'_>, snip: &str, (open, close): (char, char), span: Span, add_semi: bool) {
|
||||||
|
|
|
||||||
76
clippy_lints/src/operators/decimal_bitwise_operands.rs
Normal file
76
clippy_lints/src/operators/decimal_bitwise_operands.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
|
use clippy_utils::numeric_literal;
|
||||||
|
use clippy_utils::numeric_literal::NumericLiteral;
|
||||||
|
use clippy_utils::source::SpanRangeExt;
|
||||||
|
use rustc_ast::LitKind;
|
||||||
|
use rustc_data_structures::packed::Pu128;
|
||||||
|
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
use super::DECIMAL_BITWISE_OPERANDS;
|
||||||
|
|
||||||
|
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, left: &'tcx Expr<'_>, right: &'tcx Expr<'_>) {
|
||||||
|
if !matches!(op, BinOpKind::BitAnd | BinOpKind::BitOr | BinOpKind::BitXor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for expr in [left, right] {
|
||||||
|
check_expr(cx, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
|
match &expr.kind {
|
||||||
|
ExprKind::Block(block, _) => {
|
||||||
|
if let Some(block_expr) = block.expr {
|
||||||
|
check_expr(cx, block_expr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExprKind::Cast(cast_expr, _) => {
|
||||||
|
check_expr(cx, cast_expr);
|
||||||
|
},
|
||||||
|
ExprKind::Unary(_, unary_expr) => {
|
||||||
|
check_expr(cx, unary_expr);
|
||||||
|
},
|
||||||
|
ExprKind::AddrOf(_, _, addr_of_expr) => {
|
||||||
|
check_expr(cx, addr_of_expr);
|
||||||
|
},
|
||||||
|
ExprKind::Lit(lit) => {
|
||||||
|
if let LitKind::Int(Pu128(val), _) = lit.node
|
||||||
|
&& !is_single_digit(val)
|
||||||
|
&& !is_power_of_twoish(val)
|
||||||
|
&& let Some(src) = lit.span.get_source_text(cx)
|
||||||
|
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
|
||||||
|
&& num_lit.is_decimal()
|
||||||
|
{
|
||||||
|
emit_lint(cx, lit.span, num_lit.suffix, val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_power_of_twoish(val: u128) -> bool {
|
||||||
|
val.is_power_of_two() || val.wrapping_add(1).is_power_of_two()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_single_digit(val: u128) -> bool {
|
||||||
|
val <= 9
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_lint(cx: &LateContext<'_>, span: Span, suffix: Option<&str>, val: u128) {
|
||||||
|
span_lint_and_help(
|
||||||
|
cx,
|
||||||
|
DECIMAL_BITWISE_OPERANDS,
|
||||||
|
span,
|
||||||
|
"using decimal literal for bitwise operation",
|
||||||
|
None,
|
||||||
|
format!(
|
||||||
|
"use binary ({}), hex ({}), or octal ({}) notation for better readability",
|
||||||
|
numeric_literal::format(&format!("{val:#b}"), suffix, false),
|
||||||
|
numeric_literal::format(&format!("{val:#x}"), suffix, false),
|
||||||
|
numeric_literal::format(&format!("{val:#o}"), suffix, false),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ mod assign_op_pattern;
|
||||||
mod bit_mask;
|
mod bit_mask;
|
||||||
mod cmp_owned;
|
mod cmp_owned;
|
||||||
mod const_comparisons;
|
mod const_comparisons;
|
||||||
|
mod decimal_bitwise_operands;
|
||||||
mod double_comparison;
|
mod double_comparison;
|
||||||
mod duration_subsec;
|
mod duration_subsec;
|
||||||
mod eq_op;
|
mod eq_op;
|
||||||
|
|
@ -935,6 +936,28 @@ declare_clippy_lint! {
|
||||||
"use of disallowed default division and remainder operations"
|
"use of disallowed default division and remainder operations"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for decimal literals used as bit masks in bitwise operations.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Using decimal literals for bit masks can make the code less readable and obscure the intended bit pattern.
|
||||||
|
/// Binary, hexadecimal, or octal literals make the bit pattern more explicit and easier to understand at a glance.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// let a = 14 & 6; // Bit pattern is not immediately clear
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// let a = 0b1110 & 0b0110;
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.93.0"]
|
||||||
|
pub DECIMAL_BITWISE_OPERANDS,
|
||||||
|
pedantic,
|
||||||
|
"use binary, hex, or octal literals for bitwise operations"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Operators {
|
pub struct Operators {
|
||||||
arithmetic_context: numeric_arithmetic::Context,
|
arithmetic_context: numeric_arithmetic::Context,
|
||||||
verbose_bit_mask_threshold: u64,
|
verbose_bit_mask_threshold: u64,
|
||||||
|
|
@ -984,6 +1007,7 @@ impl_lint_pass!(Operators => [
|
||||||
MANUAL_IS_MULTIPLE_OF,
|
MANUAL_IS_MULTIPLE_OF,
|
||||||
MANUAL_DIV_CEIL,
|
MANUAL_DIV_CEIL,
|
||||||
INVALID_UPCAST_COMPARISONS,
|
INVALID_UPCAST_COMPARISONS,
|
||||||
|
DECIMAL_BITWISE_OPERANDS
|
||||||
]);
|
]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for Operators {
|
impl<'tcx> LateLintPass<'tcx> for Operators {
|
||||||
|
|
@ -1003,6 +1027,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
|
||||||
needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
|
needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
|
||||||
manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv);
|
manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv);
|
||||||
manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv);
|
manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv);
|
||||||
|
decimal_bitwise_operands::check(cx, op.node, lhs, rhs);
|
||||||
}
|
}
|
||||||
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
|
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
|
||||||
bit_mask::check(cx, e, op.node, lhs, rhs);
|
bit_mask::check(cx, e, op.node, lhs, rhs);
|
||||||
|
|
@ -1028,6 +1053,9 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
|
||||||
},
|
},
|
||||||
ExprKind::AssignOp(op, lhs, rhs) => {
|
ExprKind::AssignOp(op, lhs, rhs) => {
|
||||||
let bin_op = op.node.into();
|
let bin_op = op.node.into();
|
||||||
|
if !e.span.from_expansion() {
|
||||||
|
decimal_bitwise_operands::check(cx, bin_op, lhs, rhs);
|
||||||
|
}
|
||||||
self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs);
|
self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs);
|
||||||
misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs);
|
misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs);
|
||||||
modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false);
|
modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false);
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,11 @@ mod wrong_transmute;
|
||||||
use clippy_config::Conf;
|
use clippy_config::Conf;
|
||||||
use clippy_utils::is_in_const_context;
|
use clippy_utils::is_in_const_context;
|
||||||
use clippy_utils::msrvs::Msrv;
|
use clippy_utils::msrvs::Msrv;
|
||||||
|
use clippy_utils::sugg::Sugg;
|
||||||
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Expr, ExprKind, QPath};
|
use rustc_hir::{Expr, ExprKind, QPath};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
|
|
@ -490,6 +493,32 @@ impl Transmute {
|
||||||
pub fn new(conf: &'static Conf) -> Self {
|
pub fn new(conf: &'static Conf) -> Self {
|
||||||
Self { msrv: conf.msrv }
|
Self { msrv: conf.msrv }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When transmuting, a struct containing a single field works like the field.
|
||||||
|
/// This function extracts the field type and the expression to get the field.
|
||||||
|
fn extract_struct_field<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
e: &'tcx Expr<'_>,
|
||||||
|
outer_type: Ty<'tcx>,
|
||||||
|
outer: &'tcx Expr<'tcx>,
|
||||||
|
) -> (Ty<'tcx>, Sugg<'tcx>) {
|
||||||
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
let outer_sugg = Sugg::hir_with_context(cx, outer, e.span.ctxt(), "..", &mut applicability);
|
||||||
|
if let ty::Adt(struct_def, struct_args) = *outer_type.kind()
|
||||||
|
&& struct_def.is_struct()
|
||||||
|
&& let mut fields = struct_def.all_fields()
|
||||||
|
&& let Some(first) = fields.next()
|
||||||
|
&& fields.next().is_none()
|
||||||
|
&& first.vis.is_accessible_from(cx.tcx.parent_module(outer.hir_id), cx.tcx)
|
||||||
|
{
|
||||||
|
(
|
||||||
|
first.ty(cx.tcx, struct_args),
|
||||||
|
Sugg::NonParen(format!("{}.{}", outer_sugg.maybe_paren(), first.name).into()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(outer_type, outer_sugg)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<'tcx> LateLintPass<'tcx> for Transmute {
|
impl<'tcx> LateLintPass<'tcx> for Transmute {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||||
|
|
@ -516,14 +545,17 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A struct having a single pointer can be treated like a pointer.
|
||||||
|
let (from_field_ty, from_field_expr) = Self::extract_struct_field(cx, e, from_ty, arg);
|
||||||
|
|
||||||
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||||
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
||||||
| transmuting_null::check(cx, e, arg, to_ty)
|
| transmuting_null::check(cx, e, arg, to_ty)
|
||||||
| transmute_null_to_fn::check(cx, e, arg, to_ty)
|
| transmute_null_to_fn::check(cx, e, arg, to_ty)
|
||||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
|
| transmute_ptr_to_ref::check(cx, e, from_field_ty, to_ty, from_field_expr.clone(), path, self.msrv)
|
||||||
| missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id)
|
| missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id)
|
||||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||||
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv)
|
| transmute_ptr_to_ptr::check(cx, e, from_field_ty, to_ty, from_field_expr, self.msrv)
|
||||||
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
|
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
|
||||||
| transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)
|
| transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)
|
||||||
| (unsound_collection_transmute::check(cx, e, from_ty, to_ty)
|
| (unsound_collection_transmute::check(cx, e, from_ty, to_ty)
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,9 @@ pub(super) fn check<'tcx>(
|
||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
arg: &'tcx Expr<'_>,
|
arg: sugg::Sugg<'_>,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
|
||||||
let arg_sugg = sugg::Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut applicability);
|
|
||||||
match (from_ty.kind(), to_ty.kind()) {
|
match (from_ty.kind(), to_ty.kind()) {
|
||||||
(ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => {
|
(ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => {
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
|
|
@ -34,7 +32,7 @@ pub(super) fn check<'tcx>(
|
||||||
diag.span_suggestion_verbose(
|
diag.span_suggestion_verbose(
|
||||||
e.span,
|
e.span,
|
||||||
"use `pointer::cast` instead",
|
"use `pointer::cast` instead",
|
||||||
format!("{}.cast::<{to_pointee_ty}>()", arg_sugg.maybe_paren()),
|
format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
} else if from_pointee_ty == to_pointee_ty
|
} else if from_pointee_ty == to_pointee_ty
|
||||||
|
|
@ -49,14 +47,14 @@ pub(super) fn check<'tcx>(
|
||||||
diag.span_suggestion_verbose(
|
diag.span_suggestion_verbose(
|
||||||
e.span,
|
e.span,
|
||||||
format!("use `pointer::{method}` instead"),
|
format!("use `pointer::{method}` instead"),
|
||||||
format!("{}.{method}()", arg_sugg.maybe_paren()),
|
format!("{}.{method}()", arg.maybe_paren()),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
diag.span_suggestion_verbose(
|
diag.span_suggestion_verbose(
|
||||||
e.span,
|
e.span,
|
||||||
"use an `as` cast instead",
|
"use an `as` cast instead",
|
||||||
arg_sugg.as_ty(to_ty),
|
arg.as_ty(to_ty),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(
|
||||||
e: &'tcx Expr<'_>,
|
e: &'tcx Expr<'_>,
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
arg: &'tcx Expr<'_>,
|
arg: sugg::Sugg<'_>,
|
||||||
path: &'tcx Path<'_>,
|
path: &'tcx Path<'_>,
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
|
@ -27,7 +27,6 @@ pub(super) fn check<'tcx>(
|
||||||
e.span,
|
e.span,
|
||||||
format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"),
|
format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"),
|
||||||
|diag| {
|
|diag| {
|
||||||
let arg = sugg::Sugg::hir(cx, arg, "..");
|
|
||||||
let (deref, cast) = match mutbl {
|
let (deref, cast) = match mutbl {
|
||||||
Mutability::Mut => ("&mut *", "*mut"),
|
Mutability::Mut => ("&mut *", "*mut"),
|
||||||
Mutability::Not => ("&*", "*const"),
|
Mutability::Not => ("&*", "*const"),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use clippy_config::Conf;
|
use clippy_config::Conf;
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::is_from_proc_macro;
|
|
||||||
use clippy_utils::msrvs::{self, Msrv};
|
use clippy_utils::msrvs::{self, Msrv};
|
||||||
use clippy_utils::res::MaybeResPath;
|
use clippy_utils::res::MaybeResPath;
|
||||||
use clippy_utils::visitors::for_each_local_use_after_expr;
|
use clippy_utils::visitors::local_used_once;
|
||||||
|
use clippy_utils::{get_enclosing_block, is_from_proc_macro};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_ast::LitKind;
|
use rustc_ast::LitKind;
|
||||||
use rustc_hir::{Expr, ExprKind, Node, PatKind};
|
use rustc_hir::{Expr, ExprKind, Node, PatKind};
|
||||||
|
|
@ -11,7 +11,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use std::ops::ControlFlow;
|
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
|
@ -86,7 +85,7 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &
|
||||||
ExprKind::Path(_) => Some(elements.iter().collect()),
|
ExprKind::Path(_) => Some(elements.iter().collect()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
&& all_bindings_are_for_conv(cx, &[ty], expr, elements, &locals, ToType::Array)
|
&& all_bindings_are_for_conv(cx, &[ty], elements, &locals, ToType::Array)
|
||||||
&& !is_from_proc_macro(cx, expr)
|
&& !is_from_proc_macro(cx, expr)
|
||||||
{
|
{
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
|
|
@ -123,7 +122,7 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &
|
||||||
ExprKind::Path(_) => Some(elements.iter().collect()),
|
ExprKind::Path(_) => Some(elements.iter().collect()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
&& all_bindings_are_for_conv(cx, tys, expr, elements, &locals, ToType::Tuple)
|
&& all_bindings_are_for_conv(cx, tys, elements, &locals, ToType::Tuple)
|
||||||
&& !is_from_proc_macro(cx, expr)
|
&& !is_from_proc_macro(cx, expr)
|
||||||
{
|
{
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
|
|
@ -148,7 +147,6 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &
|
||||||
fn all_bindings_are_for_conv<'tcx>(
|
fn all_bindings_are_for_conv<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
final_tys: &[Ty<'tcx>],
|
final_tys: &[Ty<'tcx>],
|
||||||
expr: &Expr<'_>,
|
|
||||||
elements: &[Expr<'_>],
|
elements: &[Expr<'_>],
|
||||||
locals: &[&Expr<'_>],
|
locals: &[&Expr<'_>],
|
||||||
kind: ToType,
|
kind: ToType,
|
||||||
|
|
@ -166,13 +164,30 @@ fn all_bindings_are_for_conv<'tcx>(
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.all_equal()
|
.all_equal()
|
||||||
// Fix #11124, very convenient utils function! ❤️
|
&& locals.iter().zip(local_parents.iter()).all(|(&l, &parent)| {
|
||||||
&& locals
|
if let Node::LetStmt(_) = parent {
|
||||||
.iter()
|
return true;
|
||||||
.all(|&l| for_each_local_use_after_expr(cx, l, expr.hir_id, |_| ControlFlow::Break::<()>(())).is_continue())
|
}
|
||||||
|
|
||||||
|
let Some(b) = get_enclosing_block(cx, l) else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
local_used_once(cx, b, l).is_some()
|
||||||
|
})
|
||||||
&& local_parents.first().is_some_and(|node| {
|
&& local_parents.first().is_some_and(|node| {
|
||||||
let Some(ty) = match node {
|
let Some(ty) = match node {
|
||||||
Node::Pat(pat) => Some(pat.hir_id),
|
Node::Pat(pat)
|
||||||
|
if let PatKind::Tuple(pats, _) | PatKind::Slice(pats, None, []) = &pat.kind
|
||||||
|
&& pats.iter().zip(locals.iter()).all(|(p, l)| {
|
||||||
|
if let PatKind::Binding(_, id, _, _) = p.kind {
|
||||||
|
id == *l
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}) =>
|
||||||
|
{
|
||||||
|
Some(pat.hir_id)
|
||||||
|
},
|
||||||
Node::LetStmt(l) => Some(l.hir_id),
|
Node::LetStmt(l) => Some(l.hir_id),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
@ -186,7 +201,9 @@ fn all_bindings_are_for_conv<'tcx>(
|
||||||
tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal()
|
tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal()
|
||||||
},
|
},
|
||||||
(ToType::Tuple, ty::Array(ty, len)) => {
|
(ToType::Tuple, ty::Array(ty, len)) => {
|
||||||
let Some(len) = len.try_to_target_usize(cx.tcx) else { return false };
|
let Some(len) = len.try_to_target_usize(cx.tcx) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
len as usize == elements.len() && final_tys.iter().chain(once(ty)).all_equal()
|
len as usize == elements.len() && final_tys.iter().chain(once(ty)).all_equal()
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
|
||||||
|
|
@ -180,14 +180,19 @@ impl Local {
|
||||||
field_indices,
|
field_indices,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
let field_projections = place
|
||||||
|
.projections
|
||||||
|
.iter()
|
||||||
|
.filter(|proj| matches!(proj.kind, ProjectionKind::Field(_, _)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
is_potentially_local_place(*local_id, place)
|
is_potentially_local_place(*local_id, place)
|
||||||
// If there were projections other than field projections, err on the side of caution and say that they
|
// If there were projections other than field projections, err on the side of caution and say that they
|
||||||
// _might_ be mutating something.
|
// _might_ be mutating something.
|
||||||
//
|
//
|
||||||
// The reason we use `<=` and not `==` is that a mutation of `struct` or `struct.field1` should count as
|
// The reason we use `<=` and not `==` is that a mutation of `struct` or `struct.field1` should count as
|
||||||
// mutation of the child fields such as `struct.field1.field2`
|
// mutation of the child fields such as `struct.field1.field2`
|
||||||
&& place.projections.len() <= field_indices.len()
|
&& field_projections.len() <= field_indices.len()
|
||||||
&& iter::zip(&place.projections, field_indices.iter().copied().rev()).all(|(proj, field_idx)| {
|
&& iter::zip(&field_projections, field_indices.iter().copied().rev()).all(|(proj, field_idx)| {
|
||||||
match proj.kind {
|
match proj.kind {
|
||||||
ProjectionKind::Field(f_idx, _) => f_idx == field_idx,
|
ProjectionKind::Field(f_idx, _) => f_idx == field_idx,
|
||||||
// If this is a projection we don't expect, it _might_ be mutating something
|
// If this is a projection we don't expect, it _might_ be mutating something
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sugg = snippet(cx, recv.span, "<expr>").into_owned();
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "<expr>", &mut applicability)
|
||||||
|
.0
|
||||||
|
.into_owned();
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
USELESS_CONVERSION,
|
USELESS_CONVERSION,
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ use clippy_utils::source::{snippet, snippet_indent};
|
||||||
use rustc_ast::LitKind;
|
use rustc_ast::LitKind;
|
||||||
use rustc_data_structures::packed::Pu128;
|
use rustc_data_structures::packed::Pu128;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{ConstArgKind, ExprKind, Node};
|
use rustc_hir::{ConstArgKind, Expr, ExprKind, LetStmt, LocalSource, Node};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::IsSuggestable;
|
use rustc_middle::ty::{IsSuggestable, Ty};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
|
@ -44,7 +45,7 @@ declare_clippy_lint! {
|
||||||
declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]);
|
declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]);
|
||||||
|
|
||||||
impl LateLintPass<'_> for ZeroRepeatSideEffects {
|
impl LateLintPass<'_> for ZeroRepeatSideEffects {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &rustc_hir::Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if let Some(args) = VecArgs::hir(cx, expr)
|
if let Some(args) = VecArgs::hir(cx, expr)
|
||||||
&& let VecArgs::Repeat(inner_expr, len) = args
|
&& let VecArgs::Repeat(inner_expr, len) = args
|
||||||
&& let ExprKind::Lit(l) = len.kind
|
&& let ExprKind::Lit(l) = len.kind
|
||||||
|
|
@ -69,7 +70,7 @@ impl LateLintPass<'_> for ZeroRepeatSideEffects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) {
|
fn inner_check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, inner_expr: &'_ Expr<'_>, is_vec: bool) {
|
||||||
// check if expr is a call or has a call inside it
|
// check if expr is a call or has a call inside it
|
||||||
if inner_expr.can_have_side_effects() {
|
if inner_expr.can_have_side_effects() {
|
||||||
let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id);
|
let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id);
|
||||||
|
|
@ -81,19 +82,22 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr:
|
||||||
let vec = if is_vec { "vec!" } else { "" };
|
let vec = if is_vec { "vec!" } else { "" };
|
||||||
|
|
||||||
let (span, sugg) = match parent_hir_node {
|
let (span, sugg) = match parent_hir_node {
|
||||||
Node::LetStmt(l) => (
|
Node::LetStmt(l)
|
||||||
l.span,
|
if matches!(l.source, LocalSource::AssignDesugar)
|
||||||
format!(
|
&& let mut parent_iter = cx.tcx.hir_parent_iter(l.hir_id)
|
||||||
"{inner_expr};\n{indent}let {var_name}: {return_type} = {vec}[];",
|
&& let Some((_, Node::Stmt(_))) = parent_iter.next()
|
||||||
var_name = snippet(cx, l.pat.span.source_callsite(), "..")
|
&& let Some((_, Node::Block(_))) = parent_iter.next()
|
||||||
),
|
&& let Some((_, Node::Expr(x))) = parent_iter.next() =>
|
||||||
),
|
{
|
||||||
|
(
|
||||||
|
x.span,
|
||||||
|
assign_expr_suggestion(cx, x, l.pat.span, &inner_expr, return_type, vec),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Node::LetStmt(l) => (l.span, let_stmt_suggestion(cx, l, &inner_expr, return_type, vec)),
|
||||||
Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => (
|
Node::Expr(x) if let ExprKind::Assign(l, _, _) = x.kind => (
|
||||||
x.span,
|
x.span,
|
||||||
format!(
|
assign_expr_suggestion(cx, x, l.span, &inner_expr, return_type, vec),
|
||||||
"{inner_expr};\n{indent}{var_name} = {vec}[] as {return_type}",
|
|
||||||
var_name = snippet(cx, l.span.source_callsite(), "..")
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
// NOTE: don't use the stmt span to avoid touching the trailing semicolon
|
// NOTE: don't use the stmt span to avoid touching the trailing semicolon
|
||||||
Node::Stmt(_) => (expr.span, format!("{inner_expr};\n{indent}{vec}[] as {return_type}")),
|
Node::Stmt(_) => (expr.span, format!("{inner_expr};\n{indent}{vec}[] as {return_type}")),
|
||||||
|
|
@ -131,3 +135,41 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr:
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn let_stmt_suggestion(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
let_stmt: &LetStmt<'_>,
|
||||||
|
inner_expr: &str,
|
||||||
|
return_type: Ty<'_>,
|
||||||
|
vec_str: &str,
|
||||||
|
) -> String {
|
||||||
|
let indent = snippet_indent(cx, let_stmt.span).unwrap_or_default();
|
||||||
|
format!(
|
||||||
|
"{inner_expr};\n{}let {var_name}: {return_type} = {vec_str}[];",
|
||||||
|
indent,
|
||||||
|
var_name = snippet(cx, let_stmt.pat.span.source_callsite(), "..")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_expr_suggestion(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
outer_expr: &Expr<'_>,
|
||||||
|
assign_expr_span: Span,
|
||||||
|
inner_expr: &str,
|
||||||
|
return_type: Ty<'_>,
|
||||||
|
vec_str: &str,
|
||||||
|
) -> String {
|
||||||
|
let mut parent_hir_node = cx.tcx.parent_hir_node(outer_expr.hir_id);
|
||||||
|
if let Node::Stmt(stmt) = parent_hir_node {
|
||||||
|
parent_hir_node = cx.tcx.parent_hir_node(stmt.hir_id);
|
||||||
|
}
|
||||||
|
let needs_curly = !matches!(parent_hir_node, Node::Block(_));
|
||||||
|
|
||||||
|
let indent = snippet_indent(cx, outer_expr.span).unwrap_or_default();
|
||||||
|
let var_name = snippet(cx, assign_expr_span.source_callsite(), "..");
|
||||||
|
if needs_curly {
|
||||||
|
format!("{{\n {indent}{inner_expr};\n {indent}{var_name} = {vec_str}[] as {return_type}\n{indent}}}",)
|
||||||
|
} else {
|
||||||
|
format!("{inner_expr};\n{indent}{var_name} = {vec_str}[] as {return_type}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_utils"
|
name = "clippy_utils"
|
||||||
version = "0.1.93"
|
version = "0.1.94"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "Helpful tools for writing lints, provided as they are used in Clippy"
|
description = "Helpful tools for writing lints, provided as they are used in Clippy"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
|
|
@ -16,6 +16,10 @@ itertools = "0.12"
|
||||||
rustc_apfloat = "0.2.0"
|
rustc_apfloat = "0.2.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
[lints.rust.unexpected_cfgs]
|
||||||
|
level = "warn"
|
||||||
|
check-cfg = ['cfg(bootstrap)']
|
||||||
|
|
||||||
[package.metadata.rust-analyzer]
|
[package.metadata.rust-analyzer]
|
||||||
# This crate uses #[feature(rustc_private)]
|
# This crate uses #[feature(rustc_private)]
|
||||||
rustc_private = true
|
rustc_private = true
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
|
||||||
|
|
||||||
<!-- begin autogenerated nightly -->
|
<!-- begin autogenerated nightly -->
|
||||||
```
|
```
|
||||||
nightly-2025-11-28
|
nightly-2025-12-11
|
||||||
```
|
```
|
||||||
<!-- end autogenerated nightly -->
|
<!-- end autogenerated nightly -->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ 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_RESULT_IS_VARIANT_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,67,0 { ILOG2 }
|
||||||
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
|
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
|
||||||
1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF }
|
1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF }
|
||||||
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
|
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ generate! {
|
||||||
has_significant_drop,
|
has_significant_drop,
|
||||||
hidden_glob_reexports,
|
hidden_glob_reexports,
|
||||||
hygiene,
|
hygiene,
|
||||||
|
ilog,
|
||||||
insert,
|
insert,
|
||||||
insert_str,
|
insert_str,
|
||||||
inspect,
|
inspect,
|
||||||
|
|
@ -207,6 +208,7 @@ generate! {
|
||||||
join,
|
join,
|
||||||
kw,
|
kw,
|
||||||
lazy_static,
|
lazy_static,
|
||||||
|
leading_zeros,
|
||||||
lint_vec,
|
lint_vec,
|
||||||
ln,
|
ln,
|
||||||
lock,
|
lock,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "declare_clippy_lint"
|
name = "declare_clippy_lint"
|
||||||
version = "0.1.93"
|
version = "0.1.94"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
# begin autogenerated nightly
|
# begin autogenerated nightly
|
||||||
channel = "nightly-2025-11-28"
|
channel = "nightly-2025-12-11"
|
||||||
# end autogenerated nightly
|
# end autogenerated nightly
|
||||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
|
|
|
||||||
1
tests/ui-toml/large_stack_frames_for_macros/clippy.toml
Normal file
1
tests/ui-toml/large_stack_frames_for_macros/clippy.toml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
stack-size-threshold = 0
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
//@ignore-target: i686
|
||||||
|
//@normalize-stderr-test: "\b10000(08|16|32)\b" -> "100$$PTR"
|
||||||
|
//@normalize-stderr-test: "\b2500(060|120)\b" -> "250$$PTR"
|
||||||
|
|
||||||
|
#![warn(clippy::large_stack_frames)]
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
struct ArrayDefault<const N: usize>([u8; N]);
|
||||||
|
|
||||||
|
macro_rules! mac {
|
||||||
|
($name:ident) => {
|
||||||
|
fn foo() {
|
||||||
|
let $name = 1;
|
||||||
|
println!("macro_name called");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let $name = ArrayDefault([0; 1000]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
mac!(something);
|
||||||
|
//~^ large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
//~^ large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
//~| large_stack_frames
|
||||||
|
struct S {
|
||||||
|
a: [u128; 31],
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
error: function `foo` generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:25:1
|
||||||
|
|
|
||||||
|
LL | fn foo() {
|
||||||
|
| --- this function has a stack frame size of 20 bytes
|
||||||
|
...
|
||||||
|
LL | mac!(something);
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: 20 bytes is larger than Clippy's configured `stack-size-threshold` of 0
|
||||||
|
= note: allocating large amounts of stack space can overflow the stack and cause the program to abort
|
||||||
|
= note: `-D clippy::large-stack-frames` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::large_stack_frames)]`
|
||||||
|
|
||||||
|
error: function `bar` generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:25:1
|
||||||
|
|
|
||||||
|
LL | fn bar() {
|
||||||
|
| --- this function has a stack frame size of 2000 bytes
|
||||||
|
LL | let $name = ArrayDefault([0; 1000]);
|
||||||
|
| --------- this is the largest part, at 1000 bytes for type `[u8; 1000]`
|
||||||
|
...
|
||||||
|
LL | mac!(something);
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: 2000 bytes is larger than Clippy's configured `stack-size-threshold` of 0
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:10
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: method generated by this macro may allocate a lot of stack space
|
||||||
|
--> tests/ui-toml/large_stack_frames_for_macros/large_stack_frames.rs:29:23
|
||||||
|
|
|
||||||
|
LL | #[derive(Deserialize, Serialize)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 10 previous errors
|
||||||
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
stack-size-threshold = 0
|
||||||
|
allow-large-stack-frames-in-tests = false
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// This test checks if `clippy::large_stack_frames` is working correctly when encountering functions
|
||||||
|
// generated by special compiling targets like `--test`.
|
||||||
|
//@compile-flags: --test
|
||||||
|
//@check-pass
|
||||||
|
|
||||||
|
#![warn(clippy::large_stack_frames)]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[expect(clippy::large_stack_frames)]
|
||||||
|
mod test {
|
||||||
|
#[test]
|
||||||
|
fn main_test() {}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//@aux-build:proc_macro_derive.rs
|
//@aux-build:proc_macro_derive.rs
|
||||||
|
|
||||||
#![warn(clippy::nonstandard_macro_braces)]
|
#![warn(clippy::nonstandard_macro_braces)]
|
||||||
|
#![allow(clippy::println_empty_string)]
|
||||||
|
|
||||||
extern crate proc_macro_derive;
|
extern crate proc_macro_derive;
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
|
|
@ -75,3 +76,16 @@ fn issue9913() {
|
||||||
[0]; // separate statement, not indexing into the result of println.
|
[0]; // separate statement, not indexing into the result of println.
|
||||||
//~^^ nonstandard_macro_braces
|
//~^^ nonstandard_macro_braces
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue15594() {
|
||||||
|
println!();
|
||||||
|
println!("");
|
||||||
|
println!();
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
println!("");
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
println!();
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
println!("");
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//@aux-build:proc_macro_derive.rs
|
//@aux-build:proc_macro_derive.rs
|
||||||
|
|
||||||
#![warn(clippy::nonstandard_macro_braces)]
|
#![warn(clippy::nonstandard_macro_braces)]
|
||||||
|
#![allow(clippy::println_empty_string)]
|
||||||
|
|
||||||
extern crate proc_macro_derive;
|
extern crate proc_macro_derive;
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
|
|
@ -75,3 +76,16 @@ fn issue9913() {
|
||||||
[0]; // separate statement, not indexing into the result of println.
|
[0]; // separate statement, not indexing into the result of println.
|
||||||
//~^^ nonstandard_macro_braces
|
//~^^ nonstandard_macro_braces
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue15594() {
|
||||||
|
println!();
|
||||||
|
println!("");
|
||||||
|
println![];
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
println![""];
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
println! {};
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
println! {""};
|
||||||
|
//~^ nonstandard_macro_braces
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
error: use of irregular braces for `vec!` macro
|
error: use of irregular braces for `vec!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:44:13
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:45:13
|
||||||
|
|
|
|
||||||
LL | let _ = vec! {1, 2, 3};
|
LL | let _ = vec! {1, 2, 3};
|
||||||
| ^^^^^^^^^^^^^^ help: consider writing: `vec![1, 2, 3]`
|
| ^^^^^^^^^^^^^^ help: consider writing: `vec![1, 2, 3]`
|
||||||
|
|
@ -8,31 +8,31 @@ LL | let _ = vec! {1, 2, 3};
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::nonstandard_macro_braces)]`
|
= help: to override `-D warnings` add `#[allow(clippy::nonstandard_macro_braces)]`
|
||||||
|
|
||||||
error: use of irregular braces for `format!` macro
|
error: use of irregular braces for `format!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:46:13
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:47:13
|
||||||
|
|
|
|
||||||
LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
|
LL | let _ = format!["ugh {} stop being such a good compiler", "hello"];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `format!("ugh {} stop being such a good compiler", "hello")`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `format!("ugh {} stop being such a good compiler", "hello")`
|
||||||
|
|
||||||
error: use of irregular braces for `matches!` macro
|
error: use of irregular braces for `matches!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:48:13
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:49:13
|
||||||
|
|
|
|
||||||
LL | let _ = matches!{{}, ()};
|
LL | let _ = matches!{{}, ()};
|
||||||
| ^^^^^^^^^^^^^^^^ help: consider writing: `matches!({}, ())`
|
| ^^^^^^^^^^^^^^^^ help: consider writing: `matches!({}, ())`
|
||||||
|
|
||||||
error: use of irregular braces for `quote!` macro
|
error: use of irregular braces for `quote!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:50:13
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:51:13
|
||||||
|
|
|
|
||||||
LL | let _ = quote!(let x = 1;);
|
LL | let _ = quote!(let x = 1;);
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: consider writing: `quote!{let x = 1;}`
|
| ^^^^^^^^^^^^^^^^^^ help: consider writing: `quote!{let x = 1;}`
|
||||||
|
|
||||||
error: use of irregular braces for `quote::quote!` macro
|
error: use of irregular braces for `quote::quote!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:52:13
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:53:13
|
||||||
|
|
|
|
||||||
LL | let _ = quote::quote!(match match match);
|
LL | let _ = quote::quote!(match match match);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `quote::quote!{match match match}`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `quote::quote!{match match match}`
|
||||||
|
|
||||||
error: use of irregular braces for `vec!` macro
|
error: use of irregular braces for `vec!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:18:9
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:19:9
|
||||||
|
|
|
|
||||||
LL | vec!{0, 0, 0}
|
LL | vec!{0, 0, 0}
|
||||||
| ^^^^^^^^^^^^^ help: consider writing: `vec![0, 0, 0]`
|
| ^^^^^^^^^^^^^ help: consider writing: `vec![0, 0, 0]`
|
||||||
|
|
@ -43,22 +43,46 @@ LL | let _ = test!(); // trigger when macro def is inside our own crate
|
||||||
= note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: use of irregular braces for `type_pos!` macro
|
error: use of irregular braces for `type_pos!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:62:12
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:63:12
|
||||||
|
|
|
|
||||||
LL | let _: type_pos!(usize) = vec![];
|
LL | let _: type_pos!(usize) = vec![];
|
||||||
| ^^^^^^^^^^^^^^^^ help: consider writing: `type_pos![usize]`
|
| ^^^^^^^^^^^^^^^^ help: consider writing: `type_pos![usize]`
|
||||||
|
|
||||||
error: use of irregular braces for `eprint!` macro
|
error: use of irregular braces for `eprint!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:65:5
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:66:5
|
||||||
|
|
|
|
||||||
LL | eprint!("test if user config overrides defaults");
|
LL | eprint!("test if user config overrides defaults");
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `eprint!["test if user config overrides defaults"]`
|
||||||
|
|
||||||
error: use of irregular braces for `println!` macro
|
error: use of irregular braces for `println!` macro
|
||||||
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:74:5
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:75:5
|
||||||
|
|
|
|
||||||
LL | println! {"hello world"}
|
LL | println! {"hello world"}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `println!("hello world");`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `println!("hello world");`
|
||||||
|
|
||||||
error: aborting due to 9 previous errors
|
error: use of irregular braces for `println!` macro
|
||||||
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:83:5
|
||||||
|
|
|
||||||
|
LL | println![];
|
||||||
|
| ^^^^^^^^^^ help: consider writing: `println!()`
|
||||||
|
|
||||||
|
error: use of irregular braces for `println!` macro
|
||||||
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:85:5
|
||||||
|
|
|
||||||
|
LL | println![""];
|
||||||
|
| ^^^^^^^^^^^^ help: consider writing: `println!("")`
|
||||||
|
|
||||||
|
error: use of irregular braces for `println!` macro
|
||||||
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:87:5
|
||||||
|
|
|
||||||
|
LL | println! {};
|
||||||
|
| ^^^^^^^^^^^ help: consider writing: `println!()`
|
||||||
|
|
||||||
|
error: use of irregular braces for `println!` macro
|
||||||
|
--> tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs:89:5
|
||||||
|
|
|
||||||
|
LL | println! {""};
|
||||||
|
| ^^^^^^^^^^^^^ help: consider writing: `println!("")`
|
||||||
|
|
||||||
|
error: aborting due to 13 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
||||||
allow-expect-in-consts
|
allow-expect-in-consts
|
||||||
allow-expect-in-tests
|
allow-expect-in-tests
|
||||||
allow-indexing-slicing-in-tests
|
allow-indexing-slicing-in-tests
|
||||||
|
allow-large-stack-frames-in-tests
|
||||||
allow-mixed-uninlined-format-args
|
allow-mixed-uninlined-format-args
|
||||||
allow-one-hash-in-raw-strings
|
allow-one-hash-in-raw-strings
|
||||||
allow-panic-in-tests
|
allow-panic-in-tests
|
||||||
|
|
@ -107,6 +108,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
||||||
allow-expect-in-consts
|
allow-expect-in-consts
|
||||||
allow-expect-in-tests
|
allow-expect-in-tests
|
||||||
allow-indexing-slicing-in-tests
|
allow-indexing-slicing-in-tests
|
||||||
|
allow-large-stack-frames-in-tests
|
||||||
allow-mixed-uninlined-format-args
|
allow-mixed-uninlined-format-args
|
||||||
allow-one-hash-in-raw-strings
|
allow-one-hash-in-raw-strings
|
||||||
allow-panic-in-tests
|
allow-panic-in-tests
|
||||||
|
|
@ -205,6 +207,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
|
||||||
allow-expect-in-consts
|
allow-expect-in-consts
|
||||||
allow-expect-in-tests
|
allow-expect-in-tests
|
||||||
allow-indexing-slicing-in-tests
|
allow-indexing-slicing-in-tests
|
||||||
|
allow-large-stack-frames-in-tests
|
||||||
allow-mixed-uninlined-format-args
|
allow-mixed-uninlined-format-args
|
||||||
allow-one-hash-in-raw-strings
|
allow-one-hash-in-raw-strings
|
||||||
allow-panic-in-tests
|
allow-panic-in-tests
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//@aux-build:proc_macros.rs
|
//@aux-build:proc_macros.rs
|
||||||
#![warn(clippy::borrow_as_ptr)]
|
#![warn(clippy::borrow_as_ptr)]
|
||||||
#![allow(clippy::useless_vec)]
|
#![allow(clippy::useless_vec, clippy::ptr_offset_by_literal)]
|
||||||
|
|
||||||
extern crate proc_macros;
|
extern crate proc_macros;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//@aux-build:proc_macros.rs
|
//@aux-build:proc_macros.rs
|
||||||
#![warn(clippy::borrow_as_ptr)]
|
#![warn(clippy::borrow_as_ptr)]
|
||||||
#![allow(clippy::useless_vec)]
|
#![allow(clippy::useless_vec, clippy::ptr_offset_by_literal)]
|
||||||
|
|
||||||
extern crate proc_macros;
|
extern crate proc_macros;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -472,3 +472,22 @@ fn issue15321() {
|
||||||
//~^ unnecessary_unwrap
|
//~^ unnecessary_unwrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod issue16188 {
|
||||||
|
struct Foo {
|
||||||
|
value: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
pub fn bar(&mut self) {
|
||||||
|
let print_value = |v: i32| {
|
||||||
|
println!("{}", v);
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.value.is_none() {
|
||||||
|
self.value = Some(10);
|
||||||
|
print_value(self.value.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//@ check-pass
|
//@ check-pass
|
||||||
|
|
||||||
#![allow(clippy::single_match)]
|
#![allow(clippy::single_match, clippy::ptr_offset_by_literal)]
|
||||||
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
|
|
||||||
133
tests/ui/decimal_bitwise_operands.rs
Normal file
133
tests/ui/decimal_bitwise_operands.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
#![allow(
|
||||||
|
clippy::erasing_op,
|
||||||
|
clippy::no_effect,
|
||||||
|
clippy::unnecessary_operation,
|
||||||
|
clippy::unnecessary_cast,
|
||||||
|
clippy::op_ref
|
||||||
|
)]
|
||||||
|
#![warn(clippy::decimal_bitwise_operands)]
|
||||||
|
|
||||||
|
macro_rules! bitwise_op {
|
||||||
|
($x:expr, $y:expr) => {
|
||||||
|
$x & $y;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SOME_CONST: i32 = 12345;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut x = 0;
|
||||||
|
// BAD: Bitwise operation, decimal literal, one literal
|
||||||
|
x & 9_8765_4321; //~ decimal_bitwise_operands
|
||||||
|
x & 100_i32; //~ decimal_bitwise_operands
|
||||||
|
x | (/* comment */99); //~ decimal_bitwise_operands
|
||||||
|
x ^ (99); //~ decimal_bitwise_operands
|
||||||
|
x &= 99; //~ decimal_bitwise_operands
|
||||||
|
x |= { 99 }; //~ decimal_bitwise_operands
|
||||||
|
x |= { { 99 } }; //~ decimal_bitwise_operands
|
||||||
|
x |= {
|
||||||
|
0b1000;
|
||||||
|
99 //~ decimal_bitwise_operands
|
||||||
|
};
|
||||||
|
x ^= (99); //~ decimal_bitwise_operands
|
||||||
|
|
||||||
|
// BAD: Bitwise operation, decimal literal, two literals
|
||||||
|
0b1010 & 99; //~ decimal_bitwise_operands
|
||||||
|
0b1010 | (99); //~ decimal_bitwise_operands
|
||||||
|
0b1010 ^ (/* comment */99); //~ decimal_bitwise_operands
|
||||||
|
99 & 0b1010; //~ decimal_bitwise_operands
|
||||||
|
(99) | 0b1010; //~ decimal_bitwise_operands
|
||||||
|
(/* comment */99) ^ 0b1010; //~ decimal_bitwise_operands
|
||||||
|
0xD | { 99 }; //~ decimal_bitwise_operands
|
||||||
|
88 & 99;
|
||||||
|
//~^ decimal_bitwise_operands
|
||||||
|
//~| decimal_bitwise_operands
|
||||||
|
37 & 38 & 39;
|
||||||
|
//~^ decimal_bitwise_operands
|
||||||
|
//~| decimal_bitwise_operands
|
||||||
|
//~| decimal_bitwise_operands
|
||||||
|
|
||||||
|
// GOOD: Bitwise operation, binary/hex/octal literal, one literal
|
||||||
|
x & 0b1010;
|
||||||
|
x | 0b1010;
|
||||||
|
x ^ 0b1010;
|
||||||
|
x &= 0b1010;
|
||||||
|
x |= 0b1010;
|
||||||
|
x ^= 0b1010;
|
||||||
|
x & 0xD;
|
||||||
|
x & 0o77;
|
||||||
|
x | 0o123;
|
||||||
|
x ^ 0o377;
|
||||||
|
x &= 0o777;
|
||||||
|
x |= 0o7;
|
||||||
|
x ^= 0o70;
|
||||||
|
|
||||||
|
// GOOD: Bitwise operation, binary/hex/octal literal, two literals
|
||||||
|
0b1010 & 0b1101;
|
||||||
|
0xD ^ 0xF;
|
||||||
|
0o377 ^ 0o77;
|
||||||
|
0b1101 ^ 0xFF;
|
||||||
|
|
||||||
|
// GOOD: Numeric operation, any literal
|
||||||
|
x += 99;
|
||||||
|
x -= 0b1010;
|
||||||
|
x *= 0xD;
|
||||||
|
99 + 99;
|
||||||
|
0b1010 - 0b1101;
|
||||||
|
0xD * 0xD;
|
||||||
|
|
||||||
|
// BAD: Unary, cast and reference, decimal literal
|
||||||
|
x & !100; //~ decimal_bitwise_operands
|
||||||
|
x & -100; //~ decimal_bitwise_operands
|
||||||
|
x & (100 as i32); //~ decimal_bitwise_operands
|
||||||
|
x & &100; //~ decimal_bitwise_operands
|
||||||
|
|
||||||
|
// GOOD: Unary, cast and reference, non-decimal literal
|
||||||
|
x & !0b1101;
|
||||||
|
x & -0xD;
|
||||||
|
x & (0o333 as i32);
|
||||||
|
x & &0b1010;
|
||||||
|
|
||||||
|
// GOOD: Bitwise operation, variables only
|
||||||
|
let y = 0;
|
||||||
|
x & y;
|
||||||
|
x &= y;
|
||||||
|
x + y;
|
||||||
|
x += y;
|
||||||
|
|
||||||
|
// GOOD: Macro expansion (should be ignored)
|
||||||
|
bitwise_op!(x, 123);
|
||||||
|
bitwise_op!(0b1010, 123);
|
||||||
|
|
||||||
|
// GOOD: Using const (should be ignored)
|
||||||
|
x & SOME_CONST;
|
||||||
|
x |= SOME_CONST;
|
||||||
|
|
||||||
|
// GOOD: Parenthesized binary/hex literal (should not trigger lint)
|
||||||
|
x & (0b1111);
|
||||||
|
x |= (0b1010);
|
||||||
|
x ^ (/* comment */0b1100);
|
||||||
|
(0xFF) & x;
|
||||||
|
|
||||||
|
// GOOD: Power of two and power of two minus one
|
||||||
|
x & 16; // 2^4
|
||||||
|
x | (31); // 2^5 - 1
|
||||||
|
x ^ 0x40; // 2^6 (hex)
|
||||||
|
x ^= 7; // 2^3 - 1
|
||||||
|
|
||||||
|
// GOOD: Bitwise operation, single digit decimal literal
|
||||||
|
5 & 9;
|
||||||
|
x ^ 6;
|
||||||
|
x ^= 7;
|
||||||
|
|
||||||
|
// GOOD: More complex expressions
|
||||||
|
(x + 1) & 0xFF;
|
||||||
|
(x * 2) | (y & 0xF);
|
||||||
|
(x ^ y) & 0b11110000;
|
||||||
|
x | (1 << 9);
|
||||||
|
|
||||||
|
// GOOD: Special cases
|
||||||
|
x & 0; // All bits off
|
||||||
|
x | !0; // All bits on
|
||||||
|
x ^ 1; // Toggle LSB
|
||||||
|
}
|
||||||
204
tests/ui/decimal_bitwise_operands.stderr
Normal file
204
tests/ui/decimal_bitwise_operands.stderr
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:21:9
|
||||||
|
|
|
||||||
|
LL | x & 9_8765_4321;
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b11_1010_1101_1110_0110_1000_1011_0001), hex (0x3ade_68b1), or octal (0o7_267_464_261) notation for better readability
|
||||||
|
= note: `-D clippy::decimal-bitwise-operands` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::decimal_bitwise_operands)]`
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:22:9
|
||||||
|
|
|
||||||
|
LL | x & 100_i32;
|
||||||
|
| ^^^^^^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0100_i32), hex (0x0064_i32), or octal (0o144_i32) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:23:23
|
||||||
|
|
|
||||||
|
LL | x | (/* comment */99);
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:24:10
|
||||||
|
|
|
||||||
|
LL | x ^ (99);
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:25:10
|
||||||
|
|
|
||||||
|
LL | x &= 99;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:26:12
|
||||||
|
|
|
||||||
|
LL | x |= { 99 };
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:27:14
|
||||||
|
|
|
||||||
|
LL | x |= { { 99 } };
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:30:9
|
||||||
|
|
|
||||||
|
LL | 99
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:32:11
|
||||||
|
|
|
||||||
|
LL | x ^= (99);
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:35:14
|
||||||
|
|
|
||||||
|
LL | 0b1010 & 99;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:36:15
|
||||||
|
|
|
||||||
|
LL | 0b1010 | (99);
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:37:28
|
||||||
|
|
|
||||||
|
LL | 0b1010 ^ (/* comment */99);
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:38:5
|
||||||
|
|
|
||||||
|
LL | 99 & 0b1010;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:39:6
|
||||||
|
|
|
||||||
|
LL | (99) | 0b1010;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:40:19
|
||||||
|
|
|
||||||
|
LL | (/* comment */99) ^ 0b1010;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:41:13
|
||||||
|
|
|
||||||
|
LL | 0xD | { 99 };
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:42:5
|
||||||
|
|
|
||||||
|
LL | 88 & 99;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b101_1000), hex (0x0058), or octal (0o130) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:42:10
|
||||||
|
|
|
||||||
|
LL | 88 & 99;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0011), hex (0x0063), or octal (0o143) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:45:15
|
||||||
|
|
|
||||||
|
LL | 37 & 38 & 39;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b10_0111), hex (0x0027), or octal (0o47) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:45:5
|
||||||
|
|
|
||||||
|
LL | 37 & 38 & 39;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b10_0101), hex (0x0025), or octal (0o45) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:45:10
|
||||||
|
|
|
||||||
|
LL | 37 & 38 & 39;
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b10_0110), hex (0x0026), or octal (0o46) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:80:10
|
||||||
|
|
|
||||||
|
LL | x & !100;
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:81:10
|
||||||
|
|
|
||||||
|
LL | x & -100;
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:82:10
|
||||||
|
|
|
||||||
|
LL | x & (100 as i32);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability
|
||||||
|
|
||||||
|
error: using decimal literal for bitwise operation
|
||||||
|
--> tests/ui/decimal_bitwise_operands.rs:83:10
|
||||||
|
|
|
||||||
|
LL | x & &100;
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: use binary (0b110_0100), hex (0x0064), or octal (0o144) notation for better readability
|
||||||
|
|
||||||
|
error: aborting due to 25 previous errors
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can n
|
||||||
LL | #![warn(clippy::should_assert_eq)]
|
LL | #![warn(clippy::should_assert_eq)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: lint `clippy::string_to_string` has been removed: `clippy:implicit_clone` covers those cases
|
error: lint `clippy::string_to_string` has been removed: `clippy::implicit_clone` covers those cases
|
||||||
--> tests/ui/deprecated.rs:15:9
|
--> tests/ui/deprecated.rs:15:9
|
||||||
|
|
|
|
||||||
LL | #![warn(clippy::string_to_string)]
|
LL | #![warn(clippy::string_to_string)]
|
||||||
|
|
|
||||||
|
|
@ -248,4 +248,28 @@ mod issue14449 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't suggest when it would cause `MutexGuard` to be held across an await point.
|
||||||
|
mod issue_16173 {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
async fn f() {}
|
||||||
|
|
||||||
|
async fn foo() {
|
||||||
|
let mu_map = Mutex::new(HashMap::new());
|
||||||
|
if !mu_map.lock().unwrap().contains_key(&0) {
|
||||||
|
f().await;
|
||||||
|
mu_map.lock().unwrap().insert(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if mu_map.lock().unwrap().contains_key(&1) {
|
||||||
|
todo!();
|
||||||
|
} else {
|
||||||
|
mu_map.lock().unwrap().insert(1, 42);
|
||||||
|
todo!();
|
||||||
|
f().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
||||||
|
|
@ -254,4 +254,28 @@ mod issue14449 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't suggest when it would cause `MutexGuard` to be held across an await point.
|
||||||
|
mod issue_16173 {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
async fn f() {}
|
||||||
|
|
||||||
|
async fn foo() {
|
||||||
|
let mu_map = Mutex::new(HashMap::new());
|
||||||
|
if !mu_map.lock().unwrap().contains_key(&0) {
|
||||||
|
f().await;
|
||||||
|
mu_map.lock().unwrap().insert(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if mu_map.lock().unwrap().contains_key(&1) {
|
||||||
|
todo!();
|
||||||
|
} else {
|
||||||
|
mu_map.lock().unwrap().insert(1, 42);
|
||||||
|
todo!();
|
||||||
|
f().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
||||||
|
|
@ -473,4 +473,16 @@ impl Alias2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue #16190
|
||||||
|
pub struct RefMutLenButRefIsEmpty;
|
||||||
|
impl RefMutLenButRefIsEmpty {
|
||||||
|
pub fn len(&mut self) -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
||||||
32
tests/ui/manual_ilog2.fixed
Normal file
32
tests/ui/manual_ilog2.fixed
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
//@aux-build:proc_macros.rs
|
||||||
|
#![warn(clippy::manual_ilog2)]
|
||||||
|
#![allow(clippy::unnecessary_operation)]
|
||||||
|
|
||||||
|
use proc_macros::{external, with_span};
|
||||||
|
|
||||||
|
fn foo(a: u32, b: u64) {
|
||||||
|
a.ilog2(); //~ manual_ilog2
|
||||||
|
a.ilog2(); //~ manual_ilog2
|
||||||
|
|
||||||
|
b.ilog2(); //~ manual_ilog2
|
||||||
|
64 - b.leading_zeros(); // No lint because manual ilog2 is `BIT_WIDTH - 1 - x.leading_zeros()`
|
||||||
|
|
||||||
|
// don't lint when macros are involved
|
||||||
|
macro_rules! two {
|
||||||
|
() => {
|
||||||
|
2
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! thirty_one {
|
||||||
|
() => {
|
||||||
|
31
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
a.ilog(two!());
|
||||||
|
thirty_one!() - a.leading_zeros();
|
||||||
|
|
||||||
|
external!($a.ilog(2));
|
||||||
|
with_span!(span; a.ilog(2));
|
||||||
|
}
|
||||||
32
tests/ui/manual_ilog2.rs
Normal file
32
tests/ui/manual_ilog2.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
//@aux-build:proc_macros.rs
|
||||||
|
#![warn(clippy::manual_ilog2)]
|
||||||
|
#![allow(clippy::unnecessary_operation)]
|
||||||
|
|
||||||
|
use proc_macros::{external, with_span};
|
||||||
|
|
||||||
|
fn foo(a: u32, b: u64) {
|
||||||
|
31 - a.leading_zeros(); //~ manual_ilog2
|
||||||
|
a.ilog(2); //~ manual_ilog2
|
||||||
|
|
||||||
|
63 - b.leading_zeros(); //~ manual_ilog2
|
||||||
|
64 - b.leading_zeros(); // No lint because manual ilog2 is `BIT_WIDTH - 1 - x.leading_zeros()`
|
||||||
|
|
||||||
|
// don't lint when macros are involved
|
||||||
|
macro_rules! two {
|
||||||
|
() => {
|
||||||
|
2
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! thirty_one {
|
||||||
|
() => {
|
||||||
|
31
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
a.ilog(two!());
|
||||||
|
thirty_one!() - a.leading_zeros();
|
||||||
|
|
||||||
|
external!($a.ilog(2));
|
||||||
|
with_span!(span; a.ilog(2));
|
||||||
|
}
|
||||||
23
tests/ui/manual_ilog2.stderr
Normal file
23
tests/ui/manual_ilog2.stderr
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
error: manually reimplementing `ilog2`
|
||||||
|
--> tests/ui/manual_ilog2.rs:8:5
|
||||||
|
|
|
||||||
|
LL | 31 - a.leading_zeros();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.ilog2()`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::manual-ilog2` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::manual_ilog2)]`
|
||||||
|
|
||||||
|
error: manually reimplementing `ilog2`
|
||||||
|
--> tests/ui/manual_ilog2.rs:9:5
|
||||||
|
|
|
||||||
|
LL | a.ilog(2);
|
||||||
|
| ^^^^^^^^^ help: try: `a.ilog2()`
|
||||||
|
|
||||||
|
error: manually reimplementing `ilog2`
|
||||||
|
--> tests/ui/manual_ilog2.rs:11:5
|
||||||
|
|
|
||||||
|
LL | 63 - b.leading_zeros();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `b.ilog2()`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
|
@ -58,3 +58,13 @@ fn main() {
|
||||||
let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
|
let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
|
||||||
let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
|
let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue15655() {
|
||||||
|
let _ = 5u32.saturating_sub(1u32); //~ manual_saturating_arithmetic
|
||||||
|
let _ = 5u32.checked_add(1u32).unwrap_or_default(); // ok
|
||||||
|
let _ = 5u32.checked_mul(1u32).unwrap_or_default(); // ok
|
||||||
|
|
||||||
|
let _ = 5i32.checked_sub(1i32).unwrap_or_default(); // ok
|
||||||
|
let _ = 5i32.checked_add(1i32).unwrap_or_default(); // ok
|
||||||
|
let _ = 5i32.checked_mul(1i32).unwrap_or_default(); // ok
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,3 +73,13 @@ fn main() {
|
||||||
let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
|
let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
|
||||||
let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
|
let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue15655() {
|
||||||
|
let _ = 5u32.checked_sub(1u32).unwrap_or_default(); //~ manual_saturating_arithmetic
|
||||||
|
let _ = 5u32.checked_add(1u32).unwrap_or_default(); // ok
|
||||||
|
let _ = 5u32.checked_mul(1u32).unwrap_or_default(); // ok
|
||||||
|
|
||||||
|
let _ = 5i32.checked_sub(1i32).unwrap_or_default(); // ok
|
||||||
|
let _ = 5i32.checked_add(1i32).unwrap_or_default(); // ok
|
||||||
|
let _ = 5i32.checked_mul(1i32).unwrap_or_default(); // ok
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,5 +165,11 @@ LL | | .checked_sub(-1)
|
||||||
LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
|
LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
|
||||||
| |_______________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(-1)`
|
| |_______________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(-1)`
|
||||||
|
|
||||||
error: aborting due to 24 previous errors
|
error: manual saturating arithmetic
|
||||||
|
--> tests/ui/manual_saturating_arithmetic.rs:78:13
|
||||||
|
|
|
||||||
|
LL | let _ = 5u32.checked_sub(1u32).unwrap_or_default();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `5u32.saturating_sub(1u32)`
|
||||||
|
|
||||||
|
error: aborting due to 25 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -223,3 +223,10 @@ fn msrv_1_42() {
|
||||||
let _y = matches!(Some(5), Some(0));
|
let _y = matches!(Some(5), Some(0));
|
||||||
//~^^^^ match_like_matches_macro
|
//~^^^^ match_like_matches_macro
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::option_option)]
|
||||||
|
fn issue15841(opt: Option<Option<Option<i32>>>, value: i32) {
|
||||||
|
// Lint: no if-let _in the guard_
|
||||||
|
let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() }));
|
||||||
|
//~^^^^ match_like_matches_macro
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -267,3 +267,13 @@ fn msrv_1_42() {
|
||||||
};
|
};
|
||||||
//~^^^^ match_like_matches_macro
|
//~^^^^ match_like_matches_macro
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::option_option)]
|
||||||
|
fn issue15841(opt: Option<Option<Option<i32>>>, value: i32) {
|
||||||
|
// Lint: no if-let _in the guard_
|
||||||
|
let _ = match opt {
|
||||||
|
Some(first) if (if let Some(second) = first { true } else { todo!() }) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
//~^^^^ match_like_matches_macro
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,18 @@ LL | let _y = match x {
|
||||||
LL | | Some(0) => true,
|
LL | | Some(0) => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_____^ help: try: `matches!(x, Some(0))`
|
| |_____^
|
||||||
|
|
|
|
||||||
= note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
|
= note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]`
|
= help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]`
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _y = match x {
|
||||||
|
LL - Some(0) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _y = matches!(x, Some(0));
|
||||||
|
|
|
||||||
|
|
||||||
error: redundant pattern matching, consider using `is_some()`
|
error: redundant pattern matching, consider using `is_some()`
|
||||||
--> tests/ui/match_like_matches_macro.rs:20:14
|
--> tests/ui/match_like_matches_macro.rs:20:14
|
||||||
|
|
@ -42,13 +50,28 @@ LL | let _zz = match x {
|
||||||
LL | | Some(r) if r == 0 => false,
|
LL | | Some(r) if r == 0 => false,
|
||||||
LL | | _ => true,
|
LL | | _ => true,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_____^ help: try: `!matches!(x, Some(r) if r == 0)`
|
| |_____^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _zz = match x {
|
||||||
|
LL - Some(r) if r == 0 => false,
|
||||||
|
LL - _ => true,
|
||||||
|
LL - };
|
||||||
|
LL + let _zz = !matches!(x, Some(r) if r == 0);
|
||||||
|
|
|
||||||
|
|
||||||
error: if let .. else expression looks like `matches!` macro
|
error: `if let .. else` expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:41:16
|
--> tests/ui/match_like_matches_macro.rs:41:16
|
||||||
|
|
|
|
||||||
LL | let _zzz = if let Some(5) = x { true } else { false };
|
LL | let _zzz = if let Some(5) = x { true } else { false };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(x, Some(5))`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _zzz = if let Some(5) = x { true } else { false };
|
||||||
|
LL + let _zzz = matches!(x, Some(5));
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:66:20
|
--> tests/ui/match_like_matches_macro.rs:66:20
|
||||||
|
|
@ -59,7 +82,17 @@ LL | | E::A(_) => true,
|
||||||
LL | | E::B(_) => true,
|
LL | | E::B(_) => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_________^ help: try: `matches!(x, E::A(_) | E::B(_))`
|
| |_________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _ans = match x {
|
||||||
|
LL - E::A(_) => true,
|
||||||
|
LL - E::B(_) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _ans = matches!(x, E::A(_) | E::B(_));
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:77:20
|
--> tests/ui/match_like_matches_macro.rs:77:20
|
||||||
|
|
@ -71,7 +104,19 @@ LL | | true
|
||||||
... |
|
... |
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_________^ help: try: `matches!(x, E::A(_) | E::B(_))`
|
| |_________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _ans = match x {
|
||||||
|
LL - E::A(_) => {
|
||||||
|
LL - true
|
||||||
|
LL - }
|
||||||
|
LL - E::B(_) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _ans = matches!(x, E::A(_) | E::B(_));
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:88:20
|
--> tests/ui/match_like_matches_macro.rs:88:20
|
||||||
|
|
@ -82,7 +127,17 @@ LL | | E::B(_) => false,
|
||||||
LL | | E::C => false,
|
LL | | E::C => false,
|
||||||
LL | | _ => true,
|
LL | | _ => true,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_________^ help: try: `!matches!(x, E::B(_) | E::C)`
|
| |_________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _ans = match x {
|
||||||
|
LL - E::B(_) => false,
|
||||||
|
LL - E::C => false,
|
||||||
|
LL - _ => true,
|
||||||
|
LL - };
|
||||||
|
LL + let _ans = !matches!(x, E::B(_) | E::C);
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:149:18
|
--> tests/ui/match_like_matches_macro.rs:149:18
|
||||||
|
|
@ -92,7 +147,16 @@ LL | let _z = match &z {
|
||||||
LL | | Some(3) => true,
|
LL | | Some(3) => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_________^ help: try: `matches!(z, Some(3))`
|
| |_________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _z = match &z {
|
||||||
|
LL - Some(3) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _z = matches!(z, Some(3));
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:159:18
|
--> tests/ui/match_like_matches_macro.rs:159:18
|
||||||
|
|
@ -102,7 +166,16 @@ LL | let _z = match &z {
|
||||||
LL | | Some(3) => true,
|
LL | | Some(3) => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_________^ help: try: `matches!(&z, Some(3))`
|
| |_________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _z = match &z {
|
||||||
|
LL - Some(3) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _z = matches!(&z, Some(3));
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:177:21
|
--> tests/ui/match_like_matches_macro.rs:177:21
|
||||||
|
|
@ -112,7 +185,16 @@ LL | let _ = match &z {
|
||||||
LL | | AnEnum::X => true,
|
LL | | AnEnum::X => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_____________^ help: try: `matches!(&z, AnEnum::X)`
|
| |_____________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _ = match &z {
|
||||||
|
LL - AnEnum::X => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _ = matches!(&z, AnEnum::X);
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:192:20
|
--> tests/ui/match_like_matches_macro.rs:192:20
|
||||||
|
|
@ -122,7 +204,16 @@ LL | let _res = match &val {
|
||||||
LL | | &Some(ref _a) => true,
|
LL | | &Some(ref _a) => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_________^ help: try: `matches!(&val, &Some(ref _a))`
|
| |_________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _res = match &val {
|
||||||
|
LL - &Some(ref _a) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _res = matches!(&val, &Some(ref _a));
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:205:20
|
--> tests/ui/match_like_matches_macro.rs:205:20
|
||||||
|
|
@ -132,7 +223,16 @@ LL | let _res = match &val {
|
||||||
LL | | &Some(ref _a) => true,
|
LL | | &Some(ref _a) => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_________^ help: try: `matches!(&val, &Some(ref _a))`
|
| |_________^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _res = match &val {
|
||||||
|
LL - &Some(ref _a) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _res = matches!(&val, &Some(ref _a));
|
||||||
|
|
|
||||||
|
|
||||||
error: match expression looks like `matches!` macro
|
error: match expression looks like `matches!` macro
|
||||||
--> tests/ui/match_like_matches_macro.rs:264:14
|
--> tests/ui/match_like_matches_macro.rs:264:14
|
||||||
|
|
@ -142,7 +242,35 @@ LL | let _y = match Some(5) {
|
||||||
LL | | Some(0) => true,
|
LL | | Some(0) => true,
|
||||||
LL | | _ => false,
|
LL | | _ => false,
|
||||||
LL | | };
|
LL | | };
|
||||||
| |_____^ help: try: `matches!(Some(5), Some(0))`
|
| |_____^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _y = match Some(5) {
|
||||||
|
LL - Some(0) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _y = matches!(Some(5), Some(0));
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to 14 previous errors
|
error: match expression looks like `matches!` macro
|
||||||
|
--> tests/ui/match_like_matches_macro.rs:274:13
|
||||||
|
|
|
||||||
|
LL | let _ = match opt {
|
||||||
|
| _____________^
|
||||||
|
LL | | Some(first) if (if let Some(second) = first { true } else { todo!() }) => true,
|
||||||
|
LL | | _ => false,
|
||||||
|
LL | | };
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
help: use `matches!` directly
|
||||||
|
|
|
||||||
|
LL - let _ = match opt {
|
||||||
|
LL - Some(first) if (if let Some(second) = first { true } else { todo!() }) => true,
|
||||||
|
LL - _ => false,
|
||||||
|
LL - };
|
||||||
|
LL + let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() }));
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 15 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
51
tests/ui/match_like_matches_macro_if_let_guard.rs
Normal file
51
tests/ui/match_like_matches_macro_if_let_guard.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
//@check-pass
|
||||||
|
#![warn(clippy::match_like_matches_macro)]
|
||||||
|
#![feature(if_let_guard)]
|
||||||
|
|
||||||
|
#[expect(clippy::option_option)]
|
||||||
|
fn issue15841(opt: Option<Option<Option<i32>>>, value: i32) {
|
||||||
|
let _ = match opt {
|
||||||
|
Some(first)
|
||||||
|
if let Some(second) = first
|
||||||
|
&& let Some(third) = second
|
||||||
|
&& third == value =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if-let is the second if
|
||||||
|
let _ = match opt {
|
||||||
|
Some(first)
|
||||||
|
if first.is_some()
|
||||||
|
&& let Some(second) = first =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// if-let is the third if
|
||||||
|
let _ = match opt {
|
||||||
|
Some(first)
|
||||||
|
if first.is_some()
|
||||||
|
&& first.is_none()
|
||||||
|
&& let Some(second) = first =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// don't get confused by `or`s
|
||||||
|
let _ = match opt {
|
||||||
|
Some(first)
|
||||||
|
if (first.is_some() || first.is_none())
|
||||||
|
&& let Some(second) = first =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,407 +1,191 @@
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:30:5
|
--> tests/ui/missing_asserts_for_indexing.rs:30:5
|
||||||
|
|
|
|
||||||
LL | assert!(v.len() < 5);
|
|
||||||
| -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:30:5
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:30:12
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:30:19
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:30:26
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:30:33
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
= note: asserting the length before indexing will elide bounds checks
|
||||||
= note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
|
= note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]`
|
= help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]`
|
||||||
|
help: provide the highest index that is indexed with
|
||||||
|
|
|
||||||
|
LL - assert!(v.len() < 5);
|
||||||
|
LL + assert!(v.len() > 4);
|
||||||
|
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:36:5
|
--> tests/ui/missing_asserts_for_indexing.rs:36:5
|
||||||
|
|
|
|
||||||
LL | assert!(v.len() <= 5);
|
|
||||||
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:36:5
|
|
||||||
|
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL - assert!(v.len() <= 5);
|
||||||
| ^^^^
|
LL + assert!(v.len() > 4);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:36:12
|
|
||||||
|
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:36:19
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:36:26
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:36:33
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:42:5
|
--> tests/ui/missing_asserts_for_indexing.rs:42:5
|
||||||
|
|
|
|
||||||
LL | assert!(v.len() > 3);
|
|
||||||
| -------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:42:5
|
|
||||||
|
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL - assert!(v.len() > 3);
|
||||||
| ^^^^
|
LL + assert!(v.len() > 4);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:42:12
|
|
||||||
|
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:42:19
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:42:26
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:42:33
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:48:5
|
--> tests/ui/missing_asserts_for_indexing.rs:48:5
|
||||||
|
|
|
|
||||||
LL | assert!(v.len() >= 4);
|
|
||||||
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:48:5
|
|
||||||
|
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL - assert!(v.len() >= 4);
|
||||||
| ^^^^
|
LL + assert!(v.len() > 4);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:48:12
|
|
||||||
|
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:48:19
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:48:26
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:48:33
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:66:13
|
--> tests/ui/missing_asserts_for_indexing.rs:66:13
|
||||||
|
|
|
|
||||||
LL | assert!(v.len() >= 3);
|
|
||||||
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 3)`
|
|
||||||
LL | let _ = v[0];
|
|
||||||
| _____________^
|
|
||||||
... |
|
|
||||||
LL | | let _ = v[1..4];
|
|
||||||
| |___________________^
|
|
||||||
|
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:66:13
|
|
||||||
|
|
|
||||||
LL | let _ = v[0];
|
LL | let _ = v[0];
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: slice indexed here
|
...
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:69:13
|
|
||||||
|
|
|
||||||
LL | let _ = v[1..4];
|
LL | let _ = v[1..4];
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
|
||||||
|
help: provide the highest index that is indexed with
|
||||||
|
|
|
||||||
|
LL - assert!(v.len() >= 3);
|
||||||
|
LL + assert!(v.len() > 3);
|
||||||
|
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:81:13
|
--> tests/ui/missing_asserts_for_indexing.rs:81:13
|
||||||
|
|
|
|
||||||
LL | assert!(v.len() >= 4);
|
|
||||||
| --------------------- help: provide the highest index that is indexed with: `assert!(v.len() > 4)`
|
|
||||||
LL | let _ = v[0];
|
|
||||||
| _____________^
|
|
||||||
... |
|
|
||||||
LL | | let _ = v[1..=4];
|
|
||||||
| |____________________^
|
|
||||||
|
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:81:13
|
|
||||||
|
|
|
||||||
LL | let _ = v[0];
|
LL | let _ = v[0];
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: slice indexed here
|
...
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:84:13
|
|
||||||
|
|
|
||||||
LL | let _ = v[1..=4];
|
LL | let _ = v[1..=4];
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
|
||||||
|
help: provide the highest index that is indexed with
|
||||||
|
|
|
||||||
|
LL - assert!(v.len() >= 4);
|
||||||
|
LL + assert!(v.len() > 4);
|
||||||
|
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:97:13
|
--> tests/ui/missing_asserts_for_indexing.rs:97:13
|
||||||
|
|
|
|
||||||
LL | assert!(v1.len() >= 12);
|
|
||||||
| ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
|
|
||||||
LL | assert!(v2.len() >= 15);
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
LL | let _ = v1[0] + v1[12];
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:97:13
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
LL - assert!(v1.len() >= 12);
|
||||||
| ^^^^^
|
LL + assert!(v1.len() > 12);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:97:21
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
|
||||||
| ^^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:100:13
|
--> tests/ui/missing_asserts_for_indexing.rs:100:13
|
||||||
|
|
|
|
||||||
LL | assert!(v2.len() >= 15);
|
|
||||||
| ----------------------- help: provide the highest index that is indexed with: `assert!(v2.len() > 15)`
|
|
||||||
...
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
LL | let _ = v2[5] + v2[15];
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:100:13
|
|
||||||
|
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
LL - assert!(v2.len() >= 15);
|
||||||
| ^^^^^
|
LL + assert!(v2.len() > 15);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:100:21
|
|
||||||
|
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
|
||||||
| ^^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:106:13
|
--> tests/ui/missing_asserts_for_indexing.rs:106:13
|
||||||
|
|
|
|
||||||
LL | assert!(v1.len() >= 12);
|
|
||||||
| ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() > 12)`
|
|
||||||
LL | assert!(v2.len() > 15);
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
LL | let _ = v1[0] + v1[12];
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:106:13
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
LL - assert!(v1.len() >= 12);
|
||||||
| ^^^^^
|
LL + assert!(v1.len() > 12);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:106:21
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
|
||||||
| ^^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:131:13
|
--> tests/ui/missing_asserts_for_indexing.rs:131:13
|
||||||
|
|
|
|
||||||
LL | assert!(v1.len() == 2);
|
|
||||||
| ---------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)`
|
|
||||||
...
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
LL | let _ = v1[0] + v1[1] + v1[2];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^ ^^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:131:13
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
LL - assert!(v1.len() == 2);
|
||||||
| ^^^^^
|
LL + assert!(v1.len() == 3);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:131:21
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:131:29
|
|
||||||
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:136:13
|
--> tests/ui/missing_asserts_for_indexing.rs:136:13
|
||||||
|
|
|
|
||||||
LL | assert!(2 == v3.len());
|
|
||||||
| ---------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)`
|
|
||||||
...
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
LL | let _ = v3[0] + v3[1] + v3[2];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^ ^^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:136:13
|
|
||||||
|
|
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
LL - assert!(2 == v3.len());
|
||||||
| ^^^^^
|
LL + assert!(v3.len() == 3);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:136:21
|
|
||||||
|
|
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:136:29
|
|
||||||
|
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
|
||||||
| ^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:158:13
|
--> tests/ui/missing_asserts_for_indexing.rs:158:13
|
||||||
|
|
|
|
||||||
LL | assert_eq!(v1.len(), 2);
|
|
||||||
| ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v1.len(), 3)`
|
|
||||||
...
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
LL | let _ = v1[0] + v1[1] + v1[2];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^ ^^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:158:13
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
LL - assert_eq!(v1.len(), 2);
|
||||||
| ^^^^^
|
LL + assert_eq!(v1.len(), 3);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:158:21
|
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:158:29
|
|
||||||
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:163:13
|
--> tests/ui/missing_asserts_for_indexing.rs:163:13
|
||||||
|
|
|
|
||||||
LL | assert_eq!(2, v3.len());
|
|
||||||
| ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v3.len(), 3)`
|
|
||||||
...
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
LL | let _ = v3[0] + v3[1] + v3[2];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^ ^^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:163:13
|
|
||||||
|
|
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
LL - assert_eq!(2, v3.len());
|
||||||
| ^^^^^
|
LL + assert_eq!(v3.len(), 3);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:163:21
|
|
||||||
|
|
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:163:29
|
|
||||||
|
|
|
||||||
LL | let _ = v3[0] + v3[1] + v3[2];
|
|
||||||
| ^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:172:17
|
--> tests/ui/missing_asserts_for_indexing.rs:172:17
|
||||||
|
|
|
|
||||||
LL | assert_eq!(v.len(), 2);
|
|
||||||
| ---------------------- help: provide the highest index that is indexed with: `assert_eq!(v.len(), 3)`
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
LL | let _ = v[0] + v[1] + v[2];
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^ ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:172:17
|
|
||||||
|
|
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
LL - assert_eq!(v.len(), 2);
|
||||||
| ^^^^
|
LL + assert_eq!(v.len(), 3);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:172:24
|
|
||||||
|
|
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:172:31
|
|
||||||
|
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:178:17
|
--> tests/ui/missing_asserts_for_indexing.rs:178:17
|
||||||
|
|
|
|
||||||
LL | debug_assert_eq!(v.len(), 2);
|
|
||||||
| ---------------------------- help: provide the highest index that is indexed with: `debug_assert_eq!(v.len(), 3)`
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
LL | let _ = v[0] + v[1] + v[2];
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^ ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
note: slice indexed here
|
help: provide the highest index that is indexed with
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:178:17
|
|
||||||
|
|
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
LL - debug_assert_eq!(v.len(), 2);
|
||||||
| ^^^^
|
LL + debug_assert_eq!(v.len(), 3);
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:178:24
|
|
||||||
|
|
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing.rs:178:31
|
|
||||||
|
|
|
||||||
LL | let _ = v[0] + v[1] + v[2];
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: aborting due to 15 previous errors
|
error: aborting due to 15 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,34 +2,9 @@ error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5
|
||||||
|
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^ ^^^^ ^^^^ ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v.len() > 4);`
|
= help: consider asserting the length before indexing: `assert!(v.len() > 4);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:12
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:19
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:26
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:33
|
|
||||||
|
|
|
||||||
LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
= note: asserting the length before indexing will elide bounds checks
|
||||||
= note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
|
= note: `-D clippy::missing-asserts-for-indexing` implied by `-D warnings`
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]`
|
= help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]`
|
||||||
|
|
@ -37,191 +12,82 @@ LL | v[0] + v[1] + v[2] + v[3] + v[4]
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13
|
||||||
|
|
|
|
||||||
LL | let _ = v[0];
|
|
||||||
| _____________^
|
|
||||||
... |
|
|
||||||
LL | | let _ = v[1..4];
|
|
||||||
| |___________________^
|
|
||||||
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v.len() > 3);`
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13
|
|
||||||
|
|
|
||||||
LL | let _ = v[0];
|
LL | let _ = v[0];
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: slice indexed here
|
...
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:13:13
|
|
||||||
|
|
|
||||||
LL | let _ = v[1..4];
|
LL | let _ = v[1..4];
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
|
||||||
|
= help: consider asserting the length before indexing: `assert!(v.len() > 3);`
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13
|
||||||
|
|
|
|
||||||
LL | let a = v[0];
|
|
||||||
| _____________^
|
|
||||||
LL | |
|
|
||||||
LL | |
|
|
||||||
LL | | let b = v[1];
|
|
||||||
LL | | let c = v[2];
|
|
||||||
| |________________^
|
|
||||||
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v.len() > 2);`
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13
|
|
||||||
|
|
|
||||||
LL | let a = v[0];
|
LL | let a = v[0];
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: slice indexed here
|
...
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:20:13
|
|
||||||
|
|
|
||||||
LL | let b = v[1];
|
LL | let b = v[1];
|
||||||
| ^^^^
|
| ^^^^
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:21:13
|
|
||||||
|
|
|
||||||
LL | let c = v[2];
|
LL | let c = v[2];
|
||||||
| ^^^^
|
| ^^^^
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
|
||||||
|
= help: consider asserting the length before indexing: `assert!(v.len() > 2);`
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
LL | let _ = v1[0] + v1[12];
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v1.len() > 12);`
|
= help: consider asserting the length before indexing: `assert!(v1.len() > 12);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13
|
|
||||||
|
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:21
|
|
||||||
|
|
|
||||||
LL | let _ = v1[0] + v1[12];
|
|
||||||
| ^^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13
|
||||||
|
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
LL | let _ = v2[5] + v2[15];
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
|
= help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13
|
|
||||||
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:21
|
|
||||||
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
|
||||||
| ^^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13
|
||||||
|
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
LL | let _ = v2[5] + v2[15];
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
|
= help: consider asserting the length before indexing: `assert!(v2.len() > 15);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13
|
|
||||||
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:21
|
|
||||||
|
|
|
||||||
LL | let _ = v2[5] + v2[15];
|
|
||||||
| ^^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13
|
||||||
|
|
|
|
||||||
LL | let _ = f.v[0] + f.v[1];
|
LL | let _ = f.v[0] + f.v[1];
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^ ^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(f.v.len() > 1);`
|
= help: consider asserting the length before indexing: `assert!(f.v.len() > 1);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13
|
|
||||||
|
|
|
||||||
LL | let _ = f.v[0] + f.v[1];
|
|
||||||
| ^^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:22
|
|
||||||
|
|
|
||||||
LL | let _ = f.v[0] + f.v[1];
|
|
||||||
| ^^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13
|
||||||
|
|
|
|
||||||
LL | let _ = x[0] + x[1];
|
LL | let _ = x[0] + x[1];
|
||||||
| ^^^^^^^^^^^
|
| ^^^^ ^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(x.len() > 1);`
|
= help: consider asserting the length before indexing: `assert!(x.len() > 1);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13
|
|
||||||
|
|
|
||||||
LL | let _ = x[0] + x[1];
|
|
||||||
| ^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:20
|
|
||||||
|
|
|
||||||
LL | let _ = x[0] + x[1];
|
|
||||||
| ^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13
|
||||||
|
|
|
|
||||||
LL | let _ = v1[1] + v1[2];
|
LL | let _ = v1[1] + v1[2];
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v1.len() > 2);`
|
= help: consider asserting the length before indexing: `assert!(v1.len() > 2);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13
|
|
||||||
|
|
|
||||||
LL | let _ = v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:21
|
|
||||||
|
|
|
||||||
LL | let _ = v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: indexing into a slice multiple times without an `assert`
|
error: indexing into a slice multiple times without an `assert`
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13
|
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13
|
||||||
|
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
LL | let _ = v1[0] + v1[1] + v1[2];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^ ^^^^^ ^^^^^
|
||||||
|
|
|
|
||||||
= help: consider asserting the length before indexing: `assert!(v1.len() > 2);`
|
= help: consider asserting the length before indexing: `assert!(v1.len() > 2);`
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13
|
|
||||||
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:21
|
|
||||||
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
note: slice indexed here
|
|
||||||
--> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:29
|
|
||||||
|
|
|
||||||
LL | let _ = v1[0] + v1[1] + v1[2];
|
|
||||||
| ^^^^^
|
|
||||||
= note: asserting the length before indexing will elide bounds checks
|
|
||||||
|
|
||||||
error: aborting due to 10 previous errors
|
error: aborting due to 10 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
182
tests/ui/needless_type_cast.fixed
Normal file
182
tests/ui/needless_type_cast.fixed
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
#![warn(clippy::needless_type_cast)]
|
||||||
|
#![allow(clippy::no_effect, clippy::unnecessary_cast, unused)]
|
||||||
|
|
||||||
|
fn takes_i32(x: i32) -> i32 {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic<T>(x: T) -> T {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = a as i32 + 5;
|
||||||
|
let _ = a as i32 * 2;
|
||||||
|
|
||||||
|
let b: u16 = 20;
|
||||||
|
let _ = b;
|
||||||
|
let _ = b as u32;
|
||||||
|
|
||||||
|
let c: u8 = 5;
|
||||||
|
let _ = c as u16;
|
||||||
|
let _ = c as u32;
|
||||||
|
|
||||||
|
let d: i32 = 100;
|
||||||
|
let _ = d + 1;
|
||||||
|
|
||||||
|
let e = 42u8;
|
||||||
|
let _ = e as i64;
|
||||||
|
let _ = e as i64 + 10;
|
||||||
|
|
||||||
|
let f: usize = 1;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = f as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_function_call() {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = takes_i32(a as i32);
|
||||||
|
let _ = takes_i32(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_call() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = generic(a as i32);
|
||||||
|
let _ = generic(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_method_on_cast() {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = (a as i32).checked_add(5);
|
||||||
|
let _ = (a as i32).saturating_mul(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_iterator_sum() {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let arr = [a as i32, a as i32];
|
||||||
|
let _: i32 = arr.iter().copied().sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure() {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _: i32 = [1i32, 2].iter().map(|x| x + a as i32).sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_struct_field() {
|
||||||
|
struct S {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = S {
|
||||||
|
x: a as i32,
|
||||||
|
y: a as i32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _: Option<i32> = Some(a as i32);
|
||||||
|
let _: Option<i32> = Some(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mixed_context() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = takes_i32(a as i32);
|
||||||
|
let _ = generic(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_block() {
|
||||||
|
if true {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = a as i32 + 1;
|
||||||
|
let _ = a as i32 * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_match_expr() {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = match 1 {
|
||||||
|
1 => a as i32,
|
||||||
|
_ => a as i32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_return_expr() -> i32 {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
a as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure_always_cast() {
|
||||||
|
let a: i32 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||||
|
let _ = a as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure_mixed_usage() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||||
|
let _ = a + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_generic_call() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = generic(takes_i32(a as i32));
|
||||||
|
let _ = generic(takes_i32(a as i32));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_initializer() {
|
||||||
|
// Should not lint: changing type would affect what generic() returns
|
||||||
|
let a: u8 = generic(10u8);
|
||||||
|
let _ = a as i32;
|
||||||
|
let _ = a as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_unsafe_transmute() {
|
||||||
|
// Should not lint: initializer contains unsafe block
|
||||||
|
#[allow(clippy::useless_transmute)]
|
||||||
|
let x: u32 = unsafe { std::mem::transmute(0u32) };
|
||||||
|
let _ = x as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_if_with_generic() {
|
||||||
|
// Should not lint: one branch has generic return type
|
||||||
|
let x: u8 = if true { generic(1) } else { 2 };
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_match_with_generic() {
|
||||||
|
// Should not lint: one branch has generic return type
|
||||||
|
let x: u8 = match 1 {
|
||||||
|
1 => generic(1),
|
||||||
|
_ => 2,
|
||||||
|
};
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_default() {
|
||||||
|
// Should not lint: Default::default() has generic return type
|
||||||
|
let x: u8 = Default::default();
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_loop_with_generic() {
|
||||||
|
// Should not lint: loop break has generic return type
|
||||||
|
#[allow(clippy::never_loop)]
|
||||||
|
let x: u8 = loop {
|
||||||
|
break generic(1);
|
||||||
|
};
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
182
tests/ui/needless_type_cast.rs
Normal file
182
tests/ui/needless_type_cast.rs
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
#![warn(clippy::needless_type_cast)]
|
||||||
|
#![allow(clippy::no_effect, clippy::unnecessary_cast, unused)]
|
||||||
|
|
||||||
|
fn takes_i32(x: i32) -> i32 {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic<T>(x: T) -> T {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = a as i32 + 5;
|
||||||
|
let _ = a as i32 * 2;
|
||||||
|
|
||||||
|
let b: u16 = 20;
|
||||||
|
let _ = b;
|
||||||
|
let _ = b as u32;
|
||||||
|
|
||||||
|
let c: u8 = 5;
|
||||||
|
let _ = c as u16;
|
||||||
|
let _ = c as u32;
|
||||||
|
|
||||||
|
let d: i32 = 100;
|
||||||
|
let _ = d + 1;
|
||||||
|
|
||||||
|
let e = 42u8;
|
||||||
|
let _ = e as i64;
|
||||||
|
let _ = e as i64 + 10;
|
||||||
|
|
||||||
|
let f: u8 = 1;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = f as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_function_call() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = takes_i32(a as i32);
|
||||||
|
let _ = takes_i32(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_call() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = generic(a as i32);
|
||||||
|
let _ = generic(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_method_on_cast() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = (a as i32).checked_add(5);
|
||||||
|
let _ = (a as i32).saturating_mul(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_iterator_sum() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let arr = [a as i32, a as i32];
|
||||||
|
let _: i32 = arr.iter().copied().sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _: i32 = [1i32, 2].iter().map(|x| x + a as i32).sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_struct_field() {
|
||||||
|
struct S {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = S {
|
||||||
|
x: a as i32,
|
||||||
|
y: a as i32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_option() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _: Option<i32> = Some(a as i32);
|
||||||
|
let _: Option<i32> = Some(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mixed_context() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = takes_i32(a as i32);
|
||||||
|
let _ = generic(a as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_block() {
|
||||||
|
if true {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = a as i32 + 1;
|
||||||
|
let _ = a as i32 * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_match_expr() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = match 1 {
|
||||||
|
1 => a as i32,
|
||||||
|
_ => a as i32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_return_expr() -> i32 {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
a as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure_always_cast() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
//~^ needless_type_cast
|
||||||
|
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||||
|
let _ = a as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure_mixed_usage() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||||
|
let _ = a + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_generic_call() {
|
||||||
|
let a: u8 = 10;
|
||||||
|
let _ = generic(takes_i32(a as i32));
|
||||||
|
let _ = generic(takes_i32(a as i32));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_initializer() {
|
||||||
|
// Should not lint: changing type would affect what generic() returns
|
||||||
|
let a: u8 = generic(10u8);
|
||||||
|
let _ = a as i32;
|
||||||
|
let _ = a as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_unsafe_transmute() {
|
||||||
|
// Should not lint: initializer contains unsafe block
|
||||||
|
#[allow(clippy::useless_transmute)]
|
||||||
|
let x: u32 = unsafe { std::mem::transmute(0u32) };
|
||||||
|
let _ = x as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_if_with_generic() {
|
||||||
|
// Should not lint: one branch has generic return type
|
||||||
|
let x: u8 = if true { generic(1) } else { 2 };
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_match_with_generic() {
|
||||||
|
// Should not lint: one branch has generic return type
|
||||||
|
let x: u8 = match 1 {
|
||||||
|
1 => generic(1),
|
||||||
|
_ => 2,
|
||||||
|
};
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_default() {
|
||||||
|
// Should not lint: Default::default() has generic return type
|
||||||
|
let x: u8 = Default::default();
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_loop_with_generic() {
|
||||||
|
// Should not lint: loop break has generic return type
|
||||||
|
#[allow(clippy::never_loop)]
|
||||||
|
let x: u8 = loop {
|
||||||
|
break generic(1);
|
||||||
|
};
|
||||||
|
let _ = x as i32;
|
||||||
|
}
|
||||||
71
tests/ui/needless_type_cast.stderr
Normal file
71
tests/ui/needless_type_cast.stderr
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:13:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::needless-type-cast` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::needless_type_cast)]`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `usize`
|
||||||
|
--> tests/ui/needless_type_cast.rs:33:12
|
||||||
|
|
|
||||||
|
LL | let f: u8 = 1;
|
||||||
|
| ^^ help: consider defining it as: `usize`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:39:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:52:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:59:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:66:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:77:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:99:16
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:107:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:116:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: this binding is defined as `u8` but is always cast to `i32`
|
||||||
|
--> tests/ui/needless_type_cast.rs:122:12
|
||||||
|
|
|
||||||
|
LL | let a: u8 = 10;
|
||||||
|
| ^^ help: consider defining it as: `i32`
|
||||||
|
|
||||||
|
error: aborting due to 11 previous errors
|
||||||
|
|
||||||
50
tests/ui/ptr_offset_by_literal.fixed
Normal file
50
tests/ui/ptr_offset_by_literal.fixed
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#![warn(clippy::ptr_offset_by_literal)]
|
||||||
|
#![allow(clippy::inconsistent_digit_grouping)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arr = [b'a', b'b', b'c'];
|
||||||
|
let ptr = arr.as_ptr();
|
||||||
|
|
||||||
|
let var = 32;
|
||||||
|
const CONST: isize = 42;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let _ = ptr;
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr;
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.add(5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.sub(5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.offset(var);
|
||||||
|
let _ = ptr.offset(CONST);
|
||||||
|
|
||||||
|
let _ = ptr.wrapping_add(5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.wrapping_sub(5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.sub(5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.wrapping_sub(5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
// isize::MAX and isize::MIN on 32-bit systems.
|
||||||
|
let _ = ptr.add(2_147_483_647);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.sub(2_147_483_648);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.add(5_0);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.sub(5_0);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
macro_rules! offs { { $e:expr, $offs:expr } => { $e.offset($offs) }; }
|
||||||
|
offs!(ptr, 6);
|
||||||
|
offs!(ptr, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
tests/ui/ptr_offset_by_literal.rs
Normal file
50
tests/ui/ptr_offset_by_literal.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#![warn(clippy::ptr_offset_by_literal)]
|
||||||
|
#![allow(clippy::inconsistent_digit_grouping)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arr = [b'a', b'b', b'c'];
|
||||||
|
let ptr = arr.as_ptr();
|
||||||
|
|
||||||
|
let var = 32;
|
||||||
|
const CONST: isize = 42;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let _ = ptr.offset(0);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.offset(-0);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.offset(5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.offset(-5);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.offset(var);
|
||||||
|
let _ = ptr.offset(CONST);
|
||||||
|
|
||||||
|
let _ = ptr.wrapping_offset(5isize);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.wrapping_offset(-5isize);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.offset(-(5));
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.wrapping_offset(-(5));
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
// isize::MAX and isize::MIN on 32-bit systems.
|
||||||
|
let _ = ptr.offset(2_147_483_647isize);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.offset(-2_147_483_648isize);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
let _ = ptr.offset(5_0__isize);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
let _ = ptr.offset(-5_0__isize);
|
||||||
|
//~^ ptr_offset_by_literal
|
||||||
|
|
||||||
|
macro_rules! offs { { $e:expr, $offs:expr } => { $e.offset($offs) }; }
|
||||||
|
offs!(ptr, 6);
|
||||||
|
offs!(ptr, var);
|
||||||
|
}
|
||||||
|
}
|
||||||
141
tests/ui/ptr_offset_by_literal.stderr
Normal file
141
tests/ui/ptr_offset_by_literal.stderr
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
error: use of `offset` with zero
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:12:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(0);
|
||||||
|
| ^^^----------
|
||||||
|
| |
|
||||||
|
| help: remove the call to `offset`
|
||||||
|
|
|
||||||
|
= note: `-D clippy::ptr-offset-by-literal` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::ptr_offset_by_literal)]`
|
||||||
|
|
||||||
|
error: use of `offset` with zero
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:14:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(-0);
|
||||||
|
| ^^^-----------
|
||||||
|
| |
|
||||||
|
| help: remove the call to `offset`
|
||||||
|
|
||||||
|
error: use of `offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:17:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(5);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `add` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.offset(5);
|
||||||
|
LL + let _ = ptr.add(5);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:19:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(-5);
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `sub` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.offset(-5);
|
||||||
|
LL + let _ = ptr.sub(5);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `wrapping_offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:25:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.wrapping_offset(5isize);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `wrapping_add` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.wrapping_offset(5isize);
|
||||||
|
LL + let _ = ptr.wrapping_add(5);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `wrapping_offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:27:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.wrapping_offset(-5isize);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `wrapping_sub` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.wrapping_offset(-5isize);
|
||||||
|
LL + let _ = ptr.wrapping_sub(5);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:30:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(-(5));
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `sub` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.offset(-(5));
|
||||||
|
LL + let _ = ptr.sub(5);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `wrapping_offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:32:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.wrapping_offset(-(5));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `wrapping_sub` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.wrapping_offset(-(5));
|
||||||
|
LL + let _ = ptr.wrapping_sub(5);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:36:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(2_147_483_647isize);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `add` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.offset(2_147_483_647isize);
|
||||||
|
LL + let _ = ptr.add(2_147_483_647);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:38:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(-2_147_483_648isize);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `sub` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.offset(-2_147_483_648isize);
|
||||||
|
LL + let _ = ptr.sub(2_147_483_648);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:41:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(5_0__isize);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `add` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.offset(5_0__isize);
|
||||||
|
LL + let _ = ptr.add(5_0);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: use of `offset` with a literal
|
||||||
|
--> tests/ui/ptr_offset_by_literal.rs:43:17
|
||||||
|
|
|
||||||
|
LL | let _ = ptr.offset(-5_0__isize);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `sub` instead
|
||||||
|
|
|
||||||
|
LL - let _ = ptr.offset(-5_0__isize);
|
||||||
|
LL + let _ = ptr.sub(5_0);
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 12 previous errors
|
||||||
|
|
||||||
|
|
@ -24,6 +24,13 @@ struct GenericParam<T> {
|
||||||
t: T,
|
t: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrNamed {
|
||||||
|
ptr: *const u32,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Ptr(*const u32);
|
||||||
|
|
||||||
fn transmute_ptr_to_ptr() {
|
fn transmute_ptr_to_ptr() {
|
||||||
let ptr = &1u32 as *const u32;
|
let ptr = &1u32 as *const u32;
|
||||||
let mut_ptr = &mut 1u32 as *mut u32;
|
let mut_ptr = &mut 1u32 as *mut u32;
|
||||||
|
|
@ -68,6 +75,18 @@ fn transmute_ptr_to_ptr() {
|
||||||
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
|
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue1966() {
|
||||||
|
let ptr = &1u32 as *const u32;
|
||||||
|
unsafe {
|
||||||
|
let _: *const f32 = Ptr(ptr).0.cast::<f32>();
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
|
let _: *const f32 = PtrNamed { ptr }.ptr.cast::<f32>();
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
|
let _: *mut u32 = Ptr(ptr).0.cast_mut();
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn lifetime_to_static(v: *mut &()) -> *const &'static () {
|
fn lifetime_to_static(v: *mut &()) -> *const &'static () {
|
||||||
unsafe { v as *const &() }
|
unsafe { v as *const &() }
|
||||||
//~^ transmute_ptr_to_ptr
|
//~^ transmute_ptr_to_ptr
|
||||||
|
|
@ -81,11 +100,15 @@ const _: &() = {
|
||||||
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
|
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Ptr8(*const u8);
|
||||||
#[clippy::msrv = "1.37"]
|
#[clippy::msrv = "1.37"]
|
||||||
fn msrv_1_37(ptr: *const u8) {
|
fn msrv_1_37(ptr: *const u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: *const i8 = ptr as *const i8;
|
let _: *const i8 = ptr as *const i8;
|
||||||
//~^ transmute_ptr_to_ptr
|
//~^ transmute_ptr_to_ptr
|
||||||
|
let _: *const i8 = Ptr8(ptr).0 as *const i8;
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,13 @@ struct GenericParam<T> {
|
||||||
t: T,
|
t: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrNamed {
|
||||||
|
ptr: *const u32,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Ptr(*const u32);
|
||||||
|
|
||||||
fn transmute_ptr_to_ptr() {
|
fn transmute_ptr_to_ptr() {
|
||||||
let ptr = &1u32 as *const u32;
|
let ptr = &1u32 as *const u32;
|
||||||
let mut_ptr = &mut 1u32 as *mut u32;
|
let mut_ptr = &mut 1u32 as *mut u32;
|
||||||
|
|
@ -68,6 +75,18 @@ fn transmute_ptr_to_ptr() {
|
||||||
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
|
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue1966() {
|
||||||
|
let ptr = &1u32 as *const u32;
|
||||||
|
unsafe {
|
||||||
|
let _: *const f32 = transmute(Ptr(ptr));
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
|
let _: *const f32 = transmute(PtrNamed { ptr });
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
|
let _: *mut u32 = transmute(Ptr(ptr));
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn lifetime_to_static(v: *mut &()) -> *const &'static () {
|
fn lifetime_to_static(v: *mut &()) -> *const &'static () {
|
||||||
unsafe { transmute(v) }
|
unsafe { transmute(v) }
|
||||||
//~^ transmute_ptr_to_ptr
|
//~^ transmute_ptr_to_ptr
|
||||||
|
|
@ -81,11 +100,15 @@ const _: &() = {
|
||||||
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
|
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Ptr8(*const u8);
|
||||||
#[clippy::msrv = "1.37"]
|
#[clippy::msrv = "1.37"]
|
||||||
fn msrv_1_37(ptr: *const u8) {
|
fn msrv_1_37(ptr: *const u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: *const i8 = transmute(ptr);
|
let _: *const i8 = transmute(ptr);
|
||||||
//~^ transmute_ptr_to_ptr
|
//~^ transmute_ptr_to_ptr
|
||||||
|
let _: *const i8 = transmute(Ptr8(ptr));
|
||||||
|
//~^ transmute_ptr_to_ptr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:32:29
|
--> tests/ui/transmute_ptr_to_ptr.rs:39:29
|
||||||
|
|
|
|
||||||
LL | let _: *const f32 = transmute(ptr);
|
LL | let _: *const f32 = transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
@ -13,7 +13,7 @@ LL + let _: *const f32 = ptr.cast::<f32>();
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:35:27
|
--> tests/ui/transmute_ptr_to_ptr.rs:42:27
|
||||||
|
|
|
|
||||||
LL | let _: *mut f32 = transmute(mut_ptr);
|
LL | let _: *mut f32 = transmute(mut_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -25,37 +25,37 @@ LL + let _: *mut f32 = mut_ptr.cast::<f32>();
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a reference to a reference
|
error: transmute from a reference to a reference
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:39:23
|
--> tests/ui/transmute_ptr_to_ptr.rs:46:23
|
||||||
|
|
|
|
||||||
LL | let _: &f32 = transmute(&1u32);
|
LL | let _: &f32 = transmute(&1u32);
|
||||||
| ^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
|
| ^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
|
||||||
|
|
||||||
error: transmute from a reference to a reference
|
error: transmute from a reference to a reference
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:42:23
|
--> tests/ui/transmute_ptr_to_ptr.rs:49:23
|
||||||
|
|
|
|
||||||
LL | let _: &f32 = transmute(&1f64);
|
LL | let _: &f32 = transmute(&1f64);
|
||||||
| ^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)`
|
| ^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)`
|
||||||
|
|
||||||
error: transmute from a reference to a reference
|
error: transmute from a reference to a reference
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:47:27
|
--> tests/ui/transmute_ptr_to_ptr.rs:54:27
|
||||||
|
|
|
|
||||||
LL | let _: &mut f32 = transmute(&mut 1u32);
|
LL | let _: &mut f32 = transmute(&mut 1u32);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
|
| ^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
|
||||||
|
|
||||||
error: transmute from a reference to a reference
|
error: transmute from a reference to a reference
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:50:37
|
--> tests/ui/transmute_ptr_to_ptr.rs:57:37
|
||||||
|
|
|
|
||||||
LL | let _: &GenericParam<f32> = transmute(&GenericParam { t: 1u32 });
|
LL | let _: &GenericParam<f32> = transmute(&GenericParam { t: 1u32 });
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
|
||||||
|
|
||||||
error: transmute from a reference to a reference
|
error: transmute from a reference to a reference
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:54:27
|
--> tests/ui/transmute_ptr_to_ptr.rs:61:27
|
||||||
|
|
|
|
||||||
LL | let u8_ref: &u8 = transmute(u64_ref);
|
LL | let u8_ref: &u8 = transmute(u64_ref);
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)`
|
| ^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)`
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:57:29
|
--> tests/ui/transmute_ptr_to_ptr.rs:64:29
|
||||||
|
|
|
|
||||||
LL | let _: *const u32 = transmute(mut_ptr);
|
LL | let _: *const u32 = transmute(mut_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -67,7 +67,7 @@ LL + let _: *const u32 = mut_ptr.cast_const();
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:60:27
|
--> tests/ui/transmute_ptr_to_ptr.rs:67:27
|
||||||
|
|
|
|
||||||
LL | let _: *mut u32 = transmute(ptr);
|
LL | let _: *mut u32 = transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
@ -79,7 +79,43 @@ LL + let _: *mut u32 = ptr.cast_mut();
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:72:14
|
--> tests/ui/transmute_ptr_to_ptr.rs:81:29
|
||||||
|
|
|
||||||
|
LL | let _: *const f32 = transmute(Ptr(ptr));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `pointer::cast` instead
|
||||||
|
|
|
||||||
|
LL - let _: *const f32 = transmute(Ptr(ptr));
|
||||||
|
LL + let _: *const f32 = Ptr(ptr).0.cast::<f32>();
|
||||||
|
|
|
||||||
|
|
||||||
|
error: transmute from a pointer to a pointer
|
||||||
|
--> tests/ui/transmute_ptr_to_ptr.rs:83:29
|
||||||
|
|
|
||||||
|
LL | let _: *const f32 = transmute(PtrNamed { ptr });
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `pointer::cast` instead
|
||||||
|
|
|
||||||
|
LL - let _: *const f32 = transmute(PtrNamed { ptr });
|
||||||
|
LL + let _: *const f32 = PtrNamed { ptr }.ptr.cast::<f32>();
|
||||||
|
|
|
||||||
|
|
||||||
|
error: transmute from a pointer to a pointer
|
||||||
|
--> tests/ui/transmute_ptr_to_ptr.rs:85:27
|
||||||
|
|
|
||||||
|
LL | let _: *mut u32 = transmute(Ptr(ptr));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `pointer::cast_mut` instead
|
||||||
|
|
|
||||||
|
LL - let _: *mut u32 = transmute(Ptr(ptr));
|
||||||
|
LL + let _: *mut u32 = Ptr(ptr).0.cast_mut();
|
||||||
|
|
|
||||||
|
|
||||||
|
error: transmute from a pointer to a pointer
|
||||||
|
--> tests/ui/transmute_ptr_to_ptr.rs:91:14
|
||||||
|
|
|
|
||||||
LL | unsafe { transmute(v) }
|
LL | unsafe { transmute(v) }
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
@ -91,7 +127,7 @@ LL + unsafe { v as *const &() }
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:87:28
|
--> tests/ui/transmute_ptr_to_ptr.rs:108:28
|
||||||
|
|
|
|
||||||
LL | let _: *const i8 = transmute(ptr);
|
LL | let _: *const i8 = transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
@ -103,7 +139,19 @@ LL + let _: *const i8 = ptr as *const i8;
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:95:28
|
--> tests/ui/transmute_ptr_to_ptr.rs:110:28
|
||||||
|
|
|
||||||
|
LL | let _: *const i8 = transmute(Ptr8(ptr));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use an `as` cast instead
|
||||||
|
|
|
||||||
|
LL - let _: *const i8 = transmute(Ptr8(ptr));
|
||||||
|
LL + let _: *const i8 = Ptr8(ptr).0 as *const i8;
|
||||||
|
|
|
||||||
|
|
||||||
|
error: transmute from a pointer to a pointer
|
||||||
|
--> tests/ui/transmute_ptr_to_ptr.rs:118:28
|
||||||
|
|
|
|
||||||
LL | let _: *const i8 = transmute(ptr);
|
LL | let _: *const i8 = transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
@ -115,7 +163,7 @@ LL + let _: *const i8 = ptr.cast::<i8>();
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:103:26
|
--> tests/ui/transmute_ptr_to_ptr.rs:126:26
|
||||||
|
|
|
|
||||||
LL | let _: *mut u8 = transmute(ptr);
|
LL | let _: *mut u8 = transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
@ -127,7 +175,7 @@ LL + let _: *mut u8 = ptr as *mut u8;
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:105:28
|
--> tests/ui/transmute_ptr_to_ptr.rs:128:28
|
||||||
|
|
|
|
||||||
LL | let _: *const u8 = transmute(mut_ptr);
|
LL | let _: *const u8 = transmute(mut_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -139,7 +187,7 @@ LL + let _: *const u8 = mut_ptr as *const u8;
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:113:26
|
--> tests/ui/transmute_ptr_to_ptr.rs:136:26
|
||||||
|
|
|
|
||||||
LL | let _: *mut u8 = transmute(ptr);
|
LL | let _: *mut u8 = transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
@ -151,7 +199,7 @@ LL + let _: *mut u8 = ptr.cast_mut();
|
||||||
|
|
|
|
||||||
|
|
||||||
error: transmute from a pointer to a pointer
|
error: transmute from a pointer to a pointer
|
||||||
--> tests/ui/transmute_ptr_to_ptr.rs:115:28
|
--> tests/ui/transmute_ptr_to_ptr.rs:138:28
|
||||||
|
|
|
|
||||||
LL | let _: *const u8 = transmute(mut_ptr);
|
LL | let _: *const u8 = transmute(mut_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -162,5 +210,5 @@ LL - let _: *const u8 = transmute(mut_ptr);
|
||||||
LL + let _: *const u8 = mut_ptr.cast_const();
|
LL + let _: *const u8 = mut_ptr.cast_const();
|
||||||
|
|
|
|
||||||
|
|
||||||
error: aborting due to 16 previous errors
|
error: aborting due to 20 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,52 @@ fn issue1231() {
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrRefNamed<'a> {
|
||||||
|
ptr: *const &'a u32,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrRef<'a>(*const &'a u32);
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrSliceRef<'a>(*const [&'a str]);
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrSlice(*const [i32]);
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Ptr(*const u32);
|
||||||
|
impl std::ops::Add for Ptr {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, _: Self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod ptr_mod {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Ptr(*const u32);
|
||||||
|
}
|
||||||
|
fn issue1966(u: PtrSlice, v: PtrSliceRef, w: Ptr, x: PtrRefNamed, y: PtrRef, z: ptr_mod::Ptr) {
|
||||||
|
unsafe {
|
||||||
|
let _: &i32 = &*(w.0 as *const i32);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &u32 = &*w.0;
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &&u32 = &*x.ptr.cast::<&u32>();
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
// The field is not accessible. The program should not generate code
|
||||||
|
// that accesses the field.
|
||||||
|
let _: &u32 = std::mem::transmute(z);
|
||||||
|
let _ = &*w.0.cast::<u32>();
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &[&str] = &*(v.0 as *const [&str]);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _ = &*(u.0 as *const [i32]);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &&u32 = &*y.0.cast::<&u32>();
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &u32 = &*(w + w).0;
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
|
fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
match 0 {
|
match 0 {
|
||||||
|
|
@ -89,7 +135,7 @@ fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[clippy::msrv = "1.37"]
|
#[clippy::msrv = "1.37"]
|
||||||
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
|
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32, y: PtrRef) -> &'c &'b u32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let a = 0u32;
|
let a = 0u32;
|
||||||
let a = &a as *const u32;
|
let a = &a as *const u32;
|
||||||
|
|
@ -97,10 +143,16 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
let _: &u32 = &*(a as *const u32);
|
let _: &u32 = &*(a as *const u32);
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _ = &*(Ptr(a).0 as *const u32);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
match 0 {
|
match 0 {
|
||||||
0 => &*(x as *const () as *const &u32),
|
0 => &*(x as *const () as *const &u32),
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
_ => &*(x as *const () as *const &'b u32),
|
1 => &*(x as *const () as *const &'b u32),
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
2 => &*(y.0 as *const () as *const &u32),
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
_ => &*(y.0 as *const () as *const &'b u32),
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,52 @@ fn issue1231() {
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrRefNamed<'a> {
|
||||||
|
ptr: *const &'a u32,
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrRef<'a>(*const &'a u32);
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrSliceRef<'a>(*const [&'a str]);
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PtrSlice(*const [i32]);
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct Ptr(*const u32);
|
||||||
|
impl std::ops::Add for Ptr {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, _: Self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod ptr_mod {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Ptr(*const u32);
|
||||||
|
}
|
||||||
|
fn issue1966(u: PtrSlice, v: PtrSliceRef, w: Ptr, x: PtrRefNamed, y: PtrRef, z: ptr_mod::Ptr) {
|
||||||
|
unsafe {
|
||||||
|
let _: &i32 = std::mem::transmute(w);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &u32 = std::mem::transmute(w);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &&u32 = core::mem::transmute(x);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
// The field is not accessible. The program should not generate code
|
||||||
|
// that accesses the field.
|
||||||
|
let _: &u32 = std::mem::transmute(z);
|
||||||
|
let _ = std::mem::transmute::<_, &u32>(w);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &[&str] = core::mem::transmute(v);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _ = std::mem::transmute::<_, &[i32]>(u);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &&u32 = std::mem::transmute(y);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _: &u32 = std::mem::transmute(w + w);
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
|
fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
match 0 {
|
match 0 {
|
||||||
|
|
@ -89,7 +135,7 @@ fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[clippy::msrv = "1.37"]
|
#[clippy::msrv = "1.37"]
|
||||||
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
|
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32, y: PtrRef) -> &'c &'b u32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let a = 0u32;
|
let a = 0u32;
|
||||||
let a = &a as *const u32;
|
let a = &a as *const u32;
|
||||||
|
|
@ -97,10 +143,16 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
let _: &u32 = std::mem::transmute::<_, &u32>(a);
|
let _: &u32 = std::mem::transmute::<_, &u32>(a);
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
|
let _ = std::mem::transmute::<_, &u32>(Ptr(a));
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
match 0 {
|
match 0 {
|
||||||
0 => std::mem::transmute(x),
|
0 => std::mem::transmute(x),
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
_ => std::mem::transmute::<_, &&'b u32>(x),
|
1 => std::mem::transmute::<_, &&'b u32>(x),
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
2 => std::mem::transmute(y),
|
||||||
|
//~^ transmute_ptr_to_ref
|
||||||
|
_ => std::mem::transmute::<_, &&'b u32>(y),
|
||||||
//~^ transmute_ptr_to_ref
|
//~^ transmute_ptr_to_ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,125 +61,191 @@ error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
|
||||||
LL | unsafe { std::mem::transmute::<_, Bar>(raw) };
|
LL | unsafe { std::mem::transmute::<_, Bar>(raw) };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&i32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:82:23
|
||||||
|
|
|
||||||
|
LL | let _: &i32 = std::mem::transmute(w);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(w.0 as *const i32)`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:84:23
|
||||||
|
|
|
||||||
|
LL | let _: &u32 = std::mem::transmute(w);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*w.0`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:61:18
|
--> tests/ui/transmute_ptr_to_ref.rs:86:24
|
||||||
|
|
|
||||||
|
LL | let _: &&u32 = core::mem::transmute(x);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.ptr.cast::<&u32>()`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:91:17
|
||||||
|
|
|
||||||
|
LL | let _ = std::mem::transmute::<_, &u32>(w);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*w.0.cast::<u32>()`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:93:26
|
||||||
|
|
|
||||||
|
LL | let _: &[&str] = core::mem::transmute(v);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(v.0 as *const [&str])`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:95:17
|
||||||
|
|
|
||||||
|
LL | let _ = std::mem::transmute::<_, &[i32]>(u);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(u.0 as *const [i32])`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:97:24
|
||||||
|
|
|
||||||
|
LL | let _: &&u32 = std::mem::transmute(y);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.0.cast::<&u32>()`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:99:23
|
||||||
|
|
|
||||||
|
LL | let _: &u32 = std::mem::transmute(w + w);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(w + w).0`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:107:18
|
||||||
|
|
|
|
||||||
LL | 0 => std::mem::transmute(x),
|
LL | 0 => std::mem::transmute(x),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:63:18
|
--> tests/ui/transmute_ptr_to_ref.rs:109:18
|
||||||
|
|
|
|
||||||
LL | 1 => std::mem::transmute(y),
|
LL | 1 => std::mem::transmute(y),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:65:18
|
--> tests/ui/transmute_ptr_to_ref.rs:111:18
|
||||||
|
|
|
|
||||||
LL | 2 => std::mem::transmute::<_, &&'b u32>(x),
|
LL | 2 => std::mem::transmute::<_, &&'b u32>(x),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:67:18
|
--> tests/ui/transmute_ptr_to_ref.rs:113:18
|
||||||
|
|
|
|
||||||
LL | _ => std::mem::transmute::<_, &&'b u32>(y),
|
LL | _ => std::mem::transmute::<_, &&'b u32>(y),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:78:23
|
--> tests/ui/transmute_ptr_to_ref.rs:124:23
|
||||||
|
|
|
|
||||||
LL | let _: &u32 = std::mem::transmute(a);
|
LL | let _: &u32 = std::mem::transmute(a);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:80:23
|
--> tests/ui/transmute_ptr_to_ref.rs:126:23
|
||||||
|
|
|
|
||||||
LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
|
LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:83:18
|
--> tests/ui/transmute_ptr_to_ref.rs:129:18
|
||||||
|
|
|
|
||||||
LL | 0 => std::mem::transmute(x),
|
LL | 0 => std::mem::transmute(x),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:85:18
|
--> tests/ui/transmute_ptr_to_ref.rs:131:18
|
||||||
|
|
|
|
||||||
LL | _ => std::mem::transmute::<_, &&'b u32>(x),
|
LL | _ => std::mem::transmute::<_, &&'b u32>(x),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:96:23
|
--> tests/ui/transmute_ptr_to_ref.rs:142:23
|
||||||
|
|
|
|
||||||
LL | let _: &u32 = std::mem::transmute(a);
|
LL | let _: &u32 = std::mem::transmute(a);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:98:23
|
--> tests/ui/transmute_ptr_to_ref.rs:144:23
|
||||||
|
|
|
|
||||||
LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
|
LL | let _: &u32 = std::mem::transmute::<_, &u32>(a);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:146:17
|
||||||
|
|
|
||||||
|
LL | let _ = std::mem::transmute::<_, &u32>(Ptr(a));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(Ptr(a).0 as *const u32)`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:101:18
|
--> tests/ui/transmute_ptr_to_ref.rs:149:18
|
||||||
|
|
|
|
||||||
LL | 0 => std::mem::transmute(x),
|
LL | 0 => std::mem::transmute(x),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)`
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:103:18
|
--> tests/ui/transmute_ptr_to_ref.rs:151:18
|
||||||
|
|
|
|
||||||
LL | _ => std::mem::transmute::<_, &&'b u32>(x),
|
LL | 1 => std::mem::transmute::<_, &&'b u32>(x),
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:153:18
|
||||||
|
|
|
||||||
|
LL | 2 => std::mem::transmute(y),
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(y.0 as *const () as *const &u32)`
|
||||||
|
|
||||||
|
error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
|
||||||
|
--> tests/ui/transmute_ptr_to_ref.rs:155:18
|
||||||
|
|
|
||||||
|
LL | _ => std::mem::transmute::<_, &&'b u32>(y),
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(y.0 as *const () as *const &'b u32)`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`)
|
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:113:17
|
--> tests/ui/transmute_ptr_to_ref.rs:165:17
|
||||||
|
|
|
|
||||||
LL | let _ = core::mem::transmute::<_, &[u32]>(ptr);
|
LL | let _ = core::mem::transmute::<_, &[u32]>(ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`)
|
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:115:25
|
--> tests/ui/transmute_ptr_to_ref.rs:167:25
|
||||||
|
|
|
|
||||||
LL | let _: &[u32] = core::mem::transmute(ptr);
|
LL | let _: &[u32] = core::mem::transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`)
|
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:119:17
|
--> tests/ui/transmute_ptr_to_ref.rs:171:17
|
||||||
|
|
|
|
||||||
LL | let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr);
|
LL | let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`)
|
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:121:27
|
--> tests/ui/transmute_ptr_to_ref.rs:173:27
|
||||||
|
|
|
|
||||||
LL | let _: &[&[u8]] = core::mem::transmute(a_s_ptr);
|
LL | let _: &[&[u8]] = core::mem::transmute(a_s_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`)
|
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:125:17
|
--> tests/ui/transmute_ptr_to_ref.rs:177:17
|
||||||
|
|
|
|
||||||
LL | let _ = core::mem::transmute::<_, &[i32]>(ptr);
|
LL | let _ = core::mem::transmute::<_, &[i32]>(ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`)
|
error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:127:25
|
--> tests/ui/transmute_ptr_to_ref.rs:179:25
|
||||||
|
|
|
|
||||||
LL | let _: &[i32] = core::mem::transmute(ptr);
|
LL | let _: &[i32] = core::mem::transmute(ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`)
|
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:131:17
|
--> tests/ui/transmute_ptr_to_ref.rs:183:17
|
||||||
|
|
|
|
||||||
LL | let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr);
|
LL | let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])`
|
||||||
|
|
||||||
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`)
|
error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`)
|
||||||
--> tests/ui/transmute_ptr_to_ref.rs:133:26
|
--> tests/ui/transmute_ptr_to_ref.rs:185:26
|
||||||
|
|
|
|
||||||
LL | let _: &[&str] = core::mem::transmute(a_s_ptr);
|
LL | let _: &[&str] = core::mem::transmute(a_s_ptr);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])`
|
||||||
|
|
||||||
error: aborting due to 30 previous errors
|
error: aborting due to 41 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,3 +116,26 @@ fn msrv_juust_right() {
|
||||||
let x = &[1, 2];
|
let x = &[1, 2];
|
||||||
let x = (x[0], x[1]);
|
let x = (x[0], x[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue16192() {
|
||||||
|
fn do_something(tuple: (u32, u32)) {}
|
||||||
|
fn produce_array() -> [u32; 2] {
|
||||||
|
[1, 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
let [a, b] = produce_array();
|
||||||
|
for tuple in [(a, b), (b, a)] {
|
||||||
|
do_something(tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
let [a, b] = produce_array();
|
||||||
|
let x = b;
|
||||||
|
do_something((a, b));
|
||||||
|
|
||||||
|
let [a, b] = produce_array();
|
||||||
|
do_something((b, a));
|
||||||
|
|
||||||
|
let [a, b] = produce_array();
|
||||||
|
do_something((a, b));
|
||||||
|
//~^ tuple_array_conversions
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,5 +80,13 @@ LL | let x = [x.0, x.1];
|
||||||
|
|
|
|
||||||
= help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
|
= help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
|
||||||
|
|
||||||
error: aborting due to 10 previous errors
|
error: it looks like you're trying to convert an array to a tuple
|
||||||
|
--> tests/ui/tuple_array_conversions.rs:139:18
|
||||||
|
|
|
||||||
|
LL | do_something((a, b));
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
|
||||||
|
|
||||||
|
error: aborting due to 11 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -442,3 +442,14 @@ fn issue14739() {
|
||||||
let _ = R.map(|_x| 0);
|
let _ = R.map(|_x| 0);
|
||||||
//~^ useless_conversion
|
//~^ useless_conversion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue16165() {
|
||||||
|
macro_rules! mac {
|
||||||
|
(iter $e:expr) => {
|
||||||
|
$e.iter()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in mac!(iter [1, 2]) {}
|
||||||
|
//~^ useless_conversion
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -442,3 +442,14 @@ fn issue14739() {
|
||||||
let _ = R.into_iter().map(|_x| 0);
|
let _ = R.into_iter().map(|_x| 0);
|
||||||
//~^ useless_conversion
|
//~^ useless_conversion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue16165() {
|
||||||
|
macro_rules! mac {
|
||||||
|
(iter $e:expr) => {
|
||||||
|
$e.iter()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in mac!(iter [1, 2]).into_iter() {}
|
||||||
|
//~^ useless_conversion
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -389,5 +389,11 @@ error: useless conversion to the same type: `std::ops::Range<u32>`
|
||||||
LL | let _ = R.into_iter().map(|_x| 0);
|
LL | let _ = R.into_iter().map(|_x| 0);
|
||||||
| ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R`
|
| ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R`
|
||||||
|
|
||||||
error: aborting due to 43 previous errors
|
error: useless conversion to the same type: `std::slice::Iter<'_, i32>`
|
||||||
|
--> tests/ui/useless_conversion.rs:453:14
|
||||||
|
|
|
||||||
|
LL | for _ in mac!(iter [1, 2]).into_iter() {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `mac!(iter [1, 2])`
|
||||||
|
|
||||||
|
error: aborting due to 44 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -492,6 +492,110 @@ fn issue13123() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue16089() {
|
||||||
|
trait CertainTrait: Iterator<Item = u8> {
|
||||||
|
fn iter_over_self(&mut self) {
|
||||||
|
let mut a = 0;
|
||||||
|
for r in &mut *self {
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
a = r;
|
||||||
|
}
|
||||||
|
self.use_after_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_after_iter(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_sized_trait_not_reborrowed() {
|
||||||
|
trait CertainTrait: Iterator<Item = u8> + Sized {
|
||||||
|
fn iter_over_self(&mut self) {
|
||||||
|
let mut a = 0;
|
||||||
|
// Check that the suggestion is just "self", since the trait is sized.
|
||||||
|
for r in self.by_ref() {
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
a = r;
|
||||||
|
}
|
||||||
|
self.use_after_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_after_iter(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_nested_derefs() {
|
||||||
|
struct S<T>(T);
|
||||||
|
impl<T> core::ops::Deref for S<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> core::ops::DerefMut for S<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f(mut x: S<S<&mut dyn Iterator<Item = u32>>>) {
|
||||||
|
for _ in &mut ***x {}
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_nested_derefs_last_not_sized() {
|
||||||
|
struct WithSize<T>(T);
|
||||||
|
impl<T> core::ops::Deref for WithSize<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> core::ops::DerefMut for WithSize<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The suggestion must use `&mut **x`. Using `x.by_ref()` doesn't work in this
|
||||||
|
// case, since the last type adjustment for `x` in the expression `x.next()` is
|
||||||
|
// to dereference a `?Sized` trait.
|
||||||
|
fn f(mut x: WithSize<&mut dyn Iterator<Item = u32>>) {
|
||||||
|
for _ in &mut **x {}
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_nested_derefs_last_sized() {
|
||||||
|
struct NoSize<T: ?Sized>(T);
|
||||||
|
impl<T: ?Sized> core::ops::Deref for NoSize<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: ?Sized> core::ops::DerefMut for NoSize<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SizedIter {}
|
||||||
|
|
||||||
|
impl Iterator for SizedIter {
|
||||||
|
type Item = u32;
|
||||||
|
fn next(&mut self) -> Option<u32> {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want the suggestion to be `x.by_ref()`. It works in this case since the last type
|
||||||
|
// adjustment for `x` in the expression `x.next()` is to dereference a Sized type.
|
||||||
|
fn f(mut x: NoSize<NoSize<SizedIter>>) {
|
||||||
|
for _ in x.by_ref() {}
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut it = 0..20;
|
let mut it = 0..20;
|
||||||
for _ in it {
|
for _ in it {
|
||||||
|
|
|
||||||
|
|
@ -492,6 +492,110 @@ fn issue13123() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue16089() {
|
||||||
|
trait CertainTrait: Iterator<Item = u8> {
|
||||||
|
fn iter_over_self(&mut self) {
|
||||||
|
let mut a = 0;
|
||||||
|
while let Some(r) = self.next() {
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
a = r;
|
||||||
|
}
|
||||||
|
self.use_after_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_after_iter(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_sized_trait_not_reborrowed() {
|
||||||
|
trait CertainTrait: Iterator<Item = u8> + Sized {
|
||||||
|
fn iter_over_self(&mut self) {
|
||||||
|
let mut a = 0;
|
||||||
|
// Check that the suggestion is just "self", since the trait is sized.
|
||||||
|
while let Some(r) = self.next() {
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
a = r;
|
||||||
|
}
|
||||||
|
self.use_after_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_after_iter(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_nested_derefs() {
|
||||||
|
struct S<T>(T);
|
||||||
|
impl<T> core::ops::Deref for S<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> core::ops::DerefMut for S<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f(mut x: S<S<&mut dyn Iterator<Item = u32>>>) {
|
||||||
|
while let Some(_) = x.next() {}
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_nested_derefs_last_not_sized() {
|
||||||
|
struct WithSize<T>(T);
|
||||||
|
impl<T> core::ops::Deref for WithSize<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> core::ops::DerefMut for WithSize<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The suggestion must use `&mut **x`. Using `x.by_ref()` doesn't work in this
|
||||||
|
// case, since the last type adjustment for `x` in the expression `x.next()` is
|
||||||
|
// to dereference a `?Sized` trait.
|
||||||
|
fn f(mut x: WithSize<&mut dyn Iterator<Item = u32>>) {
|
||||||
|
while let Some(_) = x.next() {}
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue16089_nested_derefs_last_sized() {
|
||||||
|
struct NoSize<T: ?Sized>(T);
|
||||||
|
impl<T: ?Sized> core::ops::Deref for NoSize<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: ?Sized> core::ops::DerefMut for NoSize<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SizedIter {}
|
||||||
|
|
||||||
|
impl Iterator for SizedIter {
|
||||||
|
type Item = u32;
|
||||||
|
fn next(&mut self) -> Option<u32> {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want the suggestion to be `x.by_ref()`. It works in this case since the last type
|
||||||
|
// adjustment for `x` in the expression `x.next()` is to dereference a Sized type.
|
||||||
|
fn f(mut x: NoSize<NoSize<SizedIter>>) {
|
||||||
|
while let Some(_) = x.next() {}
|
||||||
|
//~^ while_let_on_iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut it = 0..20;
|
let mut it = 0..20;
|
||||||
while let Some(..) = it.next() {
|
while let Some(..) = it.next() {
|
||||||
|
|
|
||||||
|
|
@ -164,10 +164,40 @@ LL | 'label: while let Some(n) = it.next() {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'label: for n in it`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'label: for n in it`
|
||||||
|
|
||||||
error: this loop could be written as a `for` loop
|
error: this loop could be written as a `for` loop
|
||||||
--> tests/ui/while_let_on_iterator.rs:497:5
|
--> tests/ui/while_let_on_iterator.rs:499:13
|
||||||
|
|
|
||||||
|
LL | while let Some(r) = self.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for r in &mut *self`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> tests/ui/while_let_on_iterator.rs:515:13
|
||||||
|
|
|
||||||
|
LL | while let Some(r) = self.next() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for r in self.by_ref()`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> tests/ui/while_let_on_iterator.rs:541:9
|
||||||
|
|
|
||||||
|
LL | while let Some(_) = x.next() {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in &mut ***x`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> tests/ui/while_let_on_iterator.rs:563:9
|
||||||
|
|
|
||||||
|
LL | while let Some(_) = x.next() {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in &mut **x`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> tests/ui/while_let_on_iterator.rs:594:9
|
||||||
|
|
|
||||||
|
LL | while let Some(_) = x.next() {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in x.by_ref()`
|
||||||
|
|
||||||
|
error: this loop could be written as a `for` loop
|
||||||
|
--> tests/ui/while_let_on_iterator.rs:601:5
|
||||||
|
|
|
|
||||||
LL | while let Some(..) = it.next() {
|
LL | while let Some(..) = it.next() {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
|
||||||
|
|
||||||
error: aborting due to 28 previous errors
|
error: aborting due to 33 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#[allow(clippy::borrow_as_ptr)]
|
#[allow(clippy::borrow_as_ptr, clippy::ptr_offset_by_literal)]
|
||||||
fn main() {
|
fn main() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let m = &mut () as *mut ();
|
let m = &mut () as *mut ();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
#![warn(clippy::zero_repeat_side_effects)]
|
#![warn(clippy::zero_repeat_side_effects)]
|
||||||
#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)]
|
#![allow(
|
||||||
#![allow(clippy::no_effect)] // only fires _after_ the fix
|
clippy::unnecessary_operation,
|
||||||
|
clippy::useless_vec,
|
||||||
|
clippy::needless_late_init,
|
||||||
|
clippy::single_match,
|
||||||
|
clippy::no_effect // only fires _after_ the fix
|
||||||
|
)]
|
||||||
|
|
||||||
fn f() -> i32 {
|
fn f() -> i32 {
|
||||||
println!("side effect");
|
println!("side effect");
|
||||||
|
|
@ -119,3 +124,26 @@ fn issue_14681() {
|
||||||
});
|
});
|
||||||
//~^ zero_repeat_side_effects
|
//~^ zero_repeat_side_effects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue_15824() {
|
||||||
|
fn f() {}
|
||||||
|
|
||||||
|
match 0 {
|
||||||
|
0 => {
|
||||||
|
f();
|
||||||
|
_ = [] as [(); 0]
|
||||||
|
},
|
||||||
|
//~^ zero_repeat_side_effects
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut a = [(); 0];
|
||||||
|
match 0 {
|
||||||
|
0 => {
|
||||||
|
f();
|
||||||
|
a = [] as [(); 0]
|
||||||
|
},
|
||||||
|
//~^ zero_repeat_side_effects
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
#![warn(clippy::zero_repeat_side_effects)]
|
#![warn(clippy::zero_repeat_side_effects)]
|
||||||
#![expect(clippy::unnecessary_operation, clippy::useless_vec, clippy::needless_late_init)]
|
#![allow(
|
||||||
#![allow(clippy::no_effect)] // only fires _after_ the fix
|
clippy::unnecessary_operation,
|
||||||
|
clippy::useless_vec,
|
||||||
|
clippy::needless_late_init,
|
||||||
|
clippy::single_match,
|
||||||
|
clippy::no_effect // only fires _after_ the fix
|
||||||
|
)]
|
||||||
|
|
||||||
fn f() -> i32 {
|
fn f() -> i32 {
|
||||||
println!("side effect");
|
println!("side effect");
|
||||||
|
|
@ -102,3 +107,20 @@ fn issue_14681() {
|
||||||
foo(&[Some(Some(S::new())); 0]);
|
foo(&[Some(Some(S::new())); 0]);
|
||||||
//~^ zero_repeat_side_effects
|
//~^ zero_repeat_side_effects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn issue_15824() {
|
||||||
|
fn f() {}
|
||||||
|
|
||||||
|
match 0 {
|
||||||
|
0 => _ = [f(); 0],
|
||||||
|
//~^ zero_repeat_side_effects
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut a = [(); 0];
|
||||||
|
match 0 {
|
||||||
|
0 => a = [f(); 0],
|
||||||
|
//~^ zero_repeat_side_effects
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:17:5
|
--> tests/ui/zero_repeat_side_effects.rs:22:5
|
||||||
|
|
|
|
||||||
LL | let a = [f(); 0];
|
LL | let a = [f(); 0];
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -13,7 +13,7 @@ LL + let a: [i32; 0] = [];
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:20:5
|
--> tests/ui/zero_repeat_side_effects.rs:25:5
|
||||||
|
|
|
|
||||||
LL | b = [f(); 0];
|
LL | b = [f(); 0];
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
@ -25,7 +25,7 @@ LL ~ b = [] as [i32; 0];
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:25:5
|
--> tests/ui/zero_repeat_side_effects.rs:30:5
|
||||||
|
|
|
|
||||||
LL | let c = vec![f(); 0];
|
LL | let c = vec![f(); 0];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -37,7 +37,7 @@ LL + let c: std::vec::Vec<i32> = vec![];
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:28:5
|
--> tests/ui/zero_repeat_side_effects.rs:33:5
|
||||||
|
|
|
|
||||||
LL | d = vec![f(); 0];
|
LL | d = vec![f(); 0];
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -49,7 +49,7 @@ LL ~ d = vec![] as std::vec::Vec<i32>;
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:32:5
|
--> tests/ui/zero_repeat_side_effects.rs:37:5
|
||||||
|
|
|
|
||||||
LL | let e = [println!("side effect"); 0];
|
LL | let e = [println!("side effect"); 0];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -61,7 +61,7 @@ LL + let e: [(); 0] = [];
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:36:5
|
--> tests/ui/zero_repeat_side_effects.rs:41:5
|
||||||
|
|
|
|
||||||
LL | let g = [{ f() }; 0];
|
LL | let g = [{ f() }; 0];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -73,7 +73,7 @@ LL + let g: [i32; 0] = [];
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:40:10
|
--> tests/ui/zero_repeat_side_effects.rs:45:10
|
||||||
|
|
|
|
||||||
LL | drop(vec![f(); 0]);
|
LL | drop(vec![f(); 0]);
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
@ -87,7 +87,7 @@ LL ~ });
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:44:5
|
--> tests/ui/zero_repeat_side_effects.rs:49:5
|
||||||
|
|
|
|
||||||
LL | vec![f(); 0];
|
LL | vec![f(); 0];
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
@ -99,7 +99,7 @@ LL ~ vec![] as std::vec::Vec<i32>;
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:46:5
|
--> tests/ui/zero_repeat_side_effects.rs:51:5
|
||||||
|
|
|
|
||||||
LL | [f(); 0];
|
LL | [f(); 0];
|
||||||
| ^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
@ -111,7 +111,7 @@ LL ~ [] as [i32; 0];
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:100:10
|
--> tests/ui/zero_repeat_side_effects.rs:105:10
|
||||||
|
|
|
|
||||||
LL | foo(&[Some(f()); 0]);
|
LL | foo(&[Some(f()); 0]);
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
@ -125,7 +125,7 @@ LL ~ });
|
||||||
|
|
|
|
||||||
|
|
||||||
error: expression with side effects as the initial value in a zero-sized array initializer
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
--> tests/ui/zero_repeat_side_effects.rs:102:10
|
--> tests/ui/zero_repeat_side_effects.rs:107:10
|
||||||
|
|
|
|
||||||
LL | foo(&[Some(Some(S::new())); 0]);
|
LL | foo(&[Some(Some(S::new())); 0]);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -138,5 +138,33 @@ LL + [] as [std::option::Option<std::option::Option<S>>; 0]
|
||||||
LL ~ });
|
LL ~ });
|
||||||
|
|
|
|
||||||
|
|
||||||
error: aborting due to 11 previous errors
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
|
--> tests/ui/zero_repeat_side_effects.rs:115:14
|
||||||
|
|
|
||||||
|
LL | 0 => _ = [f(); 0],
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider performing the side effect separately
|
||||||
|
|
|
||||||
|
LL ~ 0 => {
|
||||||
|
LL + f();
|
||||||
|
LL + _ = [] as [(); 0]
|
||||||
|
LL ~ },
|
||||||
|
|
|
||||||
|
|
||||||
|
error: expression with side effects as the initial value in a zero-sized array initializer
|
||||||
|
--> tests/ui/zero_repeat_side_effects.rs:122:14
|
||||||
|
|
|
||||||
|
LL | 0 => a = [f(); 0],
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: consider performing the side effect separately
|
||||||
|
|
|
||||||
|
LL ~ 0 => {
|
||||||
|
LL + f();
|
||||||
|
LL + a = [] as [(); 0]
|
||||||
|
LL ~ },
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 13 previous errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,6 @@ allow-unauthenticated = [
|
||||||
[mentions."clippy_lints/src/doc"]
|
[mentions."clippy_lints/src/doc"]
|
||||||
cc = ["@notriddle"]
|
cc = ["@notriddle"]
|
||||||
|
|
||||||
# Prevents mentions in commits to avoid users being spammed
|
|
||||||
[no-mentions]
|
|
||||||
|
|
||||||
# Have rustbot inform users about the *No Merge Policy*
|
# Have rustbot inform users about the *No Merge Policy*
|
||||||
[no-merges]
|
[no-merges]
|
||||||
exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust
|
exclude_titles = ["Rustup"] # exclude syncs from rust-lang/rust
|
||||||
|
|
@ -65,6 +62,7 @@ users_on_vacation = [
|
||||||
"Manishearth",
|
"Manishearth",
|
||||||
"Alexendoo",
|
"Alexendoo",
|
||||||
"y21",
|
"y21",
|
||||||
|
"blyxyas",
|
||||||
]
|
]
|
||||||
|
|
||||||
[assign.owners]
|
[assign.owners]
|
||||||
|
|
@ -77,7 +75,6 @@ users_on_vacation = [
|
||||||
"@Alexendoo",
|
"@Alexendoo",
|
||||||
"@dswij",
|
"@dswij",
|
||||||
"@Jarcho",
|
"@Jarcho",
|
||||||
"@blyxyas",
|
|
||||||
"@y21",
|
"@y21",
|
||||||
"@samueltardieu",
|
"@samueltardieu",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -637,14 +637,14 @@ pre, hr {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.dropdown-menu li.checkbox > button {
|
#menu-filters ul.dropdown-menu li.checkbox > button {
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: var(--theme-popup-bg);
|
background: var(--theme-popup-bg);
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.dropdown-menu li.checkbox > button:hover {
|
#menu-filters ul.dropdown-menu li.checkbox > button:hover {
|
||||||
background: var(--theme-hover);
|
background: var(--theme-hover);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue