Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2025-01-28 18:28:57 +01:00
commit 145d5adf04
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
282 changed files with 5798 additions and 885 deletions

View file

@ -62,5 +62,5 @@ pub(super) fn check<'tcx>(
),
applicability,
);
};
}
}

View file

@ -32,5 +32,5 @@ pub(super) fn check<'tcx>(
),
applicability,
);
};
}
}

View file

@ -46,5 +46,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
format!("{receiver}.as_bytes().get({n}).copied()"),
applicability,
);
};
}
}

View file

@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span,
// &T where T: Copy
ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
_ => return,
};
}
span_lint_and_sugg(
cx,
CLONED_INSTEAD_OF_COPIED,

View file

@ -1,8 +1,8 @@
use crate::methods::DRAIN_COLLECT;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_range_full;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{is_range_full, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
use rustc_lint::LateContext;
@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re
.then_some("Vec")
.or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
.or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs))
&& let Some(exec_context) = std_or_core(cx)
{
let recv = snippet(cx, recv.span, "<expr>");
let sugg = if let ty::Ref(..) = recv_ty.kind() {
format!("std::mem::take({recv})")
format!("{exec_context}::mem::take({recv})")
} else {
format!("std::mem::take(&mut {recv})")
format!("{exec_context}::mem::take(&mut {recv})")
};
span_lint_and_sugg(

View file

@ -37,7 +37,7 @@ pub(super) fn check(
"expect_err".to_string(),
Applicability::MachineApplicable,
);
};
}
}
/// Given a `Result<T, E>` type, return its data (`T`).

View file

@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(
if ty.is_str() && can_be_static_str(cx, arg) {
return false;
}
};
}
true
}

View file

@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span
"into_iter()".to_string(),
Applicability::MaybeIncorrect,
);
};
}
}

View file

@ -0,0 +1,43 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_with_context};
use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use super::MANUAL_REPEAT_N;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
repeat_expr: &Expr<'_>,
take_arg: &Expr<'_>,
msrv: &Msrv,
) {
if msrv.meets(msrvs::REPEAT_N)
&& !expr.span.from_expansion()
&& is_trait_method(cx, expr, sym::Iterator)
&& let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind
&& let Some(def_id) = fn_def_id(cx, repeat_expr)
&& cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id)
&& !expr_use_ctxt(cx, expr).is_ty_unified
&& let Some(std_or_core) = std_or_core(cx)
{
let mut app = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
MANUAL_REPEAT_N,
expr.span,
"this `repeat().take()` can be written more concisely",
"consider using `repeat_n()` instead",
format!(
"{std_or_core}::iter::repeat_n({}, {})",
snippet_with_context(cx, repeat_arg.span, expr.span.ctxt(), "..", &mut app).0,
snippet(cx, take_arg.span, "..")
),
app,
);
}
}

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{eager_or_lazy, higher, usage};
use clippy_utils::{eager_or_lazy, higher, std_or_core, usage};
use rustc_ast::LitKind;
use rustc_ast::ast::RangeLimits;
use rustc_data_structures::packed::Pu128;
@ -75,6 +75,7 @@ pub(super) fn check(
} = body_hir
&& !usage::BindingUsageFinder::are_params_used(cx, body_hir)
&& let Some(count) = extract_count_with_applicability(cx, range, &mut applicability)
&& let Some(exec_context) = std_or_core(cx)
{
let method_to_use_name;
let new_span;
@ -105,7 +106,7 @@ pub(super) fn check(
let mut parts = vec![
(
receiver.span.to(method_call_span),
format!("std::iter::{method_to_use_name}"),
format!("{exec_context}::iter::{method_to_use_name}"),
),
new_span,
];

View file

@ -58,6 +58,7 @@ mod manual_inspect;
mod manual_is_variant_and;
mod manual_next_back;
mod manual_ok_or;
mod manual_repeat_n;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
mod manual_try_fold;
@ -101,6 +102,7 @@ mod single_char_add_str;
mod single_char_insert_string;
mod single_char_push_string;
mod skip_while_next;
mod sliced_string_as_bytes;
mod stable_sort_primitive;
mod str_split;
mod str_splitn;
@ -130,6 +132,7 @@ mod unnecessary_to_owned;
mod unused_enumerate_index;
mod unwrap_expect_used;
mod useless_asref;
mod useless_nonzero_new_unchecked;
mod utils;
mod vec_resize_to_zero;
mod verbose_file_reads;
@ -2416,14 +2419,14 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `.then_some(..).unwrap_or(..)`
/// Checks for unnecessary method chains that can be simplified into `if .. else ..`.
///
/// ### Why is this bad?
/// This can be written more clearly with `if .. else ..`
///
/// ### Limitations
/// This lint currently only looks for usages of
/// `.then_some(..).unwrap_or(..)`, but will be expanded
/// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded
/// to account for similar patterns.
///
/// ### Example
@ -4311,6 +4314,84 @@ declare_clippy_lint! {
"using `Iterator::last` on a `DoubleEndedIterator`"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `NonZero*::new_unchecked()` being used in a `const` context.
///
/// ### Why is this bad?
///
/// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a
/// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical
/// runtime performances while not requiring `unsafe`.
///
/// ### Example
/// ```no_run
/// use std::num::NonZeroUsize;
/// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) };
/// ```
/// Use instead:
/// ```no_run
/// use std::num::NonZeroUsize;
/// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap();
/// ```
#[clippy::version = "1.86.0"]
pub USELESS_NONZERO_NEW_UNCHECKED,
complexity,
"using `NonZero::new_unchecked()` in a `const` context"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `repeat().take()` that can be replaced with `repeat_n()`.
///
/// ### Why is this bad?
///
/// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning.
///
/// ### Example
/// ```no_run
/// let _ = std::iter::repeat(10).take(3);
/// ```
/// Use instead:
/// ```no_run
/// let _ = std::iter::repeat_n(10, 3);
/// ```
#[clippy::version = "1.86.0"]
pub MANUAL_REPEAT_N,
style,
"detect `repeat().take()` that can be replaced with `repeat_n()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for string slices immediantly followed by `as_bytes`.
///
/// ### Why is this bad?
/// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic.
///
/// ### Known problems
/// In some cases, the UTF-8 validation and potential panic from string slicing may be required for
/// the code's correctness. If you need to ensure the slice boundaries fall on valid UTF-8 character
/// boundaries, the original form (`s[1..5].as_bytes()`) should be preferred.
///
/// ### Example
/// ```rust
/// let s = "Lorem ipsum";
/// s[1..5].as_bytes();
/// ```
/// Use instead:
/// ```rust
/// let s = "Lorem ipsum";
/// &s.as_bytes()[1..5];
/// ```
#[clippy::version = "1.86.0"]
pub SLICED_STRING_AS_BYTES,
perf,
"slicing a string and immediately calling as_bytes is less efficient and can lead to panics"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -4477,6 +4558,9 @@ impl_lint_pass!(Methods => [
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
UNNECESSARY_MAP_OR,
DOUBLE_ENDED_ITERATOR_LAST,
USELESS_NONZERO_NEW_UNCHECKED,
MANUAL_REPEAT_N,
SLICED_STRING_AS_BYTES,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4505,6 +4589,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
from_iter_instead_of_collect::check(cx, expr, args, func);
unnecessary_fallible_conversions::check_function(cx, expr, func);
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv);
},
ExprKind::MethodCall(method_call, receiver, args, _) => {
let method_span = method_call.ident.span;
@ -4743,6 +4828,7 @@ impl Methods {
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
redundant_as_str::check(cx, expr, recv, as_str_span, span);
}
sliced_string_as_bytes::check(cx, expr, recv);
},
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
@ -4924,8 +5010,8 @@ impl Methods {
},
("is_empty", []) => {
match method_call(recv) {
Some(("as_bytes", prev_recv, [], _, _)) => {
needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span);
Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => {
needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span);
},
Some(("as_str", recv, [], as_str_span, _)) => {
redundant_as_str::check(cx, expr, recv, as_str_span, span);
@ -4962,8 +5048,8 @@ impl Methods {
double_ended_iterator_last::check(cx, expr, recv, call_span);
},
("len", []) => {
if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span);
if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) {
needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span);
}
},
("lock", []) => {
@ -5015,7 +5101,7 @@ impl Methods {
option_map_or_none::check(cx, expr, recv, def, map);
manual_ok_or::check(cx, expr, recv, def, map);
option_map_or_err_ok::check(cx, expr, recv, def, map);
unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv);
unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv);
},
("map_or_else", [def, map]) => {
result_map_or_else_none::check(cx, expr, recv, def, map);
@ -5146,6 +5232,7 @@ impl Methods {
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", [arg]) => {
iter_out_of_bounds::check_take(cx, expr, recv, arg);
manual_repeat_n::check(cx, expr, recv, arg, &self.msrv);
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,
@ -5220,8 +5307,8 @@ impl Methods {
Some(("map", m_recv, [m_arg], span, _)) => {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
},
Some(("then_some", t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method);
},
_ => {},
}

View file

@ -8,18 +8,16 @@ use rustc_span::Span;
use super::NEEDLESS_AS_BYTES;
pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) {
if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
&& let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs()
&& (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
{
pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) {
let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs();
if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() {
let mut app = Applicability::MachineApplicable;
let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
span_lint_and_sugg(
cx,
NEEDLESS_AS_BYTES,
span,
"needless call to `as_bytes()`",
format!("needless call to `{prev_method}`"),
format!("`{method}()` can be called directly on strings"),
format!("{sugg}.{method}()"),
app,

View file

@ -1,8 +1,11 @@
use super::OBFUSCATED_IF_ELSE;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::ExprKind;
use rustc_lint::LateContext;
pub(super) fn check<'tcx>(
@ -11,19 +14,30 @@ pub(super) fn check<'tcx>(
then_recv: &'tcx hir::Expr<'_>,
then_arg: &'tcx hir::Expr<'_>,
unwrap_arg: &'tcx hir::Expr<'_>,
then_method_name: &str,
) {
// something.then_some(blah).unwrap_or(blah)
// ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
let recv_ty = cx.typeck_results().expr_ty(then_recv);
if recv_ty.is_bool() {
let mut applicability = Applicability::MachineApplicable;
let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
};
let if_then = match then_method_name {
"then" if let ExprKind::Closure(closure) = then_arg.kind => {
let body = cx.tcx.hir().body(closure.body);
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
},
"then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
_ => String::new().into(),
};
let sugg = format!(
"if {} {{ {} }} else {{ {} }}",
snippet_with_applicability(cx, then_recv.span, "..", &mut applicability),
snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability),
if_then,
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
);
@ -31,8 +45,7 @@ pub(super) fn check<'tcx>(
cx,
OBFUSCATED_IF_ELSE,
expr.span,
"use of `.then_some(..).unwrap_or(..)` can be written \
more clearly with `if .. else ..`",
"this method chain can be written more clearly with `if .. else ..`",
"try",
sugg,
applicability,

View file

@ -37,7 +37,7 @@ pub(super) fn check(
let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#);
} else {
let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#);
};
}
span_lint_and_sugg(
cx,

View file

@ -0,0 +1,29 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_lang_item;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal};
use rustc_lint::LateContext;
use super::SLICED_STRING_AS_BYTES;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) {
if let ExprKind::Index(indexed, index, _) = recv.kind
&& is_range_literal(index)
&& let ty = cx.typeck_results().expr_ty(indexed).peel_refs()
&& (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String))
{
let mut applicability = Applicability::MaybeIncorrect;
let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability);
let range = snippet_with_applicability(cx, index.span, "_", &mut applicability);
span_lint_and_sugg(
cx,
SLICED_STRING_AS_BYTES,
expr.span,
"calling `as_bytes` after slicing a string",
"try",
format!("&{stringish}.as_bytes()[{range}]"),
applicability,
);
}
}

View file

@ -87,7 +87,7 @@ pub fn check_for_loop_iter(
// skip lint
return true;
}
};
}
// the lint should not be executed if no violation happens
let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind

View file

@ -1,9 +1,8 @@
use std::borrow::Cow;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::sugg::{Sugg, make_binop};
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
use clippy_utils::visitors::is_local_used;
@ -12,7 +11,7 @@ use rustc_ast::LitKind::Bool;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use rustc_span::{Span, sym};
use super::UNNECESSARY_MAP_OR;
@ -42,13 +41,14 @@ pub(super) fn check<'a>(
recv: &Expr<'_>,
def: &Expr<'_>,
map: &Expr<'_>,
method_span: Span,
msrv: &Msrv,
) {
let ExprKind::Lit(def_kind) = def.kind else {
return;
};
let recv_ty = cx.typeck_results().expr_ty(recv);
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
let Bool(def_bool) = def_kind.node else {
return;
@ -60,6 +60,8 @@ pub(super) fn check<'a>(
Some(_) | None => return,
};
let ext_def_span = def.span.until(map.span);
let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind
&& let closure_body = cx.tcx.hir().body(map_closure.body)
&& let closure_body_value = closure_body.value.peel_blocks()
@ -114,26 +116,17 @@ pub(super) fn check<'a>(
}
.into_string();
(sugg, "a standard comparison", app)
} else if !def_bool
&& msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
{
(vec![(expr.span, sugg)], "a standard comparison", app)
} else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) {
let suggested_name = variant.method_name();
(
format!("{recv_callsite}.{suggested_name}({span_callsite})",),
vec![(method_span, suggested_name.into()), (ext_def_span, String::default())],
suggested_name,
Applicability::MachineApplicable,
)
} else if def_bool
&& matches!(variant, Variant::Some)
&& msrv.meets(msrvs::IS_NONE_OR)
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
{
} else if def_bool && matches!(variant, Variant::Some) && msrv.meets(msrvs::IS_NONE_OR) {
(
format!("{recv_callsite}.is_none_or({span_callsite})"),
vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())],
"is_none_or",
Applicability::MachineApplicable,
)
@ -145,13 +138,13 @@ pub(super) fn check<'a>(
return;
}
span_lint_and_sugg(
span_lint_and_then(
cx,
UNNECESSARY_MAP_OR,
expr.span,
"this `map_or` can be simplified",
format!("use {method} instead"),
sugg,
applicability,
|diag| {
diag.multipart_suggestion_verbose(format!("use {method} instead"), sugg, applicability);
},
);
}

View file

@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind};
use rustc_middle::ty;
use rustc_middle::ty::GenericArgKind;
use rustc_span::sym;
use rustc_span::symbol::Ident;
use std::iter;
@ -43,8 +44,7 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
},
// The two exprs are method calls.
// Check to see that the function is the same and the arguments are mirrored
// This is enough because the receiver of the method is listed in the arguments
// Check to see that the function is the same and the arguments and receivers are mirrored
(
ExprKind::MethodCall(left_segment, left_receiver, left_args, _),
ExprKind::MethodCall(right_segment, right_receiver, right_args, _),

View file

@ -217,10 +217,13 @@ fn check_into_iter_call_arg(
&& implements_trait(cx, parent_ty, iterator_trait_id, &[])
&& let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
&& let Some(receiver_snippet) = receiver.span.get_source_text(cx)
// If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624.
&& !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow)
{
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
return true;
}
let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
"copied"
} else {

View file

@ -0,0 +1,59 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_inside_always_const_context;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource};
use rustc_lint::LateContext;
use rustc_span::sym;
use super::USELESS_NONZERO_NEW_UNCHECKED;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'tcx>, args: &[Expr<'_>], msrv: &Msrv) {
if msrv.meets(msrvs::CONST_UNWRAP)
&& let ExprKind::Path(QPath::TypeRelative(ty, segment)) = func.kind
&& segment.ident.name == sym::new_unchecked
&& let [init_arg] = args
&& is_inside_always_const_context(cx.tcx, expr.hir_id)
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero)
{
let mut app = Applicability::MachineApplicable;
let ty_str = snippet_with_applicability(cx, ty.span, "_", &mut app);
let msg = format!("`{ty_str}::new()` and `Option::unwrap()` can be safely used in a `const` context");
let sugg = format!(
"{ty_str}::new({}).unwrap()",
snippet_with_applicability(cx, init_arg.span, "_", &mut app)
);
if let Node::Block(Block {
stmts: [],
span: block_span,
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
..
}) = cx.tcx.parent_hir_node(expr.hir_id)
{
if !block_span.from_expansion() {
// The expression is the only component of an `unsafe` block. Propose
// to replace the block altogether.
span_lint_and_sugg(
cx,
USELESS_NONZERO_NEW_UNCHECKED,
*block_span,
msg,
"use instead",
sugg,
app,
);
}
} else {
// The expression is enclosed in a larger `unsafe` context. Indicate that
// this may no longer be needed for the fixed expression.
span_lint_and_then(cx, USELESS_NONZERO_NEW_UNCHECKED, expr.span, msg, |diagnostic| {
diagnostic
.span_suggestion(expr.span, "use instead", sugg, app)
.note("the fixed expression does not require an `unsafe` context");
});
}
}
}