Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
dc0bb69e66
1053 changed files with 12006 additions and 10203 deletions
|
|
@ -38,6 +38,7 @@ pub(super) fn check<'tcx>(
|
|||
&& ext_str.starts_with('.')
|
||||
&& (ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()))
|
||||
&& !ext_str.chars().skip(1).all(|c| c.is_ascii_digit())
|
||||
&& let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs()
|
||||
&& (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ pub(super) fn check(
|
|||
|
||||
if is_copy(cx, ty) {
|
||||
let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) {
|
||||
Some(Node::Expr(parent)) => match parent.kind {
|
||||
Node::Expr(parent) => match parent.kind {
|
||||
// &*x is a nop, &x.clone() is not
|
||||
ExprKind::AddrOf(..) => return,
|
||||
// (*x).func() is useless, x.clone().func() can work in case func borrows self
|
||||
|
|
@ -70,7 +70,7 @@ pub(super) fn check(
|
|||
_ => false,
|
||||
},
|
||||
// local binding capturing a reference
|
||||
Some(Node::Local(l)) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => {
|
||||
Node::Local(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => {
|
||||
return;
|
||||
},
|
||||
_ => false,
|
||||
|
|
|
|||
|
|
@ -28,104 +28,102 @@ pub(super) fn check<'tcx>(
|
|||
iter_expr: &'tcx Expr<'tcx>,
|
||||
call_span: Span,
|
||||
) {
|
||||
if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) {
|
||||
match parent {
|
||||
Node::Expr(parent) => {
|
||||
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
|
||||
match get_parent_node(cx.tcx, collect_expr.hir_id) {
|
||||
Node::Expr(parent) => {
|
||||
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
|
||||
|
||||
if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let name = name.ident.as_str();
|
||||
let collect_ty = cx.typeck_results().expr_ty(collect_expr);
|
||||
if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let name = name.ident.as_str();
|
||||
let collect_ty = cx.typeck_results().expr_ty(collect_expr);
|
||||
|
||||
let sugg: String = match name {
|
||||
"len" => {
|
||||
if let Some(adt) = collect_ty.ty_adt_def()
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(adt.did()),
|
||||
Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap)
|
||||
)
|
||||
{
|
||||
"count()".into()
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
"is_empty"
|
||||
if is_is_empty_sig(cx, parent.hir_id)
|
||||
&& iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
|
||||
let sugg: String = match name {
|
||||
"len" => {
|
||||
if let Some(adt) = collect_ty.ty_adt_def()
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(adt.did()),
|
||||
Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap)
|
||||
)
|
||||
{
|
||||
"next().is_none()".into()
|
||||
},
|
||||
"contains" => {
|
||||
if is_contains_sig(cx, parent.hir_id, iter_expr)
|
||||
&& let Some(arg) = args.first()
|
||||
{
|
||||
let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind {
|
||||
(arg.span, "")
|
||||
} else {
|
||||
(arg.span, "*")
|
||||
};
|
||||
let snip = snippet_with_applicability(cx, span, "??", &mut app);
|
||||
format!("any(|x| x == {prefix}{snip})")
|
||||
"count()".into()
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
"is_empty"
|
||||
if is_is_empty_sig(cx, parent.hir_id)
|
||||
&& iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
|
||||
{
|
||||
"next().is_none()".into()
|
||||
},
|
||||
"contains" => {
|
||||
if is_contains_sig(cx, parent.hir_id, iter_expr)
|
||||
&& let Some(arg) = args.first()
|
||||
{
|
||||
let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind {
|
||||
(arg.span, "")
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
(arg.span, "*")
|
||||
};
|
||||
let snip = snippet_with_applicability(cx, span, "??", &mut app);
|
||||
format!("any(|x| x == {prefix}{snip})")
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
call_span.with_hi(parent.span.hi()),
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_COLLECT,
|
||||
call_span.with_hi(parent.span.hi()),
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
"replace with",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
}
|
||||
},
|
||||
Node::Local(l) => {
|
||||
if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(collect_expr)
|
||||
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
|
||||
.into_iter()
|
||||
.any(|item| is_type_diagnostic_item(cx, ty, item))
|
||||
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
|
||||
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
|
||||
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
|
||||
&& let [iter_call] = &*iter_calls
|
||||
{
|
||||
let mut used_count_visitor = UsedCountVisitor { cx, id, count: 0 };
|
||||
walk_block(&mut used_count_visitor, block);
|
||||
if used_count_visitor.count > 1 {
|
||||
return;
|
||||
}
|
||||
},
|
||||
Node::Local(l) => {
|
||||
if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(collect_expr)
|
||||
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
|
||||
.into_iter()
|
||||
.any(|item| is_type_diagnostic_item(cx, ty, item))
|
||||
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
|
||||
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
|
||||
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
|
||||
&& let [iter_call] = &*iter_calls
|
||||
{
|
||||
let mut used_count_visitor = UsedCountVisitor { cx, id, count: 0 };
|
||||
walk_block(&mut used_count_visitor, block);
|
||||
if used_count_visitor.count > 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Suggest replacing iter_call with iter_replacement, and removing stmt
|
||||
let mut span = MultiSpan::from_span(name_span);
|
||||
span.push_span_label(iter_call.span, "the iterator could be used here instead");
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
super::NEEDLESS_COLLECT,
|
||||
collect_expr.hir_id,
|
||||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
|diag| {
|
||||
let iter_replacement =
|
||||
format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
|
||||
diag.multipart_suggestion(
|
||||
iter_call.get_suggestion_text(),
|
||||
vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
// Suggest replacing iter_call with iter_replacement, and removing stmt
|
||||
let mut span = MultiSpan::from_span(name_span);
|
||||
span.push_span_label(iter_call.span, "the iterator could be used here instead");
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
super::NEEDLESS_COLLECT,
|
||||
collect_expr.hir_id,
|
||||
span,
|
||||
NEEDLESS_COLLECT_MSG,
|
||||
|diag| {
|
||||
let iter_replacement =
|
||||
format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
|
||||
diag.multipart_suggestion(
|
||||
iter_call.get_suggestion_text(),
|
||||
vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
|
|
@ -10,17 +10,71 @@ use rustc_span::{sym, Span};
|
|||
|
||||
use super::UNNECESSARY_FALLIBLE_CONVERSIONS;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum SpansKind {
|
||||
TraitFn { trait_span: Span, fn_span: Span },
|
||||
Fn { fn_span: Span },
|
||||
}
|
||||
|
||||
/// What function is being called and whether that call is written as a method call or a function
|
||||
/// call
|
||||
#[derive(Copy, Clone)]
|
||||
#[expect(clippy::enum_variant_names)]
|
||||
enum FunctionKind {
|
||||
/// `T::try_from(U)`
|
||||
TryFromFunction,
|
||||
TryFromFunction(Option<SpansKind>),
|
||||
/// `t.try_into()`
|
||||
TryIntoMethod,
|
||||
/// `U::try_into(t)`
|
||||
TryIntoFunction,
|
||||
TryIntoFunction(Option<SpansKind>),
|
||||
}
|
||||
|
||||
impl FunctionKind {
|
||||
fn appl_sugg(&self, parent_unwrap_call: Option<Span>, primary_span: Span) -> (Applicability, Vec<(Span, String)>) {
|
||||
let Some(unwrap_span) = parent_unwrap_call else {
|
||||
return (Applicability::Unspecified, self.default_sugg(primary_span));
|
||||
};
|
||||
|
||||
match &self {
|
||||
FunctionKind::TryFromFunction(None) | FunctionKind::TryIntoFunction(None) => {
|
||||
(Applicability::Unspecified, self.default_sugg(primary_span))
|
||||
},
|
||||
_ => (
|
||||
Applicability::MachineApplicable,
|
||||
self.machine_applicable_sugg(primary_span, unwrap_span),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_sugg(&self, primary_span: Span) -> Vec<(Span, String)> {
|
||||
let replacement = match *self {
|
||||
FunctionKind::TryFromFunction(_) => "From::from",
|
||||
FunctionKind::TryIntoFunction(_) => "Into::into",
|
||||
FunctionKind::TryIntoMethod => "into",
|
||||
};
|
||||
|
||||
vec![(primary_span, String::from(replacement))]
|
||||
}
|
||||
|
||||
fn machine_applicable_sugg(&self, primary_span: Span, unwrap_span: Span) -> Vec<(Span, String)> {
|
||||
let (trait_name, fn_name) = match self {
|
||||
FunctionKind::TryFromFunction(_) => ("From".to_owned(), "from".to_owned()),
|
||||
FunctionKind::TryIntoFunction(_) | FunctionKind::TryIntoMethod => ("Into".to_owned(), "into".to_owned()),
|
||||
};
|
||||
|
||||
let mut sugg = match *self {
|
||||
FunctionKind::TryFromFunction(Some(spans)) | FunctionKind::TryIntoFunction(Some(spans)) => match spans {
|
||||
SpansKind::TraitFn { trait_span, fn_span } => vec![(trait_span, trait_name), (fn_span, fn_name)],
|
||||
SpansKind::Fn { fn_span } => vec![(fn_span, fn_name)],
|
||||
},
|
||||
FunctionKind::TryIntoMethod => vec![(primary_span, fn_name)],
|
||||
// Or the suggestion is not machine-applicable
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
sugg.push((unwrap_span, String::new()));
|
||||
sugg
|
||||
}
|
||||
}
|
||||
|
||||
fn check<'tcx>(
|
||||
|
|
@ -35,8 +89,8 @@ fn check<'tcx>(
|
|||
&& self_ty != other_ty
|
||||
&& let Some(self_ty) = self_ty.as_type()
|
||||
&& let Some(from_into_trait) = cx.tcx.get_diagnostic_item(match kind {
|
||||
FunctionKind::TryFromFunction => sym::From,
|
||||
FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction => sym::Into,
|
||||
FunctionKind::TryFromFunction(_) => sym::From,
|
||||
FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction(_) => sym::Into,
|
||||
})
|
||||
// If `T: TryFrom<U>` and `T: From<U>` both exist, then that means that the `TryFrom`
|
||||
// _must_ be from the blanket impl and cannot have been manually implemented
|
||||
|
|
@ -45,49 +99,37 @@ fn check<'tcx>(
|
|||
&& implements_trait(cx, self_ty, from_into_trait, &[other_ty])
|
||||
&& let Some(other_ty) = other_ty.as_type()
|
||||
{
|
||||
// Extend the span to include the unwrap/expect call:
|
||||
// `foo.try_into().expect("..")`
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
//
|
||||
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
|
||||
// so that can be machine-applicable
|
||||
let parent_unwrap_call = get_parent_expr(cx, expr).and_then(|parent| {
|
||||
if let ExprKind::MethodCall(path, .., span) = parent.kind
|
||||
&& let sym::unwrap | sym::expect = path.ident.name
|
||||
{
|
||||
Some(span)
|
||||
// include `.` before `unwrap`/`expect`
|
||||
Some(span.with_lo(expr.span.hi()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let (source_ty, target_ty, sugg, span, applicability) = match kind {
|
||||
FunctionKind::TryIntoMethod if let Some(unwrap_span) = parent_unwrap_call => {
|
||||
// Extend the span to include the unwrap/expect call:
|
||||
// `foo.try_into().expect("..")`
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
//
|
||||
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
|
||||
// so that can be machine-applicable
|
||||
|
||||
(
|
||||
self_ty,
|
||||
other_ty,
|
||||
"into()",
|
||||
primary_span.with_hi(unwrap_span.hi()),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
},
|
||||
FunctionKind::TryFromFunction => (
|
||||
other_ty,
|
||||
self_ty,
|
||||
"From::from",
|
||||
primary_span,
|
||||
Applicability::Unspecified,
|
||||
),
|
||||
FunctionKind::TryIntoFunction => (
|
||||
self_ty,
|
||||
other_ty,
|
||||
"Into::into",
|
||||
primary_span,
|
||||
Applicability::Unspecified,
|
||||
),
|
||||
FunctionKind::TryIntoMethod => (self_ty, other_ty, "into", primary_span, Applicability::Unspecified),
|
||||
// If there is an unwrap/expect call, extend the span to include the call
|
||||
let span = if let Some(unwrap_call) = parent_unwrap_call {
|
||||
primary_span.with_hi(unwrap_call.hi())
|
||||
} else {
|
||||
primary_span
|
||||
};
|
||||
|
||||
let (source_ty, target_ty) = match kind {
|
||||
FunctionKind::TryIntoMethod | FunctionKind::TryIntoFunction(_) => (self_ty, other_ty),
|
||||
FunctionKind::TryFromFunction(_) => (other_ty, self_ty),
|
||||
};
|
||||
|
||||
let (applicability, sugg) = kind.appl_sugg(parent_unwrap_call, primary_span);
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNNECESSARY_FALLIBLE_CONVERSIONS,
|
||||
|
|
@ -97,7 +139,7 @@ fn check<'tcx>(
|
|||
with_forced_trimmed_paths!({
|
||||
diag.note(format!("converting `{source_ty}` to `{target_ty}` cannot fail"));
|
||||
});
|
||||
diag.span_suggestion(span, "use", sugg, applicability);
|
||||
diag.multipart_suggestion("use", sugg, applicability);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -125,13 +167,30 @@ pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Exp
|
|||
&& let Some(item_def_id) = cx.qpath_res(qpath, callee.hir_id).opt_def_id()
|
||||
&& let Some(trait_def_id) = cx.tcx.trait_of_item(item_def_id)
|
||||
{
|
||||
let qpath_spans = match qpath {
|
||||
QPath::Resolved(_, path) => {
|
||||
if let [trait_seg, fn_seg] = path.segments {
|
||||
Some(SpansKind::TraitFn {
|
||||
trait_span: trait_seg.ident.span,
|
||||
fn_span: fn_seg.ident.span,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
QPath::TypeRelative(_, seg) => Some(SpansKind::Fn {
|
||||
fn_span: seg.ident.span,
|
||||
}),
|
||||
QPath::LangItem(_, _) => unreachable!("`TryFrom` and `TryInto` are not lang items"),
|
||||
};
|
||||
|
||||
check(
|
||||
cx,
|
||||
expr,
|
||||
cx.typeck_results().node_args(callee.hir_id),
|
||||
match cx.tcx.get_diagnostic_name(trait_def_id) {
|
||||
Some(sym::TryFrom) => FunctionKind::TryFromFunction,
|
||||
Some(sym::TryInto) => FunctionKind::TryIntoFunction,
|
||||
Some(sym::TryFrom) => FunctionKind::TryFromFunction(qpath_spans),
|
||||
Some(sym::TryInto) => FunctionKind::TryIntoFunction(qpath_spans),
|
||||
_ => return,
|
||||
},
|
||||
callee.span,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_mid_ty_refs};
|
||||
use clippy_utils::ty::{
|
||||
get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs,
|
||||
};
|
||||
use clippy_utils::visitors::find_all_ret_expressions;
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -16,7 +18,8 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
||||
use rustc_middle::ty::{
|
||||
self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty,
|
||||
self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ImplPolarity, ParamTy, ProjectionPredicate,
|
||||
TraitPredicate, Ty,
|
||||
};
|
||||
use rustc_span::{sym, Symbol};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
|
|
@ -53,6 +56,8 @@ pub fn check<'tcx>(
|
|||
}
|
||||
check_other_call_arg(cx, expr, method_name, receiver);
|
||||
}
|
||||
} else {
|
||||
check_borrow_predicate(cx, expr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -590,3 +595,92 @@ fn is_to_string_on_string_like<'a>(
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym::HashSet)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::HashMap)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::BTreeMap)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::BTreeSet)
|
||||
}
|
||||
|
||||
fn is_str_and_string(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool {
|
||||
original_arg_ty.is_str() && is_type_lang_item(cx, arg_ty, LangItem::String)
|
||||
}
|
||||
|
||||
fn is_slice_and_vec(cx: &LateContext<'_>, arg_ty: Ty<'_>, original_arg_ty: Ty<'_>) -> bool {
|
||||
(original_arg_ty.is_slice() || original_arg_ty.is_array() || original_arg_ty.is_array_slice())
|
||||
&& is_type_diagnostic_item(cx, arg_ty, sym::Vec)
|
||||
}
|
||||
|
||||
// This function will check the following:
|
||||
// 1. The argument is a non-mutable reference.
|
||||
// 2. It calls `to_owned()`, `to_string()` or `to_vec()`.
|
||||
// 3. That the method is called on `String` or on `Vec` (only types supported for the moment).
|
||||
fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx>) {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = arg.kind
|
||||
&& let ExprKind::MethodCall(method_path, caller, &[], _) = expr.kind
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& let method_name = method_path.ident.name.as_str()
|
||||
&& match method_name {
|
||||
"to_owned" => cx.tcx.is_diagnostic_item(sym::to_owned_method, method_def_id),
|
||||
"to_string" => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id),
|
||||
"to_vec" => cx
|
||||
.tcx
|
||||
.impl_of_method(method_def_id)
|
||||
.filter(|&impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice())
|
||||
.is_some(),
|
||||
_ => false,
|
||||
}
|
||||
&& let original_arg_ty = cx.typeck_results().node_type(caller.hir_id).peel_refs()
|
||||
&& let arg_ty = cx.typeck_results().expr_ty(arg)
|
||||
&& let ty::Ref(_, arg_ty, Mutability::Not) = arg_ty.kind()
|
||||
// FIXME: try to fix `can_change_type` to make it work in this case.
|
||||
// && can_change_type(cx, caller, *arg_ty)
|
||||
&& let arg_ty = arg_ty.peel_refs()
|
||||
// For now we limit this lint to `String` and `Vec`.
|
||||
&& (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty))
|
||||
&& let Some(snippet) = snippet_opt(cx, caller.span)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_TO_OWNED,
|
||||
arg.span,
|
||||
&format!("unnecessary use of `{method_name}`"),
|
||||
"replace it with",
|
||||
if original_arg_ty.is_array() {
|
||||
format!("{snippet}.as_slice()")
|
||||
} else {
|
||||
snippet
|
||||
},
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// In std "map types", the getters all expect a `Borrow<Key>` generic argument. So in here, we
|
||||
// check that:
|
||||
// 1. This is a method with only one argument that doesn't come from a trait.
|
||||
// 2. That it has `Borrow` in its generic predicates.
|
||||
// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, BTreeSet`, `BTreeMap`).
|
||||
fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& cx.tcx.trait_of_item(method_def_id).is_none()
|
||||
&& let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow)
|
||||
&& cx.tcx.predicates_of(method_def_id).predicates.iter().any(|(pred, _)| {
|
||||
if let ClauseKind::Trait(trait_pred) = pred.kind().skip_binder()
|
||||
&& trait_pred.polarity == ImplPolarity::Positive
|
||||
&& trait_pred.trait_ref.def_id == borrow_id
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
&& let caller_ty = cx.typeck_results().expr_ty(caller)
|
||||
// For now we limit it to "map types".
|
||||
&& is_a_std_map_type(cx, caller_ty)
|
||||
{
|
||||
check_if_applicable_to_argument(cx, &arg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue