Skip to content

Commit 2ec8018

Browse files
committed
Move some functions into clippy_utils::ty
1 parent a9fd158 commit 2ec8018

File tree

4 files changed

+310
-283
lines changed

4 files changed

+310
-283
lines changed

clippy_lints/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ extern crate rustc_hir;
4242
extern crate rustc_hir_analysis;
4343
extern crate rustc_hir_pretty;
4444
extern crate rustc_hir_typeck;
45-
extern crate rustc_index;
4645
extern crate rustc_infer;
4746
extern crate rustc_lexer;
4847
extern crate rustc_lint;

clippy_lints/src/methods/unnecessary_to_owned.rs

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
44
use clippy_utils::msrvs::{self, Msrv};
55
use clippy_utils::source::{SpanRangeExt, snippet};
66
use clippy_utils::ty::{
7-
get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs,
7+
get_callee_generic_args_and_args, get_iterator_item_ty, implements_trait, is_copy, is_to_string_on_string_like,
8+
is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs,
89
};
910
use clippy_utils::visitors::find_all_ret_expressions;
1011
use clippy_utils::{
@@ -19,7 +20,7 @@ use rustc_lint::LateContext;
1920
use rustc_middle::mir::Mutability;
2021
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
2122
use rustc_middle::ty::{
22-
self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty,
23+
self, ClauseKind, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, TraitPredicate, Ty,
2324
};
2425
use rustc_span::Symbol;
2526
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -444,33 +445,6 @@ fn skip_addr_of_ancestors<'tcx>(
444445
None
445446
}
446447

447-
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
448-
/// `GenericArgs`, and arguments.
449-
fn get_callee_generic_args_and_args<'tcx>(
450-
cx: &LateContext<'tcx>,
451-
expr: &'tcx Expr<'tcx>,
452-
) -> Option<(
453-
DefId,
454-
GenericArgsRef<'tcx>,
455-
Option<&'tcx Expr<'tcx>>,
456-
&'tcx [Expr<'tcx>],
457-
)> {
458-
if let ExprKind::Call(callee, args) = expr.kind
459-
&& let callee_ty = cx.typeck_results().expr_ty(callee)
460-
&& let ty::FnDef(callee_def_id, _) = callee_ty.kind()
461-
{
462-
let generic_args = cx.typeck_results().node_args(callee.hir_id);
463-
return Some((*callee_def_id, generic_args, None, args));
464-
}
465-
if let ExprKind::MethodCall(_, recv, args, _) = expr.kind
466-
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
467-
{
468-
let generic_args = cx.typeck_results().node_args(expr.hir_id);
469-
return Some((method_def_id, generic_args, Some(recv), args));
470-
}
471-
None
472-
}
473-
474448
/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
475449
fn get_input_traits_and_projections<'tcx>(
476450
cx: &LateContext<'tcx>,
@@ -624,40 +598,14 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
624598
fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
625599
is_cow_into_owned(cx, method_name, method_def_id)
626600
|| (method_name != sym::to_string && is_clone_like(cx, method_name, method_def_id))
627-
|| is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
601+
|| is_to_string_on_string_like(cx, call_expr, method_def_id)
628602
}
629603

630604
/// Returns true if the named method is `Cow::into_owned`.
631605
fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
632606
method_name == sym::into_owned && is_diag_item_method(cx, method_def_id, sym::Cow)
633607
}
634608

635-
/// Returns true if the named method is `ToString::to_string` and it's called on a type that
636-
/// is string-like i.e. implements `AsRef<str>` or `Deref<Target = str>`.
637-
fn is_to_string_on_string_like<'a>(
638-
cx: &LateContext<'_>,
639-
call_expr: &'a Expr<'a>,
640-
method_name: Symbol,
641-
method_def_id: DefId,
642-
) -> bool {
643-
if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) {
644-
return false;
645-
}
646-
647-
if let Some(args) = cx.typeck_results().node_args_opt(call_expr.hir_id)
648-
&& let [generic_arg] = args.as_slice()
649-
&& let GenericArgKind::Type(ty) = generic_arg.kind()
650-
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
651-
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
652-
&& (cx.get_associated_type(ty, deref_trait_id, sym::Target) == Some(cx.tcx.types.str_)
653-
|| implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()]))
654-
{
655-
true
656-
} else {
657-
false
658-
}
659-
}
660-
661609
fn std_map_key<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
662610
match ty.kind() {
663611
ty::Adt(adt, args)
Lines changed: 20 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,18 @@
11
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir, expr_local, local_assignments, used_exactly_once};
4-
use clippy_utils::msrvs::{self, Msrv};
4+
use clippy_utils::msrvs::Msrv;
55
use clippy_utils::source::snippet_with_context;
6-
use clippy_utils::ty::{implements_trait, is_copy};
6+
use clippy_utils::ty::{build_check_predicates_with_new_ty_closure, implements_trait, is_copy};
77
use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_n_hir_expr_refs};
88
use rustc_errors::Applicability;
99
use rustc_hir::def::{DefKind, Res};
1010
use rustc_hir::def_id::{DefId, LocalDefId};
11-
use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
12-
use rustc_index::bit_set::DenseBitSet;
13-
use rustc_infer::infer::TyCtxtInferExt;
11+
use rustc_hir::{Body, Expr, ExprKind, Path, QPath};
1412
use rustc_lint::{LateContext, LateLintPass};
1513
use rustc_middle::mir::{Rvalue, StatementKind};
16-
use rustc_middle::ty::{
17-
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty,
18-
};
14+
use rustc_middle::ty::{self, ParamTy};
1915
use rustc_session::impl_lint_pass;
20-
use rustc_span::symbol::sym;
21-
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
22-
use rustc_trait_selection::traits::{Obligation, ObligationCause};
23-
use std::collections::VecDeque;
2416

2517
declare_clippy_lint! {
2618
/// ### What it does
@@ -161,7 +153,7 @@ fn path_has_args(p: &QPath<'_>) -> bool {
161153
/// - `Copy` itself, or
162154
/// - the only use of a mutable reference, or
163155
/// - not a variable (created by a function call)
164-
#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
156+
#[expect(clippy::too_many_arguments)]
165157
fn needless_borrow_count<'tcx>(
166158
cx: &LateContext<'tcx>,
167159
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
@@ -172,122 +164,33 @@ fn needless_borrow_count<'tcx>(
172164
mut expr: &Expr<'tcx>,
173165
msrv: Msrv,
174166
) -> usize {
175-
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
176-
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
177-
let meta_sized_trait_def_id = cx.tcx.lang_items().meta_sized_trait();
178-
let drop_trait_def_id = cx.tcx.lang_items().drop_trait();
179-
180-
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder();
181-
let predicates = cx.tcx.param_env(fn_id).caller_bounds();
182-
let projection_predicates = predicates
183-
.iter()
184-
.filter_map(|predicate| {
185-
if let ClauseKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
186-
Some(projection_predicate)
187-
} else {
188-
None
189-
}
190-
})
191-
.collect::<Vec<_>>();
192-
193-
let mut trait_with_ref_mut_self_method = false;
194-
195-
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
196-
if predicates
197-
.iter()
198-
.filter_map(|predicate| {
199-
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
200-
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
201-
{
202-
Some(trait_predicate.trait_ref.def_id)
203-
} else {
204-
None
205-
}
206-
})
207-
.inspect(|trait_def_id| {
208-
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
209-
})
210-
.all(|trait_def_id| {
211-
Some(trait_def_id) == destruct_trait_def_id
212-
|| Some(trait_def_id) == sized_trait_def_id
213-
|| Some(trait_def_id) == meta_sized_trait_def_id
214-
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
215-
})
216-
{
217-
return 0;
218-
}
219-
220-
// See:
221-
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
222-
// - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
223-
if projection_predicates
224-
.iter()
225-
.any(|projection_predicate| is_mixed_projection_predicate(cx, fn_id, projection_predicate))
226-
{
167+
let Some(mut check_referent_ty) =
168+
build_check_predicates_with_new_ty_closure(cx, fn_id, callee_args, arg_index, param_ty, true, msrv)
169+
else {
227170
return 0;
228-
}
171+
};
229172

230-
// `args_with_referent_ty` can be constructed outside of `check_referent` because the same
231-
// elements are modified each time `check_referent` is called.
232-
let mut args_with_referent_ty = callee_args.to_vec();
173+
let drop_trait_def_id = cx.tcx.lang_items().drop_trait();
233174

234-
let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| {
235-
if let ExprKind::Field(base, _) = &referent.kind
236-
&& let base_ty = cx.typeck_results().expr_ty(base)
237-
&& drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[]))
238-
{
239-
return false;
175+
let mut count = 0;
176+
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
177+
if let ExprKind::Field(base, _) = &referent.kind {
178+
let base_ty = cx.typeck_results().expr_ty(base);
179+
if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) {
180+
break;
181+
}
240182
}
241183

242184
let referent_ty = cx.typeck_results().expr_ty(referent);
243185

244186
if !(is_copy(cx, referent_ty)
245-
|| referent_ty.is_ref() && referent_used_exactly_once(cx, possible_borrowers, reference)
187+
|| referent_ty.is_ref() && referent_used_exactly_once(cx, possible_borrowers, expr)
246188
|| matches!(referent.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)))
247189
{
248-
return false;
249-
}
250-
251-
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
252-
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
253-
return false;
254-
}
255-
256-
if !replace_types(
257-
cx,
258-
param_ty,
259-
referent_ty,
260-
fn_sig,
261-
arg_index,
262-
&projection_predicates,
263-
&mut args_with_referent_ty,
264-
) {
265-
return false;
190+
break;
266191
}
267192

268-
predicates.iter().all(|predicate| {
269-
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
270-
&& cx
271-
.tcx
272-
.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
273-
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
274-
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].kind()
275-
&& ty.is_array()
276-
&& !msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR)
277-
{
278-
return false;
279-
}
280-
281-
let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, &args_with_referent_ty[..]);
282-
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
283-
let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
284-
infcx.predicate_must_hold_modulo_regions(&obligation)
285-
})
286-
};
287-
288-
let mut count = 0;
289-
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
290-
if !check_reference_and_referent(expr, referent) {
193+
if !check_referent_ty(referent_ty) {
291194
break;
292195
}
293196
expr = referent;
@@ -296,56 +199,6 @@ fn needless_borrow_count<'tcx>(
296199
count
297200
}
298201

299-
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
300-
cx.tcx
301-
.associated_items(trait_def_id)
302-
.in_definition_order()
303-
.any(|assoc_item| {
304-
if assoc_item.is_method() {
305-
let self_ty = cx
306-
.tcx
307-
.fn_sig(assoc_item.def_id)
308-
.instantiate_identity()
309-
.skip_binder()
310-
.inputs()[0];
311-
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
312-
} else {
313-
false
314-
}
315-
})
316-
}
317-
318-
fn is_mixed_projection_predicate<'tcx>(
319-
cx: &LateContext<'tcx>,
320-
callee_def_id: DefId,
321-
projection_predicate: &ProjectionPredicate<'tcx>,
322-
) -> bool {
323-
let generics = cx.tcx.generics_of(callee_def_id);
324-
// The predicate requires the projected type to equal a type parameter from the parent context.
325-
if let Some(term_ty) = projection_predicate.term.as_type()
326-
&& let ty::Param(term_param_ty) = term_ty.kind()
327-
&& (term_param_ty.index as usize) < generics.parent_count
328-
{
329-
// The inner-most self type is a type parameter from the current function.
330-
let mut projection_term = projection_predicate.projection_term;
331-
loop {
332-
match *projection_term.self_ty().kind() {
333-
ty::Alias(ty::Projection, inner_projection_ty) => {
334-
projection_term = inner_projection_ty.into();
335-
},
336-
ty::Param(param_ty) => {
337-
return (param_ty.index as usize) >= generics.parent_count;
338-
},
339-
_ => {
340-
return false;
341-
},
342-
}
343-
}
344-
} else {
345-
false
346-
}
347-
}
348-
349202
fn referent_used_exactly_once<'tcx>(
350203
cx: &LateContext<'tcx>,
351204
possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
@@ -376,60 +229,3 @@ fn referent_used_exactly_once<'tcx>(
376229
false
377230
}
378231
}
379-
380-
// Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
381-
// projected type that is a type parameter. Returns `false` if replacing the types would have an
382-
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
383-
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
384-
fn replace_types<'tcx>(
385-
cx: &LateContext<'tcx>,
386-
param_ty: ParamTy,
387-
new_ty: Ty<'tcx>,
388-
fn_sig: FnSig<'tcx>,
389-
arg_index: usize,
390-
projection_predicates: &[ProjectionPredicate<'tcx>],
391-
args: &mut [GenericArg<'tcx>],
392-
) -> bool {
393-
let mut replaced = DenseBitSet::new_empty(args.len());
394-
395-
let mut deque = VecDeque::with_capacity(args.len());
396-
deque.push_back((param_ty, new_ty));
397-
398-
while let Some((param_ty, new_ty)) = deque.pop_front() {
399-
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
400-
if !fn_sig
401-
.inputs_and_output
402-
.iter()
403-
.enumerate()
404-
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
405-
{
406-
return false;
407-
}
408-
409-
args[param_ty.index as usize] = GenericArg::from(new_ty);
410-
411-
// The `replaced.insert(...)` check provides some protection against infinite loops.
412-
if replaced.insert(param_ty.index) {
413-
for projection_predicate in projection_predicates {
414-
if projection_predicate.projection_term.self_ty() == param_ty.to_ty(cx.tcx)
415-
&& let Some(term_ty) = projection_predicate.term.as_type()
416-
&& let ty::Param(term_param_ty) = term_ty.kind()
417-
{
418-
let projection = projection_predicate
419-
.projection_term
420-
.with_replaced_self_ty(cx.tcx, new_ty)
421-
.expect_ty(cx.tcx)
422-
.to_ty(cx.tcx);
423-
424-
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), projection)
425-
&& args[term_param_ty.index as usize] != GenericArg::from(projected_ty)
426-
{
427-
deque.push_back((*term_param_ty, projected_ty));
428-
}
429-
}
430-
}
431-
}
432-
}
433-
434-
true
435-
}

0 commit comments

Comments
 (0)