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

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

View file

@ -6,11 +6,62 @@ document.
## Unreleased / Beta / In Rust Nightly
[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

View file

@ -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"

View file

@ -299,10 +299,10 @@ This is good, because it makes writing this particular lint less complicated.
We have to make this decision with every new Clippy lint. It boils down to using
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

View file

@ -132,6 +132,16 @@ Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
## `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)

View file

@ -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

View file

@ -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.
///

View file

@ -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 }

View file

@ -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"

View file

@ -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;
}

View file

@ -121,9 +121,9 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
&& 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
}

View file

@ -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"

View file

@ -40,9 +40,14 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
})
},
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,

View file

@ -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"

View file

@ -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);

View file

@ -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)

View file

@ -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))
}

View file

@ -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};

View file

@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item;
use clippy_utils::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,

View file

@ -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

View file

@ -93,12 +93,9 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<Hir
let mut slices: FxIndexMap<HirId, SliceLintInformation> = FxIndexMap::default();
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.

View file

@ -1,10 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use clippy_utils::macros::macro_backtrace;
use clippy_utils::source::snippet;
use 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)
}

View file

@ -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));

View file

@ -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,

View file

@ -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(')')

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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,

View file

@ -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))
});

View file

@ -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)

View file

@ -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
},

View file

@ -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"#

View file

@ -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

View file

@ -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);

View file

@ -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,

View file

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

View file

@ -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.

View file

@ -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 {

View file

@ -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"

View file

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

View file

@ -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);

View file

@ -6,8 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_loca
use core::ops::ControlFlow;
use 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;

View file

@ -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 {

View file

@ -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;

View file

@ -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>,

View file

@ -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 });
}

View file

@ -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);

View file

@ -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))
},
_ => {

View file

@ -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;

View file

@ -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()

View file

@ -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)
/// }

View file

@ -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;
}

View file

@ -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! { ... }"
{

View file

@ -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`"

View file

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

View file

@ -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(..) => (),
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

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

View file

@ -2328,10 +2328,10 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
///
/// Given functions `eq` and `hash` such that `eq(a, b) == true`
/// 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();

View file

@ -119,10 +119,20 @@ pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
/// If the macro backtrace of `span` has a macro call at the root expansion
/// (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> {

View file

@ -19,8 +19,6 @@ pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "B
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const 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"];

View file

@ -29,9 +29,10 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_trait_selection::traits::{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
}
}

View file

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

View file

@ -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"]

View file

@ -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

View file

@ -44,10 +44,18 @@ impl<T> Deref for Counted<T> {
}
}
#[derive(Hash, PartialEq, Eq)]
struct ContainsCounted {
inner: Counted<String>,
}
// This is not linted because `"mut_key::Counted"` is in
// `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());
}

View file

@ -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

View file

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

View file

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

View file

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

View file

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

View file

@ -521,4 +521,14 @@ pub fn issue_11393() {
example_rem(x, maybe_zero);
}
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() {}

View file

@ -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

View file

@ -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.
///

View file

@ -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
}
}

View file

@ -75,6 +75,14 @@ LL | let _ = &Self::WRAPPED_SELF;
|
= help: assign this const to a local or static variable, and use the variable here
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

View file

@ -65,6 +65,8 @@ fn main() {
// Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563
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 {

View file

@ -65,6 +65,8 @@ fn main() {
// Would have a suggestion after https://github.com/rust-lang/rust/blob/fdd030127cc68afec44a8d3f6341525dd34e50ae/compiler/rustc_middle/src/ty/diagnostics.rs#L554-L563
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 {

View file

@ -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

View file

@ -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
}

View file

@ -1,5 +1,5 @@
error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
--> 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

View file

@ -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 _ = || {

View file

@ -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

View file

@ -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());
}

View file

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

View file

@ -121,13 +121,12 @@ impl SelfType for AtomicUsize {
// Even though a constant contains a generic type, if it also have an interior mutable type,
// 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());
}

View file

@ -55,22 +55,34 @@ LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: a `const` item should never be interior mutable
--> 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

View file

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

View file

@ -1,6 +1,9 @@
//@aux-build:proc_macros.rs
#![warn(clippy::large_stack_arrays)]
#![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];
}

View file

@ -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

View file

@ -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());
}

View file

@ -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));
}

View file

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

View file

@ -32,7 +32,7 @@ fn main() {
// testing that the suggestion actually fits in its type
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;

View file

@ -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;

View file

@ -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();
}

View file

@ -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

View file

@ -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