Merge commit '20b085d500dfba5afe0869707bf357af3afe20be' into clippy-subtree-update

This commit is contained in:
Philipp Krones 2024-05-02 17:26:44 +02:00
parent fc15bc169e
commit 80c6f8ff7b
124 changed files with 1666 additions and 757 deletions

View File

@ -6,11 +6,62 @@ document.
## Unreleased / Beta / In Rust Nightly ## Unreleased / Beta / In Rust Nightly
[66c29b97...master](https://github.com/rust-lang/rust-clippy/compare/66c29b97...master) [93f0a9a9...master](https://github.com/rust-lang/rust-clippy/compare/93f0a9a9...master)
## Rust 1.78
Current stable, released 2024-05-02
[View all 112 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-01-26T05%3A46%3A23Z..2024-03-07T16%3A25%3A52Z+base%3Amaster)
### New Lints
* [`assigning_clones`]
[#12077](https://github.com/rust-lang/rust-clippy/pull/12077)
* [`mixed_attributes_style`]
[#12354](https://github.com/rust-lang/rust-clippy/pull/12354)
* [`empty_docs`]
[#12342](https://github.com/rust-lang/rust-clippy/pull/12342)
* [`unnecessary_get_then_check`]
[#12339](https://github.com/rust-lang/rust-clippy/pull/12339)
* [`multiple_bound_locations`]
[#12259](https://github.com/rust-lang/rust-clippy/pull/12259)
* [`unnecessary_clippy_cfg`]
[#12303](https://github.com/rust-lang/rust-clippy/pull/12303)
* [`deprecated_clippy_cfg_attr`]
[#12292](https://github.com/rust-lang/rust-clippy/pull/12292)
* [`manual_c_str_literals`]
[#11919](https://github.com/rust-lang/rust-clippy/pull/11919)
* [`ref_as_ptr`]
[#12087](https://github.com/rust-lang/rust-clippy/pull/12087)
* [`lint_groups_priority`]
[#11832](https://github.com/rust-lang/rust-clippy/pull/11832)
* [`unnecessary_result_map_or_else`]
[#12169](https://github.com/rust-lang/rust-clippy/pull/12169)
* [`to_string_trait_impl`]
[#12122](https://github.com/rust-lang/rust-clippy/pull/12122)
* [`incompatible_msrv`]
[#12160](https://github.com/rust-lang/rust-clippy/pull/12160)
### Enhancements
* [`thread_local_initializer_can_be_made_const`]: Now checks the [`msrv`] configuration
[#12405](https://github.com/rust-lang/rust-clippy/pull/12405)
* [`disallowed_macros`]: Code generated by derive macros can no longer allow this lint
[#12267](https://github.com/rust-lang/rust-clippy/pull/12267)
* [`wildcard_imports`]: Add configuration [`allowed-wildcard-imports`] to allow preconfigured wildcards
[#11979](https://github.com/rust-lang/rust-clippy/pull/11979)
### ICE Fixes
* [`ptr_as_ptr`]: No longer ICEs when the cast source is a function call to a local variable
[#12617](https://github.com/rust-lang/rust-clippy/pull/12617)
* [`cast_sign_loss`]: Avoids an infinite loop when casting two chained `.unwrap()` calls
[#12508](https://github.com/rust-lang/rust-clippy/pull/12508)
## Rust 1.77 ## Rust 1.77
Current stable, released 2024-03-18 Released 2024-03-18
[View all 93 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-12-16T18%3A20%3A00Z..2024-01-25T18%3A15%3A56Z+base%3Amaster) [View all 93 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-12-16T18%3A20%3A00Z..2024-01-25T18%3A15%3A56Z+base%3Amaster)
@ -5891,6 +5942,7 @@ Released 2018-09-13
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
[`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates [`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates
[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars [`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.1.79" version = "0.1.80"
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"
@ -30,7 +30,7 @@ color-print = "0.3.4"
anstream = "0.6.0" anstream = "0.6.0"
[dev-dependencies] [dev-dependencies]
ui_test = "0.22.2" ui_test = "0.23"
regex = "1.5.5" regex = "1.5.5"
toml = "0.7.3" toml = "0.7.3"
walkdir = "2.3" walkdir = "2.3"

View File

@ -299,10 +299,10 @@ This is good, because it makes writing this particular lint less complicated.
We have to make this decision with every new Clippy lint. It boils down to using We have to make this decision with every new Clippy lint. It boils down to using
either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
In short, the `LateLintPass` has access to type information while the In short, the `EarlyLintPass` runs before type checking and
`EarlyLintPass` doesn't. If you don't need access to type information, use the [HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass`
`EarlyLintPass`. The `EarlyLintPass` is also faster. However, linting speed has access to type information. Consider using the `LateLintPass` unless you need
hasn't really been a concern with Clippy so far. something specific from the `EarlyLintPass`.
Since we don't need type information for checking the function name, we used Since we don't need type information for checking the function name, we used
`--pass=early` when running the new lint automation and all the imports were `--pass=early` when running the new lint automation and all the imports were

View File

@ -132,6 +132,16 @@ Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) * [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
## `allow-useless-vec-in-tests`
Whether `useless_vec` should ignore test functions or `#[cfg(test)]`
**Default Value:** `false`
---
**Affected lints:**
* [`useless_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec)
## `allowed-dotfiles` ## `allowed-dotfiles`
Additional dotfiles (files or directories starting with a dot) to allow Additional dotfiles (files or directories starting with a dot) to allow
@ -506,13 +516,14 @@ The maximum byte size a `Future` can have, before it triggers the `clippy::large
## `ignore-interior-mutability` ## `ignore-interior-mutability`
A list of paths to types that should be treated like `Arc`, i.e. ignored but A list of paths to types that should be treated as if they do not contain interior mutability
for the generic parameters for determining interior mutability
**Default Value:** `["bytes::Bytes"]` **Default Value:** `["bytes::Bytes"]`
--- ---
**Affected lints:** **Affected lints:**
* [`borrow_interior_mutable_const`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const)
* [`declare_interior_mutable_const`](https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const)
* [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond) * [`ifs_same_cond`](https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond)
* [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) * [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_config" name = "clippy_config"
version = "0.1.79" version = "0.1.80"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -463,14 +463,17 @@ define_Conf! {
/// ///
/// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
(allow_print_in_tests: bool = false), (allow_print_in_tests: bool = false),
/// Lint: USELESS_VEC.
///
/// Whether `useless_vec` should ignore test functions or `#[cfg(test)]`
(allow_useless_vec_in_tests: bool = false),
/// Lint: RESULT_LARGE_ERR. /// Lint: RESULT_LARGE_ERR.
/// ///
/// The maximum size of the `Err`-variant in a `Result` returned from a function /// The maximum size of the `Err`-variant in a `Result` returned from a function
(large_error_threshold: u64 = 128), (large_error_threshold: u64 = 128),
/// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND. /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST.
/// ///
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// A list of paths to types that should be treated as if they do not contain interior mutability
/// for the generic parameters for determining interior mutability
(ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])), (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
/// Lint: UNINLINED_FORMAT_ARGS. /// Lint: UNINLINED_FORMAT_ARGS.
/// ///

View File

@ -23,7 +23,7 @@ msrv_aliases! {
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,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,63,0 { ASSIGNING_CLONES } 1,63,0 { CLONE_INTO }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.79" version = "0.1.80"
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"

View File

@ -45,7 +45,7 @@ declare_clippy_lint! {
/// a.clone_from(&b); /// a.clone_from(&b);
/// } /// }
/// ``` /// ```
#[clippy::version = "1.77.0"] #[clippy::version = "1.78.0"]
pub ASSIGNING_CLONES, pub ASSIGNING_CLONES,
perf, perf,
"assigning the result of cloning may be inefficient" "assigning the result of cloning may be inefficient"
@ -153,7 +153,7 @@ fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<
fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool { fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool {
// For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63. // For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63.
// If the current MSRV is below that, don't suggest the lint. // If the current MSRV is below that, don't suggest the lint.
if !msrv.meets(msrvs::ASSIGNING_CLONES) && matches!(call.target, TargetTrait::ToOwned) { if !msrv.meets(msrvs::CLONE_INTO) && matches!(call.target, TargetTrait::ToOwned) {
return false; return false;
} }

View File

@ -121,9 +121,9 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
&& let Some(sig) = expr_sig(cx, path) && let Some(sig) = expr_sig(cx, path)
&& let Some(input) = sig.input(index) && let Some(input) = sig.input(index)
&& !cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait() && let Some(input_ty) = input.no_bound_vars()
{ {
input.no_bound_vars().is_some() input_ty == cx.typeck_results().expr_ty_adjusted(expr)
} else { } else {
false false
} }

View File

@ -197,7 +197,7 @@ declare_clippy_lint! {
/// pedantic = { level = "warn", priority = -1 } /// pedantic = { level = "warn", priority = -1 }
/// similar_names = "allow" /// similar_names = "allow"
/// ``` /// ```
#[clippy::version = "1.76.0"] #[clippy::version = "1.78.0"]
pub LINT_GROUPS_PRIORITY, pub LINT_GROUPS_PRIORITY,
correctness, correctness,
"a lint group in `Cargo.toml` at the same priority as a lint" "a lint group in `Cargo.toml` at the same priority as a lint"

View File

@ -40,9 +40,14 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
}) })
}, },
BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right) BinOpKind::Rem => get_constant_bits(cx, right)
.unwrap_or(u64::MAX) .unwrap_or(u64::MAX)
.min(apply_reductions(cx, nbits, left, signed)), .min(apply_reductions(cx, nbits, left, signed)),
BinOpKind::BitAnd => get_constant_bits(cx, right)
.unwrap_or(u64::MAX)
.min(get_constant_bits(cx, left).unwrap_or(u64::MAX))
.min(apply_reductions(cx, nbits, right, signed))
.min(apply_reductions(cx, nbits, left, signed)),
BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
.saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())), .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())),
_ => nbits, _ => nbits,

View File

@ -708,7 +708,7 @@ declare_clippy_lint! {
/// let a_ref = &1; /// let a_ref = &1;
/// let a_ptr = std::ptr::from_ref(a_ref); /// let a_ptr = std::ptr::from_ref(a_ref);
/// ``` /// ```
#[clippy::version = "1.77.0"] #[clippy::version = "1.78.0"]
pub REF_AS_PTR, pub REF_AS_PTR,
pedantic, pedantic,
"using `as` to cast a reference to pointer" "using `as` to cast a reference to pointer"

View File

@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::visitors::{for_each_expr_with_closures, Visitable};
use clippy_utils::{get_enclosing_block, path_to_local_id}; use clippy_utils::{get_enclosing_block, path_to_local_id};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_hir::{Block, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -77,7 +77,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections:
|| is_type_lang_item(cx, ty, LangItem::String) || is_type_lang_item(cx, ty, LangItem::String)
} }
fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool { fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool {
let mut has_access = false; let mut has_access = false;
let mut has_read_access = false; let mut has_read_access = false;
@ -109,11 +109,30 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
// traits (identified as local, based on the orphan rule), pessimistically assume that they might // traits (identified as local, based on the orphan rule), pessimistically assume that they might
// have side effects, so consider them a read. // have side effects, so consider them a read.
if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id) if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id)
&& let ExprKind::MethodCall(_, receiver, _, _) = parent.kind && let ExprKind::MethodCall(_, receiver, args, _) = parent.kind
&& path_to_local_id(receiver, id) && path_to_local_id(receiver, id)
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
&& !method_def_id.is_local() && !method_def_id.is_local()
{ {
// If this "official" method takes closures,
// it has read access if one of the closures has read access.
//
// items.retain(|item| send_item(item).is_ok());
let is_read_in_closure_arg = args.iter().any(|arg| {
if let ExprKind::Closure(closure) = arg.kind
// To keep things simple, we only check the first param to see if its read.
&& let Body { params: [param, ..], value } = cx.tcx.hir().body(closure.body)
{
!has_no_read_access(cx, param.hir_id, *value)
} else {
false
}
});
if is_read_in_closure_arg {
has_read_access = true;
return ControlFlow::Break(());
}
// The method call is a statement, so the return value is not used. That's not a read access: // The method call is a statement, so the return value is not used. That's not a read access:
// //
// id.foo(args); // id.foo(args);

View File

@ -1,15 +1,14 @@
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop}; use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
use clippy_utils::visitors::for_each_expr; use clippy_utils::visitors::for_each_expr;
use clippy_utils::{ use clippy_utils::{
capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
}; };
use core::iter; use core::iter;
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefIdSet;
use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
@ -159,40 +158,36 @@ declare_clippy_lint! {
"`if` statement with shared code in all blocks" "`if` statement with shared code in all blocks"
} }
pub struct CopyAndPaste { pub struct CopyAndPaste<'tcx> {
ignore_interior_mutability: Vec<String>, ignore_interior_mutability: Vec<String>,
ignored_ty_ids: DefIdSet, interior_mut: InteriorMut<'tcx>,
} }
impl CopyAndPaste { impl CopyAndPaste<'_> {
pub fn new(ignore_interior_mutability: Vec<String>) -> Self { pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
Self { Self {
ignore_interior_mutability, ignore_interior_mutability,
ignored_ty_ids: DefIdSet::new(), interior_mut: InteriorMut::default(),
} }
} }
} }
impl_lint_pass!(CopyAndPaste => [ impl_lint_pass!(CopyAndPaste<'_> => [
IFS_SAME_COND, IFS_SAME_COND,
SAME_FUNCTIONS_IN_IF_CONDITION, SAME_FUNCTIONS_IN_IF_CONDITION,
IF_SAME_THEN_ELSE, IF_SAME_THEN_ELSE,
BRANCHES_SHARING_CODE BRANCHES_SHARING_CODE
]); ]);
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> {
fn check_crate(&mut self, cx: &LateContext<'tcx>) { fn check_crate(&mut self, cx: &LateContext<'tcx>) {
for ignored_ty in &self.ignore_interior_mutability { self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
let path: Vec<&str> = ignored_ty.split("::").collect();
for id in def_path_def_ids(cx, path.as_slice()) {
self.ignored_ty_ids.insert(id);
}
}
} }
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
let (conds, blocks) = if_sequence(expr); let (conds, blocks) = if_sequence(expr);
lint_same_cond(cx, &conds, &self.ignored_ty_ids); lint_same_cond(cx, &conds, &mut self.interior_mut);
lint_same_fns_in_if_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds);
let all_same = let all_same =
!is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
@ -570,13 +565,14 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
}) })
} }
fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool { fn method_caller_is_mutable<'tcx>(
cx: &LateContext<'tcx>,
caller_expr: &Expr<'_>,
interior_mut: &mut InteriorMut<'tcx>,
) -> bool {
let caller_ty = cx.typeck_results().expr_ty(caller_expr); let caller_ty = cx.typeck_results().expr_ty(caller_expr);
// Check if given type has inner mutability and was not set to ignored by the configuration
let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty)
&& !matches!(caller_ty.ty_adt_def(), Some(adt) if ignored_ty_ids.contains(&adt.did()));
is_inner_mut_ty interior_mut.is_interior_mut_ty(cx, caller_ty)
|| caller_ty.is_mutable_ptr() || caller_ty.is_mutable_ptr()
// `find_binding_init` will return the binding iff its not mutable // `find_binding_init` will return the binding iff its not mutable
|| path_to_local(caller_expr) || path_to_local(caller_expr)
@ -585,7 +581,7 @@ fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignore
} }
/// Implementation of `IFS_SAME_COND`. /// Implementation of `IFS_SAME_COND`.
fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) { fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) {
for (i, j) in search_same( for (i, j) in search_same(
conds, conds,
|e| hash_expr(cx, e), |e| hash_expr(cx, e),
@ -593,7 +589,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &De
// Ignore eq_expr side effects iff one of the expression kind is a method call // Ignore eq_expr side effects iff one of the expression kind is a method call
// and the caller is not a mutable, including inner mutable type. // and the caller is not a mutable, including inner mutable type.
if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind { if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind {
if method_caller_is_mutable(cx, caller, ignored_ty_ids) { if method_caller_is_mutable(cx, caller, interior_mut) {
false false
} else { } else {
SpanlessEq::new(cx).eq_expr(lhs, rhs) SpanlessEq::new(cx).eq_expr(lhs, rhs)

View File

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_in_test;
use clippy_utils::macros::{macro_backtrace, MacroCall}; use clippy_utils::macros::{macro_backtrace, MacroCall};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_in_cfg_test, is_in_test_function};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId, Node}; use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
@ -63,7 +63,7 @@ impl LateLintPass<'_> for DbgMacro {
!in_external_macro(cx.sess(), macro_call.span) && !in_external_macro(cx.sess(), macro_call.span) &&
self.checked_dbg_call_site.insert(macro_call.span) && self.checked_dbg_call_site.insert(macro_call.span) &&
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
!(self.allow_dbg_in_tests && is_in_test(cx, expr.hir_id)) !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id))
{ {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
@ -129,10 +129,6 @@ impl LateLintPass<'_> for DbgMacro {
} }
} }
fn is_in_test(cx: &LateContext<'_>, hir_id: HirId) -> bool {
is_in_test_function(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id)
}
fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> { fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> {
macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id))
} }

View File

@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{ use rustc_hir::{
self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
Pat, PatKind, Path, QPath, TyKind, UnOp, PatKind, Path, QPath, TyKind, UnOp,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};

View File

@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item; use clippy_utils::is_diag_trait_item;
use clippy_utils::macros::{ use clippy_utils::macros::{
find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall, is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
}; };
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::ty::{implements_trait, is_type_lang_item};
@ -271,9 +271,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
let mut suggest_format = |spec| { let mut suggest_format = |spec| {
let message = format!("for the {spec} to apply consider using `format!()`"); let message = format!("for the {spec} to apply consider using `format!()`");
if let Some(mac_call) = root_macro_call(arg_span) if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) {
&& self.cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
{
diag.span_suggestion( diag.span_suggestion(
self.cx.sess().source_map().span_until_char(mac_call.span, '!'), self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
message, message,

View File

@ -24,7 +24,7 @@ declare_clippy_lint! {
/// ///
/// ### Limitations /// ### Limitations
/// This lint does not check for implied bounds transitively. Meaning that /// This lint does not check for implied bounds transitively. Meaning that
/// it does't check for implied bounds from supertraits of supertraits /// it doesn't check for implied bounds from supertraits of supertraits
/// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`) /// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`)
/// ///
/// ### Example /// ### Example

View File

@ -93,12 +93,9 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<Hir
let mut slices: FxIndexMap<HirId, SliceLintInformation> = FxIndexMap::default(); let mut slices: FxIndexMap<HirId, SliceLintInformation> = FxIndexMap::default();
pat.walk_always(|pat| { pat.walk_always(|pat| {
// We'll just ignore mut and ref mut for simplicity sake right now // We'll just ignore mut and ref mut for simplicity sake right now
if let hir::PatKind::Binding( if let hir::PatKind::Binding(hir::BindingMode(by_ref, hir::Mutability::Not), value_hir_id, ident, sub_pat) =
hir::BindingMode(by_ref, hir::Mutability::Not), pat.kind
value_hir_id, && by_ref != hir::ByRef::Yes(hir::Mutability::Mut)
ident,
sub_pat,
) = pat.kind && by_ref != hir::ByRef::Yes(hir::Mutability::Mut)
{ {
// This block catches bindings with sub patterns. It would be hard to build a correct suggestion // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
// for them and it's likely that the user knows what they are doing in such a case. // for them and it's likely that the user knows what they are doing in such a case.

View File

@ -1,10 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use clippy_utils::macros::macro_backtrace;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_hir::{ArrayLen, Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, ConstKind}; use rustc_middle::ty::{self, ConstKind};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -25,20 +28,41 @@ declare_clippy_lint! {
pub struct LargeStackArrays { pub struct LargeStackArrays {
maximum_allowed_size: u128, maximum_allowed_size: u128,
prev_vec_macro_callsite: Option<Span>,
} }
impl LargeStackArrays { impl LargeStackArrays {
#[must_use] #[must_use]
pub fn new(maximum_allowed_size: u128) -> Self { pub fn new(maximum_allowed_size: u128) -> Self {
Self { maximum_allowed_size } Self {
maximum_allowed_size,
prev_vec_macro_callsite: None,
}
}
/// Check if the given span of an expr is already in a `vec!` call.
fn is_from_vec_macro(&mut self, cx: &LateContext<'_>, span: Span) -> bool {
// First, we check if this is span is within the last encountered `vec!` macro's root callsite.
self.prev_vec_macro_callsite
.is_some_and(|vec_mac| vec_mac.contains(span))
|| {
// Then, we try backtracking the macro expansions, to see if there's a `vec!` macro,
// and update the `prev_vec_macro_callsite`.
let res = macro_backtrace(span).any(|mac| cx.tcx.is_diagnostic_item(sym::vec_macro, mac.def_id));
if res {
self.prev_vec_macro_callsite = Some(span.source_callsite());
}
res
}
} }
} }
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
&& !self.is_from_vec_macro(cx, expr.span)
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
@ -54,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
}) })
&& self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
{ {
span_lint_and_help( span_lint_and_then(
cx, cx,
LARGE_STACK_ARRAYS, LARGE_STACK_ARRAYS,
expr.span, expr.span,
@ -62,12 +86,33 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
"allocating a local array larger than {} bytes", "allocating a local array larger than {} bytes",
self.maximum_allowed_size self.maximum_allowed_size
), ),
None, |diag| {
format!( if !might_be_expanded(cx, expr) {
"consider allocating on the heap with `vec!{}.into_boxed_slice()`", diag.help(format!(
snippet(cx, expr.span, "[...]") "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
), snippet(cx, expr.span, "[...]")
));
}
},
); );
} }
} }
} }
/// Only giving help messages if the expr does not contains macro expanded codes.
fn might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
/// Check if the span of `ArrayLen` of a repeat expression is within the expr's span,
/// if not, meaning this repeat expr is definitely from some proc-macro.
///
/// This is a fail-safe to a case where even the `is_from_proc_macro` is unable to determain the
/// correct result.
fn repeat_expr_might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
let ExprKind::Repeat(_, ArrayLen::Body(anon_const)) = expr.kind else {
return false;
};
let len_span = cx.tcx.def_span(anon_const.def_id);
!expr.span.contains(len_span)
}
expr.span.from_expansion() || is_from_proc_macro(cx, expr) || repeat_expr_might_be_expanded(cx, expr)
}

View File

@ -535,6 +535,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
allow_print_in_tests, allow_print_in_tests,
allow_private_module_inception, allow_private_module_inception,
allow_unwrap_in_tests, allow_unwrap_in_tests,
allow_useless_vec_in_tests,
ref allowed_dotfiles, ref allowed_dotfiles,
ref allowed_idents_below_min_chars, ref allowed_idents_below_min_chars,
ref allowed_scripts, ref allowed_scripts,
@ -754,6 +755,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
too_large_for_stack, too_large_for_stack,
msrv: msrv(), msrv: msrv(),
span_to_lint_map: BTreeMap::new(), span_to_lint_map: BTreeMap::new(),
allow_in_test: allow_useless_vec_in_tests,
}) })
}); });
store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented)); store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));

View File

@ -1,11 +1,12 @@
use super::UNUSED_ENUMERATE_INDEX; use super::UNUSED_ENUMERATE_INDEX;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::{match_def_path, pat_is_wild, sugg}; use clippy_utils::{pat_is_wild, sugg};
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_hir::{Expr, ExprKind, Pat, PatKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_span::sym;
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint. /// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
/// ///
@ -16,9 +17,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_
&& let ty = cx.typeck_results().expr_ty(arg) && let ty = cx.typeck_results().expr_ty(arg)
&& pat_is_wild(cx, &index.kind, body) && pat_is_wild(cx, &index.kind, body)
&& let ty::Adt(base, _) = *ty.kind() && let ty::Adt(base, _) = *ty.kind()
&& match_def_path(cx, base.did(), &clippy_utils::paths::CORE_ITER_ENUMERATE_STRUCT) && cx.tcx.is_diagnostic_item(sym::Enumerate, base.did())
&& let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
&& match_def_path(cx, call_id, &clippy_utils::paths::CORE_ITER_ENUMERATE_METHOD) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id)
{ {
span_lint_and_then( span_lint_and_then(
cx, cx,

View File

@ -1,12 +1,11 @@
use crate::rustc_lint::LintContext; use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call; use clippy_utils::macros::{is_panic, root_macro_call};
use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg}; use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_hir::{Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -42,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
&& !expr.span.from_expansion() && !expr.span.from_expansion()
&& let then = peel_blocks_with_stmt(then) && let then = peel_blocks_with_stmt(then)
&& let Some(macro_call) = root_macro_call(then.span) && let Some(macro_call) = root_macro_call(then.span)
&& cx.tcx.item_name(macro_call.def_id) == sym::panic && is_panic(cx, macro_call.def_id)
&& !cx.tcx.sess.source_map().is_multiline(cond.span) && !cx.tcx.sess.source_map().is_multiline(cond.span)
&& let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span) && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span)
&& let Some(panic_snippet) = panic_snippet.strip_suffix(')') && let Some(panic_snippet) = panic_snippet.strip_suffix(')')

View File

@ -1,15 +1,15 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call; use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::{higher, in_constant}; use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators};
use rustc_ast::ast::RangeLimits; use rustc_ast::ast::RangeLimits;
use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd}; use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::def_id::DefId;
use rustc_span::{sym, Span}; use rustc_span::{sym, Span};
declare_clippy_lint! { declare_clippy_lint! {
@ -97,12 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
return; return;
} }
if let Some(macro_call) = root_macro_call(expr.span) if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
&& is_matches_macro(cx, macro_call.def_id)
{
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
let range = check_pat(&arm.pat.kind); let range = check_pat(&arm.pat.kind);
check_is_ascii(cx, macro_call.span, recv, &range); check_is_ascii(cx, macro_call.span, recv, &range, None);
} }
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
&& path.ident.name == sym!(contains) && path.ident.name == sym!(contains)
@ -111,42 +109,67 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
end: Some(end), end: Some(end),
limits: RangeLimits::Closed, limits: RangeLimits::Closed,
}) = higher::Range::hir(receiver) }) = higher::Range::hir(receiver)
&& !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_))
{ {
let arg = peel_ref_operators(cx, arg);
let ty_sugg = get_ty_sugg(cx, arg, start);
let range = check_range(start, end); let range = check_range(start, end);
if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind { check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
check_is_ascii(cx, expr.span, e, &range);
} else {
check_is_ascii(cx, expr.span, arg, &range);
}
} }
} }
extract_msrv_attr!(LateContext); extract_msrv_attr!(LateContext);
} }
fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) { fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> {
if let Some(sugg) = match range { if let ExprKind::Lit(lit) = bound_expr.kind
CharRange::UpperChar => Some("is_ascii_uppercase"), && let local_hid = path_to_local(arg)?
CharRange::LowerChar => Some("is_ascii_lowercase"), && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid)
CharRange::FullChar => Some("is_ascii_alphabetic"), // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given
CharRange::Digit => Some("is_ascii_digit"), && ty_span == span
CharRange::HexDigit => Some("is_ascii_hexdigit"), {
CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None, let ty_str = match lit.node {
} { Char(_) => "char",
let default_snip = ".."; Byte(_) => "u8",
let mut app = Applicability::MachineApplicable; _ => return None,
let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); };
return Some((*ty_span, ty_str));
span_lint_and_sugg(
cx,
MANUAL_IS_ASCII_CHECK,
span,
"manual check for common ascii range",
"try",
format!("{recv}.{sugg}()"),
app,
);
} }
None
}
fn check_is_ascii(
cx: &LateContext<'_>,
span: Span,
recv: &Expr<'_>,
range: &CharRange,
ty_sugg: Option<(Span, &'_ str)>,
) {
let sugg = match range {
CharRange::UpperChar => "is_ascii_uppercase",
CharRange::LowerChar => "is_ascii_lowercase",
CharRange::FullChar => "is_ascii_alphabetic",
CharRange::Digit => "is_ascii_digit",
CharRange::HexDigit => "is_ascii_hexdigit",
CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return,
};
let default_snip = "..";
let mut app = Applicability::MachineApplicable;
let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))];
if let Some((ty_span, ty_str)) = ty_sugg {
suggestion.push((ty_span, format!("{recv}: {ty_str}")));
}
span_lint_and_then(
cx,
MANUAL_IS_ASCII_CHECK,
span,
"manual check for common ascii range",
|diag| {
diag.multipart_suggestion("try", suggestion, app);
},
);
} }
fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
@ -187,11 +210,3 @@ fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
CharRange::Otherwise CharRange::Otherwise
} }
} }
fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
return sym::matches_macro == name;
}
false
}

View File

@ -1,3 +1,4 @@
use clippy_config::msrvs::Msrv;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLetOrMatch; use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
@ -11,12 +12,12 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::Span; use rustc_span::Span;
use super::COLLAPSIBLE_MATCH; use super::{pat_contains_disallowed_or, COLLAPSIBLE_MATCH};
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) {
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
for arm in arms { for arm in arms {
check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body)); check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv);
} }
} }
} }
@ -26,8 +27,9 @@ pub(super) fn check_if_let<'tcx>(
pat: &'tcx Pat<'_>, pat: &'tcx Pat<'_>,
body: &'tcx Expr<'_>, body: &'tcx Expr<'_>,
else_expr: Option<&'tcx Expr<'_>>, else_expr: Option<&'tcx Expr<'_>>,
msrv: &Msrv,
) { ) {
check_arm(cx, false, pat, body, None, else_expr); check_arm(cx, false, pat, body, None, else_expr, msrv);
} }
fn check_arm<'tcx>( fn check_arm<'tcx>(
@ -37,6 +39,7 @@ fn check_arm<'tcx>(
outer_then_body: &'tcx Expr<'tcx>, outer_then_body: &'tcx Expr<'tcx>,
outer_guard: Option<&'tcx Expr<'tcx>>, outer_guard: Option<&'tcx Expr<'tcx>>,
outer_else_body: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>,
msrv: &Msrv,
) { ) {
let inner_expr = peel_blocks_with_stmt(outer_then_body); let inner_expr = peel_blocks_with_stmt(outer_then_body);
if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr) if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr)
@ -57,7 +60,7 @@ fn check_arm<'tcx>(
// match expression must be a local binding // match expression must be a local binding
// match <local> { .. } // match <local> { .. }
&& let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)) && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee))
&& !pat_contains_or(inner_then_pat) && !pat_contains_disallowed_or(inner_then_pat, msrv)
// the binding must come from the pattern of the containing match arm // the binding must come from the pattern of the containing match arm
// ..<local>.. => match <local> { .. } // ..<local>.. => match <local> { .. }
&& let (Some(binding_span), is_innermost_parent_pat_struct) && let (Some(binding_span), is_innermost_parent_pat_struct)
@ -142,13 +145,3 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi
}); });
(span, is_innermost_parent_pat_struct) (span, is_innermost_parent_pat_struct)
} }
fn pat_contains_or(pat: &Pat<'_>) -> bool {
let mut result = false;
pat.walk(|p| {
let is_or = matches!(p.kind, PatKind::Or(_));
result |= is_or;
!is_or
});
result
}

View File

@ -27,7 +27,7 @@ mod wild_in_or_pats;
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::source::walk_span_to_context; use clippy_utils::source::walk_span_to_context;
use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg}; use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg};
use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
@ -1040,7 +1040,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
significant_drop_in_scrutinee::check(cx, expr, ex, arms, source); significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
} }
collapsible_match::check_match(cx, arms); collapsible_match::check_match(cx, arms, &self.msrv);
if !from_expansion { if !from_expansion {
// These don't depend on a relationship between multiple arms // These don't depend on a relationship between multiple arms
match_wild_err_arm::check(cx, ex, arms); match_wild_err_arm::check(cx, ex, arms);
@ -1066,7 +1066,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
needless_match::check_match(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr);
match_on_vec_items::check(cx, ex); match_on_vec_items::check(cx, ex);
match_str_case_mismatch::check(cx, ex, arms); match_str_case_mismatch::check(cx, ex, arms);
redundant_guards::check(cx, arms); redundant_guards::check(cx, arms, &self.msrv);
if !in_constant(cx, expr.hir_id) { if !in_constant(cx, expr.hir_id) {
manual_unwrap_or::check(cx, expr, ex, arms); manual_unwrap_or::check(cx, expr, ex, arms);
@ -1083,7 +1083,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
} }
} else if let Some(if_let) = higher::IfLet::hir(cx, expr) { } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, &self.msrv);
if !from_expansion { if !from_expansion {
if let Some(else_expr) = if_let.if_else { if let Some(else_expr) = if_let.if_else {
if self.msrv.meets(msrvs::MATCHES_MACRO) { if self.msrv.meets(msrvs::MATCHES_MACRO) {
@ -1195,3 +1195,18 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar
Err(()) => true, Err(()) => true,
} }
} }
/// Checks if `pat` contains OR patterns that cannot be nested due to a too low MSRV.
fn pat_contains_disallowed_or(pat: &Pat<'_>, msrv: &Msrv) -> bool {
if msrv.meets(msrvs::OR_PATTERNS) {
return false;
}
let mut result = false;
pat.walk(|p| {
let is_or = matches!(p.kind, PatKind::Or(_));
result |= is_or;
!is_or
});
result
}

View File

@ -1,40 +1,32 @@
use clippy_config::msrvs::Msrv;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::visitors::{for_each_expr, is_local_used}; use clippy_utils::visitors::{for_each_expr, is_local_used};
use clippy_utils::{in_constant, path_to_local}; use clippy_utils::{in_constant, path_to_local};
use rustc_ast::{BorrowKind, LitKind}; use rustc_ast::{BorrowKind, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, Pat, PatKind, UnOp}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::{Span, Symbol}; use rustc_span::{sym, Span, Symbol};
use std::borrow::Cow; use std::borrow::Cow;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use super::REDUNDANT_GUARDS; use super::{pat_contains_disallowed_or, REDUNDANT_GUARDS};
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: &Msrv) {
for outer_arm in arms { for outer_arm in arms {
let Some(guard) = outer_arm.guard else { let Some(guard) = outer_arm.guard else {
continue; continue;
}; };
// `Some(x) if matches!(x, y)` // `Some(x) if matches!(x, y)`
if let ExprKind::Match( if let ExprKind::Match(scrutinee, [arm, _], MatchSource::Normal) = guard.kind
scrutinee, && matching_root_macro_call(cx, guard.span, sym::matches_macro).is_some()
[
arm,
Arm {
pat: Pat {
kind: PatKind::Wild, ..
},
..
},
],
MatchSource::Normal,
) = guard.kind
&& let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm) && let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm)
&& !pat_contains_disallowed_or(arm.pat, msrv)
{ {
let pat_span = match (arm.pat.kind, binding.byref_ident) { let pat_span = match (arm.pat.kind, binding.byref_ident) {
(PatKind::Ref(pat, _), Some(_)) => pat.span, (PatKind::Ref(pat, _), Some(_)) => pat.span,
@ -53,6 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
// `Some(x) if let Some(2) = x` // `Some(x) if let Some(2) = x`
else if let ExprKind::Let(let_expr) = guard.kind else if let ExprKind::Let(let_expr) = guard.kind
&& let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm) && let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm)
&& !pat_contains_disallowed_or(let_expr.pat, msrv)
{ {
let pat_span = match (let_expr.pat.kind, binding.byref_ident) { let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
(PatKind::Ref(pat, _), Some(_)) => pat.span, (PatKind::Ref(pat, _), Some(_)) => pat.span,

View File

@ -75,7 +75,7 @@ fn report_single_pattern(
) { ) {
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH }; let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
let ctxt = expr.span.ctxt(); let ctxt = expr.span.ctxt();
let mut app = Applicability::HasPlaceholders; let mut app = Applicability::MachineApplicable;
let els_str = els.map_or(String::new(), |els| { let els_str = els.map_or(String::new(), |els| {
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
}); });

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call};
use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::source::{indent_of, reindent_multiline, snippet};
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
@ -247,8 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
} else { } else {
None None
} }
} else if let Some(macro_call) = root_macro_call(expr.span) } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
&& cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro)
// we know for a fact that the wildcard pattern is the second arm // we know for a fact that the wildcard pattern is the second arm
&& let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
&& path_to_local_id(scrutinee, filter_param_id) && path_to_local_id(scrutinee, filter_param_id)

View File

@ -89,8 +89,7 @@ pub(super) fn check<'tcx>(
} }
match it.kind { match it.kind {
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, Mutability::Mut) => {
| PatKind::Ref(_, Mutability::Mut) => {
to_be_discarded = true; to_be_discarded = true;
false false
}, },

View File

@ -1145,11 +1145,16 @@ declare_clippy_lint! {
/// `str` as an argument, e.g., `_.split("x")`. /// `str` as an argument, e.g., `_.split("x")`.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// Performing these methods using a `char` is faster than /// While this can make a perf difference on some systems,
/// using a `str`. /// benchmarks have proven inconclusive. But at least using a
/// char literal makes it clear that we are looking at a single
/// character.
/// ///
/// ### Known problems /// ### Known problems
/// Does not catch multi-byte unicode characters. /// Does not catch multi-byte unicode characters. This is by
/// design, on many machines, splitting by a non-ascii char is
/// actually slower. Please do your own measurements instead of
/// relying solely on the results of this lint.
/// ///
/// ### Example /// ### Example
/// ```rust,ignore /// ```rust,ignore
@ -1162,7 +1167,7 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "pre 1.29.0"] #[clippy::version = "pre 1.29.0"]
pub SINGLE_CHAR_PATTERN, pub SINGLE_CHAR_PATTERN,
perf, pedantic,
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`" "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
} }
@ -3988,7 +3993,7 @@ declare_clippy_lint! {
/// let x: Result<u32, ()> = Ok(0); /// let x: Result<u32, ()> = Ok(0);
/// let y = x.unwrap_or_else(|err| handle_error(err)); /// let y = x.unwrap_or_else(|err| handle_error(err));
/// ``` /// ```
#[clippy::version = "1.77.0"] #[clippy::version = "1.78.0"]
pub UNNECESSARY_RESULT_MAP_OR_ELSE, pub UNNECESSARY_RESULT_MAP_OR_ELSE,
suspicious, suspicious,
"making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`" "making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`"
@ -4022,7 +4027,7 @@ declare_clippy_lint! {
/// needs_cstr(c"Hello"); /// needs_cstr(c"Hello");
/// unsafe { libc::puts(c"World".as_ptr()) } /// unsafe { libc::puts(c"World".as_ptr()) }
/// ``` /// ```
#[clippy::version = "1.76.0"] #[clippy::version = "1.78.0"]
pub MANUAL_C_STR_LITERALS, pub MANUAL_C_STR_LITERALS,
pedantic, pedantic,
r#"creating a `CStr` through functions when `c""` literals can be used"# r#"creating a `CStr` through functions when `c""` literals can be used"#

View File

@ -4,7 +4,7 @@ use clippy_utils::mir::{enclosing_mir, visit_local_usage};
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node}; use rustc_hir::{Expr, ExprKind, Node, PatKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::{Location, START_BLOCK}; use rustc_middle::mir::{Location, START_BLOCK};
use rustc_span::sym; use rustc_span::sym;
@ -25,6 +25,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver
&& is_unwrap_call(cx, unwrap_call_expr) && is_unwrap_call(cx, unwrap_call_expr)
&& let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id)
&& let Node::LetStmt(local) = parent && let Node::LetStmt(local) = parent
&& let PatKind::Binding(.., ident, _) = local.pat.kind
// if the binding is prefixed with `_`, it typically means
// that this guard only exists to protect a section of code
// rather than the contained data
&& !ident.as_str().starts_with('_')
&& let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id)
&& let Some((local, _)) = mir && let Some((local, _)) = mir
.local_decls .local_decls

View File

@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
/// lint for length-1 `str`s as argument for `insert_str` /// lint for length-1 `str`s as argument for `insert_str`
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) {
let base_string_snippet = let base_string_snippet =
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability); snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);

View File

@ -8,7 +8,7 @@ use rustc_span::symbol::Symbol;
use super::SINGLE_CHAR_PATTERN; use super::SINGLE_CHAR_PATTERN;
const PATTERN_METHODS: [(&str, usize); 24] = [ const PATTERN_METHODS: [(&str, usize); 22] = [
("contains", 0), ("contains", 0),
("starts_with", 0), ("starts_with", 0),
("ends_with", 0), ("ends_with", 0),
@ -27,8 +27,6 @@ const PATTERN_METHODS: [(&str, usize); 24] = [
("rmatches", 0), ("rmatches", 0),
("match_indices", 0), ("match_indices", 0),
("rmatch_indices", 0), ("rmatch_indices", 0),
("strip_prefix", 0),
("strip_suffix", 0),
("trim_start_matches", 0), ("trim_start_matches", 0),
("trim_end_matches", 0), ("trim_end_matches", 0),
("replace", 0), ("replace", 0),
@ -50,7 +48,7 @@ pub(super) fn check(
&& args.len() > pos && args.len() > pos
&& let arg = &args[pos] && let arg = &args[pos]
&& let mut applicability = Applicability::MachineApplicable && let mut applicability = Applicability::MachineApplicable
&& let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true)
{ {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,

View File

@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
/// lint for length-1 `str`s as argument for `push_str` /// lint for length-1 `str`s as argument for `push_str`
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) { if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) {
let base_string_snippet = let base_string_snippet =
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability); snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
let sugg = format!("{base_string_snippet}.push({extension_string})"); let sugg = format!("{base_string_snippet}.push({extension_string})");

View File

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then}; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then};
use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT};
use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::source::{snippet, snippet_opt};
use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild}; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -42,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
let recv_ty = cx.typeck_results().expr_ty(recv); let recv_ty = cx.typeck_results().expr_ty(recv);
if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
// If we call a method on a `std::iter::Enumerate` instance // If we call a method on a `std::iter::Enumerate` instance
&& match_def_path(cx, recv_ty_defid, &CORE_ITER_ENUMERATE_STRUCT) && cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid)
// If we are calling a method of the `Iterator` trait // If we are calling a method of the `Iterator` trait
&& is_trait_method(cx, call_expr, sym::Iterator) && is_trait_method(cx, call_expr, sym::Iterator)
// And the map argument is a closure // And the map argument is a closure
@ -75,10 +74,10 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
&& let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind && let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind
&& let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id) && let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id)
// Make sure the method call is `std::iter::Iterator::enumerate`. // Make sure the method call is `std::iter::Iterator::enumerate`.
&& match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD) && cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_defid)
{ {
// Check if the tuple type was explicit. It may be the type system _needs_ the type of the element // Check if the tuple type was explicit. It may be the type system _needs_ the type of the element
// that would be explicited in the closure. // that would be explicitly in the closure.
let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) { let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
// We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`. // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
// Fallback to `..` if we fail getting either snippet. // Fallback to `..` if we fail getting either snippet.

View File

@ -52,11 +52,17 @@ pub(super) fn get_hint_if_single_char_arg(
cx: &LateContext<'_>, cx: &LateContext<'_>,
arg: &Expr<'_>, arg: &Expr<'_>,
applicability: &mut Applicability, applicability: &mut Applicability,
ascii_only: bool,
) -> Option<String> { ) -> Option<String> {
if let ExprKind::Lit(lit) = &arg.kind if let ExprKind::Lit(lit) = &arg.kind
&& let ast::LitKind::Str(r, style) = lit.node && let ast::LitKind::Str(r, style) = lit.node
&& let string = r.as_str() && let string = r.as_str()
&& string.chars().count() == 1 && let len = if ascii_only {
string.len()
} else {
string.chars().count()
}
&& len == 1
{ {
let snip = snippet_with_applicability(cx, arg.span, string, applicability); let snip = snippet_with_applicability(cx, arg.span, string, applicability);
let ch = if let ast::StrStyle::Raw(nhash) = style { let ch = if let ast::StrStyle::Raw(nhash) = style {

View File

@ -29,7 +29,7 @@ declare_clippy_lint! {
/// F: Sized + std::fmt::Debug, /// F: Sized + std::fmt::Debug,
/// {} /// {}
/// ``` /// ```
#[clippy::version = "1.77.0"] #[clippy::version = "1.78.0"]
pub MULTIPLE_BOUND_LOCATIONS, pub MULTIPLE_BOUND_LOCATIONS,
suspicious, suspicious,
"defining generic bounds in multiple locations" "defining generic bounds in multiple locations"

View File

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_interior_mut_ty; use clippy_utils::trait_ref_of_method;
use clippy_utils::{def_path_def_ids, trait_ref_of_method}; use clippy_utils::ty::InteriorMut;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
@ -23,27 +22,15 @@ declare_clippy_lint! {
/// ### Known problems /// ### Known problems
/// ///
/// #### False Positives /// #### False Positives
/// It's correct to use a struct that contains interior mutability as a key, when its /// It's correct to use a struct that contains interior mutability as a key when its
/// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types. /// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
/// However, this lint is unable to recognize this, so it will often cause false positives in /// However, this lint is unable to recognize this, so it will often cause false positives in
/// theses cases. The `bytes` crate is a great example of this. /// these cases.
/// ///
/// #### False Negatives /// #### False Negatives
/// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind /// This lint does not follow raw pointers (`*const T` or `*mut T`) as `Hash` and `Ord`
/// indirection. For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable /// apply only to the **address** of the contained value. This can cause false negatives for
/// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`. /// custom collections that use raw pointers internally.
///
/// This lint does check a few cases for indirection. Firstly, using some standard library
/// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
/// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
/// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
/// contained type.
///
/// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
/// apply only to the **address** of the contained value. Therefore, interior mutability
/// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
/// or `Ord`, and therefore will not trigger this link. For more info, see issue
/// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
/// ///
/// ### Example /// ### Example
/// ```no_run /// ```no_run
@ -51,13 +38,12 @@ declare_clippy_lint! {
/// use std::collections::HashSet; /// use std::collections::HashSet;
/// use std::hash::{Hash, Hasher}; /// use std::hash::{Hash, Hasher};
/// use std::sync::atomic::AtomicUsize; /// use std::sync::atomic::AtomicUsize;
///# #[allow(unused)]
/// ///
/// struct Bad(AtomicUsize); /// struct Bad(AtomicUsize);
/// impl PartialEq for Bad { /// impl PartialEq for Bad {
/// fn eq(&self, rhs: &Self) -> bool { /// fn eq(&self, rhs: &Self) -> bool {
/// .. /// ..
/// ; unimplemented!(); /// # ; true
/// } /// }
/// } /// }
/// ///
@ -66,7 +52,7 @@ declare_clippy_lint! {
/// impl Hash for Bad { /// impl Hash for Bad {
/// fn hash<H: Hasher>(&self, h: &mut H) { /// fn hash<H: Hasher>(&self, h: &mut H) {
/// .. /// ..
/// ; unimplemented!(); /// # ;
/// } /// }
/// } /// }
/// ///
@ -80,25 +66,16 @@ declare_clippy_lint! {
"Check for mutable `Map`/`Set` key type" "Check for mutable `Map`/`Set` key type"
} }
#[derive(Clone)] pub struct MutableKeyType<'tcx> {
pub struct MutableKeyType {
ignore_interior_mutability: Vec<String>, ignore_interior_mutability: Vec<String>,
ignore_mut_def_ids: FxHashSet<hir::def_id::DefId>, interior_mut: InteriorMut<'tcx>,
} }
impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]);
impl<'tcx> LateLintPass<'tcx> for MutableKeyType { impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> {
fn check_crate(&mut self, cx: &LateContext<'tcx>) { fn check_crate(&mut self, cx: &LateContext<'tcx>) {
self.ignore_mut_def_ids.clear(); self.interior_mut = InteriorMut::without_pointers(cx, &self.ignore_interior_mutability);
let mut path = Vec::new();
for ty in &self.ignore_interior_mutability {
path.extend(ty.split("::"));
for id in def_path_def_ids(cx, &path[..]) {
self.ignore_mut_def_ids.insert(id);
}
path.clear();
}
} }
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
@ -121,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
} }
} }
fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &hir::LetStmt<'tcx>) {
if let hir::PatKind::Wild = local.pat.kind { if let hir::PatKind::Wild = local.pat.kind {
return; return;
} }
@ -129,15 +106,15 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
} }
} }
impl MutableKeyType { impl<'tcx> MutableKeyType<'tcx> {
pub fn new(ignore_interior_mutability: Vec<String>) -> Self { pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
Self { Self {
ignore_interior_mutability, ignore_interior_mutability,
ignore_mut_def_ids: FxHashSet::default(), interior_mut: InteriorMut::default(),
} }
} }
fn check_sig(&self, cx: &LateContext<'_>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'_>) { fn check_sig(&mut self, cx: &LateContext<'tcx>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'tcx>) {
let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity(); let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity();
for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
self.check_ty_(cx, hir_ty.span, *ty); self.check_ty_(cx, hir_ty.span, *ty);
@ -151,7 +128,7 @@ impl MutableKeyType {
// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
// generics (because the compiler cannot ensure immutability for unknown types). // generics (because the compiler cannot ensure immutability for unknown types).
fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = ty.peel_refs(); let ty = ty.peel_refs();
if let ty::Adt(def, args) = ty.kind() { if let ty::Adt(def, args) = ty.kind() {
let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
@ -162,11 +139,7 @@ impl MutableKeyType {
} }
let subst_ty = args.type_at(0); let subst_ty = args.type_at(0);
// Determines if a type contains interior mutability which would affect its implementation of if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
// [`Hash`] or [`Ord`].
if is_interior_mut_ty(cx, subst_ty)
&& !matches!(subst_ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did()))
{
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
} }
} }

View File

@ -1,6 +1,6 @@
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::{sym, Span, Symbol}; use rustc_span::{sym, Span, Symbol};
@ -35,6 +35,16 @@ declare_clippy_lint! {
/// println!("{}", elem); /// println!("{}", elem);
/// } /// }
/// ``` /// ```
///
/// ### Known Problems
/// When doing things such as:
/// ```ignore
/// let v = vec![0, 1, 2];
/// v.iter().for_each(|elem| unsafe {
/// libc::printf(c"%d\n".as_ptr(), elem);
/// });
/// ```
/// This lint will not trigger.
#[clippy::version = "1.53.0"] #[clippy::version = "1.53.0"]
pub NEEDLESS_FOR_EACH, pub NEEDLESS_FOR_EACH,
pedantic, pedantic,
@ -68,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
// e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
&& let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind
&& let body = cx.tcx.hir().body(body) && let body = cx.tcx.hir().body(body)
&& let ExprKind::Block(..) = body.value.kind // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
// and suggesting `for … in … { unsafe { } }` is a little ugly.
&& let ExprKind::Block(Block { rules: BlockCheckMode::DefaultBlock, .. }, ..) = body.value.kind
{ {
let mut ret_collector = RetCollector::default(); let mut ret_collector = RetCollector::default();
ret_collector.visit_expr(body.value); ret_collector.visit_expr(body.value);

View File

@ -6,8 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_loca
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_errors::{Applicability, MultiSpan}; use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::{ use rustc_hir::{
BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind,
StmtKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;

View File

@ -83,7 +83,9 @@ fn should_skip<'tcx>(
} }
if is_self(arg) { if is_self(arg) {
return true; // Interestingly enough, `self` arguments make `is_from_proc_macro` return `true`, hence why
// we return early here.
return false;
} }
if let PatKind::Binding(.., name, _) = arg.pat.kind { if let PatKind::Binding(.., name, _) = arg.pat.kind {
@ -185,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
} }
// Collect variables mutably used and spans which will need dereferencings from the // Collect variables mutably used and spans which will need dereferencings from the
// function body. // function body.
let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = { let mutably_used_vars = {
let mut ctx = MutablyUsedVariablesCtxt { let mut ctx = MutablyUsedVariablesCtxt {
mutably_used_vars: HirIdSet::default(), mutably_used_vars: HirIdSet::default(),
prev_bind: None, prev_bind: None,
@ -217,7 +219,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures); check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures);
} }
} }
ctx ctx.generate_mutably_used_ids_from_aliases()
}; };
for ((&input, &_), arg) in it { for ((&input, &_), arg) in it {
// Only take `&mut` arguments. // Only take `&mut` arguments.
@ -309,14 +311,24 @@ struct MutablyUsedVariablesCtxt<'tcx> {
} }
impl<'tcx> MutablyUsedVariablesCtxt<'tcx> { impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
fn add_mutably_used_var(&mut self, mut used_id: HirId) { fn add_mutably_used_var(&mut self, used_id: HirId) {
while let Some(id) = self.aliases.get(&used_id) {
self.mutably_used_vars.insert(used_id);
used_id = *id;
}
self.mutably_used_vars.insert(used_id); self.mutably_used_vars.insert(used_id);
} }
// Because the alias may come after the mutable use of a variable, we need to fill the map at
// the end.
fn generate_mutably_used_ids_from_aliases(mut self) -> HirIdSet {
let all_ids = self.mutably_used_vars.iter().copied().collect::<Vec<_>>();
for mut used_id in all_ids {
while let Some(id) = self.aliases.get(&used_id) {
self.mutably_used_vars.insert(used_id);
used_id = *id;
}
self.mutably_used_vars.insert(used_id);
}
self.mutably_used_vars
}
fn would_be_alias_cycle(&self, alias: HirId, mut target: HirId) -> bool { fn would_be_alias_cycle(&self, alias: HirId, mut target: HirId) -> bool {
while let Some(id) = self.aliases.get(&target) { while let Some(id) = self.aliases.get(&target) {
if *id == alias { if *id == alias {

View File

@ -9,8 +9,8 @@ use rustc_ast::ast::Attribute;
use rustc_errors::{Applicability, Diag}; use rustc_errors::{Applicability, Diag};
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, QPath,
QPath, TyKind, TyKind,
}; };
use rustc_hir_typeck::expr_use_visitor as euv; use rustc_hir_typeck::expr_use_visitor as euv;
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;

View File

@ -182,17 +182,17 @@ impl LateLintPass<'_> for NonCanonicalImpls {
if block.stmts.is_empty() if block.stmts.is_empty()
&& let Some(expr) = block.expr && let Some(expr) = block.expr
&& let ExprKind::Call( && expr_is_cmp(cx, &expr.kind, impl_item, &mut needs_fully_qualified)
Expr { {
kind: ExprKind::Path(some_path), }
hir_id: some_hir_id, // Fix #12683, allow [`needless_return`] here
.. else if block.expr.is_none()
}, && let Some(stmt) = block.stmts.first()
[cmp_expr], && let rustc_hir::StmtKind::Semi(Expr {
) = expr.kind kind: ExprKind::Ret(Some(Expr { kind: ret_kind, .. })),
&& is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome) ..
// Fix #11178, allow `Self::cmp(self, ..)` too }) = stmt.kind
&& self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified) && expr_is_cmp(cx, ret_kind, impl_item, &mut needs_fully_qualified)
{ {
} else { } else {
// If `Self` and `Rhs` are not the same type, bail. This makes creating a valid // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
@ -245,6 +245,30 @@ impl LateLintPass<'_> for NonCanonicalImpls {
} }
} }
/// Return true if `expr_kind` is a `cmp` call.
fn expr_is_cmp<'tcx>(
cx: &LateContext<'tcx>,
expr_kind: &'tcx ExprKind<'tcx>,
impl_item: &ImplItem<'_>,
needs_fully_qualified: &mut bool,
) -> bool {
if let ExprKind::Call(
Expr {
kind: ExprKind::Path(some_path),
hir_id: some_hir_id,
..
},
[cmp_expr],
) = expr_kind
{
is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
// Fix #11178, allow `Self::cmp(self, ..)` too
&& self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, needs_fully_qualified)
} else {
false
}
}
/// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`. /// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`.
fn self_cmp_call<'tcx>( fn self_cmp_call<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,

View File

@ -5,9 +5,9 @@
use std::ptr; use std::ptr;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::in_constant;
use clippy_utils::macros::macro_backtrace; use clippy_utils::macros::macro_backtrace;
use clippy_utils::{def_path_def_ids, in_constant}; use clippy_utils::ty::InteriorMut;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{ use rustc_hir::{
@ -52,8 +52,8 @@ declare_clippy_lint! {
/// There're other enums plus associated constants cases that the lint cannot handle. /// There're other enums plus associated constants cases that the lint cannot handle.
/// ///
/// Types that have underlying or potential interior mutability trigger the lint whether /// Types that have underlying or potential interior mutability trigger the lint whether
/// the interior mutable field is used or not. See issues /// the interior mutable field is used or not. See issue
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
/// ///
/// ### Example /// ### Example
/// ```no_run /// ```no_run
@ -170,42 +170,22 @@ fn lint(cx: &LateContext<'_>, source: Source) {
}); });
} }
#[derive(Clone)] pub struct NonCopyConst<'tcx> {
pub struct NonCopyConst {
ignore_interior_mutability: Vec<String>, ignore_interior_mutability: Vec<String>,
ignore_mut_def_ids: FxHashSet<DefId>, interior_mut: InteriorMut<'tcx>,
} }
impl_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
impl NonCopyConst { impl<'tcx> NonCopyConst<'tcx> {
pub fn new(ignore_interior_mutability: Vec<String>) -> Self { pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
Self { Self {
ignore_interior_mutability, ignore_interior_mutability,
ignore_mut_def_ids: FxHashSet::default(), interior_mut: InteriorMut::default(),
} }
} }
fn is_ty_ignored(&self, ty: Ty<'_>) -> bool { fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
matches!(ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did()))
}
fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
// Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
// making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
// 'unfrozen'. However, this code causes a false negative in which
// a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell<T>`.
// Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
// since it works when a pointer indirection involves (`Cell<*const T>`).
// Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
// but I'm not sure whether it's a decent way, if possible.
cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env)
}
fn is_value_unfrozen_raw_inner<'tcx>(&self, cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
if self.is_ty_ignored(ty) {
return false;
}
match *ty.kind() { match *ty.kind() {
// the fact that we have to dig into every structs to search enums // the fact that we have to dig into every structs to search enums
// leads us to the point checking `UnsafeCell` directly is the only option. // leads us to the point checking `UnsafeCell` directly is the only option.
@ -216,8 +196,7 @@ impl NonCopyConst {
ty::Array(ty, _) => val ty::Array(ty, _) => val
.unwrap_branch() .unwrap_branch()
.iter() .iter()
.any(|field| self.is_value_unfrozen_raw_inner(cx, *field, ty)), .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
ty::Adt(def, _) if def.is_union() => false,
ty::Adt(def, args) if def.is_enum() => { ty::Adt(def, args) if def.is_enum() => {
let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap(); let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap();
let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap()); let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
@ -230,24 +209,23 @@ impl NonCopyConst {
.iter() .iter()
.map(|field| field.ty(cx.tcx, args)), .map(|field| field.ty(cx.tcx, args)),
) )
.any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, field, ty)) .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty))
}, },
ty::Adt(def, args) => val ty::Adt(def, args) => val
.unwrap_branch() .unwrap_branch()
.iter() .iter()
.zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args))) .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
.any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)), .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
ty::Tuple(tys) => val ty::Tuple(tys) => val
.unwrap_branch() .unwrap_branch()
.iter() .iter()
.zip(tys) .zip(tys)
.any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)), .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
_ => false, _ => false,
} }
} }
fn is_value_unfrozen_raw<'tcx>( fn is_value_unfrozen_raw(
&self,
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>, result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
@ -277,11 +255,11 @@ impl NonCopyConst {
// I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
matches!(err, ErrorHandled::TooGeneric(..)) matches!(err, ErrorHandled::TooGeneric(..))
}, },
|val| val.map_or(true, |val| self.is_value_unfrozen_raw_inner(cx, val, ty)), |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)),
) )
} }
fn is_value_unfrozen_poly<'tcx>(&self, cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
let def_id = body_id.hir_id.owner.to_def_id(); let def_id = body_id.hir_id.owner.to_def_id();
let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id); let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
let instance = ty::Instance::new(def_id, args); let instance = ty::Instance::new(def_id, args);
@ -291,17 +269,17 @@ impl NonCopyConst {
}; };
let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, DUMMY_SP); let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, DUMMY_SP);
self.is_value_unfrozen_raw(cx, result, ty) Self::is_value_unfrozen_raw(cx, result, ty)
} }
fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
let args = cx.typeck_results().node_args(hir_id); let args = cx.typeck_results().node_args(hir_id);
let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP); let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
self.is_value_unfrozen_raw(cx, result, ty) Self::is_value_unfrozen_raw(cx, result, ty)
} }
pub fn const_eval_resolve<'tcx>( pub fn const_eval_resolve(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
ct: ty::UnevaluatedConst<'tcx>, ct: ty::UnevaluatedConst<'tcx>,
@ -321,26 +299,17 @@ impl NonCopyConst {
} }
} }
impl<'tcx> LateLintPass<'tcx> for NonCopyConst { impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
fn check_crate(&mut self, cx: &LateContext<'tcx>) { fn check_crate(&mut self, cx: &LateContext<'tcx>) {
self.ignore_mut_def_ids.clear(); self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
let mut path = Vec::new();
for ty in &self.ignore_interior_mutability {
path.extend(ty.split("::"));
for id in def_path_def_ids(cx, &path[..]) {
self.ignore_mut_def_ids.insert(id);
}
path.clear();
}
} }
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
if let ItemKind::Const(.., body_id) = it.kind { if let ItemKind::Const(.., body_id) = it.kind {
let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); let ty = cx.tcx.type_of(it.owner_id).instantiate_identity();
if !ignored_macro(cx, it) if !ignored_macro(cx, it)
&& !self.is_ty_ignored(ty) && self.interior_mut.is_interior_mut_ty(cx, ty)
&& Self::is_unfrozen(cx, ty) && Self::is_value_unfrozen_poly(cx, body_id, ty)
&& self.is_value_unfrozen_poly(cx, body_id, ty)
{ {
lint(cx, Source::Item { item: it.span }); lint(cx, Source::Item { item: it.span });
} }
@ -354,7 +323,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// Normalize assoc types because ones originated from generic params // Normalize assoc types because ones originated from generic params
// bounded other traits could have their bound. // bounded other traits could have their bound.
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized) if self.interior_mut.is_interior_mut_ty(cx, normalized)
// When there's no default value, lint it only according to its type; // When there's no default value, lint it only according to its type;
// in other words, lint consts whose value *could* be unfrozen, not definitely is. // in other words, lint consts whose value *could* be unfrozen, not definitely is.
// This feels inconsistent with how the lint treats generic types, // This feels inconsistent with how the lint treats generic types,
@ -367,7 +336,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// i.e. having an enum doesn't necessary mean a type has a frozen variant. // i.e. having an enum doesn't necessary mean a type has a frozen variant.
// And, implementing it isn't a trivial task; it'll probably end up // And, implementing it isn't a trivial task; it'll probably end up
// re-implementing the trait predicate evaluation specific to `Freeze`. // re-implementing the trait predicate evaluation specific to `Freeze`.
&& body_id_opt.map_or(true, |body_id| self.is_value_unfrozen_poly(cx, body_id, normalized)) && body_id_opt.map_or(true, |body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized))
{ {
lint(cx, Source::Assoc { item: trait_item.span }); lint(cx, Source::Assoc { item: trait_item.span });
} }
@ -409,8 +378,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);` // e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
&& let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity() && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity()
&& let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty) && let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty)
&& !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized) && self.interior_mut.is_interior_mut_ty(cx, normalized)
&& self.is_value_unfrozen_poly(cx, *body_id, normalized) && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
{ {
lint(cx, Source::Assoc { item: impl_item.span }); lint(cx, Source::Assoc { item: impl_item.span });
} }
@ -420,9 +389,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
// Normalize assoc types originated from generic params. // Normalize assoc types originated from generic params.
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
if !self.is_ty_ignored(ty) if self.interior_mut.is_interior_mut_ty(cx, normalized)
&& Self::is_unfrozen(cx, ty) && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
&& self.is_value_unfrozen_poly(cx, *body_id, normalized)
{ {
lint(cx, Source::Assoc { item: impl_item.span }); lint(cx, Source::Assoc { item: impl_item.span });
} }
@ -517,9 +485,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
cx.typeck_results().expr_ty(dereferenced_expr) cx.typeck_results().expr_ty(dereferenced_expr)
}; };
if !self.is_ty_ignored(ty) if self.interior_mut.is_interior_mut_ty(cx, ty)
&& Self::is_unfrozen(cx, ty) && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
&& self.is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
{ {
lint(cx, Source::Expr { expr: expr.span }); lint(cx, Source::Expr { expr: expr.span });
} }

View File

@ -7,14 +7,13 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use {rustc_ast as ast, rustc_hir as hir}; use {rustc_ast as ast, rustc_hir as hir};
const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]];
const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"];
const INTEGER_METHODS: &[Symbol] = &[ const DISALLOWED_INT_METHODS: &[Symbol] = &[
sym::saturating_div, sym::saturating_div,
sym::wrapping_div, sym::wrapping_div,
sym::wrapping_rem, sym::wrapping_rem,
@ -27,8 +26,8 @@ pub struct ArithmeticSideEffects {
allowed_unary: FxHashSet<String>, allowed_unary: FxHashSet<String>,
// Used to check whether expressions are constants, such as in enum discriminants and consts // Used to check whether expressions are constants, such as in enum discriminants and consts
const_span: Option<Span>, const_span: Option<Span>,
disallowed_int_methods: FxHashSet<Symbol>,
expr_span: Option<Span>, expr_span: Option<Span>,
integer_methods: FxHashSet<Symbol>,
} }
impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]);
@ -53,8 +52,8 @@ impl ArithmeticSideEffects {
allowed_binary, allowed_binary,
allowed_unary, allowed_unary,
const_span: None, const_span: None,
disallowed_int_methods: DISALLOWED_INT_METHODS.iter().copied().collect(),
expr_span: None, expr_span: None,
integer_methods: INTEGER_METHODS.iter().copied().collect(),
} }
} }
@ -91,10 +90,10 @@ impl ArithmeticSideEffects {
fn has_specific_allowed_type_and_operation<'tcx>( fn has_specific_allowed_type_and_operation<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
lhs_ty: Ty<'tcx>, lhs_ty: Ty<'tcx>,
op: &Spanned<hir::BinOpKind>, op: hir::BinOpKind,
rhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>,
) -> bool { ) -> bool {
let is_div_or_rem = matches!(op.node, hir::BinOpKind::Div | hir::BinOpKind::Rem); let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem);
let is_non_zero_u = |cx: &LateContext<'tcx>, ty: Ty<'tcx>| { let is_non_zero_u = |cx: &LateContext<'tcx>, ty: Ty<'tcx>| {
let tcx = cx.tcx; let tcx = cx.tcx;
@ -166,13 +165,35 @@ impl ArithmeticSideEffects {
None None
} }
/// Methods like `add_assign` are send to their `BinOps` references.
fn manage_sugar_methods<'tcx>(
&mut self,
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
lhs: &'tcx hir::Expr<'_>,
ps: &hir::PathSegment<'_>,
rhs: &'tcx hir::Expr<'_>,
) {
if ps.ident.name == sym::add || ps.ident.name == sym::add_assign {
self.manage_bin_ops(cx, expr, hir::BinOpKind::Add, lhs, rhs);
} else if ps.ident.name == sym::div || ps.ident.name == sym::div_assign {
self.manage_bin_ops(cx, expr, hir::BinOpKind::Div, lhs, rhs);
} else if ps.ident.name == sym::mul || ps.ident.name == sym::mul_assign {
self.manage_bin_ops(cx, expr, hir::BinOpKind::Mul, lhs, rhs);
} else if ps.ident.name == sym::rem || ps.ident.name == sym::rem_assign {
self.manage_bin_ops(cx, expr, hir::BinOpKind::Rem, lhs, rhs);
} else if ps.ident.name == sym::sub || ps.ident.name == sym::sub_assign {
self.manage_bin_ops(cx, expr, hir::BinOpKind::Sub, lhs, rhs);
}
}
/// Manages when the lint should be triggered. Operations in constant environments, hard coded /// Manages when the lint should be triggered. Operations in constant environments, hard coded
/// types, custom allowed types and non-constant operations that won't overflow are ignored. /// types, custom allowed types and non-constant operations that don't overflow are ignored.
fn manage_bin_ops<'tcx>( fn manage_bin_ops<'tcx>(
&mut self, &mut self,
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>, expr: &'tcx hir::Expr<'_>,
op: &Spanned<hir::BinOpKind>, op: hir::BinOpKind,
lhs: &'tcx hir::Expr<'_>, lhs: &'tcx hir::Expr<'_>,
rhs: &'tcx hir::Expr<'_>, rhs: &'tcx hir::Expr<'_>,
) { ) {
@ -180,7 +201,7 @@ impl ArithmeticSideEffects {
return; return;
} }
if !matches!( if !matches!(
op.node, op,
hir::BinOpKind::Add hir::BinOpKind::Add
| hir::BinOpKind::Div | hir::BinOpKind::Div
| hir::BinOpKind::Mul | hir::BinOpKind::Mul
@ -204,7 +225,7 @@ impl ArithmeticSideEffects {
return; return;
} }
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node { if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op {
// At least for integers, shifts are already handled by the CTFE // At least for integers, shifts are already handled by the CTFE
return; return;
} }
@ -213,7 +234,7 @@ impl ArithmeticSideEffects {
Self::literal_integer(cx, actual_rhs), Self::literal_integer(cx, actual_rhs),
) { ) {
(None, None) => false, (None, None) => false,
(None, Some(n)) => match (&op.node, n) { (None, Some(n)) => match (&op, n) {
// Division and module are always valid if applied to non-zero integers // Division and module are always valid if applied to non-zero integers
(hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true, (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
// Adding or subtracting zeros is always a no-op // Adding or subtracting zeros is always a no-op
@ -223,7 +244,7 @@ impl ArithmeticSideEffects {
=> true, => true,
_ => false, _ => false,
}, },
(Some(n), None) => match (&op.node, n) { (Some(n), None) => match (&op, n) {
// Adding or subtracting zeros is always a no-op // Adding or subtracting zeros is always a no-op
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
// Multiplication by 1 or 0 will never overflow // Multiplication by 1 or 0 will never overflow
@ -249,6 +270,7 @@ impl ArithmeticSideEffects {
&mut self, &mut self,
args: &'tcx [hir::Expr<'_>], args: &'tcx [hir::Expr<'_>],
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
ps: &'tcx hir::PathSegment<'_>, ps: &'tcx hir::PathSegment<'_>,
receiver: &'tcx hir::Expr<'_>, receiver: &'tcx hir::Expr<'_>,
) { ) {
@ -262,7 +284,8 @@ impl ArithmeticSideEffects {
if !Self::is_integral(instance_ty) { if !Self::is_integral(instance_ty) {
return; return;
} }
if !self.integer_methods.contains(&ps.ident.name) { self.manage_sugar_methods(cx, expr, receiver, ps, arg);
if !self.disallowed_int_methods.contains(&ps.ident.name) {
return; return;
} }
let (actual_arg, _) = peel_hir_expr_refs(arg); let (actual_arg, _) = peel_hir_expr_refs(arg);
@ -310,10 +333,10 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
} }
match &expr.kind { match &expr.kind {
hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => {
self.manage_bin_ops(cx, expr, op, lhs, rhs); self.manage_bin_ops(cx, expr, op.node, lhs, rhs);
}, },
hir::ExprKind::MethodCall(ps, receiver, args, _) => { hir::ExprKind::MethodCall(ps, receiver, args, _) => {
self.manage_method_call(args, cx, ps, receiver); self.manage_method_call(args, cx, expr, ps, receiver);
}, },
hir::ExprKind::Unary(un_op, un_expr) => { hir::ExprKind::Unary(un_op, un_expr) => {
self.manage_unary_ops(cx, expr, un_expr, *un_op); self.manage_unary_ops(cx, expr, un_expr, *un_op);

View File

@ -11,9 +11,8 @@ use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::{HirId, HirIdMap}; use rustc_hir::hir_id::{HirId, HirIdMap};
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{ use rustc_hir::{
self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind,
ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety,
TyKind, Unsafety,
}; };
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_infer::traits::{Obligation, ObligationCause};
@ -687,9 +686,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
.filter_map(|(i, arg)| { .filter_map(|(i, arg)| {
let param = &body.params[arg.idx]; let param = &body.params[arg.idx];
match param.pat.kind { match param.pat.kind {
PatKind::Binding(BindingMode::NONE, id, _, None) PatKind::Binding(BindingMode::NONE, id, _, None) if !is_lint_allowed(cx, PTR_ARG, param.hir_id) => {
if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
{
Some((id, i)) Some((id, i))
}, },
_ => { _ => {

View File

@ -14,8 +14,8 @@ use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt,
Stmt, StmtKind, StmtKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;

View File

@ -1,7 +1,7 @@
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::VecArgs; use clippy_utils::higher::VecArgs;
use clippy_utils::macros::root_macro_call; use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::{expr_or_init, fn_def_id, match_def_path, paths}; use clippy_utils::{expr_or_init, fn_def_id, match_def_path, paths};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -65,8 +65,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, s
/// Checks `vec![Vec::with_capacity(x); n]` /// Checks `vec![Vec::with_capacity(x); n]`
fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some(mac_call) = root_macro_call(expr.span) if matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some()
&& cx.tcx.is_diagnostic_item(sym::vec_macro, mac_call.def_id)
&& let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr) && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr)
&& fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY))
&& !len_expr.span.from_expansion() && !len_expr.span.from_expansion()

View File

@ -28,7 +28,7 @@ declare_clippy_lint! {
/// fn size(&self) -> usize { /// fn size(&self) -> usize {
/// // Note that `&self` as an argument is a `&&Foo`: Because `self` /// // Note that `&self` as an argument is a `&&Foo`: Because `self`
/// // is already a reference, `&self` is a double-reference. /// // is already a reference, `&self` is a double-reference.
/// // The return value of `size_of_val()` therefor is the /// // The return value of `size_of_val()` therefore is the
/// // size of the reference-type, not the size of `self`. /// // size of the reference-type, not the size of `self`.
/// std::mem::size_of_val(&self) /// std::mem::size_of_val(&self)
/// } /// }

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call; use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::{ use clippy_utils::{
get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local,
@ -145,9 +145,7 @@ impl SlowVectorInit {
// Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro. // Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro.
// This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an // This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an
// empty vec // empty vec
if expr.span.from_expansion() if expr.span.from_expansion() && matching_root_macro_call(cx, expr.span, sym::vec_macro).is_none() {
&& root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro)
{
return None; return None;
} }

View File

@ -1,13 +1,14 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::macro_backtrace;
use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks}; use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{intravisit, ExprKind}; use rustc_hir::{intravisit, Expr, ExprKind};
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::sym::thread_local_macro; use rustc_span::sym::{self, thread_local_macro};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -69,6 +70,26 @@ fn is_thread_local_initializer(
) )
} }
fn is_unreachable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(macro_call) = macro_backtrace(expr.span).next()
&& let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
{
return (matches!(
diag_name,
sym::core_panic_macro
| sym::std_panic_macro
| sym::core_panic_2015_macro
| sym::std_panic_2015_macro
| sym::core_panic_2021_macro
) && !cx.tcx.hir().is_inside_const_context(expr.hir_id))
|| matches!(
diag_name,
sym::unimplemented_macro | sym::todo_macro | sym::unreachable_macro | sym::unreachable_2015_macro
);
}
false
}
#[inline] #[inline]
fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool { fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool {
// Building MIR for `fn`s with unsatisfiable preds results in ICE. // Building MIR for `fn`s with unsatisfiable preds results in ICE.
@ -102,12 +123,17 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
// for details on this issue, see: // for details on this issue, see:
// https://github.com/rust-lang/rust-clippy/pull/12276 // https://github.com/rust-lang/rust-clippy/pull/12276
&& !cx.tcx.is_const_fn(defid) && !cx.tcx.is_const_fn(defid)
&& initializer_can_be_made_const(cx, defid, &self.msrv)
// we know that the function is const-qualifiable, so now
// we need only to get the initializer expression to span-lint it.
&& let ExprKind::Block(block, _) = body.value.kind && let ExprKind::Block(block, _) = body.value.kind
&& let Some(unpeeled) = block.expr && let Some(unpeeled) = block.expr
&& let ret_expr = peel_blocks(unpeeled) && let ret_expr = peel_blocks(unpeeled)
// A common pattern around threadlocal! is to make the value unreachable
// to force an initialization before usage
// https://github.com/rust-lang/rust-clippy/issues/12637
// we ensure that this is reachable before we check in mir
&& !is_unreachable(cx, ret_expr)
&& initializer_can_be_made_const(cx, defid, &self.msrv)
// we know that the function is const-qualifiable, so now
// we need only to get the initializer expression to span-lint it.
&& let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }") && let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }")
&& initializer_snippet != "thread_local! { ... }" && initializer_snippet != "thread_local! { ... }"
{ {

View File

@ -38,7 +38,7 @@ declare_clippy_lint! {
/// } /// }
/// } /// }
/// ``` /// ```
#[clippy::version = "1.77.0"] #[clippy::version = "1.78.0"]
pub TO_STRING_TRAIT_IMPL, pub TO_STRING_TRAIT_IMPL,
style, style,
"check for direct implementations of `ToString`" "check for direct implementations of `ToString`"

View File

@ -237,6 +237,7 @@ impl TraitBounds {
} }
} }
#[allow(clippy::mutable_key_type)]
fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
struct SpanlessTy<'cx, 'tcx> { struct SpanlessTy<'cx, 'tcx> {
ty: &'tcx Ty<'tcx>, ty: &'tcx Ty<'tcx>,

View File

@ -12,8 +12,8 @@ mod vec_box;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitItem, Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitFn,
TraitItemKind, TyKind, TraitItem, TraitItemKind, TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
@ -420,7 +420,13 @@ impl<'tcx> LateLintPass<'tcx> for Types {
TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
self.check_ty(cx, ty, context); self.check_ty(cx, ty, context);
}, },
TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context), TraitItemKind::Fn(ref sig, trait_method) => {
// Check only methods without body
// Methods with body are covered by check_fn.
if let TraitFn::Required(_) = trait_method {
self.check_fn_decl(cx, sig.decl, context);
}
},
TraitItemKind::Type(..) => (), TraitItemKind::Type(..) => (),
} }
} }

View File

@ -7,8 +7,8 @@ use rustc_ast::LitIntType;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::{ use rustc_hir::{
ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatKind,
PatKind, QPath, StmtKind, TyKind, QPath, StmtKind, TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;

View File

@ -7,7 +7,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_copy; use clippy_utils::ty::is_copy;
use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::visitors::for_each_local_use_after_expr;
use clippy_utils::{get_parent_expr, higher, is_trait_method}; use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -22,6 +22,7 @@ pub struct UselessVec {
pub too_large_for_stack: u64, pub too_large_for_stack: u64,
pub msrv: Msrv, pub msrv: Msrv,
pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>, pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
pub allow_in_test: bool,
} }
declare_clippy_lint! { declare_clippy_lint! {
@ -57,6 +58,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else { let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else {
return; return;
}; };
if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) {
return;
};
// the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!`
let callsite = expr.span.parent_callsite().unwrap_or(expr.span); let callsite = expr.span.parent_callsite().unwrap_or(expr.span);

View File

@ -6,9 +6,7 @@ use clippy_utils::{get_parent_expr, path_to_local_id};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{ use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp};
BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;

View File

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_utils" name = "clippy_utils"
version = "0.1.79" version = "0.1.80"
edition = "2021" edition = "2021"
publish = false publish = false

View File

@ -2328,10 +2328,10 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
/// ///
/// Given functions `eq` and `hash` such that `eq(a, b) == true` /// Given functions `eq` and `hash` such that `eq(a, b) == true`
/// implies `hash(a) == hash(b)` /// implies `hash(a) == hash(b)`
pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> pub fn search_same<T, Hash, Eq>(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec<(&T, &T)>
where where
Hash: Fn(&T) -> u64, Hash: FnMut(&T) -> u64,
Eq: Fn(&T, &T) -> bool, Eq: FnMut(&T, &T) -> bool,
{ {
match exprs { match exprs {
[a, b] if eq(a, b) => return vec![(a, b)], [a, b] if eq(a, b) => return vec![(a, b)],
@ -2505,8 +2505,9 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl Fn(&[Sym
/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function /// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool { pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
with_test_item_names(tcx, tcx.parent_module(id), |names| { with_test_item_names(tcx, tcx.parent_module(id), |names| {
tcx.hir() let node = tcx.hir_node(id);
.parent_iter(id) once((id, node))
.chain(tcx.hir().parent_iter(id))
// Since you can nest functions we need to collect all until we leave // Since you can nest functions we need to collect all until we leave
// function scope // function scope
.any(|(_id, node)| { .any(|(_id, node)| {
@ -2547,6 +2548,11 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
.any(|parent_id| is_cfg_test(tcx, parent_id)) .any(|parent_id| is_cfg_test(tcx, parent_id))
} }
/// Checks if the node is in a `#[test]` function or has any parent node marked `#[cfg(test)]`
pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
is_in_test_function(tcx, hir_id) || is_in_cfg_test(tcx, hir_id)
}
/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied.
pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
let hir = tcx.hir(); let hir = tcx.hir();

View File

@ -119,10 +119,20 @@ pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
/// If the macro backtrace of `span` has a macro call at the root expansion /// If the macro backtrace of `span` has a macro call at the root expansion
/// (i.e. not a nested macro call), returns `Some` with the `MacroCall` /// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
///
/// If you only want to check whether the root macro has a specific name,
/// consider using [`matching_root_macro_call`] instead.
pub fn root_macro_call(span: Span) -> Option<MacroCall> { pub fn root_macro_call(span: Span) -> Option<MacroCall> {
macro_backtrace(span).last() macro_backtrace(span).last()
} }
/// A combination of [`root_macro_call`] and
/// [`is_diagnostic_item`](rustc_middle::ty::TyCtxt::is_diagnostic_item) that returns a `MacroCall`
/// at the root expansion if only it matches the given name.
pub fn matching_root_macro_call(cx: &LateContext<'_>, span: Span, name: Symbol) -> Option<MacroCall> {
root_macro_call(span).filter(|mc| cx.tcx.is_diagnostic_item(name, mc.def_id))
}
/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node" /// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
/// produced by the macro call, as in [`first_node_in_macro`]. /// produced by the macro call, as in [`first_node_in_macro`].
pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> { pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {

View File

@ -19,8 +19,6 @@ pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "B
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
pub const CORE_ITER_ENUMERATE_METHOD: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "enumerate"];
pub const CORE_ITER_ENUMERATE_STRUCT: [&str; 5] = ["core", "iter", "adapters", "enumerate", "Enumerate"];
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"]; pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"];
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
@ -73,7 +71,6 @@ pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"];
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"]; pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"]; pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
@ -81,7 +78,6 @@ pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"]; pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"]; pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
@ -108,7 +104,6 @@ pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "Vec
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"]; pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"];
pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"]; pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"]; pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"];
pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"]; pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"];

View File

@ -29,9 +29,10 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_trait_selection::traits::{Obligation, ObligationCause}; use rustc_trait_selection::traits::{Obligation, ObligationCause};
use std::assert_matches::debug_assert_matches; use std::assert_matches::debug_assert_matches;
use std::collections::hash_map::Entry;
use std::iter; use std::iter;
use crate::{match_def_path, path_res}; use crate::{def_path_def_ids, match_def_path, path_res};
mod type_certainty; mod type_certainty;
pub use type_certainty::expr_type_is_certain; pub use type_certainty::expr_type_is_certain;
@ -1198,47 +1199,88 @@ pub fn make_normalized_projection<'tcx>(
helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?) helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?)
} }
/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`] /// Helper to check if given type has inner mutability such as [`std::cell::Cell`] or
/// etc. /// [`std::cell::RefCell`].
pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { #[derive(Default, Debug)]
match *ty.kind() { pub struct InteriorMut<'tcx> {
ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty), ignored_def_ids: FxHashSet<DefId>,
ty::Slice(inner_ty) => is_interior_mut_ty(cx, inner_ty), ignore_pointers: bool,
ty::Array(inner_ty, size) => { tys: FxHashMap<Ty<'tcx>, Option<bool>>,
size.try_eval_target_usize(cx.tcx, cx.param_env) }
.map_or(true, |u| u != 0)
&& is_interior_mut_ty(cx, inner_ty) impl<'tcx> InteriorMut<'tcx> {
}, pub fn new(cx: &LateContext<'tcx>, ignore_interior_mutability: &[String]) -> Self {
ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)), let ignored_def_ids = ignore_interior_mutability
ty::Adt(def, args) => {
// Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
// that of their type parameters. Note: we don't include `HashSet` and `HashMap`
// because they have no impl for `Hash` or `Ord`.
let def_id = def.did();
let is_std_collection = [
sym::Option,
sym::Result,
sym::LinkedList,
sym::Vec,
sym::VecDeque,
sym::BTreeMap,
sym::BTreeSet,
sym::Rc,
sym::Arc,
]
.iter() .iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); .flat_map(|ignored_ty| {
let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); let path: Vec<&str> = ignored_ty.split("::").collect();
if is_std_collection || is_box { def_path_def_ids(cx, path.as_slice())
// The type is mutable if any of its type parameters are })
args.types().any(|ty| is_interior_mut_ty(cx, ty)) .collect();
} else {
!ty.has_escaping_bound_vars() Self {
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() ignored_def_ids,
&& !ty.is_freeze(cx.tcx, cx.param_env) ..Self::default()
} }
}, }
_ => false,
pub fn without_pointers(cx: &LateContext<'tcx>, ignore_interior_mutability: &[String]) -> Self {
Self {
ignore_pointers: true,
..Self::new(cx, ignore_interior_mutability)
}
}
/// Check if given type has inner mutability such as [`std::cell::Cell`] or
/// [`std::cell::RefCell`] etc.
pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match self.tys.entry(ty) {
Entry::Occupied(o) => return *o.get() == Some(true),
// Temporarily insert a `None` to break cycles
Entry::Vacant(v) => v.insert(None),
};
let interior_mut = match *ty.kind() {
ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.is_interior_mut_ty(cx, inner_ty),
ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.is_interior_mut_ty(cx, inner_ty),
ty::Array(inner_ty, size) => {
size.try_eval_target_usize(cx.tcx, cx.param_env)
.map_or(true, |u| u != 0)
&& self.is_interior_mut_ty(cx, inner_ty)
},
ty::Tuple(fields) => fields.iter().any(|ty| self.is_interior_mut_ty(cx, ty)),
ty::Adt(def, _) if def.is_unsafe_cell() => true,
ty::Adt(def, args) => {
let is_std_collection = matches!(
cx.tcx.get_diagnostic_name(def.did()),
Some(
sym::LinkedList
| sym::Vec
| sym::VecDeque
| sym::BTreeMap
| sym::BTreeSet
| sym::HashMap
| sym::HashSet
| sym::Arc
| sym::Rc
)
);
if is_std_collection || def.is_box() {
// Include the types from std collections that are behind pointers internally
args.types().any(|ty| self.is_interior_mut_ty(cx, ty))
} else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() {
false
} else {
def.all_fields()
.any(|f| self.is_interior_mut_ty(cx, f.ty(cx.tcx, args)))
}
},
_ => false,
};
self.tys.insert(ty, Some(interior_mut));
interior_mut
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "declare_clippy_lint" name = "declare_clippy_lint"
version = "0.1.79" version = "0.1.80"
edition = "2021" edition = "2021"
publish = false publish = false

View File

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly-2024-04-18" channel = "nightly-2024-05-02"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View File

@ -4,6 +4,7 @@
#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(rust_2018_idioms, unused_lifetimes)]
#![allow(unused_extern_crates)] #![allow(unused_extern_crates)]
use ui_test::custom_flags::rustfix::RustfixMode;
use ui_test::spanned::Spanned; use ui_test::spanned::Spanned;
use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling}; use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, Mode, OutputConflictHandling};
@ -122,10 +123,11 @@ fn base_config(test_dir: &str) -> (Config, Args) {
out_dir: target_dir.join("ui_test"), out_dir: target_dir.join("ui_test"),
..Config::rustc(Path::new("tests").join(test_dir)) ..Config::rustc(Path::new("tests").join(test_dir))
}; };
config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo { config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo)).into();
rustfix: ui_test::RustfixMode::Everything, config
})) .comment_defaults
.into(); .base()
.set_custom("rustfix", RustfixMode::Everything);
config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into(); config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into();
config.with_args(&args); config.with_args(&args);
let current_exe_path = env::current_exe().unwrap(); let current_exe_path = env::current_exe().unwrap();
@ -235,13 +237,12 @@ fn run_ui_cargo() {
.push(("RUSTFLAGS".into(), Some("-Dwarnings".into()))); .push(("RUSTFLAGS".into(), Some("-Dwarnings".into())));
// We need to do this while we still have a rustc in the `program` field. // We need to do this while we still have a rustc in the `program` field.
config.fill_host_and_target().unwrap(); config.fill_host_and_target().unwrap();
config.dependencies_crate_manifest_path = None;
config.program.program.set_file_name(if cfg!(windows) { config.program.program.set_file_name(if cfg!(windows) {
"cargo-clippy.exe" "cargo-clippy.exe"
} else { } else {
"cargo-clippy" "cargo-clippy"
}); });
config.comment_defaults.base().edition = Default::default(); config.comment_defaults.base().custom.clear();
config config
.comment_defaults .comment_defaults

View File

@ -44,10 +44,18 @@ impl<T> Deref for Counted<T> {
} }
} }
#[derive(Hash, PartialEq, Eq)]
struct ContainsCounted {
inner: Counted<String>,
}
// This is not linted because `"mut_key::Counted"` is in // This is not linted because `"mut_key::Counted"` is in
// `arc_like_types` in `clippy.toml` // `arc_like_types` in `clippy.toml`
fn should_not_take_this_arg(_v: HashSet<Counted<String>>) {} fn should_not_take_this_arg(_v: HashSet<Counted<String>>) {}
fn indirect(_: HashMap<ContainsCounted, usize>) {}
fn main() { fn main() {
should_not_take_this_arg(HashSet::new()); should_not_take_this_arg(HashSet::new());
indirect(HashMap::new());
} }

View File

@ -11,6 +11,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
allow-print-in-tests allow-print-in-tests
allow-private-module-inception allow-private-module-inception
allow-unwrap-in-tests allow-unwrap-in-tests
allow-useless-vec-in-tests
allowed-dotfiles allowed-dotfiles
allowed-duplicate-crates allowed-duplicate-crates
allowed-idents-below-min-chars allowed-idents-below-min-chars
@ -91,6 +92,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
allow-print-in-tests allow-print-in-tests
allow-private-module-inception allow-private-module-inception
allow-unwrap-in-tests allow-unwrap-in-tests
allow-useless-vec-in-tests
allowed-dotfiles allowed-dotfiles
allowed-duplicate-crates allowed-duplicate-crates
allowed-idents-below-min-chars allowed-idents-below-min-chars
@ -171,6 +173,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
allow-print-in-tests allow-print-in-tests
allow-private-module-inception allow-private-module-inception
allow-unwrap-in-tests allow-unwrap-in-tests
allow-useless-vec-in-tests
allowed-dotfiles allowed-dotfiles
allowed-duplicate-crates allowed-duplicate-crates
allowed-idents-below-min-chars allowed-idents-below-min-chars

View File

@ -0,0 +1 @@
allow-useless-vec-in-tests = true

View File

@ -0,0 +1,26 @@
//@compile-flags: --test
#![warn(clippy::useless_vec)]
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
fn foo(_: &[u32]) {}
fn main() {
foo(&[1_u32]);
}
#[test]
pub fn in_test() {
foo(&vec![2_u32]);
}
#[cfg(test)]
fn in_cfg_test() {
foo(&vec![3_u32]);
}
#[cfg(test)]
mod mod1 {
fn in_cfg_test_mod() {
super::foo(&vec![4_u32]);
}
}

View File

@ -0,0 +1,26 @@
//@compile-flags: --test
#![warn(clippy::useless_vec)]
#![allow(clippy::unnecessary_operation, clippy::no_effect)]
fn foo(_: &[u32]) {}
fn main() {
foo(&vec![1_u32]);
}
#[test]
pub fn in_test() {
foo(&vec![2_u32]);
}
#[cfg(test)]
fn in_cfg_test() {
foo(&vec![3_u32]);
}
#[cfg(test)]
mod mod1 {
fn in_cfg_test_mod() {
super::foo(&vec![4_u32]);
}
}

View File

@ -0,0 +1,11 @@
error: useless use of `vec!`
--> tests/ui-toml/useless_vec/useless_vec.rs:8:9
|
LL | foo(&vec![1_u32]);
| ^^^^^^^^^^^^ help: you can use a slice directly: `&[1_u32]`
|
= note: `-D clippy::useless-vec` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::useless_vec)]`
error: aborting due to 1 previous error

View File

@ -521,4 +521,14 @@ pub fn issue_11393() {
example_rem(x, maybe_zero); example_rem(x, maybe_zero);
} }
pub fn issue_12318() {
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
let mut one: i32 = 1;
one.add_assign(1);
one.div_assign(1);
one.mul_assign(1);
one.rem_assign(1);
one.sub_assign(1);
}
fn main() {} fn main() {}

View File

@ -715,5 +715,17 @@ error: arithmetic operation that can potentially result in unexpected side-effec
LL | x % maybe_zero LL | x % maybe_zero
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: aborting due to 119 previous errors error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:527:5
|
LL | one.add_assign(1);
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:531:5
|
LL | one.sub_assign(1);
| ^^^^^^^^^^^^^^^^^
error: aborting due to 121 previous errors

View File

@ -9,6 +9,7 @@ use proc_macro::token_stream::IntoIter;
use proc_macro::Delimiter::{self, Brace, Parenthesis}; use proc_macro::Delimiter::{self, Brace, Parenthesis};
use proc_macro::Spacing::{self, Alone, Joint}; use proc_macro::Spacing::{self, Alone, Joint};
use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT};
use syn::spanned::Spanned;
type Result<T> = core::result::Result<T, TokenStream>; type Result<T> = core::result::Result<T, TokenStream>;
@ -124,6 +125,22 @@ fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Resul
Ok(()) Ok(())
} }
/// Takes an array repeat expression such as `[0_u32; 2]`, and return the tokens with 10 times the
/// original size, which turns to `[0_u32; 20]`.
#[proc_macro]
pub fn make_it_big(input: TokenStream) -> TokenStream {
let mut expr_repeat = syn::parse_macro_input!(input as syn::ExprRepeat);
let len_span = expr_repeat.len.span();
if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len {
if let syn::Lit::Int(lit_int) = &expr_lit.lit {
let orig_val = lit_int.base10_parse::<usize>().expect("not a valid length parameter");
let new_val = orig_val.saturating_mul(10);
expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val);
}
}
quote::quote!(#expr_repeat).into()
}
/// Within the item this attribute is attached to, an `inline!` macro is available which expands the /// Within the item this attribute is attached to, an `inline!` macro is available which expands the
/// contained tokens as though they came from a macro expansion. /// contained tokens as though they came from a macro expansion.
/// ///

View File

@ -158,7 +158,7 @@ trait BothOfCellAndGeneric<T> {
const INDIRECT: Cell<*const T>; const INDIRECT: Cell<*const T>;
fn function() { fn function() {
let _ = &Self::DIRECT; let _ = &Self::DIRECT; //~ ERROR: interior mutability
let _ = &Self::INDIRECT; //~ ERROR: interior mutability let _ = &Self::INDIRECT; //~ ERROR: interior mutability
} }
} }
@ -168,7 +168,7 @@ impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> {
const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
fn function() { fn function() {
let _ = &Self::DIRECT; let _ = &Self::DIRECT; //~ ERROR: interior mutability
let _ = &Self::INDIRECT; //~ ERROR: interior mutability let _ = &Self::INDIRECT; //~ ERROR: interior mutability
} }
} }

View File

@ -75,6 +75,14 @@ LL | let _ = &Self::WRAPPED_SELF;
| |
= help: assign this const to a local or static variable, and use the variable here = help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> tests/ui/borrow_interior_mutable_const/traits.rs:161:18
|
LL | let _ = &Self::DIRECT;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed error: a `const` item with interior mutability should not be borrowed
--> tests/ui/borrow_interior_mutable_const/traits.rs:162:18 --> tests/ui/borrow_interior_mutable_const/traits.rs:162:18
| |
@ -83,6 +91,14 @@ LL | let _ = &Self::INDIRECT;
| |
= help: assign this const to a local or static variable, and use the variable here = help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> tests/ui/borrow_interior_mutable_const/traits.rs:171:18
|
LL | let _ = &Self::DIRECT;
| ^^^^^^^^^^^^
|
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed error: a `const` item with interior mutability should not be borrowed
--> tests/ui/borrow_interior_mutable_const/traits.rs:172:18 --> tests/ui/borrow_interior_mutable_const/traits.rs:172:18
| |
@ -123,5 +139,5 @@ LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9);
| |
= help: assign this const to a local or static variable, and use the variable here = help: assign this const to a local or static variable, and use the variable here
error: aborting due to 15 previous errors error: aborting due to 17 previous errors

View File

@ -65,6 +65,8 @@ fn main() {
// Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563
let mut unnameable = Box::new(Option::default()); let mut unnameable = Box::new(Option::default());
let _ = unnameable.insert(|| {}); let _ = unnameable.insert(|| {});
let _ = Box::into_raw(Box::new(String::default()));
} }
fn ret_ty_fn() -> Box<bool> { fn ret_ty_fn() -> Box<bool> {
@ -75,6 +77,16 @@ fn call_ty_fn(_b: Box<u8>) {
issue_9621_dyn_trait(); issue_9621_dyn_trait();
} }
struct X<T>(T);
impl<T: Default> X<T> {
fn x(_: Box<T>) {}
fn same_generic_param() {
Self::x(Box::default());
}
}
use std::io::{Read, Result}; use std::io::{Read, Result};
impl Read for ImplementsDefault { impl Read for ImplementsDefault {

View File

@ -65,6 +65,8 @@ fn main() {
// Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563 // Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563
let mut unnameable = Box::new(Option::default()); let mut unnameable = Box::new(Option::default());
let _ = unnameable.insert(|| {}); let _ = unnameable.insert(|| {});
let _ = Box::into_raw(Box::new(String::default()));
} }
fn ret_ty_fn() -> Box<bool> { fn ret_ty_fn() -> Box<bool> {
@ -75,6 +77,16 @@ fn call_ty_fn(_b: Box<u8>) {
issue_9621_dyn_trait(); issue_9621_dyn_trait();
} }
struct X<T>(T);
impl<T: Default> X<T> {
fn x(_: Box<T>) {}
fn same_generic_param() {
Self::x(Box::new(T::default()));
}
}
use std::io::{Read, Result}; use std::io::{Read, Result};
impl Read for ImplementsDefault { impl Read for ImplementsDefault {

View File

@ -55,5 +55,11 @@ error: `Box::new(_)` of default value
LL | call_ty_fn(Box::new(u8::default())); LL | call_ty_fn(Box::new(u8::default()));
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
error: aborting due to 9 previous errors error: `Box::new(_)` of default value
--> tests/ui/box_default.rs:86:17
|
LL | Self::x(Box::new(T::default()));
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
error: aborting due to 10 previous errors

View File

@ -13,7 +13,8 @@
clippy::cast_abs_to_unsigned, clippy::cast_abs_to_unsigned,
clippy::no_effect, clippy::no_effect,
clippy::unnecessary_operation, clippy::unnecessary_operation,
clippy::unnecessary_literal_unwrap clippy::unnecessary_literal_unwrap,
clippy::identity_op
)] )]
fn main() { fn main() {
@ -479,3 +480,21 @@ fn issue12506() -> usize {
let bar: Result<Option<i64>, u32> = Ok(Some(10)); let bar: Result<Option<i64>, u32> = Ok(Some(10));
bar.unwrap().unwrap() as usize bar.unwrap().unwrap() as usize
} }
fn issue12721() {
fn x() -> u64 {
u64::MAX
}
// Don't lint.
(255 & 999999u64) as u8;
// Don't lint.
let _ = ((x() & 255) & 999999) as u8;
// Don't lint.
let _ = (999999 & (x() & 255)) as u8;
(256 & 999999u64) as u8;
//~^ ERROR: casting `u64` to `u8` may truncate the value
(255 % 999999u64) as u8;
//~^ ERROR: casting `u64` to `u8` may truncate the value
}

View File

@ -1,5 +1,5 @@
error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
--> tests/ui/cast.rs:22:5 --> tests/ui/cast.rs:23:5
| |
LL | x0 as f32; LL | x0 as f32;
| ^^^^^^^^^ | ^^^^^^^^^
@ -8,37 +8,37 @@ LL | x0 as f32;
= help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]` = help: to override `-D warnings` add `#[allow(clippy::cast_precision_loss)]`
error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
--> tests/ui/cast.rs:26:5 --> tests/ui/cast.rs:27:5
| |
LL | x1 as f32; LL | x1 as f32;
| ^^^^^^^^^ | ^^^^^^^^^
error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
--> tests/ui/cast.rs:28:5 --> tests/ui/cast.rs:29:5
| |
LL | x1 as f64; LL | x1 as f64;
| ^^^^^^^^^ | ^^^^^^^^^
error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
--> tests/ui/cast.rs:31:5 --> tests/ui/cast.rs:32:5
| |
LL | x2 as f32; LL | x2 as f32;
| ^^^^^^^^^ | ^^^^^^^^^
error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
--> tests/ui/cast.rs:34:5 --> tests/ui/cast.rs:35:5
| |
LL | x3 as f32; LL | x3 as f32;
| ^^^^^^^^^ | ^^^^^^^^^
error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
--> tests/ui/cast.rs:36:5 --> tests/ui/cast.rs:37:5
| |
LL | x3 as f64; LL | x3 as f64;
| ^^^^^^^^^ | ^^^^^^^^^
error: casting `f32` to `i32` may truncate the value error: casting `f32` to `i32` may truncate the value
--> tests/ui/cast.rs:39:5 --> tests/ui/cast.rs:40:5
| |
LL | 1f32 as i32; LL | 1f32 as i32;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -48,7 +48,7 @@ LL | 1f32 as i32;
= help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]`
error: casting `f32` to `u32` may truncate the value error: casting `f32` to `u32` may truncate the value
--> tests/ui/cast.rs:41:5 --> tests/ui/cast.rs:42:5
| |
LL | 1f32 as u32; LL | 1f32 as u32;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -56,7 +56,7 @@ LL | 1f32 as u32;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `f32` to `u32` may lose the sign of the value error: casting `f32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:41:5 --> tests/ui/cast.rs:42:5
| |
LL | 1f32 as u32; LL | 1f32 as u32;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -65,7 +65,7 @@ LL | 1f32 as u32;
= help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]` = help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]`
error: casting `f64` to `f32` may truncate the value error: casting `f64` to `f32` may truncate the value
--> tests/ui/cast.rs:45:5 --> tests/ui/cast.rs:46:5
| |
LL | 1f64 as f32; LL | 1f64 as f32;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -73,7 +73,7 @@ LL | 1f64 as f32;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `i32` to `i8` may truncate the value error: casting `i32` to `i8` may truncate the value
--> tests/ui/cast.rs:47:5 --> tests/ui/cast.rs:48:5
| |
LL | 1i32 as i8; LL | 1i32 as i8;
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -85,7 +85,7 @@ LL | i8::try_from(1i32);
| ~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~
error: casting `i32` to `u8` may truncate the value error: casting `i32` to `u8` may truncate the value
--> tests/ui/cast.rs:49:5 --> tests/ui/cast.rs:50:5
| |
LL | 1i32 as u8; LL | 1i32 as u8;
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -97,7 +97,7 @@ LL | u8::try_from(1i32);
| ~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~
error: casting `f64` to `isize` may truncate the value error: casting `f64` to `isize` may truncate the value
--> tests/ui/cast.rs:51:5 --> tests/ui/cast.rs:52:5
| |
LL | 1f64 as isize; LL | 1f64 as isize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -105,7 +105,7 @@ LL | 1f64 as isize;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `f64` to `usize` may truncate the value error: casting `f64` to `usize` may truncate the value
--> tests/ui/cast.rs:53:5 --> tests/ui/cast.rs:54:5
| |
LL | 1f64 as usize; LL | 1f64 as usize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -113,13 +113,13 @@ LL | 1f64 as usize;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `f64` to `usize` may lose the sign of the value error: casting `f64` to `usize` may lose the sign of the value
--> tests/ui/cast.rs:53:5 --> tests/ui/cast.rs:54:5
| |
LL | 1f64 as usize; LL | 1f64 as usize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: casting `u32` to `u16` may truncate the value error: casting `u32` to `u16` may truncate the value
--> tests/ui/cast.rs:56:5 --> tests/ui/cast.rs:57:5
| |
LL | 1f32 as u32 as u16; LL | 1f32 as u32 as u16;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
@ -131,7 +131,7 @@ LL | u16::try_from(1f32 as u32);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `f32` to `u32` may truncate the value error: casting `f32` to `u32` may truncate the value
--> tests/ui/cast.rs:56:5 --> tests/ui/cast.rs:57:5
| |
LL | 1f32 as u32 as u16; LL | 1f32 as u32 as u16;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -139,13 +139,13 @@ LL | 1f32 as u32 as u16;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `f32` to `u32` may lose the sign of the value error: casting `f32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:56:5 --> tests/ui/cast.rs:57:5
| |
LL | 1f32 as u32 as u16; LL | 1f32 as u32 as u16;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: casting `i32` to `i8` may truncate the value error: casting `i32` to `i8` may truncate the value
--> tests/ui/cast.rs:61:22 --> tests/ui/cast.rs:62:22
| |
LL | let _x: i8 = 1i32 as _; LL | let _x: i8 = 1i32 as _;
| ^^^^^^^^^ | ^^^^^^^^^
@ -157,7 +157,7 @@ LL | let _x: i8 = 1i32.try_into();
| ~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~
error: casting `f32` to `i32` may truncate the value error: casting `f32` to `i32` may truncate the value
--> tests/ui/cast.rs:63:9 --> tests/ui/cast.rs:64:9
| |
LL | 1f32 as i32; LL | 1f32 as i32;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -165,7 +165,7 @@ LL | 1f32 as i32;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `f64` to `i32` may truncate the value error: casting `f64` to `i32` may truncate the value
--> tests/ui/cast.rs:65:9 --> tests/ui/cast.rs:66:9
| |
LL | 1f64 as i32; LL | 1f64 as i32;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -173,7 +173,7 @@ LL | 1f64 as i32;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `f32` to `u8` may truncate the value error: casting `f32` to `u8` may truncate the value
--> tests/ui/cast.rs:67:9 --> tests/ui/cast.rs:68:9
| |
LL | 1f32 as u8; LL | 1f32 as u8;
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -181,13 +181,13 @@ LL | 1f32 as u8;
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
error: casting `f32` to `u8` may lose the sign of the value error: casting `f32` to `u8` may lose the sign of the value
--> tests/ui/cast.rs:67:9 --> tests/ui/cast.rs:68:9
| |
LL | 1f32 as u8; LL | 1f32 as u8;
| ^^^^^^^^^^ | ^^^^^^^^^^
error: casting `u8` to `i8` may wrap around the value error: casting `u8` to `i8` may wrap around the value
--> tests/ui/cast.rs:72:5 --> tests/ui/cast.rs:73:5
| |
LL | 1u8 as i8; LL | 1u8 as i8;
| ^^^^^^^^^ | ^^^^^^^^^
@ -196,31 +196,31 @@ LL | 1u8 as i8;
= help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]`
error: casting `u16` to `i16` may wrap around the value error: casting `u16` to `i16` may wrap around the value
--> tests/ui/cast.rs:75:5 --> tests/ui/cast.rs:76:5
| |
LL | 1u16 as i16; LL | 1u16 as i16;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: casting `u32` to `i32` may wrap around the value error: casting `u32` to `i32` may wrap around the value
--> tests/ui/cast.rs:77:5 --> tests/ui/cast.rs:78:5
| |
LL | 1u32 as i32; LL | 1u32 as i32;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: casting `u64` to `i64` may wrap around the value error: casting `u64` to `i64` may wrap around the value
--> tests/ui/cast.rs:79:5 --> tests/ui/cast.rs:80:5
| |
LL | 1u64 as i64; LL | 1u64 as i64;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: casting `usize` to `isize` may wrap around the value error: casting `usize` to `isize` may wrap around the value
--> tests/ui/cast.rs:81:5 --> tests/ui/cast.rs:82:5
| |
LL | 1usize as isize; LL | 1usize as isize;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: casting `usize` to `i8` may truncate the value error: casting `usize` to `i8` may truncate the value
--> tests/ui/cast.rs:84:5 --> tests/ui/cast.rs:85:5
| |
LL | 1usize as i8; LL | 1usize as i8;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
@ -232,7 +232,7 @@ LL | i8::try_from(1usize);
| ~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~
error: casting `usize` to `i16` may truncate the value error: casting `usize` to `i16` may truncate the value
--> tests/ui/cast.rs:87:5 --> tests/ui/cast.rs:88:5
| |
LL | 1usize as i16; LL | 1usize as i16;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -244,7 +244,7 @@ LL | i16::try_from(1usize);
| ~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers error: casting `usize` to `i16` may wrap around the value on targets with 16-bit wide pointers
--> tests/ui/cast.rs:87:5 --> tests/ui/cast.rs:88:5
| |
LL | 1usize as i16; LL | 1usize as i16;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -253,7 +253,7 @@ LL | 1usize as i16;
= note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types
error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
--> tests/ui/cast.rs:92:5 --> tests/ui/cast.rs:93:5
| |
LL | 1usize as i32; LL | 1usize as i32;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -265,19 +265,19 @@ LL | i32::try_from(1usize);
| ~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
--> tests/ui/cast.rs:92:5 --> tests/ui/cast.rs:93:5
| |
LL | 1usize as i32; LL | 1usize as i32;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers error: casting `usize` to `i64` may wrap around the value on targets with 64-bit wide pointers
--> tests/ui/cast.rs:96:5 --> tests/ui/cast.rs:97:5
| |
LL | 1usize as i64; LL | 1usize as i64;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers error: casting `u16` to `isize` may wrap around the value on targets with 16-bit wide pointers
--> tests/ui/cast.rs:101:5 --> tests/ui/cast.rs:102:5
| |
LL | 1u16 as isize; LL | 1u16 as isize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -286,13 +286,13 @@ LL | 1u16 as isize;
= note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types = note: for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types
error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
--> tests/ui/cast.rs:105:5 --> tests/ui/cast.rs:106:5
| |
LL | 1u32 as isize; LL | 1u32 as isize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
--> tests/ui/cast.rs:108:5 --> tests/ui/cast.rs:109:5
| |
LL | 1u64 as isize; LL | 1u64 as isize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -304,55 +304,55 @@ LL | isize::try_from(1u64);
| ~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
--> tests/ui/cast.rs:108:5 --> tests/ui/cast.rs:109:5
| |
LL | 1u64 as isize; LL | 1u64 as isize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:113:5 --> tests/ui/cast.rs:114:5
| |
LL | -1i32 as u32; LL | -1i32 as u32;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: casting `isize` to `usize` may lose the sign of the value error: casting `isize` to `usize` may lose the sign of the value
--> tests/ui/cast.rs:116:5 --> tests/ui/cast.rs:117:5
| |
LL | -1isize as usize; LL | -1isize as usize;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: casting `i8` to `u8` may lose the sign of the value error: casting `i8` to `u8` may lose the sign of the value
--> tests/ui/cast.rs:127:5 --> tests/ui/cast.rs:128:5
| |
LL | (i8::MIN).abs() as u8; LL | (i8::MIN).abs() as u8;
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: casting `i64` to `u64` may lose the sign of the value error: casting `i64` to `u64` may lose the sign of the value
--> tests/ui/cast.rs:131:5 --> tests/ui/cast.rs:132:5
| |
LL | (-1i64).abs() as u64; LL | (-1i64).abs() as u64;
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: casting `isize` to `usize` may lose the sign of the value error: casting `isize` to `usize` may lose the sign of the value
--> tests/ui/cast.rs:132:5 --> tests/ui/cast.rs:133:5
| |
LL | (-1isize).abs() as usize; LL | (-1isize).abs() as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i64` to `u64` may lose the sign of the value error: casting `i64` to `u64` may lose the sign of the value
--> tests/ui/cast.rs:139:5 --> tests/ui/cast.rs:140:5
| |
LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i64` to `u64` may lose the sign of the value error: casting `i64` to `u64` may lose the sign of the value
--> tests/ui/cast.rs:154:5 --> tests/ui/cast.rs:155:5
| |
LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i64` to `i8` may truncate the value error: casting `i64` to `i8` may truncate the value
--> tests/ui/cast.rs:205:5 --> tests/ui/cast.rs:206:5
| |
LL | (-99999999999i64).min(1) as i8; LL | (-99999999999i64).min(1) as i8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -364,7 +364,7 @@ LL | i8::try_from((-99999999999i64).min(1));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `u8` may truncate the value error: casting `u64` to `u8` may truncate the value
--> tests/ui/cast.rs:219:5 --> tests/ui/cast.rs:220:5
| |
LL | 999999u64.clamp(0, 256) as u8; LL | 999999u64.clamp(0, 256) as u8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -376,7 +376,7 @@ LL | u8::try_from(999999u64.clamp(0, 256));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E2` to `u8` may truncate the value error: casting `main::E2` to `u8` may truncate the value
--> tests/ui/cast.rs:242:21 --> tests/ui/cast.rs:243:21
| |
LL | let _ = self as u8; LL | let _ = self as u8;
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -388,7 +388,7 @@ LL | let _ = u8::try_from(self);
| ~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~
error: casting `main::E2::B` to `u8` will truncate the value error: casting `main::E2::B` to `u8` will truncate the value
--> tests/ui/cast.rs:244:21 --> tests/ui/cast.rs:245:21
| |
LL | let _ = Self::B as u8; LL | let _ = Self::B as u8;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -397,7 +397,7 @@ LL | let _ = Self::B as u8;
= help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]` = help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]`
error: casting `main::E5` to `i8` may truncate the value error: casting `main::E5` to `i8` may truncate the value
--> tests/ui/cast.rs:286:21 --> tests/ui/cast.rs:287:21
| |
LL | let _ = self as i8; LL | let _ = self as i8;
| ^^^^^^^^^^ | ^^^^^^^^^^
@ -409,13 +409,13 @@ LL | let _ = i8::try_from(self);
| ~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~
error: casting `main::E5::A` to `i8` will truncate the value error: casting `main::E5::A` to `i8` will truncate the value
--> tests/ui/cast.rs:288:21 --> tests/ui/cast.rs:289:21
| |
LL | let _ = Self::A as i8; LL | let _ = Self::A as i8;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: casting `main::E6` to `i16` may truncate the value error: casting `main::E6` to `i16` may truncate the value
--> tests/ui/cast.rs:305:21 --> tests/ui/cast.rs:306:21
| |
LL | let _ = self as i16; LL | let _ = self as i16;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -427,7 +427,7 @@ LL | let _ = i16::try_from(self);
| ~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~
error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
--> tests/ui/cast.rs:324:21 --> tests/ui/cast.rs:325:21
| |
LL | let _ = self as usize; LL | let _ = self as usize;
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -439,7 +439,7 @@ LL | let _ = usize::try_from(self);
| ~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E10` to `u16` may truncate the value error: casting `main::E10` to `u16` may truncate the value
--> tests/ui/cast.rs:371:21 --> tests/ui/cast.rs:372:21
| |
LL | let _ = self as u16; LL | let _ = self as u16;
| ^^^^^^^^^^^ | ^^^^^^^^^^^
@ -451,7 +451,7 @@ LL | let _ = u16::try_from(self);
| ~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value error: casting `u32` to `u8` may truncate the value
--> tests/ui/cast.rs:382:13 --> tests/ui/cast.rs:383:13
| |
LL | let c = (q >> 16) as u8; LL | let c = (q >> 16) as u8;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@ -463,7 +463,7 @@ LL | let c = u8::try_from(q >> 16);
| ~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value error: casting `u32` to `u8` may truncate the value
--> tests/ui/cast.rs:386:13 --> tests/ui/cast.rs:387:13
| |
LL | let c = (q / 1000) as u8; LL | let c = (q / 1000) as u8;
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
@ -475,85 +475,85 @@ LL | let c = u8::try_from(q / 1000);
| ~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:398:9 --> tests/ui/cast.rs:399:9
| |
LL | (x * x) as u32; LL | (x * x) as u32;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:403:32 --> tests/ui/cast.rs:404:32
| |
LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 };
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:405:5 --> tests/ui/cast.rs:406:5
| |
LL | (2_i32).checked_pow(3).unwrap() as u32; LL | (2_i32).checked_pow(3).unwrap() as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:406:5 --> tests/ui/cast.rs:407:5
| |
LL | (-2_i32).pow(3) as u32; LL | (-2_i32).pow(3) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:411:5 --> tests/ui/cast.rs:412:5
| |
LL | (-5_i32 % 2) as u32; LL | (-5_i32 % 2) as u32;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:413:5 --> tests/ui/cast.rs:414:5
| |
LL | (-5_i32 % -2) as u32; LL | (-5_i32 % -2) as u32;
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:416:5 --> tests/ui/cast.rs:417:5
| |
LL | (-2_i32 >> 1) as u32; LL | (-2_i32 >> 1) as u32;
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:420:5 --> tests/ui/cast.rs:421:5
| |
LL | (x * x) as u32; LL | (x * x) as u32;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:421:5 --> tests/ui/cast.rs:422:5
| |
LL | (x * x * x) as u32; LL | (x * x * x) as u32;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:425:5 --> tests/ui/cast.rs:426:5
| |
LL | (y * y * y * y * -2) as u16; LL | (y * y * y * y * -2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:427:5 --> tests/ui/cast.rs:428:5
| |
LL | (y * y * y / y * 2) as u16; LL | (y * y * y / y * 2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:428:5 --> tests/ui/cast.rs:429:5
| |
LL | (y * y / y * 2) as u16; LL | (y * y / y * 2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:430:5 --> tests/ui/cast.rs:431:5
| |
LL | (y / y * y * -2) as u16; LL | (y / y * y * -2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `/` error: equal expressions as operands to `/`
--> tests/ui/cast.rs:430:6 --> tests/ui/cast.rs:431:6
| |
LL | (y / y * y * -2) as u16; LL | (y / y * y * -2) as u16;
| ^^^^^ | ^^^^^
@ -561,97 +561,97 @@ LL | (y / y * y * -2) as u16;
= note: `#[deny(clippy::eq_op)]` on by default = note: `#[deny(clippy::eq_op)]` on by default
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:433:5 --> tests/ui/cast.rs:434:5
| |
LL | (y + y + y + -2) as u16; LL | (y + y + y + -2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:435:5 --> tests/ui/cast.rs:436:5
| |
LL | (y + y + y + 2) as u16; LL | (y + y + y + 2) as u16;
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:439:5 --> tests/ui/cast.rs:440:5
| |
LL | (z + -2) as u16; LL | (z + -2) as u16;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: casting `i16` to `u16` may lose the sign of the value error: casting `i16` to `u16` may lose the sign of the value
--> tests/ui/cast.rs:441:5 --> tests/ui/cast.rs:442:5
| |
LL | (z + z + 2) as u16; LL | (z + z + 2) as u16;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:444:9 --> tests/ui/cast.rs:445:9
| |
LL | (a * a * b * b * c * c) as u32; LL | (a * a * b * b * c * c) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:445:9 --> tests/ui/cast.rs:446:9
| |
LL | (a * b * c) as u32; LL | (a * b * c) as u32;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:447:9 --> tests/ui/cast.rs:448:9
| |
LL | (a * -b * c) as u32; LL | (a * -b * c) as u32;
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:449:9 --> tests/ui/cast.rs:450:9
| |
LL | (a * b * c * c) as u32; LL | (a * b * c * c) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:450:9 --> tests/ui/cast.rs:451:9
| |
LL | (a * -2) as u32; LL | (a * -2) as u32;
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:452:9 --> tests/ui/cast.rs:453:9
| |
LL | (a * b * c * -2) as u32; LL | (a * b * c * -2) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:454:9 --> tests/ui/cast.rs:455:9
| |
LL | (a / b) as u32; LL | (a / b) as u32;
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:455:9 --> tests/ui/cast.rs:456:9
| |
LL | (a / b * c) as u32; LL | (a / b * c) as u32;
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:457:9 --> tests/ui/cast.rs:458:9
| |
LL | (a / b + b * c) as u32; LL | (a / b + b * c) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:459:9 --> tests/ui/cast.rs:460:9
| |
LL | a.saturating_pow(3) as u32; LL | a.saturating_pow(3) as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:461:9 --> tests/ui/cast.rs:462:9
| |
LL | (a.abs() * b.pow(2) / c.abs()) as u32 LL | (a.abs() * b.pow(2) / c.abs()) as u32
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value error: casting `i32` to `u32` may lose the sign of the value
--> tests/ui/cast.rs:469:21 --> tests/ui/cast.rs:470:21
| |
LL | let _ = i32::MIN as u32; // cast_sign_loss LL | let _ = i32::MIN as u32; // cast_sign_loss
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@ -662,7 +662,7 @@ LL | m!();
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
error: casting `u32` to `u8` may truncate the value error: casting `u32` to `u8` may truncate the value
--> tests/ui/cast.rs:470:21 --> tests/ui/cast.rs:471:21
| |
LL | let _ = u32::MAX as u8; // cast_possible_truncation LL | let _ = u32::MAX as u8; // cast_possible_truncation
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
@ -678,7 +678,7 @@ LL | let _ = u8::try_from(u32::MAX); // cast_possible_truncation
| ~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~
error: casting `f64` to `f32` may truncate the value error: casting `f64` to `f32` may truncate the value
--> tests/ui/cast.rs:471:21 --> tests/ui/cast.rs:472:21
| |
LL | let _ = std::f64::consts::PI as f32; // cast_possible_truncation LL | let _ = std::f64::consts::PI as f32; // cast_possible_truncation
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -690,7 +690,7 @@ LL | m!();
= note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
--> tests/ui/cast.rs:480:5 --> tests/ui/cast.rs:481:5
| |
LL | bar.unwrap().unwrap() as usize LL | bar.unwrap().unwrap() as usize
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -702,10 +702,34 @@ LL | usize::try_from(bar.unwrap().unwrap())
| |
error: casting `i64` to `usize` may lose the sign of the value error: casting `i64` to `usize` may lose the sign of the value
--> tests/ui/cast.rs:480:5 --> tests/ui/cast.rs:481:5
| |
LL | bar.unwrap().unwrap() as usize LL | bar.unwrap().unwrap() as usize
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 90 previous errors error: casting `u64` to `u8` may truncate the value
--> tests/ui/cast.rs:496:5
|
LL | (256 & 999999u64) as u8;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u8::try_from(256 & 999999u64);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `u8` may truncate the value
--> tests/ui/cast.rs:498:5
|
LL | (255 % 999999u64) as u8;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u8::try_from(255 % 999999u64);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 92 previous errors

View File

@ -4,7 +4,8 @@
clippy::needless_return, clippy::needless_return,
clippy::no_effect, clippy::no_effect,
clippy::single_match, clippy::single_match,
clippy::uninlined_format_args clippy::uninlined_format_args,
clippy::let_unit_value
)] )]
fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) { fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
@ -238,13 +239,22 @@ fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u
}, },
_ => return, _ => return,
} }
match make::<Option<E<u32>>>() { #[clippy::msrv = "1.52.0"]
let _ = match make::<Option<E<u32>>>() {
Some(val) => match val { Some(val) => match val {
E::A(val) | E::B(val) => foo(val), E::A(val) | E::B(val) => foo(val),
_ => return, _ => return,
}, },
_ => return, _ => return,
} };
#[clippy::msrv = "1.53.0"]
let _ = match make::<Option<E<u32>>>() {
Some(val) => match val {
E::A(val) | E::B(val) => foo(val),
_ => return,
},
_ => return,
};
if let Ok(val) = res_opt { if let Ok(val) = res_opt {
if let Some(n) = val { if let Some(n) = val {
let _ = || { let _ = || {

View File

@ -1,5 +1,5 @@
error: this `match` can be collapsed into the outer `match` error: this `match` can be collapsed into the outer `match`
--> tests/ui/collapsible_match.rs:13:20 --> tests/ui/collapsible_match.rs:14:20
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ____________________^ | ____________________^
@ -10,7 +10,7 @@ LL | | },
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:13:12 --> tests/ui/collapsible_match.rs:14:12
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ^^^ replace this binding | ^^^ replace this binding
@ -21,7 +21,7 @@ LL | Some(n) => foo(n),
= help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]`
error: this `match` can be collapsed into the outer `match` error: this `match` can be collapsed into the outer `match`
--> tests/ui/collapsible_match.rs:23:20 --> tests/ui/collapsible_match.rs:24:20
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ____________________^ | ____________________^
@ -32,7 +32,7 @@ LL | | },
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:23:12 --> tests/ui/collapsible_match.rs:24:12
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ^^^ replace this binding | ^^^ replace this binding
@ -41,7 +41,7 @@ LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `if let` can be collapsed into the outer `if let` error: this `if let` can be collapsed into the outer `if let`
--> tests/ui/collapsible_match.rs:33:9 --> tests/ui/collapsible_match.rs:34:9
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
LL | | LL | |
@ -50,7 +50,7 @@ LL | | }
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:32:15 --> tests/ui/collapsible_match.rs:33:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ replace this binding | ^^^ replace this binding
@ -58,7 +58,7 @@ LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `if let` can be collapsed into the outer `if let` error: this `if let` can be collapsed into the outer `if let`
--> tests/ui/collapsible_match.rs:41:9 --> tests/ui/collapsible_match.rs:42:9
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
LL | | LL | |
@ -69,7 +69,7 @@ LL | | }
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:40:15 --> tests/ui/collapsible_match.rs:41:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ replace this binding | ^^^ replace this binding
@ -77,7 +77,7 @@ LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `match` can be collapsed into the outer `if let` error: this `match` can be collapsed into the outer `if let`
--> tests/ui/collapsible_match.rs:53:9 --> tests/ui/collapsible_match.rs:54:9
| |
LL | / match val { LL | / match val {
LL | | LL | |
@ -87,7 +87,7 @@ LL | | }
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:52:15 --> tests/ui/collapsible_match.rs:53:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ replace this binding | ^^^ replace this binding
@ -96,7 +96,7 @@ LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `if let` can be collapsed into the outer `match` error: this `if let` can be collapsed into the outer `match`
--> tests/ui/collapsible_match.rs:63:13 --> tests/ui/collapsible_match.rs:64:13
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
LL | | LL | |
@ -105,7 +105,7 @@ LL | | }
| |_____________^ | |_____________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:62:12 --> tests/ui/collapsible_match.rs:63:12
| |
LL | Ok(val) => { LL | Ok(val) => {
| ^^^ replace this binding | ^^^ replace this binding
@ -113,7 +113,7 @@ LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `match` can be collapsed into the outer `if let` error: this `match` can be collapsed into the outer `if let`
--> tests/ui/collapsible_match.rs:73:9 --> tests/ui/collapsible_match.rs:74:9
| |
LL | / match val { LL | / match val {
LL | | LL | |
@ -123,7 +123,7 @@ LL | | }
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:72:15 --> tests/ui/collapsible_match.rs:73:15
| |
LL | if let Ok(val) = res_opt { LL | if let Ok(val) = res_opt {
| ^^^ replace this binding | ^^^ replace this binding
@ -132,7 +132,7 @@ LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `if let` can be collapsed into the outer `match` error: this `if let` can be collapsed into the outer `match`
--> tests/ui/collapsible_match.rs:85:13 --> tests/ui/collapsible_match.rs:86:13
| |
LL | / if let Some(n) = val { LL | / if let Some(n) = val {
LL | | LL | |
@ -143,7 +143,7 @@ LL | | }
| |_____________^ | |_____________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:84:12 --> tests/ui/collapsible_match.rs:85:12
| |
LL | Ok(val) => { LL | Ok(val) => {
| ^^^ replace this binding | ^^^ replace this binding
@ -151,7 +151,7 @@ LL | if let Some(n) = val {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `match` can be collapsed into the outer `match` error: this `match` can be collapsed into the outer `match`
--> tests/ui/collapsible_match.rs:97:20 --> tests/ui/collapsible_match.rs:98:20
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ____________________^ | ____________________^
@ -162,7 +162,7 @@ LL | | },
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:97:12 --> tests/ui/collapsible_match.rs:98:12
| |
LL | Ok(val) => match val { LL | Ok(val) => match val {
| ^^^ replace this binding | ^^^ replace this binding
@ -171,7 +171,7 @@ LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `match` can be collapsed into the outer `match` error: this `match` can be collapsed into the outer `match`
--> tests/ui/collapsible_match.rs:107:22 --> tests/ui/collapsible_match.rs:108:22
| |
LL | Some(val) => match val { LL | Some(val) => match val {
| ______________________^ | ______________________^
@ -182,7 +182,7 @@ LL | | },
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:107:14 --> tests/ui/collapsible_match.rs:108:14
| |
LL | Some(val) => match val { LL | Some(val) => match val {
| ^^^ replace this binding | ^^^ replace this binding
@ -190,8 +190,26 @@ LL |
LL | Some(n) => foo(n), LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: this `match` can be collapsed into the outer `match`
--> tests/ui/collapsible_match.rs:252:22
|
LL | Some(val) => match val {
| ______________________^
LL | | E::A(val) | E::B(val) => foo(val),
LL | | _ => return,
LL | | },
| |_________^
|
help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:252:14
|
LL | Some(val) => match val {
| ^^^ replace this binding
LL | E::A(val) | E::B(val) => foo(val),
| ^^^^^^^^^^^^^^^^^^^^^ with this pattern
error: this `if let` can be collapsed into the outer `if let` error: this `if let` can be collapsed into the outer `if let`
--> tests/ui/collapsible_match.rs:273:9 --> tests/ui/collapsible_match.rs:283:9
| |
LL | / if let Some(u) = a { LL | / if let Some(u) = a {
LL | | LL | |
@ -200,7 +218,7 @@ LL | | }
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:272:27 --> tests/ui/collapsible_match.rs:282:27
| |
LL | if let Issue9647::A { a, .. } = x { LL | if let Issue9647::A { a, .. } = x {
| ^ replace this binding | ^ replace this binding
@ -208,7 +226,7 @@ LL | if let Some(u) = a {
| ^^^^^^^ with this pattern, prefixed by a: | ^^^^^^^ with this pattern, prefixed by a:
error: this `if let` can be collapsed into the outer `if let` error: this `if let` can be collapsed into the outer `if let`
--> tests/ui/collapsible_match.rs:282:9 --> tests/ui/collapsible_match.rs:292:9
| |
LL | / if let Some(u) = a { LL | / if let Some(u) = a {
LL | | LL | |
@ -217,12 +235,12 @@ LL | | }
| |_________^ | |_________^
| |
help: the outer pattern can be modified to include the inner pattern help: the outer pattern can be modified to include the inner pattern
--> tests/ui/collapsible_match.rs:281:35 --> tests/ui/collapsible_match.rs:291:35
| |
LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Issue9647::A { a: Some(a), .. } = x {
| ^ replace this binding | ^ replace this binding
LL | if let Some(u) = a { LL | if let Some(u) = a {
| ^^^^^^^ with this pattern | ^^^^^^^ with this pattern
error: aborting due to 12 previous errors error: aborting due to 13 previous errors

View File

@ -222,3 +222,17 @@ fn supported_types() {
//~^ ERROR: collection is never read //~^ ERROR: collection is never read
x.push_front(1); x.push_front(1);
} }
fn issue11783() {
struct Sender;
impl Sender {
fn send(&self, msg: String) -> Result<(), ()> {
// pretend to send message
println!("{msg}");
Ok(())
}
}
let mut users: Vec<Sender> = vec![];
users.retain(|user| user.send("hello".to_string()).is_ok());
}

View File

@ -3,7 +3,8 @@
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] #![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
fn main() { fn main() {
let _ = #[coroutine] || { let _ = #[coroutine]
|| {
yield; yield;
}; };
} }

View File

@ -121,13 +121,12 @@ impl SelfType for AtomicUsize {
// Even though a constant contains a generic type, if it also have an interior mutable type, // Even though a constant contains a generic type, if it also have an interior mutable type,
// it should be linted at the definition site. // it should be linted at the definition site.
trait BothOfCellAndGeneric<T> { trait BothOfCellAndGeneric<T> {
// this is a false negative in the current implementation. const DIRECT: Cell<T>; //~ ERROR: interior mutable
const DIRECT: Cell<T>;
const INDIRECT: Cell<*const T>; //~ ERROR: interior mutable const INDIRECT: Cell<*const T>; //~ ERROR: interior mutable
} }
impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 { impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 {
const DIRECT: Cell<T> = Cell::new(T::DEFAULT); const DIRECT: Cell<T> = Cell::new(T::DEFAULT); //~ ERROR: interior mutable
const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
} }

View File

@ -55,22 +55,34 @@ LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable error: a `const` item should never be interior mutable
--> tests/ui/declare_interior_mutable_const/traits.rs:126:5 --> tests/ui/declare_interior_mutable_const/traits.rs:124:5
|
LL | const DIRECT: Cell<T>;
| ^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> tests/ui/declare_interior_mutable_const/traits.rs:125:5
| |
LL | const INDIRECT: Cell<*const T>; LL | const INDIRECT: Cell<*const T>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable error: a `const` item should never be interior mutable
--> tests/ui/declare_interior_mutable_const/traits.rs:142:5 --> tests/ui/declare_interior_mutable_const/traits.rs:129:5
|
LL | const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> tests/ui/declare_interior_mutable_const/traits.rs:141:5
| |
LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable error: a `const` item should never be interior mutable
--> tests/ui/declare_interior_mutable_const/traits.rs:148:5 --> tests/ui/declare_interior_mutable_const/traits.rs:147:5
| |
LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 11 previous errors error: aborting due to 13 previous errors

View File

@ -71,3 +71,8 @@ mod tests {
} }
} }
} }
#[test]
fn test_with_disallowed_name() {
let foo = 0;
}

View File

@ -1,6 +1,9 @@
//@aux-build:proc_macros.rs
#![warn(clippy::large_stack_arrays)] #![warn(clippy::large_stack_arrays)]
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
extern crate proc_macros;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct S { struct S {
pub data: [u64; 32], pub data: [u64; 32],
@ -55,3 +58,48 @@ fn main() {
[(); 20_000_000], [(); 20_000_000],
); );
} }
#[allow(clippy::useless_vec)]
fn issue_12586() {
macro_rules! dummy {
($n:expr) => {
$n
};
// Weird rule to test help messages.
($a:expr => $b:expr) => {
[$a, $b, $a, $b]
//~^ ERROR: allocating a local array larger than 512000 bytes
};
($id:ident; $n:literal) => {
dummy!(::std::vec![$id;$n])
};
($($id:expr),+ $(,)?) => {
::std::vec![$($id),*]
}
}
macro_rules! create_then_move {
($id:ident; $n:literal) => {{
let _x_ = [$id; $n];
//~^ ERROR: allocating a local array larger than 512000 bytes
_x_
}};
}
let x = [0u32; 50_000];
let y = vec![x, x, x, x, x];
let y = vec![dummy![x, x, x, x, x]];
let y = vec![dummy![[x, x, x, x, x]]];
let y = dummy![x, x, x, x, x];
let y = [x, x, dummy!(x), x, x];
//~^ ERROR: allocating a local array larger than 512000 bytes
let y = dummy![x => x];
let y = dummy![x;5];
let y = dummy!(vec![dummy![x, x, x, x, x]]);
let y = dummy![[x, x, x, x, x]];
//~^ ERROR: allocating a local array larger than 512000 bytes
let y = proc_macros::make_it_big!([x; 1]);
//~^ ERROR: allocating a local array larger than 512000 bytes
let y = vec![proc_macros::make_it_big!([x; 10])];
let y = vec![create_then_move![x; 5]; 5];
}

View File

@ -1,5 +1,5 @@
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:29:14 --> tests/ui/large_stack_arrays.rs:32:14
| |
LL | let _x = [build(); 3]; LL | let _x = [build(); 3];
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | let _x = [build(); 3];
= help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]` = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:32:14 --> tests/ui/large_stack_arrays.rs:35:14
| |
LL | let _y = [build(), build(), build()]; LL | let _y = [build(), build(), build()];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -17,7 +17,7 @@ LL | let _y = [build(), build(), build()];
= help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()` = help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:38:9 --> tests/ui/large_stack_arrays.rs:41:9
| |
LL | [0u32; 20_000_000], LL | [0u32; 20_000_000],
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
@ -25,7 +25,7 @@ LL | [0u32; 20_000_000],
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:40:9 --> tests/ui/large_stack_arrays.rs:43:9
| |
LL | [S { data: [0; 32] }; 5000], LL | [S { data: [0; 32] }; 5000],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -33,7 +33,7 @@ LL | [S { data: [0; 32] }; 5000],
= help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:42:9 --> tests/ui/large_stack_arrays.rs:45:9
| |
LL | [Some(""); 20_000_000], LL | [Some(""); 20_000_000],
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
@ -41,7 +41,7 @@ LL | [Some(""); 20_000_000],
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:44:9 --> tests/ui/large_stack_arrays.rs:47:9
| |
LL | [E::T(0); 5000], LL | [E::T(0); 5000],
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
@ -49,12 +49,56 @@ LL | [E::T(0); 5000],
= help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:46:9 --> tests/ui/large_stack_arrays.rs:49:9
| |
LL | [0u8; usize::MAX], LL | [0u8; usize::MAX],
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()` = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()`
error: aborting due to 7 previous errors error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:93:13
|
LL | let y = [x, x, dummy!(x), x, x];
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with `vec![x, x, dummy!(x), x, x].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:70:13
|
LL | [$a, $b, $a, $b]
| ^^^^^^^^^^^^^^^^
...
LL | let y = dummy![x => x];
| -------------- in this macro invocation
|
= note: this error originates in the macro `dummy` (in Nightly builds, run with -Z macro-backtrace for more info)
error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:98:20
|
LL | let y = dummy![[x, x, x, x, x]];
| ^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with `vec![x, x, x, x, x].into_boxed_slice()`
error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:101:39
|
LL | let y = proc_macros::make_it_big!([x; 1]);
| ^^^^^^
error: allocating a local array larger than 512000 bytes
--> tests/ui/large_stack_arrays.rs:82:23
|
LL | let _x_ = [$id; $n];
| ^^^^^^^^^
...
LL | let y = vec![create_then_move![x; 5]; 5];
| ----------------------- in this macro invocation
|
= note: this error originates in the macro `create_then_move` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 12 previous errors

View File

@ -55,3 +55,30 @@ fn msrv_1_47() {
const FOO: bool = 'x'.is_ascii_digit(); const FOO: bool = 'x'.is_ascii_digit();
const BAR: bool = 'x'.is_ascii_hexdigit(); const BAR: bool = 'x'.is_ascii_hexdigit();
} }
#[allow(clippy::deref_addrof, clippy::needless_borrow)]
fn with_refs() {
let cool_letter = &&'g';
cool_letter.is_ascii_digit();
cool_letter.is_ascii_lowercase();
}
fn generics() {
fn a<U>(u: &U) -> bool
where
char: PartialOrd<U>,
U: PartialOrd<char> + ?Sized,
{
('A'..='Z').contains(u)
}
fn take_while<Item, F>(cond: F)
where
Item: Sized,
F: Fn(Item) -> bool,
{
}
take_while(|c: char| c.is_ascii_uppercase());
take_while(|c: u8| c.is_ascii_uppercase());
take_while(|c: char| c.is_ascii_uppercase());
}

View File

@ -55,3 +55,30 @@ fn msrv_1_47() {
const FOO: bool = matches!('x', '0'..='9'); const FOO: bool = matches!('x', '0'..='9');
const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
} }
#[allow(clippy::deref_addrof, clippy::needless_borrow)]
fn with_refs() {
let cool_letter = &&'g';
('0'..='9').contains(&&cool_letter);
('a'..='z').contains(*cool_letter);
}
fn generics() {
fn a<U>(u: &U) -> bool
where
char: PartialOrd<U>,
U: PartialOrd<char> + ?Sized,
{
('A'..='Z').contains(u)
}
fn take_while<Item, F>(cond: F)
where
Item: Sized,
F: Fn(Item) -> bool,
{
}
take_while(|c| ('A'..='Z').contains(&c));
take_while(|c| (b'A'..=b'Z').contains(&c));
take_while(|c: char| ('A'..='Z').contains(&c));
}

View File

@ -133,5 +133,45 @@ error: manual check for common ascii range
LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F'); LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()`
error: aborting due to 22 previous errors error: manual check for common ascii range
--> tests/ui/manual_is_ascii_check.rs:62:5
|
LL | ('0'..='9').contains(&&cool_letter);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_digit()`
error: manual check for common ascii range
--> tests/ui/manual_is_ascii_check.rs:63:5
|
LL | ('a'..='z').contains(*cool_letter);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_lowercase()`
error: manual check for common ascii range
--> tests/ui/manual_is_ascii_check.rs:81:20
|
LL | take_while(|c| ('A'..='Z').contains(&c));
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL | take_while(|c: char| c.is_ascii_uppercase());
| ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
error: manual check for common ascii range
--> tests/ui/manual_is_ascii_check.rs:82:20
|
LL | take_while(|c| (b'A'..=b'Z').contains(&c));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL | take_while(|c: u8| c.is_ascii_uppercase());
| ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
error: manual check for common ascii range
--> tests/ui/manual_is_ascii_check.rs:83:26
|
LL | take_while(|c: char| ('A'..='Z').contains(&c));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()`
error: aborting due to 27 previous errors

View File

@ -32,7 +32,7 @@ fn main() {
// testing that the suggestion actually fits in its type // testing that the suggestion actually fits in its type
let fail30 = 127_i8; // should be i8 let fail30 = 127_i8; // should be i8
let fail31 = 240_u8; // should be u8 let fail31 = 240_u8; // should be u8
let ok32 = 360_8; // doesnt fit in either, should be ignored let ok32 = 360_8; // doesn't fit in either, should be ignored
let fail33 = 0x1234_i16; let fail33 = 0x1234_i16;
let fail34 = 0xABCD_u16; let fail34 = 0xABCD_u16;
let ok35 = 0x12345_16; let ok35 = 0x12345_16;

View File

@ -32,7 +32,7 @@ fn main() {
// testing that the suggestion actually fits in its type // testing that the suggestion actually fits in its type
let fail30 = 127_8; // should be i8 let fail30 = 127_8; // should be i8
let fail31 = 240_8; // should be u8 let fail31 = 240_8; // should be u8
let ok32 = 360_8; // doesnt fit in either, should be ignored let ok32 = 360_8; // doesn't fit in either, should be ignored
let fail33 = 0x1234_16; let fail33 = 0x1234_16;
let fail34 = 0xABCD_16; let fail34 = 0xABCD_16;
let ok35 = 0x12345_16; let ok35 = 0x12345_16;

View File

@ -5,7 +5,7 @@ use std::rc::Rc;
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed; use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc; use std::sync::Arc;
//@no-rustfix
struct Key(AtomicUsize); struct Key(AtomicUsize);
impl Clone for Key { impl Clone for Key {
@ -77,8 +77,6 @@ fn main() {
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
let _map = HashMap::<&mut Cell<usize>, usize>::new(); let _map = HashMap::<&mut Cell<usize>, usize>::new();
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
let _map = HashMap::<&mut usize, usize>::new();
//~^ ERROR: mutable key type
// Collection types from `std` who's impl of `Hash` or `Ord` delegate their type parameters // Collection types from `std` who's impl of `Hash` or `Ord` delegate their type parameters
let _map = HashMap::<Vec<Cell<usize>>, usize>::new(); let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
@ -92,8 +90,6 @@ fn main() {
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new(); let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
//~^ ERROR: mutable key type
// Smart pointers from `std` who's impl of `Hash` or `Ord` delegate their type parameters // Smart pointers from `std` who's impl of `Hash` or `Ord` delegate their type parameters
let _map = HashMap::<Box<Cell<usize>>, usize>::new(); let _map = HashMap::<Box<Cell<usize>>, usize>::new();
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
@ -101,4 +97,8 @@ fn main() {
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
let _map = HashMap::<Arc<Cell<usize>>, usize>::new(); let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
//~^ ERROR: mutable key type //~^ ERROR: mutable key type
// Not interior mutability
let _map = HashMap::<&mut usize, usize>::new();
let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
} }

View File

@ -38,70 +38,58 @@ LL | let _map = HashMap::<&mut Cell<usize>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:80:5 --> tests/ui/mut_key.rs:81:5
|
LL | let _map = HashMap::<&mut usize, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> tests/ui/mut_key.rs:83:5
| |
LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new(); LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:85:5 --> tests/ui/mut_key.rs:83:5
| |
LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new(); LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:87:5 --> tests/ui/mut_key.rs:85:5
| |
LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new(); LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:89:5 --> tests/ui/mut_key.rs:87:5
| |
LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new(); LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:91:5 --> tests/ui/mut_key.rs:89:5
| |
LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new(); LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:93:5 --> tests/ui/mut_key.rs:91:5
| |
LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new(); LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:95:5 --> tests/ui/mut_key.rs:94:5
|
LL | let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type
--> tests/ui/mut_key.rs:98:5
| |
LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new(); LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:100:5 --> tests/ui/mut_key.rs:96:5
| |
LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new(); LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: mutable key type error: mutable key type
--> tests/ui/mut_key.rs:102:5 --> tests/ui/mut_key.rs:98:5
| |
LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new(); LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 17 previous errors error: aborting due to 15 previous errors

View File

@ -113,6 +113,10 @@ fn should_not_lint() {
let _ = v.iter().for_each(|elem| { let _ = v.iter().for_each(|elem| {
acc += elem; acc += elem;
}); });
// `for_each` has a closure with an unsafe block.
v.iter().for_each(|elem| unsafe {
acc += elem;
});
} }
fn main() {} fn main() {}

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