mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 00:34:06 +00:00
Merge commit '20b085d500dfba5afe0869707bf357af3afe20be' into clippy-subtree-update
This commit is contained in:
parent
fc15bc169e
commit
80c6f8ff7b
56
CHANGELOG.md
56
CHANGELOG.md
@ -6,11 +6,62 @@ document.
|
||||
|
||||
## 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
|
||||
|
||||
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)
|
||||
|
||||
@ -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-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-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-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
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.79"
|
||||
version = "0.1.80"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
@ -30,7 +30,7 @@ color-print = "0.3.4"
|
||||
anstream = "0.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ui_test = "0.22.2"
|
||||
ui_test = "0.23"
|
||||
regex = "1.5.5"
|
||||
toml = "0.7.3"
|
||||
walkdir = "2.3"
|
||||
|
@ -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
|
||||
either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
|
||||
|
||||
In short, the `LateLintPass` has access to type information while the
|
||||
`EarlyLintPass` doesn't. If you don't need access to type information, use the
|
||||
`EarlyLintPass`. The `EarlyLintPass` is also faster. However, linting speed
|
||||
hasn't really been a concern with Clippy so far.
|
||||
In short, the `EarlyLintPass` runs before type checking and
|
||||
[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass`
|
||||
has access to type information. Consider using the `LateLintPass` unless you need
|
||||
something specific from the `EarlyLintPass`.
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
|
||||
|
||||
## `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`
|
||||
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`
|
||||
A list of paths to types that should be treated like `Arc`, i.e. ignored but
|
||||
for the generic parameters for determining interior mutability
|
||||
A list of paths to types that should be treated as if they do not contain interior mutability
|
||||
|
||||
**Default Value:** `["bytes::Bytes"]`
|
||||
|
||||
---
|
||||
**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)
|
||||
* [`mutable_key_type`](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_config"
|
||||
version = "0.1.79"
|
||||
version = "0.1.80"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -463,14 +463,17 @@ define_Conf! {
|
||||
///
|
||||
/// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
|
||||
(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.
|
||||
///
|
||||
/// The maximum size of the `Err`-variant in a `Result` returned from a function
|
||||
(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
|
||||
/// for the generic parameters for determining interior mutability
|
||||
/// A list of paths to types that should be treated as if they do not contain interior mutability
|
||||
(ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
|
||||
/// Lint: UNINLINED_FORMAT_ARGS.
|
||||
///
|
||||
|
@ -23,7 +23,7 @@ msrv_aliases! {
|
||||
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
|
||||
1,68,0 { PATH_MAIN_SEPARATOR_STR }
|
||||
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,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST }
|
||||
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.79"
|
||||
version = "0.1.80"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -45,7 +45,7 @@ declare_clippy_lint! {
|
||||
/// a.clone_from(&b);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.77.0"]
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub ASSIGNING_CLONES,
|
||||
perf,
|
||||
"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 {
|
||||
// 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 !msrv.meets(msrvs::ASSIGNING_CLONES) && matches!(call.target, TargetTrait::ToOwned) {
|
||||
if !msrv.meets(msrvs::CLONE_INTO) && matches!(call.target, TargetTrait::ToOwned) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
&& let Some(sig) = expr_sig(cx, path)
|
||||
&& 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 {
|
||||
false
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ declare_clippy_lint! {
|
||||
/// pedantic = { level = "warn", priority = -1 }
|
||||
/// similar_names = "allow"
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub LINT_GROUPS_PRIORITY,
|
||||
correctness,
|
||||
"a lint group in `Cargo.toml` at the same priority as a lint"
|
||||
|
@ -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))
|
||||
})
|
||||
},
|
||||
BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
|
||||
BinOpKind::Rem => get_constant_bits(cx, right)
|
||||
.unwrap_or(u64::MAX)
|
||||
.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)
|
||||
.saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())),
|
||||
_ => nbits,
|
||||
|
@ -708,7 +708,7 @@ declare_clippy_lint! {
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = std::ptr::from_ref(a_ref);
|
||||
/// ```
|
||||
#[clippy::version = "1.77.0"]
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub REF_AS_PTR,
|
||||
pedantic,
|
||||
"using `as` to cast a reference to pointer"
|
||||
|
@ -1,9 +1,9 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
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 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_session::declare_lint_pass;
|
||||
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)
|
||||
}
|
||||
|
||||
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_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
|
||||
// have side effects, so consider them a read.
|
||||
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)
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
|
||||
&& !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:
|
||||
//
|
||||
// id.foo(args);
|
||||
|
@ -1,15 +1,14 @@
|
||||
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::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::{
|
||||
capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt,
|
||||
if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
||||
capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
|
||||
is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
|
||||
};
|
||||
use core::iter;
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefIdSet;
|
||||
use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
@ -159,40 +158,36 @@ declare_clippy_lint! {
|
||||
"`if` statement with shared code in all blocks"
|
||||
}
|
||||
|
||||
pub struct CopyAndPaste {
|
||||
pub struct CopyAndPaste<'tcx> {
|
||||
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 {
|
||||
Self {
|
||||
ignore_interior_mutability,
|
||||
ignored_ty_ids: DefIdSet::new(),
|
||||
interior_mut: InteriorMut::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CopyAndPaste => [
|
||||
impl_lint_pass!(CopyAndPaste<'_> => [
|
||||
IFS_SAME_COND,
|
||||
SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
IF_SAME_THEN_ELSE,
|
||||
BRANCHES_SHARING_CODE
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
for ignored_ty in &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);
|
||||
}
|
||||
}
|
||||
self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
let all_same =
|
||||
!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);
|
||||
// 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()
|
||||
// `find_binding_init` will return the binding iff its not mutable
|
||||
|| 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`.
|
||||
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(
|
||||
conds,
|
||||
|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
|
||||
// and the caller is not a mutable, including inner mutable type.
|
||||
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
|
||||
} else {
|
||||
SpanlessEq::new(cx).eq_expr(lhs, rhs)
|
||||
|
@ -1,10 +1,10 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_in_test;
|
||||
use clippy_utils::macros::{macro_backtrace, MacroCall};
|
||||
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_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, HirId, Node};
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::impl_lint_pass;
|
||||
@ -63,7 +63,7 @@ impl LateLintPass<'_> for DbgMacro {
|
||||
!in_external_macro(cx.sess(), 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
|
||||
!(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;
|
||||
|
||||
@ -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> {
|
||||
macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id))
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
|
||||
Pat, PatKind, Path, QPath, TyKind, UnOp,
|
||||
self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
|
||||
PatKind, Path, QPath, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
|
@ -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::macros::{
|
||||
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::ty::{implements_trait, is_type_lang_item};
|
||||
@ -271,9 +271,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
|
||||
let mut suggest_format = |spec| {
|
||||
let message = format!("for the {spec} to apply consider using `format!()`");
|
||||
|
||||
if let Some(mac_call) = root_macro_call(arg_span)
|
||||
&& self.cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
|
||||
{
|
||||
if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) {
|
||||
diag.span_suggestion(
|
||||
self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
|
||||
message,
|
||||
|
@ -24,7 +24,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Limitations
|
||||
/// 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`)
|
||||
///
|
||||
/// ### Example
|
||||
|
@ -93,12 +93,9 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<Hir
|
||||
let mut slices: FxIndexMap<HirId, SliceLintInformation> = FxIndexMap::default();
|
||||
pat.walk_always(|pat| {
|
||||
// We'll just ignore mut and ref mut for simplicity sake right now
|
||||
if let hir::PatKind::Binding(
|
||||
hir::BindingMode(by_ref, hir::Mutability::Not),
|
||||
value_hir_id,
|
||||
ident,
|
||||
sub_pat,
|
||||
) = pat.kind && by_ref != hir::ByRef::Yes(hir::Mutability::Mut)
|
||||
if let hir::PatKind::Binding(hir::BindingMode(by_ref, hir::Mutability::Not), value_hir_id, 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
|
||||
// for them and it's likely that the user knows what they are doing in such a case.
|
||||
|
@ -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 rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
|
||||
use rustc_hir::{ArrayLen, Expr, ExprKind, Item, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, ConstKind};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -25,20 +28,41 @@ declare_clippy_lint! {
|
||||
|
||||
pub struct LargeStackArrays {
|
||||
maximum_allowed_size: u128,
|
||||
prev_vec_macro_callsite: Option<Span>,
|
||||
}
|
||||
|
||||
impl LargeStackArrays {
|
||||
#[must_use]
|
||||
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<'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
|
||||
&& !self.is_from_vec_macro(cx, expr.span)
|
||||
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
||||
&& 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)
|
||||
{
|
||||
span_lint_and_help(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
LARGE_STACK_ARRAYS,
|
||||
expr.span,
|
||||
@ -62,12 +86,33 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
||||
"allocating a local array larger than {} bytes",
|
||||
self.maximum_allowed_size
|
||||
),
|
||||
None,
|
||||
format!(
|
||||
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
|
||||
snippet(cx, expr.span, "[...]")
|
||||
),
|
||||
|diag| {
|
||||
if !might_be_expanded(cx, expr) {
|
||||
diag.help(format!(
|
||||
"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)
|
||||
}
|
||||
|
@ -535,6 +535,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||
allow_print_in_tests,
|
||||
allow_private_module_inception,
|
||||
allow_unwrap_in_tests,
|
||||
allow_useless_vec_in_tests,
|
||||
ref allowed_dotfiles,
|
||||
ref allowed_idents_below_min_chars,
|
||||
ref allowed_scripts,
|
||||
@ -754,6 +755,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||
too_large_for_stack,
|
||||
msrv: msrv(),
|
||||
span_to_lint_map: BTreeMap::new(),
|
||||
allow_in_test: allow_useless_vec_in_tests,
|
||||
})
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
|
||||
|
@ -1,11 +1,12 @@
|
||||
use super::UNUSED_ENUMERATE_INDEX;
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
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::{Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::sym;
|
||||
|
||||
/// 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)
|
||||
&& pat_is_wild(cx, &index.kind, body)
|
||||
&& 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)
|
||||
&& 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(
|
||||
cx,
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::rustc_lint::LintContext;
|
||||
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 rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -42,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
|
||||
&& !expr.span.from_expansion()
|
||||
&& let then = peel_blocks_with_stmt(then)
|
||||
&& 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)
|
||||
&& let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span)
|
||||
&& let Some(panic_snippet) = panic_snippet.strip_suffix(')')
|
||||
|
@ -1,15 +1,15 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::root_macro_call;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::matching_root_macro_call;
|
||||
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::LitKind::{Byte, Char};
|
||||
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_middle::ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -97,12 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(macro_call) = root_macro_call(expr.span)
|
||||
&& is_matches_macro(cx, macro_call.def_id)
|
||||
{
|
||||
if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
|
||||
if let ExprKind::Match(recv, [arm, ..], _) = expr.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
|
||||
&& path.ident.name == sym!(contains)
|
||||
@ -111,42 +109,67 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
||||
end: Some(end),
|
||||
limits: RangeLimits::Closed,
|
||||
}) = 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);
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind {
|
||||
check_is_ascii(cx, expr.span, e, &range);
|
||||
} else {
|
||||
check_is_ascii(cx, expr.span, arg, &range);
|
||||
}
|
||||
check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) {
|
||||
if let Some(sugg) = match range {
|
||||
CharRange::UpperChar => Some("is_ascii_uppercase"),
|
||||
CharRange::LowerChar => Some("is_ascii_lowercase"),
|
||||
CharRange::FullChar => Some("is_ascii_alphabetic"),
|
||||
CharRange::Digit => Some("is_ascii_digit"),
|
||||
CharRange::HexDigit => Some("is_ascii_hexdigit"),
|
||||
CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None,
|
||||
} {
|
||||
let default_snip = "..";
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_IS_ASCII_CHECK,
|
||||
span,
|
||||
"manual check for common ascii range",
|
||||
"try",
|
||||
format!("{recv}.{sugg}()"),
|
||||
app,
|
||||
);
|
||||
fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> {
|
||||
if let ExprKind::Lit(lit) = bound_expr.kind
|
||||
&& let local_hid = path_to_local(arg)?
|
||||
&& let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid)
|
||||
// `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given
|
||||
&& ty_span == span
|
||||
{
|
||||
let ty_str = match lit.node {
|
||||
Char(_) => "char",
|
||||
Byte(_) => "u8",
|
||||
_ => return None,
|
||||
};
|
||||
return Some((*ty_span, ty_str));
|
||||
}
|
||||
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 {
|
||||
@ -187,11 +210,3 @@ fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
|
||||
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
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::IfLetOrMatch;
|
||||
use clippy_utils::source::snippet;
|
||||
@ -11,12 +12,12 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
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)) {
|
||||
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<'_>,
|
||||
body: &'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>(
|
||||
@ -37,6 +39,7 @@ fn check_arm<'tcx>(
|
||||
outer_then_body: &'tcx Expr<'tcx>,
|
||||
outer_guard: Option<&'tcx Expr<'tcx>>,
|
||||
outer_else_body: Option<&'tcx Expr<'tcx>>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
let inner_expr = peel_blocks_with_stmt(outer_then_body);
|
||||
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 <local> { .. }
|
||||
&& 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
|
||||
// ..<local>.. => match <local> { .. }
|
||||
&& 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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ mod wild_in_or_pats;
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
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 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_middle::lint::in_external_macro;
|
||||
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);
|
||||
}
|
||||
|
||||
collapsible_match::check_match(cx, arms);
|
||||
collapsible_match::check_match(cx, arms, &self.msrv);
|
||||
if !from_expansion {
|
||||
// These don't depend on a relationship between multiple 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);
|
||||
match_on_vec_items::check(cx, ex);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
} 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 let Some(else_expr) = if_let.if_else {
|
||||
if self.msrv.meets(msrvs::MATCHES_MACRO) {
|
||||
@ -1195,3 +1195,18 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar
|
||||
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
|
||||
}
|
||||
|
@ -1,40 +1,32 @@
|
||||
use clippy_config::msrvs::Msrv;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::matching_root_macro_call;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::{for_each_expr, is_local_used};
|
||||
use clippy_utils::{in_constant, path_to_local};
|
||||
use rustc_ast::{BorrowKind, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
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_span::symbol::Ident;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
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 {
|
||||
let Some(guard) = outer_arm.guard else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// `Some(x) if matches!(x, y)`
|
||||
if let ExprKind::Match(
|
||||
scrutinee,
|
||||
[
|
||||
arm,
|
||||
Arm {
|
||||
pat: Pat {
|
||||
kind: PatKind::Wild, ..
|
||||
},
|
||||
..
|
||||
},
|
||||
],
|
||||
MatchSource::Normal,
|
||||
) = guard.kind
|
||||
if let ExprKind::Match(scrutinee, [arm, _], MatchSource::Normal) = guard.kind
|
||||
&& matching_root_macro_call(cx, guard.span, sym::matches_macro).is_some()
|
||||
&& 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) {
|
||||
(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`
|
||||
else if let ExprKind::Let(let_expr) = guard.kind
|
||||
&& 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) {
|
||||
(PatKind::Ref(pat, _), Some(_)) => pat.span,
|
||||
|
@ -75,7 +75,7 @@ fn report_single_pattern(
|
||||
) {
|
||||
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut app = Applicability::HasPlaceholders;
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let els_str = els.map_or(String::new(), |els| {
|
||||
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
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::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
|
||||
@ -247,8 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if let Some(macro_call) = root_macro_call(expr.span)
|
||||
&& cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro)
|
||||
} else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
|
||||
// we know for a fact that the wildcard pattern is the second arm
|
||||
&& let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
|
||||
&& path_to_local_id(scrutinee, filter_param_id)
|
||||
|
@ -89,8 +89,7 @@ pub(super) fn check<'tcx>(
|
||||
}
|
||||
|
||||
match it.kind {
|
||||
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _)
|
||||
| PatKind::Ref(_, Mutability::Mut) => {
|
||||
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, Mutability::Mut) => {
|
||||
to_be_discarded = true;
|
||||
false
|
||||
},
|
||||
|
@ -1145,11 +1145,16 @@ declare_clippy_lint! {
|
||||
/// `str` as an argument, e.g., `_.split("x")`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Performing these methods using a `char` is faster than
|
||||
/// using a `str`.
|
||||
/// While this can make a perf difference on some systems,
|
||||
/// benchmarks have proven inconclusive. But at least using a
|
||||
/// char literal makes it clear that we are looking at a single
|
||||
/// character.
|
||||
///
|
||||
/// ### 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
|
||||
/// ```rust,ignore
|
||||
@ -1162,7 +1167,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub SINGLE_CHAR_PATTERN,
|
||||
perf,
|
||||
pedantic,
|
||||
"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 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,
|
||||
suspicious,
|
||||
"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");
|
||||
/// unsafe { libc::puts(c"World".as_ptr()) }
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MANUAL_C_STR_LITERALS,
|
||||
pedantic,
|
||||
r#"creating a `CStr` through functions when `c""` literals can be used"#
|
||||
|
@ -4,7 +4,7 @@ use clippy_utils::mir::{enclosing_mir, visit_local_usage};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_hir::{Expr, ExprKind, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::{Location, START_BLOCK};
|
||||
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)
|
||||
&& let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id)
|
||||
&& 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((local, _)) = mir
|
||||
.local_decls
|
||||
|
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_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<'_>]) {
|
||||
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 =
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
|
||||
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
|
||||
|
@ -8,7 +8,7 @@ use rustc_span::symbol::Symbol;
|
||||
|
||||
use super::SINGLE_CHAR_PATTERN;
|
||||
|
||||
const PATTERN_METHODS: [(&str, usize); 24] = [
|
||||
const PATTERN_METHODS: [(&str, usize); 22] = [
|
||||
("contains", 0),
|
||||
("starts_with", 0),
|
||||
("ends_with", 0),
|
||||
@ -27,8 +27,6 @@ const PATTERN_METHODS: [(&str, usize); 24] = [
|
||||
("rmatches", 0),
|
||||
("match_indices", 0),
|
||||
("rmatch_indices", 0),
|
||||
("strip_prefix", 0),
|
||||
("strip_suffix", 0),
|
||||
("trim_start_matches", 0),
|
||||
("trim_end_matches", 0),
|
||||
("replace", 0),
|
||||
@ -50,7 +48,7 @@ pub(super) fn check(
|
||||
&& args.len() > pos
|
||||
&& let arg = &args[pos]
|
||||
&& 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(
|
||||
cx,
|
||||
|
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_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<'_>]) {
|
||||
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 =
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
||||
let sugg = format!("{base_string_snippet}.push({extension_string})");
|
||||
|
@ -1,7 +1,6 @@
|
||||
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::{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_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
|
||||
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);
|
||||
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
|
||||
&& 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
|
||||
&& is_trait_method(cx, call_expr, sym::Iterator)
|
||||
// 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 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`.
|
||||
&& 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
|
||||
// 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) {
|
||||
// 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.
|
||||
|
@ -52,11 +52,17 @@ pub(super) fn get_hint_if_single_char_arg(
|
||||
cx: &LateContext<'_>,
|
||||
arg: &Expr<'_>,
|
||||
applicability: &mut Applicability,
|
||||
ascii_only: bool,
|
||||
) -> Option<String> {
|
||||
if let ExprKind::Lit(lit) = &arg.kind
|
||||
&& let ast::LitKind::Str(r, style) = lit.node
|
||||
&& 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 ch = if let ast::StrStyle::Raw(nhash) = style {
|
||||
|
@ -29,7 +29,7 @@ declare_clippy_lint! {
|
||||
/// F: Sized + std::fmt::Debug,
|
||||
/// {}
|
||||
/// ```
|
||||
#[clippy::version = "1.77.0"]
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MULTIPLE_BOUND_LOCATIONS,
|
||||
suspicious,
|
||||
"defining generic bounds in multiple locations"
|
||||
|
@ -1,7 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::is_interior_mut_ty;
|
||||
use clippy_utils::{def_path_def_ids, trait_ref_of_method};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use clippy_utils::ty::InteriorMut;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
@ -23,27 +22,15 @@ declare_clippy_lint! {
|
||||
/// ### Known problems
|
||||
///
|
||||
/// #### 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.
|
||||
/// 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
|
||||
/// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
|
||||
/// indirection. For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
|
||||
/// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
|
||||
///
|
||||
/// 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).
|
||||
/// This lint does not follow raw pointers (`*const T` or `*mut T`) as `Hash` and `Ord`
|
||||
/// apply only to the **address** of the contained value. This can cause false negatives for
|
||||
/// custom collections that use raw pointers internally.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
@ -51,13 +38,12 @@ declare_clippy_lint! {
|
||||
/// use std::collections::HashSet;
|
||||
/// use std::hash::{Hash, Hasher};
|
||||
/// use std::sync::atomic::AtomicUsize;
|
||||
///# #[allow(unused)]
|
||||
///
|
||||
/// struct Bad(AtomicUsize);
|
||||
/// impl PartialEq for Bad {
|
||||
/// fn eq(&self, rhs: &Self) -> bool {
|
||||
/// ..
|
||||
/// ; unimplemented!();
|
||||
/// # ; true
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@ -66,7 +52,7 @@ declare_clippy_lint! {
|
||||
/// impl Hash for Bad {
|
||||
/// fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
/// ..
|
||||
/// ; unimplemented!();
|
||||
/// # ;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
@ -80,25 +66,16 @@ declare_clippy_lint! {
|
||||
"Check for mutable `Map`/`Set` key type"
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MutableKeyType {
|
||||
pub struct MutableKeyType<'tcx> {
|
||||
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>) {
|
||||
self.ignore_mut_def_ids.clear();
|
||||
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();
|
||||
}
|
||||
self.interior_mut = InteriorMut::without_pointers(cx, &self.ignore_interior_mutability);
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
Self {
|
||||
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();
|
||||
for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
|
||||
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
|
||||
// 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();
|
||||
if let ty::Adt(def, args) = ty.kind() {
|
||||
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);
|
||||
// Determines if a type contains interior mutability which would affect its implementation of
|
||||
// [`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()))
|
||||
{
|
||||
if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
|
||||
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc_errors::Applicability;
|
||||
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_session::declare_lint_pass;
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
@ -35,6 +35,16 @@ declare_clippy_lint! {
|
||||
/// 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"]
|
||||
pub NEEDLESS_FOR_EACH,
|
||||
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.
|
||||
&& let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind
|
||||
&& 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();
|
||||
ret_collector.visit_expr(body.value);
|
||||
|
@ -6,8 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_loca
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::{Applicability, MultiSpan};
|
||||
use rustc_hir::{
|
||||
BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
|
||||
StmtKind,
|
||||
BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
@ -83,7 +83,9 @@ fn should_skip<'tcx>(
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -185,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
||||
}
|
||||
// Collect variables mutably used and spans which will need dereferencings from the
|
||||
// function body.
|
||||
let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = {
|
||||
let mutably_used_vars = {
|
||||
let mut ctx = MutablyUsedVariablesCtxt {
|
||||
mutably_used_vars: HirIdSet::default(),
|
||||
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);
|
||||
}
|
||||
}
|
||||
ctx
|
||||
ctx.generate_mutably_used_ids_from_aliases()
|
||||
};
|
||||
for ((&input, &_), arg) in it {
|
||||
// Only take `&mut` arguments.
|
||||
@ -309,14 +311,24 @@ struct MutablyUsedVariablesCtxt<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
|
||||
fn add_mutably_used_var(&mut self, mut used_id: HirId) {
|
||||
while let Some(id) = self.aliases.get(&used_id) {
|
||||
self.mutably_used_vars.insert(used_id);
|
||||
used_id = *id;
|
||||
}
|
||||
fn add_mutably_used_var(&mut self, used_id: HirId) {
|
||||
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 {
|
||||
while let Some(id) = self.aliases.get(&target) {
|
||||
if *id == alias {
|
||||
|
@ -9,8 +9,8 @@ use rustc_ast::ast::Attribute;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind,
|
||||
QPath, TyKind,
|
||||
BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, QPath,
|
||||
TyKind,
|
||||
};
|
||||
use rustc_hir_typeck::expr_use_visitor as euv;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
|
@ -182,17 +182,17 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
||||
|
||||
if block.stmts.is_empty()
|
||||
&& let Some(expr) = block.expr
|
||||
&& 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, &mut needs_fully_qualified)
|
||||
&& expr_is_cmp(cx, &expr.kind, impl_item, &mut needs_fully_qualified)
|
||||
{
|
||||
}
|
||||
// Fix #12683, allow [`needless_return`] here
|
||||
else if block.expr.is_none()
|
||||
&& let Some(stmt) = block.stmts.first()
|
||||
&& let rustc_hir::StmtKind::Semi(Expr {
|
||||
kind: ExprKind::Ret(Some(Expr { kind: ret_kind, .. })),
|
||||
..
|
||||
}) = stmt.kind
|
||||
&& expr_is_cmp(cx, ret_kind, impl_item, &mut needs_fully_qualified)
|
||||
{
|
||||
} else {
|
||||
// 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, ..)`.
|
||||
fn self_cmp_call<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
@ -5,9 +5,9 @@
|
||||
use std::ptr;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::in_constant;
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use clippy_utils::{def_path_def_ids, in_constant};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use clippy_utils::ty::InteriorMut;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{
|
||||
@ -52,8 +52,8 @@ declare_clippy_lint! {
|
||||
/// 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
|
||||
/// the interior mutable field is used or not. See issues
|
||||
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
|
||||
/// the interior mutable field is used or not. See issue
|
||||
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
@ -170,42 +170,22 @@ fn lint(cx: &LateContext<'_>, source: Source) {
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NonCopyConst {
|
||||
pub struct NonCopyConst<'tcx> {
|
||||
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 {
|
||||
Self {
|
||||
ignore_interior_mutability,
|
||||
ignore_mut_def_ids: FxHashSet::default(),
|
||||
interior_mut: InteriorMut::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ty_ignored(&self, ty: Ty<'_>) -> 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;
|
||||
}
|
||||
fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
match *ty.kind() {
|
||||
// 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.
|
||||
@ -216,8 +196,7 @@ impl NonCopyConst {
|
||||
ty::Array(ty, _) => val
|
||||
.unwrap_branch()
|
||||
.iter()
|
||||
.any(|field| self.is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Adt(def, _) if def.is_union() => false,
|
||||
.any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Adt(def, args) if def.is_enum() => {
|
||||
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());
|
||||
@ -230,24 +209,23 @@ impl NonCopyConst {
|
||||
.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::Adt(def, args) => val
|
||||
.unwrap_branch()
|
||||
.iter()
|
||||
.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
|
||||
.unwrap_branch()
|
||||
.iter()
|
||||
.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,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_value_unfrozen_raw<'tcx>(
|
||||
&self,
|
||||
fn is_value_unfrozen_raw(
|
||||
cx: &LateContext<'tcx>,
|
||||
result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
|
||||
ty: Ty<'tcx>,
|
||||
@ -277,11 +255,11 @@ impl NonCopyConst {
|
||||
// I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
|
||||
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 args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
|
||||
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 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 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>,
|
||||
param_env: ty::ParamEnv<'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>) {
|
||||
self.ignore_mut_def_ids.clear();
|
||||
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();
|
||||
}
|
||||
self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
|
||||
if let ItemKind::Const(.., body_id) = it.kind {
|
||||
let ty = cx.tcx.type_of(it.owner_id).instantiate_identity();
|
||||
if !ignored_macro(cx, it)
|
||||
&& !self.is_ty_ignored(ty)
|
||||
&& Self::is_unfrozen(cx, ty)
|
||||
&& self.is_value_unfrozen_poly(cx, body_id, ty)
|
||||
&& self.interior_mut.is_interior_mut_ty(cx, ty)
|
||||
&& Self::is_value_unfrozen_poly(cx, body_id, ty)
|
||||
{
|
||||
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
|
||||
// bounded other traits could have their bound.
|
||||
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;
|
||||
// in other words, lint consts whose value *could* be unfrozen, not definitely is.
|
||||
// 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.
|
||||
// And, implementing it isn't a trivial task; it'll probably end up
|
||||
// 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 });
|
||||
}
|
||||
@ -409,8 +378,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
||||
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
|
||||
&& let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity()
|
||||
&& let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty)
|
||||
&& !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized)
|
||||
&& self.is_value_unfrozen_poly(cx, *body_id, normalized)
|
||||
&& self.interior_mut.is_interior_mut_ty(cx, normalized)
|
||||
&& Self::is_value_unfrozen_poly(cx, *body_id, normalized)
|
||||
{
|
||||
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.
|
||||
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||
|
||||
if !self.is_ty_ignored(ty)
|
||||
&& Self::is_unfrozen(cx, ty)
|
||||
&& self.is_value_unfrozen_poly(cx, *body_id, normalized)
|
||||
if self.interior_mut.is_interior_mut_ty(cx, normalized)
|
||||
&& Self::is_value_unfrozen_poly(cx, *body_id, normalized)
|
||||
{
|
||||
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)
|
||||
};
|
||||
|
||||
if !self.is_ty_ignored(ty)
|
||||
&& Self::is_unfrozen(cx, ty)
|
||||
&& self.is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
|
||||
if self.interior_mut.is_interior_mut_ty(cx, ty)
|
||||
&& Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
|
||||
{
|
||||
lint(cx, Source::Expr { expr: expr.span });
|
||||
}
|
||||
|
@ -7,14 +7,13 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, Symbol};
|
||||
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_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"];
|
||||
const INTEGER_METHODS: &[Symbol] = &[
|
||||
const DISALLOWED_INT_METHODS: &[Symbol] = &[
|
||||
sym::saturating_div,
|
||||
sym::wrapping_div,
|
||||
sym::wrapping_rem,
|
||||
@ -27,8 +26,8 @@ pub struct ArithmeticSideEffects {
|
||||
allowed_unary: FxHashSet<String>,
|
||||
// Used to check whether expressions are constants, such as in enum discriminants and consts
|
||||
const_span: Option<Span>,
|
||||
disallowed_int_methods: FxHashSet<Symbol>,
|
||||
expr_span: Option<Span>,
|
||||
integer_methods: FxHashSet<Symbol>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]);
|
||||
@ -53,8 +52,8 @@ impl ArithmeticSideEffects {
|
||||
allowed_binary,
|
||||
allowed_unary,
|
||||
const_span: None,
|
||||
disallowed_int_methods: DISALLOWED_INT_METHODS.iter().copied().collect(),
|
||||
expr_span: None,
|
||||
integer_methods: INTEGER_METHODS.iter().copied().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,10 +90,10 @@ impl ArithmeticSideEffects {
|
||||
fn has_specific_allowed_type_and_operation<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
op: &Spanned<hir::BinOpKind>,
|
||||
op: hir::BinOpKind,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
) -> 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 tcx = cx.tcx;
|
||||
|
||||
@ -166,13 +165,35 @@ impl ArithmeticSideEffects {
|
||||
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
|
||||
/// 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>(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
op: &Spanned<hir::BinOpKind>,
|
||||
op: hir::BinOpKind,
|
||||
lhs: &'tcx hir::Expr<'_>,
|
||||
rhs: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
@ -180,7 +201,7 @@ impl ArithmeticSideEffects {
|
||||
return;
|
||||
}
|
||||
if !matches!(
|
||||
op.node,
|
||||
op,
|
||||
hir::BinOpKind::Add
|
||||
| hir::BinOpKind::Div
|
||||
| hir::BinOpKind::Mul
|
||||
@ -204,7 +225,7 @@ impl ArithmeticSideEffects {
|
||||
return;
|
||||
}
|
||||
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
|
||||
return;
|
||||
}
|
||||
@ -213,7 +234,7 @@ impl ArithmeticSideEffects {
|
||||
Self::literal_integer(cx, actual_rhs),
|
||||
) {
|
||||
(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
|
||||
(hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
|
||||
// Adding or subtracting zeros is always a no-op
|
||||
@ -223,7 +244,7 @@ impl ArithmeticSideEffects {
|
||||
=> true,
|
||||
_ => false,
|
||||
},
|
||||
(Some(n), None) => match (&op.node, n) {
|
||||
(Some(n), None) => match (&op, n) {
|
||||
// Adding or subtracting zeros is always a no-op
|
||||
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
|
||||
// Multiplication by 1 or 0 will never overflow
|
||||
@ -249,6 +270,7 @@ impl ArithmeticSideEffects {
|
||||
&mut self,
|
||||
args: &'tcx [hir::Expr<'_>],
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
ps: &'tcx hir::PathSegment<'_>,
|
||||
receiver: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
@ -262,7 +284,8 @@ impl ArithmeticSideEffects {
|
||||
if !Self::is_integral(instance_ty) {
|
||||
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;
|
||||
}
|
||||
let (actual_arg, _) = peel_hir_expr_refs(arg);
|
||||
@ -310,10 +333,10 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
|
||||
}
|
||||
match &expr.kind {
|
||||
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, _) => {
|
||||
self.manage_method_call(args, cx, ps, receiver);
|
||||
self.manage_method_call(args, cx, expr, ps, receiver);
|
||||
},
|
||||
hir::ExprKind::Unary(un_op, un_expr) => {
|
||||
self.manage_unary_ops(cx, expr, un_expr, *un_op);
|
||||
|
@ -11,9 +11,8 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::hir_id::{HirId, HirIdMap};
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
|
||||
ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
|
||||
TyKind, Unsafety,
|
||||
self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind,
|
||||
ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
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)| {
|
||||
let param = &body.params[arg.idx];
|
||||
match param.pat.kind {
|
||||
PatKind::Binding(BindingMode::NONE, id, _, None)
|
||||
if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
|
||||
{
|
||||
PatKind::Binding(BindingMode::NONE, id, _, None) if !is_lint_allowed(cx, PTR_ARG, param.hir_id) => {
|
||||
Some((id, i))
|
||||
},
|
||||
_ => {
|
||||
|
@ -14,8 +14,8 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{
|
||||
BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath,
|
||||
Stmt, StmtKind,
|
||||
BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt,
|
||||
StmtKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
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::{expr_or_init, fn_def_id, match_def_path, paths};
|
||||
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]`
|
||||
fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(mac_call) = root_macro_call(expr.span)
|
||||
&& cx.tcx.is_diagnostic_item(sym::vec_macro, mac_call.def_id)
|
||||
if matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some()
|
||||
&& 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))
|
||||
&& !len_expr.span.from_expansion()
|
||||
|
@ -28,7 +28,7 @@ declare_clippy_lint! {
|
||||
/// fn size(&self) -> usize {
|
||||
/// // Note that `&self` as an argument is a `&&Foo`: Because `self`
|
||||
/// // 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`.
|
||||
/// std::mem::size_of_val(&self)
|
||||
/// }
|
||||
|
@ -1,5 +1,5 @@
|
||||
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::{
|
||||
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.
|
||||
// This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an
|
||||
// empty vec
|
||||
if expr.span.from_expansion()
|
||||
&& root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro)
|
||||
{
|
||||
if expr.span.from_expansion() && matching_root_macro_call(cx, expr.span, sym::vec_macro).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
use clippy_config::msrvs::{self, Msrv};
|
||||
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::source::snippet;
|
||||
use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{intravisit, ExprKind};
|
||||
use rustc_hir::{intravisit, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym::thread_local_macro;
|
||||
use rustc_span::sym::{self, thread_local_macro};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### 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]
|
||||
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.
|
||||
@ -102,12 +123,17 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
|
||||
// for details on this issue, see:
|
||||
// https://github.com/rust-lang/rust-clippy/pull/12276
|
||||
&& !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 Some(unpeeled) = block.expr
|
||||
&& 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! { ... }")
|
||||
&& initializer_snippet != "thread_local! { ... }"
|
||||
{
|
||||
|
@ -38,7 +38,7 @@ declare_clippy_lint! {
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.77.0"]
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub TO_STRING_TRAIT_IMPL,
|
||||
style,
|
||||
"check for direct implementations of `ToString`"
|
||||
|
@ -237,6 +237,7 @@ impl TraitBounds {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mutable_key_type)]
|
||||
fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
|
||||
struct SpanlessTy<'cx, 'tcx> {
|
||||
ty: &'tcx Ty<'tcx>,
|
||||
|
@ -12,8 +12,8 @@ mod vec_box;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitItem,
|
||||
TraitItemKind, TyKind,
|
||||
Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitFn,
|
||||
TraitItem, TraitItemKind, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
@ -420,7 +420,13 @@ impl<'tcx> LateLintPass<'tcx> for Types {
|
||||
TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
|
||||
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(..) => (),
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ use rustc_ast::LitIntType;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{
|
||||
ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit,
|
||||
PatKind, QPath, StmtKind, TyKind,
|
||||
ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatKind,
|
||||
QPath, StmtKind, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
@ -7,7 +7,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_copy;
|
||||
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_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -22,6 +22,7 @@ pub struct UselessVec {
|
||||
pub too_large_for_stack: u64,
|
||||
pub msrv: Msrv,
|
||||
pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
|
||||
pub allow_in_test: bool,
|
||||
}
|
||||
|
||||
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 {
|
||||
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!`
|
||||
let callsite = expr.span.parent_callsite().unwrap_or(expr.span);
|
||||
|
||||
|
@ -6,9 +6,7 @@ use clippy_utils::{get_parent_expr, path_to_local_id};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{
|
||||
BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp,
|
||||
};
|
||||
use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.79"
|
||||
version = "0.1.80"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
@ -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`
|
||||
/// 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
|
||||
Hash: Fn(&T) -> u64,
|
||||
Eq: Fn(&T, &T) -> bool,
|
||||
Hash: FnMut(&T) -> u64,
|
||||
Eq: FnMut(&T, &T) -> bool,
|
||||
{
|
||||
match exprs {
|
||||
[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
|
||||
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: HirId) -> bool {
|
||||
with_test_item_names(tcx, tcx.parent_module(id), |names| {
|
||||
tcx.hir()
|
||||
.parent_iter(id)
|
||||
let node = tcx.hir_node(id);
|
||||
once((id, node))
|
||||
.chain(tcx.hir().parent_iter(id))
|
||||
// Since you can nest functions we need to collect all until we leave
|
||||
// function scope
|
||||
.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))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
let hir = tcx.hir();
|
||||
|
@ -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
|
||||
/// (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> {
|
||||
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"
|
||||
/// 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> {
|
||||
|
@ -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 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_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_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"];
|
||||
@ -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 SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
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: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
|
||||
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_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
|
||||
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_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
|
||||
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_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
|
||||
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 VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"];
|
||||
pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"];
|
||||
|
@ -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::{Obligation, ObligationCause};
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::iter;
|
||||
|
||||
use crate::{match_def_path, path_res};
|
||||
use crate::{def_path_def_ids, match_def_path, path_res};
|
||||
|
||||
mod type_certainty;
|
||||
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)?)
|
||||
}
|
||||
|
||||
/// Check if given type has inner mutability such as [`std::cell::Cell`] or [`std::cell::RefCell`]
|
||||
/// etc.
|
||||
pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
match *ty.kind() {
|
||||
ty::Ref(_, inner_ty, mutbl) => mutbl == Mutability::Mut || is_interior_mut_ty(cx, inner_ty),
|
||||
ty::Slice(inner_ty) => 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)
|
||||
&& is_interior_mut_ty(cx, inner_ty)
|
||||
},
|
||||
ty::Tuple(fields) => fields.iter().any(|ty| is_interior_mut_ty(cx, ty)),
|
||||
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,
|
||||
]
|
||||
/// Helper to check if given type has inner mutability such as [`std::cell::Cell`] or
|
||||
/// [`std::cell::RefCell`].
|
||||
#[derive(Default, Debug)]
|
||||
pub struct InteriorMut<'tcx> {
|
||||
ignored_def_ids: FxHashSet<DefId>,
|
||||
ignore_pointers: bool,
|
||||
tys: FxHashMap<Ty<'tcx>, Option<bool>>,
|
||||
}
|
||||
|
||||
impl<'tcx> InteriorMut<'tcx> {
|
||||
pub fn new(cx: &LateContext<'tcx>, ignore_interior_mutability: &[String]) -> Self {
|
||||
let ignored_def_ids = ignore_interior_mutability
|
||||
.iter()
|
||||
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
|
||||
let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
|
||||
if is_std_collection || is_box {
|
||||
// The type is mutable if any of its type parameters are
|
||||
args.types().any(|ty| is_interior_mut_ty(cx, ty))
|
||||
} else {
|
||||
!ty.has_escaping_bound_vars()
|
||||
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
|
||||
&& !ty.is_freeze(cx.tcx, cx.param_env)
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
.flat_map(|ignored_ty| {
|
||||
let path: Vec<&str> = ignored_ty.split("::").collect();
|
||||
def_path_def_ids(cx, path.as_slice())
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
ignored_def_ids,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.79"
|
||||
version = "0.1.80"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2024-04-18"
|
||||
channel = "nightly-2024-05-02"
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
|
@ -4,6 +4,7 @@
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
#![allow(unused_extern_crates)]
|
||||
|
||||
use ui_test::custom_flags::rustfix::RustfixMode;
|
||||
use ui_test::spanned::Spanned;
|
||||
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"),
|
||||
..Config::rustc(Path::new("tests").join(test_dir))
|
||||
};
|
||||
config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo {
|
||||
rustfix: ui_test::RustfixMode::Everything,
|
||||
}))
|
||||
.into();
|
||||
config.comment_defaults.base().mode = Some(Spanned::dummy(Mode::Yolo)).into();
|
||||
config
|
||||
.comment_defaults
|
||||
.base()
|
||||
.set_custom("rustfix", RustfixMode::Everything);
|
||||
config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into();
|
||||
config.with_args(&args);
|
||||
let current_exe_path = env::current_exe().unwrap();
|
||||
@ -235,13 +237,12 @@ fn run_ui_cargo() {
|
||||
.push(("RUSTFLAGS".into(), Some("-Dwarnings".into())));
|
||||
// We need to do this while we still have a rustc in the `program` field.
|
||||
config.fill_host_and_target().unwrap();
|
||||
config.dependencies_crate_manifest_path = None;
|
||||
config.program.program.set_file_name(if cfg!(windows) {
|
||||
"cargo-clippy.exe"
|
||||
} else {
|
||||
"cargo-clippy"
|
||||
});
|
||||
config.comment_defaults.base().edition = Default::default();
|
||||
config.comment_defaults.base().custom.clear();
|
||||
|
||||
config
|
||||
.comment_defaults
|
||||
|
@ -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
|
||||
// `arc_like_types` in `clippy.toml`
|
||||
fn should_not_take_this_arg(_v: HashSet<Counted<String>>) {}
|
||||
|
||||
fn indirect(_: HashMap<ContainsCounted, usize>) {}
|
||||
|
||||
fn main() {
|
||||
should_not_take_this_arg(HashSet::new());
|
||||
indirect(HashMap::new());
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
||||
allow-print-in-tests
|
||||
allow-private-module-inception
|
||||
allow-unwrap-in-tests
|
||||
allow-useless-vec-in-tests
|
||||
allowed-dotfiles
|
||||
allowed-duplicate-crates
|
||||
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-private-module-inception
|
||||
allow-unwrap-in-tests
|
||||
allow-useless-vec-in-tests
|
||||
allowed-dotfiles
|
||||
allowed-duplicate-crates
|
||||
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-private-module-inception
|
||||
allow-unwrap-in-tests
|
||||
allow-useless-vec-in-tests
|
||||
allowed-dotfiles
|
||||
allowed-duplicate-crates
|
||||
allowed-idents-below-min-chars
|
||||
|
1
tests/ui-toml/useless_vec/clippy.toml
Normal file
1
tests/ui-toml/useless_vec/clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
allow-useless-vec-in-tests = true
|
26
tests/ui-toml/useless_vec/useless_vec.fixed
Normal file
26
tests/ui-toml/useless_vec/useless_vec.fixed
Normal 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]);
|
||||
}
|
||||
}
|
26
tests/ui-toml/useless_vec/useless_vec.rs
Normal file
26
tests/ui-toml/useless_vec/useless_vec.rs
Normal 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]);
|
||||
}
|
||||
}
|
11
tests/ui-toml/useless_vec/useless_vec.stderr
Normal file
11
tests/ui-toml/useless_vec/useless_vec.stderr
Normal 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
|
||||
|
@ -521,4 +521,14 @@ pub fn issue_11393() {
|
||||
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() {}
|
||||
|
@ -715,5 +715,17 @@ error: arithmetic operation that can potentially result in unexpected side-effec
|
||||
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
|
||||
|
||||
|
@ -9,6 +9,7 @@ use proc_macro::token_stream::IntoIter;
|
||||
use proc_macro::Delimiter::{self, Brace, Parenthesis};
|
||||
use proc_macro::Spacing::{self, Alone, Joint};
|
||||
use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// contained tokens as though they came from a macro expansion.
|
||||
///
|
||||
|
@ -158,7 +158,7 @@ trait BothOfCellAndGeneric<T> {
|
||||
const INDIRECT: Cell<*const T>;
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::DIRECT;
|
||||
let _ = &Self::DIRECT; //~ 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());
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::DIRECT;
|
||||
let _ = &Self::DIRECT; //~ ERROR: interior mutability
|
||||
let _ = &Self::INDIRECT; //~ ERROR: interior mutability
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,14 @@ LL | let _ = &Self::WRAPPED_SELF;
|
||||
|
|
||||
= 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
|
||||
--> 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
|
||||
|
||||
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
|
||||
--> 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
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
error: aborting due to 17 previous errors
|
||||
|
||||
|
@ -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
|
||||
let mut unnameable = Box::new(Option::default());
|
||||
let _ = unnameable.insert(|| {});
|
||||
|
||||
let _ = Box::into_raw(Box::new(String::default()));
|
||||
}
|
||||
|
||||
fn ret_ty_fn() -> Box<bool> {
|
||||
@ -75,6 +77,16 @@ fn call_ty_fn(_b: Box<u8>) {
|
||||
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};
|
||||
|
||||
impl Read for ImplementsDefault {
|
||||
|
@ -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
|
||||
let mut unnameable = Box::new(Option::default());
|
||||
let _ = unnameable.insert(|| {});
|
||||
|
||||
let _ = Box::into_raw(Box::new(String::default()));
|
||||
}
|
||||
|
||||
fn ret_ty_fn() -> Box<bool> {
|
||||
@ -75,6 +77,16 @@ fn call_ty_fn(_b: Box<u8>) {
|
||||
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};
|
||||
|
||||
impl Read for ImplementsDefault {
|
||||
|
@ -55,5 +55,11 @@ error: `Box::new(_)` of default value
|
||||
LL | call_ty_fn(Box::new(u8::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
|
||||
|
||||
|
@ -13,7 +13,8 @@
|
||||
clippy::cast_abs_to_unsigned,
|
||||
clippy::no_effect,
|
||||
clippy::unnecessary_operation,
|
||||
clippy::unnecessary_literal_unwrap
|
||||
clippy::unnecessary_literal_unwrap,
|
||||
clippy::identity_op
|
||||
)]
|
||||
|
||||
fn main() {
|
||||
@ -479,3 +480,21 @@ fn issue12506() -> usize {
|
||||
let bar: Result<Option<i64>, u32> = Ok(Some(10));
|
||||
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
|
||||
}
|
||||
|
@ -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)
|
||||
--> tests/ui/cast.rs:22:5
|
||||
--> tests/ui/cast.rs:23:5
|
||||
|
|
||||
LL | x0 as f32;
|
||||
| ^^^^^^^^^
|
||||
@ -8,37 +8,37 @@ LL | x0 as f32;
|
||||
= 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)
|
||||
--> tests/ui/cast.rs:26:5
|
||||
--> tests/ui/cast.rs:27:5
|
||||
|
|
||||
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)
|
||||
--> tests/ui/cast.rs:28:5
|
||||
--> tests/ui/cast.rs:29:5
|
||||
|
|
||||
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)
|
||||
--> tests/ui/cast.rs:31:5
|
||||
--> tests/ui/cast.rs:32:5
|
||||
|
|
||||
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)
|
||||
--> tests/ui/cast.rs:34:5
|
||||
--> tests/ui/cast.rs:35:5
|
||||
|
|
||||
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)
|
||||
--> tests/ui/cast.rs:36:5
|
||||
--> tests/ui/cast.rs:37:5
|
||||
|
|
||||
LL | x3 as f64;
|
||||
| ^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
@ -48,7 +48,7 @@ LL | 1f32 as i32;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::cast_possible_truncation)]`
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
@ -56,7 +56,7 @@ LL | 1f32 as u32;
|
||||
= 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
|
||||
--> tests/ui/cast.rs:41:5
|
||||
--> tests/ui/cast.rs:42:5
|
||||
|
|
||||
LL | 1f32 as u32;
|
||||
| ^^^^^^^^^^^
|
||||
@ -65,7 +65,7 @@ LL | 1f32 as u32;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::cast_sign_loss)]`
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
@ -73,7 +73,7 @@ LL | 1f64 as f32;
|
||||
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
@ -85,7 +85,7 @@ LL | i8::try_from(1i32);
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
@ -97,7 +97,7 @@ LL | u8::try_from(1i32);
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -105,7 +105,7 @@ LL | 1f64 as isize;
|
||||
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -113,13 +113,13 @@ LL | 1f64 as usize;
|
||||
= 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
|
||||
--> tests/ui/cast.rs:53:5
|
||||
--> tests/ui/cast.rs:54:5
|
||||
|
|
||||
LL | 1f64 as usize;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
@ -131,7 +131,7 @@ LL | u16::try_from(1f32 as u32);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
@ -139,13 +139,13 @@ LL | 1f32 as u32 as u16;
|
||||
= 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
|
||||
--> tests/ui/cast.rs:56:5
|
||||
--> tests/ui/cast.rs:57:5
|
||||
|
|
||||
LL | 1f32 as u32 as u16;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
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 _;
|
||||
| ^^^^^^^^^
|
||||
@ -157,7 +157,7 @@ LL | let _x: i8 = 1i32.try_into();
|
||||
| ~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
@ -165,7 +165,7 @@ LL | 1f32 as i32;
|
||||
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
@ -173,7 +173,7 @@ LL | 1f64 as i32;
|
||||
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
@ -181,13 +181,13 @@ LL | 1f32 as u8;
|
||||
= 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
|
||||
--> tests/ui/cast.rs:67:9
|
||||
--> tests/ui/cast.rs:68:9
|
||||
|
|
||||
LL | 1f32 as u8;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^
|
||||
@ -196,31 +196,31 @@ LL | 1u8 as i8;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]`
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^
|
||||
@ -232,7 +232,7 @@ LL | i8::try_from(1usize);
|
||||
| ~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -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
|
||||
--> tests/ui/cast.rs:87:5
|
||||
--> tests/ui/cast.rs:88:5
|
||||
|
|
||||
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
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -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
|
||||
--> tests/ui/cast.rs:92:5
|
||||
--> tests/ui/cast.rs:93:5
|
||||
|
|
||||
LL | 1usize as i32;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -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
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -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
|
||||
--> tests/ui/cast.rs:108:5
|
||||
--> tests/ui/cast.rs:109:5
|
||||
|
|
||||
LL | 1u64 as isize;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -364,7 +364,7 @@ LL | i8::try_from((-99999999999i64).min(1));
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -376,7 +376,7 @@ LL | u8::try_from(999999u64.clamp(0, 256));
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
@ -388,7 +388,7 @@ LL | let _ = u8::try_from(self);
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
@ -397,7 +397,7 @@ LL | let _ = Self::B as u8;
|
||||
= help: to override `-D warnings` add `#[allow(clippy::cast_enum_truncation)]`
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^
|
||||
@ -409,13 +409,13 @@ LL | let _ = i8::try_from(self);
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^
|
||||
@ -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
|
||||
--> tests/ui/cast.rs:324:21
|
||||
--> tests/ui/cast.rs:325:21
|
||||
|
|
||||
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
|
||||
--> tests/ui/cast.rs:371:21
|
||||
--> tests/ui/cast.rs:372:21
|
||||
|
|
||||
LL | let _ = self as u16;
|
||||
| ^^^^^^^^^^^
|
||||
@ -451,7 +451,7 @@ LL | let _ = u16::try_from(self);
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@ -463,7 +463,7 @@ LL | let c = u8::try_from(q >> 16);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
@ -475,85 +475,85 @@ LL | let c = u8::try_from(q / 1000);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
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 };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^
|
||||
@ -561,97 +561,97 @@ LL | (y / y * y * -2) as u16;
|
||||
= note: `#[deny(clippy::eq_op)]` on by default
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@ -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)
|
||||
|
||||
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
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -678,7 +678,7 @@ LL | let _ = u8::try_from(u32::MAX); // cast_possible_truncation
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -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)
|
||||
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -702,10 +702,34 @@ LL | usize::try_from(bar.unwrap().unwrap())
|
||||
|
|
||||
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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
|
||||
|
||||
|
@ -4,7 +4,8 @@
|
||||
clippy::needless_return,
|
||||
clippy::no_effect,
|
||||
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>) {
|
||||
@ -238,13 +239,22 @@ fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
match make::<Option<E<u32>>>() {
|
||||
#[clippy::msrv = "1.52.0"]
|
||||
let _ = match make::<Option<E<u32>>>() {
|
||||
Some(val) => match val {
|
||||
E::A(val) | E::B(val) => foo(val),
|
||||
_ => 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 Some(n) = val {
|
||||
let _ = || {
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
| ____________________^
|
||||
@ -10,7 +10,7 @@ LL | | },
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -21,7 +21,7 @@ LL | Some(n) => foo(n),
|
||||
= help: to override `-D warnings` add `#[allow(clippy::collapsible_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 {
|
||||
| ____________________^
|
||||
@ -32,7 +32,7 @@ LL | | },
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -41,7 +41,7 @@ LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 | |
|
||||
@ -50,7 +50,7 @@ LL | | }
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -58,7 +58,7 @@ LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 | |
|
||||
@ -69,7 +69,7 @@ LL | | }
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -77,7 +77,7 @@ LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 | |
|
||||
@ -87,7 +87,7 @@ LL | | }
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -96,7 +96,7 @@ LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 | |
|
||||
@ -105,7 +105,7 @@ LL | | }
|
||||
| |_____________^
|
||||
|
|
||||
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) => {
|
||||
| ^^^ replace this binding
|
||||
@ -113,7 +113,7 @@ LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 | |
|
||||
@ -123,7 +123,7 @@ LL | | }
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -132,7 +132,7 @@ LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 | |
|
||||
@ -143,7 +143,7 @@ LL | | }
|
||||
| |_____________^
|
||||
|
|
||||
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) => {
|
||||
| ^^^ replace this binding
|
||||
@ -151,7 +151,7 @@ LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 {
|
||||
| ____________________^
|
||||
@ -162,7 +162,7 @@ LL | | },
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -171,7 +171,7 @@ LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
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 {
|
||||
| ______________________^
|
||||
@ -182,7 +182,7 @@ LL | | },
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^^^ replace this binding
|
||||
@ -190,8 +190,26 @@ LL |
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ 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`
|
||||
--> tests/ui/collapsible_match.rs:273:9
|
||||
--> tests/ui/collapsible_match.rs:283:9
|
||||
|
|
||||
LL | / if let Some(u) = a {
|
||||
LL | |
|
||||
@ -200,7 +218,7 @@ LL | | }
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^ replace this binding
|
||||
@ -208,7 +226,7 @@ LL | if let Some(u) = a {
|
||||
| ^^^^^^^ with this pattern, prefixed by a:
|
||||
|
||||
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 | |
|
||||
@ -217,12 +235,12 @@ LL | | }
|
||||
| |_________^
|
||||
|
|
||||
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 {
|
||||
| ^ replace this binding
|
||||
LL | if let Some(u) = a {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
@ -222,3 +222,17 @@ fn supported_types() {
|
||||
//~^ ERROR: collection is never read
|
||||
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());
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
|
||||
|
||||
fn main() {
|
||||
let _ = #[coroutine] || {
|
||||
let _ = #[coroutine]
|
||||
|| {
|
||||
yield;
|
||||
};
|
||||
}
|
||||
|
@ -121,13 +121,12 @@ impl SelfType for AtomicUsize {
|
||||
// Even though a constant contains a generic type, if it also have an interior mutable type,
|
||||
// it should be linted at the definition site.
|
||||
trait BothOfCellAndGeneric<T> {
|
||||
// this is a false negative in the current implementation.
|
||||
const DIRECT: Cell<T>;
|
||||
const DIRECT: Cell<T>; //~ ERROR: interior mutable
|
||||
const INDIRECT: Cell<*const T>; //~ ERROR: interior mutable
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -55,22 +55,34 @@ LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
|
@ -71,3 +71,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_disallowed_name() {
|
||||
let foo = 0;
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
//@aux-build:proc_macros.rs
|
||||
#![warn(clippy::large_stack_arrays)]
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
extern crate proc_macros;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct S {
|
||||
pub data: [u64; 32],
|
||||
@ -55,3 +58,48 @@ fn main() {
|
||||
[(); 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];
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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];
|
||||
| ^^^^^^^^^^^^
|
||||
@ -9,7 +9,7 @@ LL | let _x = [build(); 3];
|
||||
= help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
|
||||
|
||||
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()];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -17,7 +17,7 @@ LL | let _y = [build(), build(), build()];
|
||||
= help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()`
|
||||
|
||||
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],
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
@ -25,7 +25,7 @@ LL | [0u32; 20_000_000],
|
||||
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
|
||||
|
||||
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],
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -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()`
|
||||
|
||||
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],
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -41,7 +41,7 @@ LL | [Some(""); 20_000_000],
|
||||
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
|
||||
|
||||
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],
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@ -49,12 +49,56 @@ LL | [E::T(0); 5000],
|
||||
= help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
|
||||
|
||||
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],
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= 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
|
||||
|
||||
|
@ -55,3 +55,30 @@ fn msrv_1_47() {
|
||||
const FOO: bool = 'x'.is_ascii_digit();
|
||||
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());
|
||||
}
|
||||
|
@ -55,3 +55,30 @@ fn msrv_1_47() {
|
||||
const FOO: bool = matches!('x', '0'..='9');
|
||||
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));
|
||||
}
|
||||
|
@ -133,5 +133,45 @@ error: manual check for common ascii range
|
||||
LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
|
||||
|
||||
|
@ -32,7 +32,7 @@ fn main() {
|
||||
// testing that the suggestion actually fits in its type
|
||||
let fail30 = 127_i8; // should be i8
|
||||
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 fail34 = 0xABCD_u16;
|
||||
let ok35 = 0x12345_16;
|
||||
|
@ -32,7 +32,7 @@ fn main() {
|
||||
// testing that the suggestion actually fits in its type
|
||||
let fail30 = 127_8; // should be i8
|
||||
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 fail34 = 0xABCD_16;
|
||||
let ok35 = 0x12345_16;
|
||||
|
@ -5,7 +5,7 @@ use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::Arc;
|
||||
//@no-rustfix
|
||||
|
||||
struct Key(AtomicUsize);
|
||||
|
||||
impl Clone for Key {
|
||||
@ -77,8 +77,6 @@ fn main() {
|
||||
//~^ ERROR: mutable key type
|
||||
let _map = HashMap::<&mut Cell<usize>, usize>::new();
|
||||
//~^ 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
|
||||
let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
|
||||
//~^ ERROR: mutable key type
|
||||
@ -92,8 +90,6 @@ fn main() {
|
||||
//~^ ERROR: mutable key type
|
||||
let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new();
|
||||
//~^ 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
|
||||
let _map = HashMap::<Box<Cell<usize>>, usize>::new();
|
||||
//~^ ERROR: mutable key type
|
||||
@ -101,4 +97,8 @@ fn main() {
|
||||
//~^ ERROR: mutable key type
|
||||
let _map = HashMap::<Arc<Cell<usize>>, usize>::new();
|
||||
//~^ ERROR: mutable key type
|
||||
|
||||
// Not interior mutability
|
||||
let _map = HashMap::<&mut usize, usize>::new();
|
||||
let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
|
||||
}
|
||||
|
@ -38,70 +38,58 @@ LL | let _map = HashMap::<&mut Cell<usize>, usize>::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: mutable key type
|
||||
--> tests/ui/mut_key.rs:80:5
|
||||
|
|
||||
LL | let _map = HashMap::<&mut usize, usize>::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: mutable key type
|
||||
--> tests/ui/mut_key.rs:83:5
|
||||
--> tests/ui/mut_key.rs:81:5
|
||||
|
|
||||
LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: mutable key type
|
||||
--> tests/ui/mut_key.rs:95:5
|
||||
|
|
||||
LL | let _map = HashMap::<Result<&mut usize, ()>, usize>::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: mutable key type
|
||||
--> tests/ui/mut_key.rs:98:5
|
||||
--> tests/ui/mut_key.rs:94:5
|
||||
|
|
||||
LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
error: aborting due to 15 previous errors
|
||||
|
||||
|
@ -113,6 +113,10 @@ fn should_not_lint() {
|
||||
let _ = v.iter().for_each(|elem| {
|
||||
acc += elem;
|
||||
});
|
||||
// `for_each` has a closure with an unsafe block.
|
||||
v.iter().for_each(|elem| unsafe {
|
||||
acc += elem;
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user