Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
ab7e525929
377 changed files with 6426 additions and 3724 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ¶m, 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)))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue