]> git.lizzy.rs Git - rust.git/blob - clippy_utils/src/ty.rs
2ceda3511fe44642b1a822dcddd5eb8857e8da2b
[rust.git] / clippy_utils / src / ty.rs
1 //! Util methods for [`rustc_middle::ty`]
2
3 #![allow(clippy::module_name_repetitions)]
4
5 use core::ops::ControlFlow;
6 use rustc_ast::ast::Mutability;
7 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8 use rustc_hir as hir;
9 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
10 use rustc_hir::def_id::DefId;
11 use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
12 use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}};
13 use rustc_lint::LateContext;
14 use rustc_middle::mir::interpret::{ConstValue, Scalar};
15 use rustc_middle::ty::{
16     self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
17     ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
18     VariantDef, VariantDiscr,
19 };
20 use rustc_middle::ty::{GenericArg, GenericArgKind};
21 use rustc_span::symbol::Ident;
22 use rustc_span::{sym, Span, Symbol, DUMMY_SP};
23 use rustc_target::abi::{Size, VariantIdx};
24 use rustc_trait_selection::infer::InferCtxtExt;
25 use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
26 use std::iter;
27
28 use crate::{match_def_path, path_res, paths};
29
30 // Checks if the given type implements copy.
31 pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
32     ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
33 }
34
35 /// This checks whether a given type is known to implement Debug.
36 pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
37     cx.tcx
38         .get_diagnostic_item(sym::Debug)
39         .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
40 }
41
42 /// Checks whether a type can be partially moved.
43 pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
44     if has_drop(cx, ty) || is_copy(cx, ty) {
45         return false;
46     }
47     match ty.kind() {
48         ty::Param(_) => false,
49         ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
50         _ => true,
51     }
52 }
53
54 /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
55 /// constructor.
56 pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
57     ty.walk().any(|inner| match inner.unpack() {
58         GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
59         GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
60     })
61 }
62
63 /// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
64 /// constructor of the same type.
65 ///
66 /// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
67 /// will also return `true`.
68 pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
69     ty.walk().any(|inner| match inner.unpack() {
70         GenericArgKind::Type(inner_ty) => {
71             if inner_ty == needle {
72                 return true;
73             }
74
75             if inner_ty.ty_adt_def() == needle.ty_adt_def() {
76                 return true;
77             }
78
79             if let ty::Opaque(def_id, _) = *inner_ty.kind() {
80                 for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
81                     match predicate.kind().skip_binder() {
82                         // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
83                         // and check substituions to find `U`.
84                         ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
85                             if trait_predicate
86                                 .trait_ref
87                                 .substs
88                                 .types()
89                                 .skip(1) // Skip the implicit `Self` generic parameter
90                                 .any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle))
91                             {
92                                 return true;
93                             }
94                         },
95                         // For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
96                         // so we check the term for `U`.
97                         ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => {
98                             if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
99                                 if contains_ty_adt_constructor_opaque(cx, ty, needle) {
100                                     return true;
101                                 }
102                             };
103                         },
104                         _ => (),
105                     }
106                 }
107             }
108
109             false
110         },
111         GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
112     })
113 }
114
115 /// Resolves `<T as Iterator>::Item` for `T`
116 /// Do not invoke without first verifying that the type implements `Iterator`
117 pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
118     cx.tcx
119         .get_diagnostic_item(sym::Iterator)
120         .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item"))
121 }
122
123 /// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
124 /// implements a trait marked with a diagnostic item use [`implements_trait`].
125 ///
126 /// For a further exploitation what diagnostic items are see [diagnostic items] in
127 /// rustc-dev-guide.
128 ///
129 /// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
130 pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
131     match ty.kind() {
132         ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
133         _ => None,
134     }
135 }
136
137 /// Returns true if ty has `iter` or `iter_mut` methods
138 pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
139     // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
140     // exists and has the desired signature. Unfortunately FnCtxt is not exported
141     // so we can't use its `lookup_method` method.
142     let into_iter_collections: &[Symbol] = &[
143         sym::Vec,
144         sym::Option,
145         sym::Result,
146         sym::BTreeMap,
147         sym::BTreeSet,
148         sym::VecDeque,
149         sym::LinkedList,
150         sym::BinaryHeap,
151         sym::HashSet,
152         sym::HashMap,
153         sym::PathBuf,
154         sym::Path,
155         sym::Receiver,
156     ];
157
158     let ty_to_check = match probably_ref_ty.kind() {
159         ty::Ref(_, ty_to_check, _) => *ty_to_check,
160         _ => probably_ref_ty,
161     };
162
163     let def_id = match ty_to_check.kind() {
164         ty::Array(..) => return Some(sym::array),
165         ty::Slice(..) => return Some(sym::slice),
166         ty::Adt(adt, _) => adt.did(),
167         _ => return None,
168     };
169
170     for &name in into_iter_collections {
171         if cx.tcx.is_diagnostic_item(name, def_id) {
172             return Some(cx.tcx.item_name(def_id));
173         }
174     }
175     None
176 }
177
178 /// Checks whether a type implements a trait.
179 /// The function returns false in case the type contains an inference variable.
180 ///
181 /// See:
182 /// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
183 /// * [Common tools for writing lints] for an example how to use this function and other options.
184 ///
185 /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
186 pub fn implements_trait<'tcx>(
187     cx: &LateContext<'tcx>,
188     ty: Ty<'tcx>,
189     trait_id: DefId,
190     ty_params: &[GenericArg<'tcx>],
191 ) -> bool {
192     implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg)))
193 }
194
195 /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
196 pub fn implements_trait_with_env<'tcx>(
197     tcx: TyCtxt<'tcx>,
198     param_env: ParamEnv<'tcx>,
199     ty: Ty<'tcx>,
200     trait_id: DefId,
201     ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
202 ) -> bool {
203     // Clippy shouldn't have infer types
204     assert!(!ty.needs_infer());
205
206     let ty = tcx.erase_regions(ty);
207     if ty.has_escaping_bound_vars() {
208         return false;
209     }
210     let infcx = tcx.infer_ctxt().build();
211     let orig = TypeVariableOrigin {
212         kind: TypeVariableOriginKind::MiscVariable,
213         span: DUMMY_SP,
214     };
215     let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())));
216     infcx
217         .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
218         .must_apply_modulo_regions()
219 }
220
221 /// Checks whether this type implements `Drop`.
222 pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
223     match ty.ty_adt_def() {
224         Some(def) => def.has_dtor(cx.tcx),
225         None => false,
226     }
227 }
228
229 // Returns whether the type has #[must_use] attribute
230 pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
231     match ty.kind() {
232         ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
233         ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
234         ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
235             // for the Array case we don't need to care for the len == 0 case
236             // because we don't want to lint functions returning empty arrays
237             is_must_use_ty(cx, *ty)
238         },
239         ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
240         ty::Opaque(def_id, _) => {
241             for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
242                 if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() {
243                     if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
244                         return true;
245                     }
246                 }
247             }
248             false
249         },
250         ty::Dynamic(binder, _, _) => {
251             for predicate in binder.iter() {
252                 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
253                     if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
254                         return true;
255                     }
256                 }
257             }
258             false
259         },
260         _ => false,
261     }
262 }
263
264 // FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
265 // this function can be removed once the `normalize` method does not panic when normalization does
266 // not succeed
267 /// Checks if `Ty` is normalizable. This function is useful
268 /// to avoid crashes on `layout_of`.
269 pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
270     is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
271 }
272
273 fn is_normalizable_helper<'tcx>(
274     cx: &LateContext<'tcx>,
275     param_env: ty::ParamEnv<'tcx>,
276     ty: Ty<'tcx>,
277     cache: &mut FxHashMap<Ty<'tcx>, bool>,
278 ) -> bool {
279     if let Some(&cached_result) = cache.get(&ty) {
280         return cached_result;
281     }
282     // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
283     cache.insert(ty, false);
284     let infcx = cx.tcx.infer_ctxt().build();
285     let cause = rustc_middle::traits::ObligationCause::dummy();
286     let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
287         match ty.kind() {
288             ty::Adt(def, substs) => def.variants().iter().all(|variant| {
289                 variant
290                     .fields
291                     .iter()
292                     .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
293             }),
294             _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
295                 GenericArgKind::Type(inner_ty) if inner_ty != ty => {
296                     is_normalizable_helper(cx, param_env, inner_ty, cache)
297                 },
298                 _ => true, // if inner_ty == ty, we've already checked it
299             }),
300         }
301     } else {
302         false
303     };
304     cache.insert(ty, result);
305     result
306 }
307
308 /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
309 /// integer or floating-point number type). For checking aggregation of primitive types (e.g.
310 /// tuples and slices of primitive type) see `is_recursively_primitive_type`
311 pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
312     matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
313 }
314
315 /// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
316 /// floating-point number type, a `str`, or an array, slice, or tuple of those types).
317 pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
318     match *ty.kind() {
319         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
320         ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
321         ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
322         ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
323         _ => false,
324     }
325 }
326
327 /// Checks if the type is a reference equals to a diagnostic item
328 pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
329     match ty.kind() {
330         ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
331             ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
332             _ => false,
333         },
334         _ => false,
335     }
336 }
337
338 /// Checks if the type is equal to a diagnostic item. To check if a type implements a
339 /// trait marked with a diagnostic item use [`implements_trait`].
340 ///
341 /// For a further exploitation what diagnostic items are see [diagnostic items] in
342 /// rustc-dev-guide.
343 ///
344 /// ---
345 ///
346 /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
347 ///
348 /// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
349 pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
350     match ty.kind() {
351         ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
352         _ => false,
353     }
354 }
355
356 /// Checks if the type is equal to a lang item.
357 ///
358 /// Returns `false` if the `LangItem` is not defined.
359 pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
360     match ty.kind() {
361         ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()),
362         _ => false,
363     }
364 }
365
366 /// Return `true` if the passed `typ` is `isize` or `usize`.
367 pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
368     matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
369 }
370
371 /// Checks if type is struct, enum or union type with the given def path.
372 ///
373 /// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
374 /// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
375 pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
376     match ty.kind() {
377         ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
378         _ => false,
379     }
380 }
381
382 /// Checks if the drop order for a type matters. Some std types implement drop solely to
383 /// deallocate memory. For these types, and composites containing them, changing the drop order
384 /// won't result in any observable side effects.
385 pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
386     fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
387         if !seen.insert(ty) {
388             return false;
389         }
390         if !ty.has_significant_drop(cx.tcx, cx.param_env) {
391             false
392         }
393         // Check for std types which implement drop, but only for memory allocation.
394         else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
395             || matches!(
396                 get_type_diagnostic_name(cx, ty),
397                 Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
398             )
399             || match_type(cx, ty, &paths::WEAK_RC)
400             || match_type(cx, ty, &paths::WEAK_ARC)
401         {
402             // Check all of the generic arguments.
403             if let ty::Adt(_, subs) = ty.kind() {
404                 subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
405             } else {
406                 true
407             }
408         } else if !cx
409             .tcx
410             .lang_items()
411             .drop_trait()
412             .map_or(false, |id| implements_trait(cx, ty, id, &[]))
413         {
414             // This type doesn't implement drop, so no side effects here.
415             // Check if any component type has any.
416             match ty.kind() {
417                 ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
418                 ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
419                 ty::Adt(adt, subs) => adt
420                     .all_fields()
421                     .map(|f| f.ty(cx.tcx, subs))
422                     .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
423                 _ => true,
424             }
425         } else {
426             true
427         }
428     }
429
430     needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
431 }
432
433 /// Peels off all references on the type. Returns the underlying type and the number of references
434 /// removed.
435 pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
436     fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
437         if let ty::Ref(_, ty, _) = ty.kind() {
438             peel(*ty, count + 1)
439         } else {
440             (ty, count)
441         }
442     }
443     peel(ty, 0)
444 }
445
446 /// Peels off all references on the type. Returns the underlying type, the number of references
447 /// removed, and whether the pointer is ultimately mutable or not.
448 pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
449     fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
450         match ty.kind() {
451             ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
452             ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
453             _ => (ty, count, mutability),
454         }
455     }
456     f(ty, 0, Mutability::Mut)
457 }
458
459 /// Returns `true` if the given type is an `unsafe` function.
460 pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
461     match ty.kind() {
462         ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
463         _ => false,
464     }
465 }
466
467 /// Returns the base type for HIR references and pointers.
468 pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
469     match ty.kind {
470         TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
471         _ => ty,
472     }
473 }
474
475 /// Returns the base type for references and raw pointers, and count reference
476 /// depth.
477 pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
478     fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
479         match ty.kind() {
480             ty::Ref(_, ty, _) => inner(*ty, depth + 1),
481             _ => (ty, depth),
482         }
483     }
484     inner(ty, 0)
485 }
486
487 /// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
488 /// otherwise returns `false`
489 pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
490     match (&a.kind(), &b.kind()) {
491         (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
492             if did_a != did_b {
493                 return false;
494             }
495
496             substs_a
497                 .iter()
498                 .zip(substs_b.iter())
499                 .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
500                     (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
501                     (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
502                         same_type_and_consts(type_a, type_b)
503                     },
504                     _ => true,
505                 })
506         },
507         _ => a == b,
508     }
509 }
510
511 /// Checks if a given type looks safe to be uninitialized.
512 pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
513     match *ty.kind() {
514         ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
515         ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
516         ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
517         _ => false,
518     }
519 }
520
521 /// Gets an iterator over all predicates which apply to the given item.
522 pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
523     let mut next_id = Some(id);
524     iter::from_fn(move || {
525         next_id.take().map(|id| {
526             let preds = tcx.predicates_of(id);
527             next_id = preds.parent;
528             preds.predicates.iter()
529         })
530     })
531     .flatten()
532 }
533
534 /// A signature for a function like type.
535 #[derive(Clone, Copy)]
536 pub enum ExprFnSig<'tcx> {
537     Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
538     Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
539     Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
540 }
541 impl<'tcx> ExprFnSig<'tcx> {
542     /// Gets the argument type at the given offset. This will return `None` when the index is out of
543     /// bounds only for variadic functions, otherwise this will panic.
544     pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
545         match self {
546             Self::Sig(sig, _) => {
547                 if sig.c_variadic() {
548                     sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
549                 } else {
550                     Some(sig.input(i))
551                 }
552             },
553             Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
554             Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
555         }
556     }
557
558     /// Gets the argument type at the given offset. For closures this will also get the type as
559     /// written. This will return `None` when the index is out of bounds only for variadic
560     /// functions, otherwise this will panic.
561     pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
562         match self {
563             Self::Sig(sig, _) => {
564                 if sig.c_variadic() {
565                     sig.inputs()
566                         .map_bound(|inputs| inputs.get(i).copied())
567                         .transpose()
568                         .map(|arg| (None, arg))
569                 } else {
570                     Some((None, sig.input(i)))
571                 }
572             },
573             Self::Closure(decl, sig) => Some((
574                 decl.and_then(|decl| decl.inputs.get(i)),
575                 sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
576             )),
577             Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
578         }
579     }
580
581     /// Gets the result type, if one could be found. Note that the result type of a trait may not be
582     /// specified.
583     pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
584         match self {
585             Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
586             Self::Trait(_, output, _) => output,
587         }
588     }
589
590     pub fn predicates_id(&self) -> Option<DefId> {
591         if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
592             id
593         } else {
594             None
595         }
596     }
597 }
598
599 /// If the expression is function like, get the signature for it.
600 pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
601     if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
602         Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
603     } else {
604         ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
605     }
606 }
607
608 /// If the type is function like, get the signature for it.
609 pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
610     if ty.is_box() {
611         return ty_sig(cx, ty.boxed_ty());
612     }
613     match *ty.kind() {
614         ty::Closure(id, subs) => {
615             let decl = id
616                 .as_local()
617                 .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
618             Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
619         },
620         ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
621         ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)),
622         ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
623         ty::Dynamic(bounds, _, _) => {
624             let lang_items = cx.tcx.lang_items();
625             match bounds.principal() {
626                 Some(bound)
627                     if Some(bound.def_id()) == lang_items.fn_trait()
628                         || Some(bound.def_id()) == lang_items.fn_once_trait()
629                         || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
630                 {
631                     let output = bounds
632                         .projection_bounds()
633                         .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
634                         .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
635                     Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None))
636                 },
637                 _ => None,
638             }
639         },
640         ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
641             Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
642             _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
643         },
644         ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
645         _ => None,
646     }
647 }
648
649 fn sig_from_bounds<'tcx>(
650     cx: &LateContext<'tcx>,
651     ty: Ty<'tcx>,
652     predicates: &'tcx [Predicate<'tcx>],
653     predicates_id: Option<DefId>,
654 ) -> Option<ExprFnSig<'tcx>> {
655     let mut inputs = None;
656     let mut output = None;
657     let lang_items = cx.tcx.lang_items();
658
659     for pred in predicates {
660         match pred.kind().skip_binder() {
661             PredicateKind::Clause(ty::Clause::Trait(p))
662                 if (lang_items.fn_trait() == Some(p.def_id())
663                     || lang_items.fn_mut_trait() == Some(p.def_id())
664                     || lang_items.fn_once_trait() == Some(p.def_id()))
665                     && p.self_ty() == ty =>
666             {
667                 let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
668                 if inputs.map_or(false, |inputs| i != inputs) {
669                     // Multiple different fn trait impls. Is this even allowed?
670                     return None;
671                 }
672                 inputs = Some(i);
673             },
674             PredicateKind::Clause(ty::Clause::Projection(p))
675                 if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
676                     && p.projection_ty.self_ty() == ty =>
677             {
678                 if output.is_some() {
679                     // Multiple different fn trait impls. Is this even allowed?
680                     return None;
681                 }
682                 output = Some(pred.kind().rebind(p.term.ty().unwrap()));
683             },
684             _ => (),
685         }
686     }
687
688     inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
689 }
690
691 fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
692     let mut inputs = None;
693     let mut output = None;
694     let lang_items = cx.tcx.lang_items();
695
696     for (pred, _) in cx
697         .tcx
698         .bound_explicit_item_bounds(ty.item_def_id)
699         .subst_iter_copied(cx.tcx, ty.substs)
700     {
701         match pred.kind().skip_binder() {
702             PredicateKind::Clause(ty::Clause::Trait(p))
703                 if (lang_items.fn_trait() == Some(p.def_id())
704                     || lang_items.fn_mut_trait() == Some(p.def_id())
705                     || lang_items.fn_once_trait() == Some(p.def_id())) =>
706             {
707                 let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
708
709                 if inputs.map_or(false, |inputs| inputs != i) {
710                     // Multiple different fn trait impls. Is this even allowed?
711                     return None;
712                 }
713                 inputs = Some(i);
714             },
715             PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
716                 if output.is_some() {
717                     // Multiple different fn trait impls. Is this even allowed?
718                     return None;
719                 }
720                 output = pred.kind().rebind(p.term.ty()).transpose();
721             },
722             _ => (),
723         }
724     }
725
726     inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
727 }
728
729 #[derive(Clone, Copy)]
730 pub enum EnumValue {
731     Unsigned(u128),
732     Signed(i128),
733 }
734 impl core::ops::Add<u32> for EnumValue {
735     type Output = Self;
736     fn add(self, n: u32) -> Self::Output {
737         match self {
738             Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
739             Self::Signed(x) => Self::Signed(x + i128::from(n)),
740         }
741     }
742 }
743
744 /// Attempts to read the given constant as though it were an enum value.
745 #[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
746 pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
747     if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
748         match tcx.type_of(id).kind() {
749             ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
750                 1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
751                 2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
752                 4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
753                 8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
754                 16 => value.assert_bits(Size::from_bytes(16)) as i128,
755                 _ => return None,
756             })),
757             ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
758                 1 => value.assert_bits(Size::from_bytes(1)),
759                 2 => value.assert_bits(Size::from_bytes(2)),
760                 4 => value.assert_bits(Size::from_bytes(4)),
761                 8 => value.assert_bits(Size::from_bytes(8)),
762                 16 => value.assert_bits(Size::from_bytes(16)),
763                 _ => return None,
764             })),
765             _ => None,
766         }
767     } else {
768         None
769     }
770 }
771
772 /// Gets the value of the given variant.
773 pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
774     let variant = &adt.variant(i);
775     match variant.discr {
776         VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
777         VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
778             VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
779             VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
780         },
781     }
782 }
783
784 /// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
785 /// platform specific `libc::<platform>::c_void` types in libc.
786 pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
787     if let ty::Adt(adt, _) = ty.kind()
788         && let &[krate, .., name] = &*cx.get_def_path(adt.did())
789         && let sym::libc | sym::core | sym::std = krate
790         && name.as_str() == "c_void"
791     {
792         true
793     } else {
794         false
795     }
796 }
797
798 pub fn for_each_top_level_late_bound_region<B>(
799     ty: Ty<'_>,
800     f: impl FnMut(BoundRegion) -> ControlFlow<B>,
801 ) -> ControlFlow<B> {
802     struct V<F> {
803         index: u32,
804         f: F,
805     }
806     impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<'tcx> for V<F> {
807         type BreakTy = B;
808         fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
809             if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
810                 (self.f)(bound)
811             } else {
812                 ControlFlow::Continue(())
813             }
814         }
815         fn visit_binder<T: TypeVisitable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
816             self.index += 1;
817             let res = t.super_visit_with(self);
818             self.index -= 1;
819             res
820         }
821     }
822     ty.visit_with(&mut V { index: 0, f })
823 }
824
825 pub struct AdtVariantInfo {
826     pub ind: usize,
827     pub size: u64,
828
829     /// (ind, size)
830     pub fields_size: Vec<(usize, u64)>,
831 }
832
833 impl AdtVariantInfo {
834     /// Returns ADT variants ordered by size
835     pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List<GenericArg<'tcx>>) -> Vec<Self> {
836         let mut variants_size = adt
837             .variants()
838             .iter()
839             .enumerate()
840             .map(|(i, variant)| {
841                 let mut fields_size = variant
842                     .fields
843                     .iter()
844                     .enumerate()
845                     .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst))))
846                     .collect::<Vec<_>>();
847                 fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size)));
848
849                 Self {
850                     ind: i,
851                     size: fields_size.iter().map(|(_, size)| size).sum(),
852                     fields_size,
853                 }
854             })
855             .collect::<Vec<_>>();
856         variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
857         variants_size
858     }
859 }
860
861 /// Gets the struct or enum variant from the given `Res`
862 pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
863     match res {
864         Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
865         Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
866         Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
867         Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
868             let var_id = cx.tcx.parent(id);
869             Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
870         },
871         Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
872         _ => None,
873     }
874 }
875
876 /// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
877 pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
878     let ty::Param(ty) = *ty.kind() else {
879         return false;
880     };
881     let lang = tcx.lang_items();
882     let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id))
883         = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
884     else {
885         return false;
886     };
887     predicates
888         .iter()
889         .try_fold(false, |found, p| {
890             if let PredicateKind::Clause(ty::Clause::Trait(p)) = p.kind().skip_binder()
891             && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
892             && ty.index == self_ty.index
893         {
894             // This should use `super_traits_of`, but that's a private function.
895             if p.trait_ref.def_id == fn_once_id {
896                 return Some(true);
897             } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
898                 return None;
899             }
900         }
901             Some(found)
902         })
903         .unwrap_or(false)
904 }
905
906 /// Comes up with an "at least" guesstimate for the type's size, not taking into
907 /// account the layout of type parameters.
908 pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
909     use rustc_middle::ty::layout::LayoutOf;
910     if !is_normalizable(cx, cx.param_env, ty) {
911         return 0;
912     }
913     match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
914         (Ok(size), _) => size,
915         (Err(_), ty::Tuple(list)) => list.as_substs().types().map(|t| approx_ty_size(cx, t)).sum(),
916         (Err(_), ty::Array(t, n)) => {
917             n.try_eval_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
918         },
919         (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
920             .variants()
921             .iter()
922             .map(|v| {
923                 v.fields
924                     .iter()
925                     .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
926                     .sum::<u64>()
927             })
928             .sum(),
929         (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
930             .variants()
931             .iter()
932             .map(|v| {
933                 v.fields
934                     .iter()
935                     .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
936                     .sum::<u64>()
937             })
938             .max()
939             .unwrap_or_default(),
940         (Err(_), ty::Adt(def, subst)) if def.is_union() => def
941             .variants()
942             .iter()
943             .map(|v| {
944                 v.fields
945                     .iter()
946                     .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
947                     .max()
948                     .unwrap_or_default()
949             })
950             .max()
951             .unwrap_or_default(),
952         (Err(_), _) => 0,
953     }
954 }
955
956 /// Makes the projection type for the named associated type in the given impl or trait impl.
957 ///
958 /// This function is for associated types which are "known" to exist, and as such, will only return
959 /// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions
960 /// enabled this will check that the named associated type exists, the correct number of
961 /// substitutions are given, and that the correct kinds of substitutions are given (lifetime,
962 /// constant or type). This will not check if type normalization would succeed.
963 pub fn make_projection<'tcx>(
964     tcx: TyCtxt<'tcx>,
965     container_id: DefId,
966     assoc_ty: Symbol,
967     substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
968 ) -> Option<ProjectionTy<'tcx>> {
969     fn helper<'tcx>(
970         tcx: TyCtxt<'tcx>,
971         container_id: DefId,
972         assoc_ty: Symbol,
973         substs: SubstsRef<'tcx>,
974     ) -> Option<ProjectionTy<'tcx>> {
975         let Some(assoc_item) = tcx
976             .associated_items(container_id)
977             .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id)
978         else {
979             debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`");
980             return None;
981         };
982         #[cfg(debug_assertions)]
983         {
984             let generics = tcx.generics_of(assoc_item.def_id);
985             let generic_count = generics.parent_count + generics.params.len();
986             let params = generics
987                 .parent
988                 .map_or([].as_slice(), |id| &*tcx.generics_of(id).params)
989                 .iter()
990                 .chain(&generics.params)
991                 .map(|x| &x.kind);
992
993             debug_assert!(
994                 generic_count == substs.len(),
995                 "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
996                     note: the expected parameters are: {:#?}\n\
997                     the given arguments are: `{:#?}`",
998                 assoc_item.def_id,
999                 substs.len(),
1000                 generic_count,
1001                 params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
1002                 substs,
1003             );
1004
1005             if let Some((idx, (param, arg))) = params
1006                 .clone()
1007                 .zip(substs.iter().map(GenericArg::unpack))
1008                 .enumerate()
1009                 .find(|(_, (param, arg))| {
1010                     !matches!(
1011                         (param, arg),
1012                         (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1013                             | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1014                             | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_))
1015                     )
1016                 })
1017             {
1018                 debug_assert!(
1019                     false,
1020                     "mismatched subst type at index {}: expected a {}, found `{:?}`\n\
1021                         note: the expected parameters are {:#?}\n\
1022                         the given arguments are {:#?}",
1023                     idx,
1024                     param.descr(),
1025                     arg,
1026                     params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
1027                     substs,
1028                 );
1029             }
1030         }
1031
1032         Some(ProjectionTy {
1033             substs,
1034             item_def_id: assoc_item.def_id,
1035         })
1036     }
1037     helper(
1038         tcx,
1039         container_id,
1040         assoc_ty,
1041         tcx.mk_substs(substs.into_iter().map(Into::into)),
1042     )
1043 }
1044
1045 /// Normalizes the named associated type in the given impl or trait impl.
1046 ///
1047 /// This function is for associated types which are "known" to be valid with the given
1048 /// substitutions, and as such, will only return `None` when debug assertions are disabled in order
1049 /// to prevent ICE's. With debug assertions enabled this will check that that type normalization
1050 /// succeeds as well as everything checked by `make_projection`.
1051 pub fn make_normalized_projection<'tcx>(
1052     tcx: TyCtxt<'tcx>,
1053     param_env: ParamEnv<'tcx>,
1054     container_id: DefId,
1055     assoc_ty: Symbol,
1056     substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
1057 ) -> Option<Ty<'tcx>> {
1058     fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: ProjectionTy<'tcx>) -> Option<Ty<'tcx>> {
1059         #[cfg(debug_assertions)]
1060         if let Some((i, subst)) = ty
1061             .substs
1062             .iter()
1063             .enumerate()
1064             .find(|(_, subst)| subst.has_late_bound_regions())
1065         {
1066             debug_assert!(
1067                 false,
1068                 "substs contain late-bound region at index `{i}` which can't be normalized.\n\
1069                     use `TyCtxt::erase_late_bound_regions`\n\
1070                     note: subst is `{subst:#?}`",
1071             );
1072             return None;
1073         }
1074         match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.item_def_id, ty.substs)) {
1075             Ok(ty) => Some(ty),
1076             Err(e) => {
1077                 debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
1078                 None
1079             },
1080         }
1081     }
1082     helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
1083 }