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

This commit is contained in:
Philipp Krones 2025-04-03 21:31:02 +02:00
commit ab7e525929
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
377 changed files with 6426 additions and 3724 deletions

View file

@ -192,10 +192,10 @@ impl BindInsteadOfMap {
}
fn is_variant(&self, cx: &LateContext<'_>, res: Res) -> bool {
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
if let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) {
return cx.tcx.parent(id) == variant_id;
}
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res
&& let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item)
{
return cx.tcx.parent(id) == variant_id;
}
false
}

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
use clippy_utils::ty::is_type_lang_item;
use rustc_ast::ast::LitKind;
@ -16,14 +17,15 @@ pub(super) fn check<'tcx>(
call_span: Span,
recv: &'tcx Expr<'_>,
arg: &'tcx Expr<'_>,
msrv: Msrv,
) {
if let ExprKind::MethodCall(path_segment, ..) = recv.kind {
if matches!(
if let ExprKind::MethodCall(path_segment, ..) = recv.kind
&& matches!(
path_segment.ident.name.as_str(),
"to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase"
) {
return;
}
)
{
return;
}
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
@ -58,11 +60,15 @@ pub(super) fn check<'tcx>(
let suggestion_source = reindent_multiline(
&format!(
"std::path::Path::new({})
"std::path::Path::new({recv_source})
.extension()
.map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))",
recv_source,
ext_str.strip_prefix('.').unwrap()
.{}|ext| ext.eq_ignore_ascii_case(\"{}\"))",
if msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND) {
"is_some_and("
} else {
"map_or(false, "
},
ext_str.strip_prefix('.').unwrap(),
),
true,
Some(indent_of(cx, call_span).unwrap_or(0) + 4),

View file

@ -40,10 +40,10 @@ pub(super) fn check(
.map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target);
let ty = cx.typeck_results().expr_ty(expr);
if let ty::Ref(_, inner, _) = arg_ty.kind() {
if let ty::Ref(..) = inner.kind() {
return; // don't report clone_on_copy
}
if let ty::Ref(_, inner, _) = arg_ty.kind()
&& let ty::Ref(..) = inner.kind()
{
return; // don't report clone_on_copy
}
if is_copy(cx, ty) {

View file

@ -54,10 +54,11 @@ pub(super) fn check<'tcx>(
if is_type_lang_item(cx, arg_ty, hir::LangItem::String) {
return false;
}
if let ty::Ref(_, ty, ..) = arg_ty.kind() {
if ty.is_str() && can_be_static_str(cx, arg) {
return false;
}
if let ty::Ref(_, ty, ..) = arg_ty.kind()
&& ty.is_str()
&& can_be_static_str(cx, arg)
{
return false;
}
true
}

View file

@ -1,10 +1,14 @@
use super::FILTER_MAP_BOOL_THEN;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_copy;
use clippy_utils::{is_from_proc_macro, is_trait_method, peel_blocks};
use clippy_utils::{
CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks,
};
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_hir::{Expr, ExprKind, HirId, Param, Pat};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::Binder;
use rustc_middle::ty::adjustment::Adjust;
@ -44,17 +48,69 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
&& let Some(filter) = recv.span.get_source_text(cx)
&& let Some(map) = then_body.span.get_source_text(cx)
{
span_lint_and_sugg(
span_lint_and_then(
cx,
FILTER_MAP_BOOL_THEN,
call_span,
"usage of `bool::then` in `filter_map`",
"use `filter` then `map` instead",
format!(
"filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})",
derefs = "*".repeat(needed_derefs)
),
Applicability::MachineApplicable,
|diag| {
if can_filter_and_then_move_to_closure(cx, &param, recv, then_body) {
diag.span_suggestion(
call_span,
"use `filter` then `map` instead",
format!(
"filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})",
derefs = "*".repeat(needed_derefs)
),
Applicability::MachineApplicable,
);
} else {
diag.help("consider using `filter` then `map` instead");
}
},
);
}
}
/// Returns a set of all bindings found in the given pattern.
fn find_bindings_from_pat(pat: &Pat<'_>) -> FxHashSet<HirId> {
let mut bindings = FxHashSet::default();
pat.walk(|p| {
if let rustc_hir::PatKind::Binding(_, hir_id, _, _) = p.kind {
bindings.insert(hir_id);
}
true
});
bindings
}
/// Returns true if we can take a closure parameter and have it in both the `filter` function and
/// the`map` function. This is not the case if:
///
/// - The `filter` would contain an early return,
/// - `filter` and `then` contain captures, and any of those are &mut
fn can_filter_and_then_move_to_closure<'tcx>(
cx: &LateContext<'tcx>,
param: &Param<'tcx>,
filter: &'tcx Expr<'tcx>,
then: &'tcx Expr<'tcx>,
) -> bool {
if contains_return(filter) {
return false;
}
let Some(filter_captures) = can_move_expr_to_closure(cx, filter) else {
return true;
};
let Some(then_captures) = can_move_expr_to_closure(cx, then) else {
return true;
};
let param_bindings = find_bindings_from_pat(param.pat);
filter_captures.iter().all(|(hir_id, filter_cap)| {
param_bindings.contains(hir_id)
|| !then_captures
.get(hir_id)
.is_some_and(|then_cap| matches!(*filter_cap | *then_cap, CaptureKind::Ref(Mutability::Mut)))
})
}

View file

@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp
&& implements_trait(cx, arg_ty, iter_id, &[])
{
// `expr` implements `FromIterator` trait
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_paren();
let turbofish = extract_turbofish(cx, expr, ty);
let sugg = format!("{iter_expr}.collect::<{turbofish}>()");
span_lint_and_sugg(

View file

@ -14,15 +14,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
if expr.span.in_external_macro(cx.sess().source_map()) || !receiver.span.eq_ctxt(expr.span) {
return;
}
if let Some(parent) = get_parent_expr(cx, expr) {
if let Some(parent) = get_parent_expr(cx, parent) {
if is_inside_always_const_context(cx.tcx, expr.hir_id)
&& let Some(macro_call) = root_macro_call(parent.span)
&& is_assert_macro(cx, macro_call.def_id)
{
return;
}
}
if let Some(parent) = get_parent_expr(cx, expr)
&& let Some(parent) = get_parent_expr(cx, parent)
&& is_inside_always_const_context(cx.tcx, expr.hir_id)
&& let Some(macro_call) = root_macro_call(parent.span)
&& is_assert_macro(cx, macro_call.def_id)
{
return;
}
let init_expr = expr_or_init(cx, receiver);
if !receiver.span.eq_ctxt(init_expr.span) {

View file

@ -8,14 +8,14 @@ use rustc_span::sym;
use super::ITERATOR_STEP_BY_ZERO;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
if is_trait_method(cx, expr, sym::Iterator) {
if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) {
span_lint(
cx,
ITERATOR_STEP_BY_ZERO,
expr.span,
"`Iterator::step_by(0)` will panic at runtime",
);
}
if is_trait_method(cx, expr, sym::Iterator)
&& let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg)
{
span_lint(
cx,
ITERATOR_STEP_BY_ZERO,
expr.span,
"`Iterator::step_by(0)` will panic at runtime",
);
}
}

View file

@ -106,15 +106,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
};
let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
if let hir::ExprKind::Lit(lit) = &expr.kind {
if let ast::LitKind::Int(value, _) = lit.node {
if value == maxval {
return Some(MinMax::Max);
}
if let hir::ExprKind::Lit(lit) = &expr.kind
&& let ast::LitKind::Int(value, _) = lit.node
{
if value == maxval {
return Some(MinMax::Max);
}
if check_min && value == minval {
return Some(MinMax::Min);
}
if check_min && value == minval {
return Some(MinMax::Min);
}
}
@ -125,10 +125,10 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
return r;
}
if ty.is_signed() {
if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
return check_lit(val, true);
}
if ty.is_signed()
&& let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind
{
return check_lit(val, true);
}
None

View file

@ -77,7 +77,7 @@ pub(super) fn check(
s @ Cow::Borrowed(_) => s,
},
RepeatKind::String => Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app)
.maybe_par()
.maybe_paren()
.to_string()
.into(),
};

View file

@ -51,19 +51,19 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
let closure_expr = peel_blocks(closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, Mutability::Not) => {
if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind {
if ident_eq(name, closure_expr) {
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind
&& ident_eq(name, closure_expr)
{
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
},
hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) => {
match closure_expr.kind {
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
if ident_eq(name, inner) {
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
if ident_eq(name, inner)
&& let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind()
{
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
},
hir::ExprKind::MethodCall(method, obj, [], _) => {
@ -114,19 +114,17 @@ fn handle_path(
) {
if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
&& cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id)
{
// The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option`
// and `Result`.
if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind()
&& let args = args.as_slice()
&& let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type())
&& let ty::Ref(_, ty, Mutability::Not) = ty.kind()
&& let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind()
&& lst.iter().all(|l| l.as_type() == Some(*ty))
&& !should_call_clone_as_function(cx, *ty)
{
lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs()));
}
&& let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind()
&& let args = args.as_slice()
&& let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type())
&& let ty::Ref(_, ty, Mutability::Not) = ty.kind()
&& let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind()
&& lst.iter().all(|l| l.as_type() == Some(*ty))
&& !should_call_clone_as_function(cx, *ty)
{
lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs()));
}
}

View file

@ -41,7 +41,7 @@ fn extract_count_with_applicability(
return Some(format!("{count}"));
}
let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability)
.maybe_par()
.maybe_paren()
.into_string();
if lower_bound == 0 {
if range.limits == RangeLimits::Closed {

View file

@ -478,9 +478,6 @@ declare_clippy_lint! {
/// Because you usually call `expect()` on the `Result`
/// directly to get a better error message.
///
/// ### Known problems
/// The error type needs to implement `Debug`
///
/// ### Example
/// ```no_run
/// # let x = Ok::<_, ()>(());
@ -2429,7 +2426,7 @@ declare_clippy_lint! {
///
/// ### Limitations
/// This lint currently only looks for usages of
/// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded
/// `.{then, then_some}(..).{unwrap_or, unwrap_or_else, unwrap_or_default}(..)`, but will be expanded
/// to account for similar patterns.
///
/// ### Example
@ -4478,7 +4475,7 @@ declare_clippy_lint! {
/// ```no_run
/// let _ = std::io::Error::other("bad".to_string());
/// ```
#[clippy::version = "1.86.0"]
#[clippy::version = "1.87.0"]
pub IO_OTHER_ERROR,
style,
"calling `std::io::Error::new(std::io::ErrorKind::Other, _)`"
@ -4667,11 +4664,12 @@ impl_lint_pass!(Methods => [
pub fn method_call<'tcx>(
recv: &'tcx Expr<'tcx>,
) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> {
if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind {
if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() {
let name = path.ident.name.as_str();
return Some((name, receiver, args, path.ident.span, call_span));
}
if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind
&& !args.iter().any(|e| e.span.from_expansion())
&& !receiver.span.from_expansion()
{
let name = path.ident.name.as_str();
return Some((name, receiver, args, path.ident.span, call_span));
}
None
}
@ -4992,7 +4990,7 @@ impl Methods {
},
("ends_with", [arg]) => {
if let ExprKind::MethodCall(.., span) = expr.kind {
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg, self.msrv);
}
path_ends_with_ext::check(cx, recv, arg, expr, self.msrv, &self.allowed_dotfiles);
},
@ -5421,15 +5419,21 @@ impl Methods {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
},
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or");
obfuscated_if_else::check(cx, expr, t_recv, t_arg, Some(u_arg), then_method, "unwrap_or");
},
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
("unwrap_or_default", []) => {
if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) {
manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv);
match method_call(recv) {
Some(("map", m_recv, [arg], span, _)) => {
manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv);
},
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, None, then_method, "unwrap_or_default");
},
_ => {},
}
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
},
@ -5441,7 +5445,15 @@ impl Methods {
Some(("map", recv, [map_arg], _, _))
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or_else");
obfuscated_if_else::check(
cx,
expr,
t_recv,
t_arg,
Some(u_arg),
then_method,
"unwrap_or_else",
);
},
_ => {
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");

View file

@ -377,20 +377,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
return;
}
if let Some(hir_id) = path_to_local(recv) {
if let Some(index) = self.hir_id_uses_map.remove(&hir_id) {
if self
.illegal_mutable_capture_ids
.intersection(&self.current_mutably_captured_ids)
.next()
.is_none()
{
if let Some(hir_id) = self.current_statement_hir_id {
self.hir_id_uses_map.insert(hir_id, index);
}
} else {
self.uses[index] = None;
if let Some(hir_id) = path_to_local(recv)
&& let Some(index) = self.hir_id_uses_map.remove(&hir_id)
{
if self
.illegal_mutable_capture_ids
.intersection(&self.current_mutably_captured_ids)
.next()
.is_none()
{
if let Some(hir_id) = self.current_statement_hir_id {
self.hir_id_uses_map.insert(hir_id, index);
}
} else {
self.uses[index] = None;
}
}
}

View file

@ -9,26 +9,27 @@ use super::NEEDLESS_OPTION_TAKE;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
// Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) {
if let Some(function_name) = source_of_temporary_value(recv) {
span_lint_and_then(
cx,
NEEDLESS_OPTION_TAKE,
expr.span,
"called `Option::take()` on a temporary value",
|diag| {
diag.note(format!(
"`{function_name}` creates a temporary value, so calling take() has no effect"
));
diag.span_suggestion(
expr.span.with_lo(recv.span.hi()),
"remove",
"",
Applicability::MachineApplicable,
);
},
);
}
if !recv.is_syntactic_place_expr()
&& is_expr_option(cx, recv)
&& let Some(function_name) = source_of_temporary_value(recv)
{
span_lint_and_then(
cx,
NEEDLESS_OPTION_TAKE,
expr.span,
"called `Option::take()` on a temporary value",
|diag| {
diag.note(format!(
"`{function_name}` creates a temporary value, so calling take() has no effect"
));
diag.span_suggestion(
expr.span.with_lo(recv.span.hi()),
"remove",
"",
Applicability::MachineApplicable,
);
},
);
}
}
@ -44,10 +45,10 @@ fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn source_of_temporary_value<'a>(expr: &'a Expr<'_>) -> Option<&'a str> {
match expr.peel_borrows().kind {
ExprKind::Call(function, _) => {
if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind {
if !func_path.segments.is_empty() {
return Some(func_path.segments[0].ident.name.as_str());
}
if let ExprKind::Path(QPath::Resolved(_, func_path)) = function.kind
&& !func_path.segments.is_empty()
{
return Some(func_path.segments[0].ident.name.as_str());
}
if let ExprKind::Path(QPath::TypeRelative(_, func_path_segment)) = function.kind {
return Some(func_path_segment.ident.name.as_str());

View file

@ -14,14 +14,17 @@ pub(super) fn check<'tcx>(
expr: &'tcx hir::Expr<'_>,
then_recv: &'tcx hir::Expr<'_>,
then_arg: &'tcx hir::Expr<'_>,
unwrap_arg: &'tcx hir::Expr<'_>,
unwrap_arg: Option<&'tcx hir::Expr<'_>>,
then_method_name: &str,
unwrap_method_name: &str,
) {
let recv_ty = cx.typeck_results().expr_ty(then_recv);
if recv_ty.is_bool() {
let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) {
let then_eager = switch_to_eager_eval(cx, then_arg);
let unwrap_eager = unwrap_arg.is_none_or(|arg| switch_to_eager_eval(cx, arg));
let mut applicability = if then_eager && unwrap_eager {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
@ -36,16 +39,17 @@ pub(super) fn check<'tcx>(
_ => return,
};
// FIXME: Add `unwrap_or_else` symbol
// FIXME: Add `unwrap_or_else` and `unwrap_or_default` symbol
let els = match unwrap_method_name {
"unwrap_or" => snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability),
"unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.kind => {
"unwrap_or" => snippet_with_applicability(cx, unwrap_arg.unwrap().span, "..", &mut applicability),
"unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.unwrap().kind => {
let body = cx.tcx.hir_body(closure.body);
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
},
"unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.kind => {
snippet_with_applicability(cx, unwrap_arg.span, "_", &mut applicability) + "()"
"unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.unwrap().kind => {
snippet_with_applicability(cx, unwrap_arg.unwrap().span, "_", &mut applicability) + "()"
},
"unwrap_or_default" => "Default::default()".into(),
_ => return,
};

View file

@ -15,21 +15,22 @@ use super::SEEK_FROM_CURRENT;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
let ty = cx.typeck_results().expr_ty(recv);
if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) {
if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) {
let mut applicability = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
if let Some(def_id) = cx.tcx.get_diagnostic_item(sym::IoSeek)
&& implements_trait(cx, ty, def_id, &[])
&& arg_is_seek_from_current(cx, arg)
{
let mut applicability = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
span_lint_and_sugg(
cx,
SEEK_FROM_CURRENT,
expr.span,
"using `SeekFrom::Current` to start from current position",
"replace with",
format!("{snip}.stream_position()"),
applicability,
);
}
span_lint_and_sugg(
cx,
SEEK_FROM_CURRENT,
expr.span,
"using `SeekFrom::Current` to start from current position",
"replace with",
format!("{snip}.stream_position()"),
applicability,
);
}
}
@ -38,13 +39,11 @@ fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
&& let ExprKind::Path(ref path) = f.kind
&& let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id()
&& is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id)
{
// check if argument of `SeekFrom::Current` is `0`
if let ExprKind::Lit(lit) = arg.kind
&& let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node
{
return true;
}
&& let ExprKind::Lit(lit) = arg.kind
&& let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node
{
return true;
}
false

View file

@ -238,15 +238,14 @@ fn indirect_usage<'tcx>(
unwrap_kind: Some(unwrap_kind),
..
} = iter_usage
&& parent_id == local_hir_id
{
if parent_id == local_hir_id {
return Some(IndirectUsage {
name: ident.name,
span: stmt.span,
init_expr,
unwrap_kind,
});
}
return Some(IndirectUsage {
name: ident.name,
span: stmt.span,
init_expr,
unwrap_kind,
});
}
}

View file

@ -13,11 +13,11 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<
&& let closure_body = cx.tcx.hir_body(closure.body)
&& !cx.typeck_results().expr_ty(closure_body.value).is_unit()
{
if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx)
// A variable is used mutably inside of the closure. Suppress the lint.
if !map_mutated_vars.is_empty() {
return;
}
&& !map_mutated_vars.is_empty()
{
return;
}
span_lint_and_help(
cx,

View file

@ -1,11 +1,10 @@
use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_copy;
use clippy_utils::usage::mutated_variables;
use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext;
@ -45,30 +44,32 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
&& is_res_lang_ctor(cx, path_res(cx, expr), OptionSome)
&& let hir::ExprKind::Path(_) = args[0].kind
{
span_lint_and_sugg(
span_lint(
cx,
UNNECESSARY_FILTER_MAP,
expr.span,
format!("{name} is unnecessary"),
"try removing the filter_map",
String::new(),
Applicability::MaybeIncorrect,
String::from("this call to `.filter_map(..)` is unnecessary"),
);
return;
}
if name == "filter_map" {
"map(..)"
} else {
"map(..).next()"
}
if name == "filter_map" { "map" } else { "map(..).next()" }
} else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
match cx.typeck_results().expr_ty(body.value).kind() {
ty::Adt(adt, subst)
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) =>
{
if name == "filter_map" { "filter" } else { "find" }
if name == "filter_map" { "filter(..)" } else { "find(..)" }
},
_ => return,
}
} else {
return;
};
span_lint_and_sugg(
span_lint(
cx,
if name == "filter_map" {
UNNECESSARY_FILTER_MAP
@ -76,10 +77,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
UNNECESSARY_FIND_MAP
},
expr.span,
format!("this `.{name}` can be written more simply"),
"try instead",
sugg.to_string(),
Applicability::MaybeIncorrect,
format!("this `.{name}(..)` can be written more simply using `.{sugg}`"),
);
}
}

View file

@ -23,56 +23,56 @@ pub(super) fn check<'tcx>(
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
if is_option || is_result || is_bool {
if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind {
let body = cx.tcx.hir_body(body);
let body_expr = &body.value;
if (is_option || is_result || is_bool)
&& let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind
{
let body = cx.tcx.hir_body(body);
let body_expr = &body.value;
if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
return false;
}
if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
return false;
}
if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
let msg = if is_option {
"unnecessary closure used to substitute value for `Option::None`"
} else if is_result {
"unnecessary closure used to substitute value for `Result::Err`"
} else {
"unnecessary closure used with `bool::then`"
};
let applicability = if body
.params
.iter()
// bindings are checked to be unused above
.all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
&& matches!(
fn_decl.output,
FnRetTy::DefaultReturn(_)
| FnRetTy::Return(hir::Ty {
kind: hir::TyKind::Infer(()),
..
})
) {
Applicability::MachineApplicable
} else {
// replacing the lambda may break type inference
Applicability::MaybeIncorrect
};
if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
let msg = if is_option {
"unnecessary closure used to substitute value for `Option::None`"
} else if is_result {
"unnecessary closure used to substitute value for `Result::Err`"
} else {
"unnecessary closure used with `bool::then`"
};
let applicability = if body
.params
.iter()
// bindings are checked to be unused above
.all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
&& matches!(
fn_decl.output,
FnRetTy::DefaultReturn(_)
| FnRetTy::Return(hir::Ty {
kind: hir::TyKind::Infer(()),
..
})
) {
Applicability::MachineApplicable
} else {
// replacing the lambda may break type inference
Applicability::MaybeIncorrect
};
// This is a duplicate of what's happening in clippy_lints::methods::method_call,
// which isn't ideal, We want to get the method call span,
// but prefer to avoid changing the signature of the function itself.
if let hir::ExprKind::MethodCall(.., span) = expr.kind {
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
diag.span_suggestion_verbose(
span,
format!("use `{simplify_using}` instead"),
format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
applicability,
);
});
return true;
}
// This is a duplicate of what's happening in clippy_lints::methods::method_call,
// which isn't ideal, We want to get the method call span,
// but prefer to avoid changing the signature of the function itself.
if let hir::ExprKind::MethodCall(.., span) = expr.kind {
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
diag.span_suggestion_verbose(
span,
format!("use `{simplify_using}` instead"),
format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
applicability,
);
});
return true;
}
}
}

View file

@ -76,7 +76,7 @@ pub(super) fn check<'a>(
&& ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool))
&& let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l }
&& switch_to_eager_eval(cx, non_binding_location)
// xor, because if its both then thats a strange edge case and
// xor, because if its both then that's a strange edge case and
// we can just ignore it, since by default clippy will error on this
&& (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id))
&& !is_local_used(cx, non_binding_location, hir_id)
@ -92,7 +92,7 @@ pub(super) fn check<'a>(
// we may need to add parens around the suggestion
// in case the parent expression has additional method calls,
// since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)`
// being converted to `Some(5) == Some(5).then(|| 1)` isnt
// being converted to `Some(5) == Some(5).then(|| 1)` isn't
// the same thing
let inner_non_binding = Sugg::NonParen(Cow::Owned(format!(
@ -109,8 +109,8 @@ pub(super) fn check<'a>(
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) {
match parent_expr.kind {
ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(),
ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(),
ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_paren(),
ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_paren(),
_ => binop,
}
} else {

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;