Skip to main content

clippy_utils/ty/
mod.rs

1//! Util methods for [`rustc_middle::ty`]
2
3#![expect(clippy::module_name_repetitions)]
4
5use core::ops::ControlFlow;
6use itertools::Itertools as _;
7use rustc_abi::{BackendRepr, FieldsShape, VariantIdx, Variants};
8use rustc_ast::ast::Mutability;
9use rustc_attr_ir::lang_items::LangItem;
10use rustc_data_structures::fx::{FxHashMap, FxHashSet};
11use rustc_errors::pluralize;
12use rustc_hir as hir;
13use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
14use rustc_hir::def_id::DefId;
15use rustc_hir::{Expr, ExprKind, FnDecl};
16use rustc_hir_analysis::lower_ty;
17use rustc_infer::infer::TyCtxtInferExt as _;
18use rustc_lint::LateContext;
19use rustc_lint::unused::must_use::{IsTyMustUse, MustUsePath, is_ty_must_use};
20use rustc_middle::mir::ConstValue;
21use rustc_middle::mir::interpret::Scalar;
22use rustc_middle::traits::EvaluationResult;
23use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind};
24use rustc_middle::ty::layout::{LayoutError, LayoutOf as _, TyAndLayout};
25use rustc_middle::ty::{
26    self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg,
27    GenericArgKind, GenericArgsRef, IntTy, ProjectionAliasTy, Region, RegionKind, TraitRef, Ty, TyCtxt,
28    TypeSuperVisitable as _, TypeVisitable, TypeVisitableExt as _, TypeVisitor, UintTy, Unnormalized, VariantDef,
29    VariantDiscr,
30};
31use rustc_span::symbol::Ident;
32use rustc_span::{DUMMY_SP, Span, Symbol};
33use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
34use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt as _;
35use rustc_trait_selection::traits::{Obligation, ObligationCause};
36use std::collections::hash_map::Entry;
37use std::{debug_assert_matches, iter, mem};
38
39use crate::paths::{PathNS, lookup_path_str};
40use crate::res::{MaybeDef as _, MaybeQPath as _};
41use crate::{over, sym};
42
43mod type_certainty;
44pub use type_certainty::expr_type_is_certain;
45
46/// Lower a [`hir::Ty`] to a [`rustc_middle::ty::Ty`].
47pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
48    cx.typeck_results
49        .filter(|results| results.hir_owner == hir_ty.hir_id.owner)
50        .and_then(|results| results.node_type_opt(hir_ty.hir_id))
51        .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty))
52}
53
54/// Checks if the given type implements copy.
55pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
56    cx.type_is_copy_modulo_regions(ty)
57}
58
59/// This checks whether a given type is known to implement Debug.
60pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
61    cx.tcx
62        .get_diagnostic_item(sym::Debug)
63        .is_some_and(|debug| implements_trait(cx, ty, debug, &[]))
64}
65
66/// Checks whether a type can be partially moved.
67pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
68    if has_drop(cx, ty) || is_copy(cx, ty) {
69        return false;
70    }
71    match ty.kind() {
72        ty::Param(_) => false,
73        ty::Adt(def, subs) => def
74            .all_fields()
75            .any(|f| !is_copy(cx, f.ty(cx.tcx, subs).skip_norm_wip())),
76        _ => true,
77    }
78}
79
80/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
81/// constructor.
82pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
83    ty.walk().any(|inner| match inner.kind() {
84        GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
85        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
86    })
87}
88
89/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
90/// constructor of the same type.
91///
92/// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
93/// will also return `true`.
94pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
95    fn contains_ty_adt_constructor_opaque_inner<'tcx>(
96        cx: &LateContext<'tcx>,
97        ty: Ty<'tcx>,
98        needle: Ty<'tcx>,
99        seen: &mut FxHashSet<DefId>,
100    ) -> bool {
101        ty.walk().any(|inner| match inner.kind() {
102            GenericArgKind::Type(inner_ty) => {
103                if inner_ty == needle {
104                    return true;
105                }
106
107                if inner_ty.ty_adt_def() == needle.ty_adt_def() {
108                    return true;
109                }
110
111                if let ty::Alias(
112                    _,
113                    AliasTy {
114                        kind: ty::Opaque { def_id },
115                        ..
116                    },
117                ) = *inner_ty.kind()
118                {
119                    if !seen.insert(def_id) {
120                        return false;
121                    }
122
123                    for (predicate, _span) in cx
124                        .tcx
125                        .explicit_item_self_bounds(def_id)
126                        .iter_identity_copied()
127                        .map(Unnormalized::skip_norm_wip)
128                    {
129                        match predicate.kind().skip_binder() {
130                            // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
131                            // and check substitutions to find `U`.
132                            ty::ClauseKind::Trait(trait_predicate)
133                                if trait_predicate
134                                    .trait_ref
135                                    .args
136                                    .types()
137                                    .skip(1) // Skip the implicit `Self` generic parameter
138                                    .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) =>
139                            {
140                                return true;
141                            },
142                            // For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
143                            // so we check the term for `U`.
144                            ty::ClauseKind::Projection(projection_predicate) => {
145                                if let ty::TermKind::Ty(ty) = projection_predicate.term.kind()
146                                    && contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)
147                                {
148                                    return true;
149                                }
150                            },
151                            _ => (),
152                        }
153                    }
154                }
155
156                false
157            },
158            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
159        })
160    }
161
162    // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not
163    // visited twice.
164    let mut seen = FxHashSet::default();
165    contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen)
166}
167
168/// Resolves `<T as Iterator>::Item` for `T`
169/// Do not invoke without first verifying that the type implements `Iterator`
170pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
171    cx.tcx
172        .get_diagnostic_item(sym::Iterator)
173        .and_then(|iter_did| cx.get_associated_type(ty, iter_did, sym::Item))
174}
175
176/// Returns true if `ty` is a type on which calling `Clone` through a function instead of
177/// as a method, such as `Arc::clone()` is considered idiomatic.
178///
179/// Lints should avoid suggesting to replace instances of `ty::Clone()` by `.clone()` for objects
180/// of those types.
181pub fn should_call_clone_as_function(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
182    matches!(
183        ty.opt_diag_name(cx),
184        Some(sym::Arc | sym::ArcWeak | sym::Rc | sym::RcWeak)
185    )
186}
187
188/// If `ty` is known to have a `iter` or `iter_mut` method, returns a symbol representing the type.
189pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
190    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
191    // exists and has the desired signature. Unfortunately FnCtxt is not exported
192    // so we can't use its `lookup_method` method.
193    let into_iter_collections: &[Symbol] = &[
194        sym::Vec,
195        sym::Option,
196        sym::Result,
197        sym::BTreeMap,
198        sym::BTreeSet,
199        sym::VecDeque,
200        sym::LinkedList,
201        sym::BinaryHeap,
202        sym::HashSet,
203        sym::HashMap,
204        sym::PathBuf,
205        sym::Path,
206        sym::MpscReceiver,
207        sym::MpmcReceiver,
208    ];
209
210    let ty_to_check = match probably_ref_ty.kind() {
211        ty::Ref(_, ty_to_check, _) => *ty_to_check,
212        _ => probably_ref_ty,
213    };
214
215    let def_id = match ty_to_check.kind() {
216        ty::Array(..) => return Some(sym::array),
217        ty::Slice(..) => return Some(sym::slice),
218        ty::Adt(adt, _) => adt.did(),
219        _ => return None,
220    };
221
222    for &name in into_iter_collections {
223        if cx.tcx.is_diagnostic_item(name, def_id) {
224            return Some(cx.tcx.item_name(def_id));
225        }
226    }
227    None
228}
229
230/// Checks whether a type implements a trait.
231/// The function returns false in case the type contains an inference variable.
232///
233/// See [Common tools for writing lints] for an example how to use this function and other options.
234///
235/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/HEAD/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
236pub fn implements_trait<'tcx>(
237    cx: &LateContext<'tcx>,
238    ty: Ty<'tcx>,
239    trait_id: DefId,
240    args: &[GenericArg<'tcx>],
241) -> bool {
242    implements_trait_with_env_from_iter(
243        cx.tcx,
244        cx.typing_env(),
245        ty,
246        trait_id,
247        None,
248        args.iter().map(|&x| Some(x)),
249    )
250}
251
252/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
253///
254/// The `callee_id` argument is used to determine whether this is a function call in a `const fn`
255/// environment, used for checking const traits.
256pub fn implements_trait_with_env<'tcx>(
257    tcx: TyCtxt<'tcx>,
258    typing_env: ty::TypingEnv<'tcx>,
259    ty: Ty<'tcx>,
260    trait_id: DefId,
261    callee_id: Option<DefId>,
262    args: &[GenericArg<'tcx>],
263) -> bool {
264    implements_trait_with_env_from_iter(tcx, typing_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x)))
265}
266
267/// Same as `implements_trait_from_env` but takes the arguments as an iterator.
268pub fn implements_trait_with_env_from_iter<'tcx>(
269    tcx: TyCtxt<'tcx>,
270    typing_env: ty::TypingEnv<'tcx>,
271    ty: Ty<'tcx>,
272    trait_id: DefId,
273    callee_id: Option<DefId>,
274    args: impl IntoIterator<Item = impl Into<Option<GenericArg<'tcx>>>>,
275) -> bool {
276    // Clippy shouldn't have infer types
277    assert!(!ty.has_infer());
278
279    // If a `callee_id` is passed, then we assert that it is a body owner
280    // through calling `body_owner_kind`, which would panic if the callee
281    // does not have a body.
282    if let Some(callee_id) = callee_id {
283        let _ = tcx.hir_body_owner_kind(callee_id);
284    }
285
286    let ty = tcx.erase_and_anonymize_regions(ty);
287    if ty.has_escaping_bound_vars() {
288        return false;
289    }
290
291    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
292    let args = args
293        .into_iter()
294        .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()));
295
296    let trait_ref = TraitRef::new(tcx, trait_id, [GenericArg::from(ty)].into_iter().chain(args));
297
298    debug_assert_matches!(
299        tcx.def_kind(trait_id),
300        DefKind::Trait | DefKind::TraitAlias,
301        "`DefId` must belong to a trait or trait alias"
302    );
303    #[cfg(debug_assertions)]
304    assert_generic_args_match(tcx, trait_id, trait_ref.args);
305
306    let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
307    infcx
308        .evaluate_obligation(&obligation)
309        .is_ok_and(EvaluationResult::must_apply_modulo_regions)
310}
311
312/// Checks whether this type implements `Drop`.
313pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
314    ty.ty_adt_def().is_some_and(|def| def.has_dtor(cx.tcx))
315}
316
317/// Returns whether the `ty` has `#[must_use]` attribute, or acts like it does according to the
318/// compiler determination. For example, if `ty` is a `Result`/`ControlFlow` whose `Err`/`Break`
319/// payload is an uninhabited type, the `Ok`/`Continue` payload type will be used instead.
320///
321/// The [`MustUsePath`] can be used to describe the type through [`describe_must_use_type`].
322pub fn opt_must_use_path<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<MustUsePath> {
323    // `is_ty_must_use` requires an expression, whose `hir_id` will be used to determine whether
324    // certain types are visibly uninhabited from the module containing the expression.
325    // `cx.last_node_with_lint_attrs` is initialized to the crate/module `hir_id` when linting
326    // a new crate/module. If it is overriden, it is with an `hir_id` pertaining to the same
327    // create/module. We can use this in a dummy expression instead of asking all callers
328    // to provide a local `hir_id` which would not add more information.
329    let dummy_expr = Expr {
330        hir_id: cx.last_node_with_lint_attrs,
331        span: DUMMY_SP,
332        kind: ExprKind::Ret(None),
333    };
334    match is_ty_must_use(cx, ty, &dummy_expr) {
335        IsTyMustUse::Yes(path) => Some(path),
336        _ => None,
337    }
338}
339
340/// Describe a [`MustUsePath`] returned by [`opt_must_use_path`].
341pub fn describe_must_use_type(cx: &LateContext<'_>, path: &MustUsePath) -> String {
342    describe_must_use_type_inner(cx, path, "", "", 1)
343}
344
345// This is a rip-off from the compiler's `rustc_lint/src/unused/must_use.rs`
346fn describe_must_use_type_inner(
347    cx: &LateContext<'_>,
348    path: &MustUsePath,
349    descr_pre: &str,
350    descr_post: &str,
351    plural_len: usize,
352) -> String {
353    let plural_suffix = pluralize!(plural_len);
354
355    match path {
356        MustUsePath::Boxed(path) => {
357            let descr_pre = &format!("{descr_pre}boxed ");
358            describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len)
359        },
360        MustUsePath::Pinned(path) => {
361            let descr_pre = &format!("{descr_pre}pinned ");
362            describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len)
363        },
364        MustUsePath::Opaque(path) => {
365            let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
366            describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len)
367        },
368        MustUsePath::TraitObject(path) => {
369            let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
370            describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len)
371        },
372        MustUsePath::TupleElement(elems) => elems
373            .iter()
374            .map(|(index, path)| {
375                let descr_post = &format!(" in tuple element {index}");
376                describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len)
377            })
378            .join(", "),
379        MustUsePath::Result(path) => {
380            let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}");
381            describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len)
382        },
383        MustUsePath::ControlFlow(path) => {
384            let descr_post = &format!(" in a `ControlFlow` with an uninhabited break{descr_post}");
385            describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len)
386        },
387        MustUsePath::Array(path, len) => {
388            let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
389            describe_must_use_type_inner(
390                cx,
391                path,
392                descr_pre,
393                descr_post,
394                plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
395            )
396        },
397        MustUsePath::Closure(_) => {
398            format!(
399                "{descr_pre}{} closure{plural_suffix}{descr_post}",
400                if plural_len == 1 {
401                    "one".to_string()
402                } else {
403                    plural_len.to_string()
404                }
405            )
406        },
407        MustUsePath::Coroutine(_) => {
408            format!(
409                "{descr_pre}{} coroutine{plural_suffix}{descr_post}",
410                if plural_len == 1 {
411                    "one".to_string()
412                } else {
413                    plural_len.to_string()
414                }
415            )
416        },
417        MustUsePath::Def(_, def_id, _) => {
418            format!(
419                "{descr_pre}`{}`{plural_suffix}{descr_post}",
420                cx.tcx.def_path_str(*def_id)
421            )
422        },
423    }
424}
425
426/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
427/// integer or floating-point number type).
428///
429/// For checking aggregation of primitive types (e.g. tuples and slices of primitive type) see
430/// `is_recursively_primitive_type`
431pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
432    matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
433}
434
435/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
436/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
437pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
438    match *ty.kind() {
439        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
440        ty::Ref(_, inner, _) if inner.is_str() => true,
441        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
442        ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
443        _ => false,
444    }
445}
446
447/// Return `true` if the passed `typ` is `isize` or `usize`.
448pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
449    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
450}
451
452/// Checks if the drop order for a type matters.
453///
454/// Some std types implement drop solely to deallocate memory. For these types, and composites
455/// containing them, changing the drop order won't result in any observable side effects.
456pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
457    fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
458        if !seen.insert(ty) {
459            return false;
460        }
461        if !ty.has_significant_drop(cx.tcx, cx.typing_env()) {
462            false
463        }
464        // Check for std types which implement drop, but only for memory allocation.
465        else if ty.is_lang_item(cx, LangItem::OwnedBox)
466            || matches!(
467                ty.opt_diag_name(cx),
468                Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type | sym::RcWeak | sym::ArcWeak)
469            )
470        {
471            // Check all of the generic arguments.
472            if let ty::Adt(_, subs) = ty.kind() {
473                subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
474            } else {
475                true
476            }
477        } else if !cx
478            .tcx
479            .lang_items()
480            .drop_trait()
481            .is_some_and(|id| implements_trait(cx, ty, id, &[]))
482        {
483            // This type doesn't implement drop, so no side effects here.
484            // Check if any component type has any.
485            match ty.kind() {
486                ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
487                ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
488                ty::Adt(adt, subs) => adt
489                    .all_fields()
490                    .map(|f| f.ty(cx.tcx, subs).skip_norm_wip())
491                    .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
492                _ => true,
493            }
494        } else {
495            true
496        }
497    }
498
499    needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
500}
501
502/// Returns `true` if `ty` denotes an `unsafe fn`.
503pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
504    ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe()
505}
506
507/// Peels off all references on the type. Returns the underlying type, the number of references
508/// removed, and, if there were any such references, whether the pointer is ultimately mutable or
509/// not.
510pub fn peel_and_count_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize, Option<Mutability>) {
511    let mut count = 0;
512    let mut mutbl = None;
513    while let ty::Ref(_, dest_ty, m) = ty.kind() {
514        ty = *dest_ty;
515        count += 1;
516        mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m)));
517    }
518    (ty, count, mutbl)
519}
520
521/// Peels off `n` references on the type. Returns the underlying type and, if any references
522/// were removed, whether the pointer is ultimately mutable or not.
523pub fn peel_n_ty_refs(mut ty: Ty<'_>, n: usize) -> (Ty<'_>, Option<Mutability>) {
524    let mut mutbl = None;
525    for _ in 0..n {
526        if let ty::Ref(_, dest_ty, m) = ty.kind() {
527            ty = *dest_ty;
528            mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m)));
529        } else {
530            break;
531        }
532    }
533    (ty, mutbl)
534}
535
536/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores
537/// lifetimes.
538///
539/// For example, the function would return `true` for
540/// - `u32` and `u32`
541/// - `[u8; N]` and `[u8; M]`, if `N=M`
542/// - `Option<T>` and `Option<U>`, if `same_type_modulo_regions(T, U)` holds
543/// - `&'a str` and `&'b str`
544///
545/// and `false` for:
546/// - `Result<u32, String>` and `Result<usize, String>`
547pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
548    match (a.kind(), b.kind()) {
549        (ty::Adt(did_a, args_a), ty::Adt(did_b, args_b)) => {
550            if did_a != did_b {
551                return false;
552            }
553
554            iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
555                (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
556                (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
557                    same_type_modulo_regions(type_a, type_b)
558                },
559                _ => true,
560            })
561        },
562        (ty::Ref(_, a, mut_a), ty::Ref(_, b, mut_b)) => mut_a == mut_b && same_type_modulo_regions(*a, *b),
563        (ty::Tuple(as_), ty::Tuple(bs)) => over(as_, bs, |a, b| same_type_modulo_regions(*a, *b)),
564        (ty::Array(a, na), ty::Array(b, nb)) => na == nb && same_type_modulo_regions(*a, *b),
565        _ => a == b,
566    }
567}
568
569/// Checks if a given type looks safe to be uninitialized.
570pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
571    match cx.layout_of(ty) {
572        Ok(layout) => is_uninit_value_valid_for_layout(cx, layout),
573        // The type layout is either not concrete enough yet or too large, fall back to structural check instead
574        Err(LayoutError::TooGeneric(_) | LayoutError::SizeOverflow(_)) => is_uninit_value_valid_for_ty_fallback(cx, ty),
575        Err(_) => false,
576    }
577}
578
579fn is_uninit_value_valid_for_layout<'tcx>(cx: &LateContext<'tcx>, layout: TyAndLayout<'tcx>) -> bool {
580    // ZSTs contribute no bytes to the vector buffer
581    if layout.layout.is_zst() {
582        return true;
583    }
584
585    match layout.layout.backend_repr {
586        BackendRepr::Scalar(s) => s.is_uninit_valid(),
587        BackendRepr::ScalarPair { a, b, .. } => a.is_uninit_valid() && b.is_uninit_valid(),
588        BackendRepr::SimdVector { element, count: _ } | BackendRepr::SimdScalableVector { element, .. } => {
589            element.is_uninit_valid()
590        },
591        // Here validity is determined by the structural fields instead.
592        BackendRepr::Memory { .. } => match &layout.layout.variants {
593            Variants::Single { .. } => match &layout.layout.fields {
594                FieldsShape::Primitive => {
595                    debug_assert!(false, "Both Scalar primitives and ! should be handled above.");
596                    false
597                },
598                // Arrays are valid if empty, or if their elements are valid.
599                FieldsShape::Array { count, .. } => {
600                    if *count == 0 {
601                        true
602                    } else {
603                        is_uninit_value_valid_for_layout(cx, layout.field(cx, 0))
604                    }
605                },
606                // Structs like types are valid only if all fields are valid.
607                FieldsShape::Arbitrary { offsets, .. } => {
608                    (0..offsets.len()).all(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i)))
609                },
610                // Unions are valid if at least one field is valid.
611                FieldsShape::Union(count) => {
612                    (0..count.get()).any(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i)))
613                },
614            },
615            // Types with no valid variants must be uninhabited
616            Variants::Empty => true,
617            // Enum like with multiple inhabited variants have a discriminant, they cannot be uninitialized.
618            Variants::Multiple { .. } => false,
619        },
620    }
621}
622
623/// Fallback for polymorphic types where `layout_of` fails
624fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
625    let ty::PseudoCanonicalInput { typing_env, value: ty } = cx
626        .typing_env()
627        .with_post_analysis_normalized_invalidating_rigid_aliases(cx.tcx, ty);
628
629    match *ty.kind() {
630        // The array length may be polymorphic, let's try the inner type.
631        ty::Array(component, len) => {
632            // Zero-length arrays are always valid
633            if len.try_to_target_usize(cx.tcx) == Some(0) {
634                return true;
635            }
636            is_uninit_value_valid_for_ty(cx, component)
637        },
638        // Peek through tuples and try their fallbacks.
639        ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
640        // For Unions, check if any field is uninit
641        ty::Adt(adt, args) if adt.is_union() => adt.all_fields().any(|field| {
642            let unnormalized_field_ty = field.ty(cx.tcx, args);
643            let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else {
644                debug_assert!(
645                    false,
646                    "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly."
647                );
648                return false;
649            };
650            is_uninit_value_valid_for_ty(cx, field_ty)
651        }),
652        // Types (e.g. `UnsafeCell<MaybeUninit<T>>`) that recursively contain only types that can be uninit
653        // can themselves be uninit too.
654        // This also applies for single variant enums, whose validity is determined by their fields.
655        ty::Adt(adt, args) if adt.is_struct() || adt.variants().len() == 1 => adt.all_fields().all(|field| {
656            let unnormalized_field_ty = field.ty(cx.tcx, args);
657            let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else {
658                debug_assert!(
659                    false,
660                    "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly."
661                );
662                return false;
663            };
664
665            is_uninit_value_valid_for_ty(cx, field_ty)
666        }),
667        // Without a usable whole type layout,
668        // conservatively reject remaining enum cases
669        ty::Adt(adt, _) if adt.is_enum() => false,
670        // Conservatively reject remaining types
671        _ => false,
672    }
673}
674
675/// Gets an iterator over all clauses which apply to the given item.
676pub fn all_clauses_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(ty::Clause<'_>, Span)> {
677    let mut next_id = Some(id);
678    iter::from_fn(move || {
679        next_id.take().map(|id| {
680            let gen_clauses = tcx.clauses_of(id);
681            next_id = gen_clauses.parent;
682            gen_clauses.clauses.iter()
683        })
684    })
685    .flatten()
686}
687
688/// A signature for a function like type.
689#[derive(Clone, Copy, Debug)]
690pub enum ExprFnSig<'tcx> {
691    Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
692    Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
693    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
694}
695impl<'tcx> ExprFnSig<'tcx> {
696    /// Gets the argument type at the given offset. This will return `None` when the index is out of
697    /// bounds only for variadic functions, otherwise this will panic.
698    pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
699        match self {
700            Self::Sig(sig, _) => {
701                if sig.c_variadic() {
702                    sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
703                } else {
704                    Some(sig.input(i))
705                }
706            },
707            Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
708            Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
709        }
710    }
711
712    /// Gets the argument type at the given offset. For closures this will also get the type as
713    /// written. This will return `None` when the index is out of bounds only for variadic
714    /// functions, otherwise this will panic.
715    pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
716        match self {
717            Self::Sig(sig, _) => {
718                if sig.c_variadic() {
719                    sig.inputs()
720                        .map_bound(|inputs| inputs.get(i).copied())
721                        .transpose()
722                        .map(|arg| (None, arg))
723                } else {
724                    Some((None, sig.input(i)))
725                }
726            },
727            Self::Closure(decl, sig) => Some((
728                decl.and_then(|decl| decl.inputs.get(i)),
729                sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
730            )),
731            Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
732        }
733    }
734
735    /// Gets the result type, if one could be found. Note that the result type of a trait may not be
736    /// specified.
737    pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
738        match self {
739            Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
740            Self::Trait(_, output, _) => output,
741        }
742    }
743
744    /// Gets the `DefId` of the item whose predicates apply to this signature, if one could be found.
745    pub fn predicates_id(&self) -> Option<DefId> {
746        if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
747            id
748        } else {
749            None
750        }
751    }
752}
753
754/// If the expression is function like, get the signature for it.
755pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
756    if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr.res(cx) {
757        Some(ExprFnSig::Sig(
758            cx.tcx.fn_sig(id).instantiate_identity().skip_norm_wip(),
759            Some(id),
760        ))
761    } else {
762        ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
763    }
764}
765
766/// If the type is function like, get the signature for it.
767pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
768    if let Some(boxed_ty) = ty.boxed_ty() {
769        return ty_sig(cx, boxed_ty);
770    }
771    match *ty.kind() {
772        ty::Closure(id, subs) => {
773            let decl = id
774                .as_local()
775                .and_then(|id| cx.tcx.hir_fn_decl_by_hir_id(cx.tcx.local_def_id_to_hir_id(id)));
776            Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
777        },
778        ty::FnDef(id, subs) => Some(ExprFnSig::Sig(
779            cx.tcx
780                .fn_sig(id)
781                .instantiate(cx.tcx, subs.no_bound_vars().unwrap())
782                .skip_norm_wip(),
783            Some(id),
784        )),
785        ty::Alias(
786            _,
787            AliasTy {
788                kind: ty::Opaque { def_id },
789                args,
790                ..
791            },
792        ) => sig_from_bounds(
793            cx,
794            ty,
795            cx.tcx
796                .item_self_bounds(def_id)
797                .iter_instantiated(cx.tcx, args)
798                .map(|clause| cx.tcx.normalize_erasing_regions(cx.typing_env(), clause)),
799            cx.tcx.opt_parent(def_id),
800        ),
801        ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)),
802        ty::Dynamic(bounds, _) => {
803            let lang_items = cx.tcx.lang_items();
804            match bounds.principal() {
805                Some(bound)
806                    if Some(bound.def_id()) == lang_items.fn_trait()
807                        || Some(bound.def_id()) == lang_items.fn_once_trait()
808                        || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
809                {
810                    let output = bounds
811                        .projection_bounds()
812                        .find(|p| lang_items.fn_once_output().is_some_and(|id| id == p.item_def_id()))
813                        .map(|p| p.map_bound(|p| p.term.expect_type()));
814                    Some(ExprFnSig::Trait(bound.map_bound(|b| b.args.type_at(0)), output, None))
815                },
816                _ => None,
817            }
818        },
819        ty::Alias(_, alias) if let Some(proj) = alias.try_to_projection() => match cx
820            .tcx
821            .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty))
822        {
823            Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
824            _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
825        },
826        ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
827        _ => None,
828    }
829}
830
831fn sig_from_bounds<'tcx>(
832    cx: &LateContext<'tcx>,
833    ty: Ty<'tcx>,
834    clauses: impl IntoIterator<Item = ty::Clause<'tcx>>,
835    predicates_id: Option<DefId>,
836) -> Option<ExprFnSig<'tcx>> {
837    let mut inputs = None;
838    let mut output = None;
839    let lang_items = cx.tcx.lang_items();
840
841    for clause in clauses {
842        match clause.kind().skip_binder() {
843            ty::ClauseKind::Trait(p)
844                if (lang_items.fn_trait() == Some(p.def_id())
845                    || lang_items.fn_mut_trait() == Some(p.def_id())
846                    || lang_items.fn_once_trait() == Some(p.def_id()))
847                    && p.self_ty() == ty =>
848            {
849                let i = clause.kind().rebind(p.trait_ref.args.type_at(1));
850                if inputs.is_some_and(|inputs| i != inputs) {
851                    // Multiple different fn trait impls. Is this even allowed?
852                    return None;
853                }
854                inputs = Some(i);
855            },
856            ty::ClauseKind::Projection(p)
857                if Some(p.projection_term.expect_projection_def_id()) == lang_items.fn_once_output()
858                    && p.projection_term.self_ty() == ty =>
859            {
860                if output.is_some() {
861                    // Multiple different fn trait impls. Is this even allowed?
862                    return None;
863                }
864                output = Some(clause.kind().rebind(p.term.expect_type()));
865            },
866            _ => (),
867        }
868    }
869
870    inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
871}
872
873fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionAliasTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
874    let mut inputs = None;
875    let mut output = None;
876    let lang_items = cx.tcx.lang_items();
877
878    for (pred, _) in cx
879        .tcx
880        .explicit_item_bounds(ty.kind)
881        .iter_instantiated_copied(cx.tcx, ty.args)
882        .map(Unnormalized::skip_norm_wip)
883    {
884        match pred.kind().skip_binder() {
885            ty::ClauseKind::Trait(p)
886                if (lang_items.fn_trait() == Some(p.def_id())
887                    || lang_items.fn_mut_trait() == Some(p.def_id())
888                    || lang_items.fn_once_trait() == Some(p.def_id())) =>
889            {
890                let i = pred.kind().rebind(p.trait_ref.args.type_at(1));
891
892                if inputs.is_some_and(|inputs| inputs != i) {
893                    // Multiple different fn trait impls. Is this even allowed?
894                    return None;
895                }
896                inputs = Some(i);
897            },
898            ty::ClauseKind::Projection(p)
899                if Some(p.projection_term.expect_projection_def_id()) == lang_items.fn_once_output() =>
900            {
901                if output.is_some() {
902                    // Multiple different fn trait impls. Is this even allowed?
903                    return None;
904                }
905                output = pred.kind().rebind(p.term.as_type()).transpose();
906            },
907            _ => (),
908        }
909    }
910
911    inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
912}
913
914#[derive(Clone, Copy)]
915pub enum EnumValue {
916    Unsigned(u128),
917    Signed(i128),
918}
919impl core::ops::Add<u32> for EnumValue {
920    type Output = Self;
921    fn add(self, n: u32) -> Self::Output {
922        match self {
923            Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
924            Self::Signed(x) => Self::Signed(x + i128::from(n)),
925        }
926    }
927}
928
929/// Attempts to read the given constant as though it were an enum value.
930pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
931    if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
932        match tcx.type_of(id).instantiate_identity().skip_norm_wip().kind() {
933            ty::Int(_) => Some(EnumValue::Signed(value.to_int(value.size()))),
934            ty::Uint(_) => Some(EnumValue::Unsigned(value.to_uint(value.size()))),
935            _ => None,
936        }
937    } else {
938        None
939    }
940}
941
942/// Gets the value of the given variant.
943pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
944    let variant = &adt.variant(i);
945    match variant.discr {
946        VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
947        VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
948            VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
949            VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
950        },
951    }
952}
953
954/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
955/// platform specific `libc::<platform>::c_void` types in libc.
956pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
957    if let ty::Adt(adt, _) = ty.kind()
958        && let &[krate, .., name] = &*cx.get_def_path(adt.did())
959        && let sym::libc | sym::core | sym::std = krate
960        && name == sym::c_void
961    {
962        true
963    } else {
964        false
965    }
966}
967
968/// Calls `f` for each late-bound region in `ty` that is bound by the enclosing binder.
969///
970/// This ignores regions bound by nested binders.
971pub fn for_each_top_level_late_bound_region<'cx, B>(
972    ty: Ty<'cx>,
973    f: impl FnMut(BoundRegion<'cx>) -> ControlFlow<B>,
974) -> ControlFlow<B> {
975    struct V<F> {
976        index: u32,
977        f: F,
978    }
979    impl<'tcx, B, F: FnMut(BoundRegion<'tcx>) -> ControlFlow<B>> TypeVisitor<TyCtxt<'tcx>> for V<F> {
980        type Result = ControlFlow<B>;
981        fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
982            if let RegionKind::ReBound(BoundVarIndexKind::Bound(idx), bound) = r.kind()
983                && idx.as_u32() == self.index
984            {
985                (self.f)(bound)
986            } else {
987                ControlFlow::Continue(())
988            }
989        }
990        fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
991            self.index += 1;
992            let res = t.super_visit_with(self);
993            self.index -= 1;
994            res
995        }
996    }
997    ty.visit_with(&mut V { index: 0, f })
998}
999
1000pub struct AdtVariantInfo {
1001    pub ind: usize,
1002    pub size: u64,
1003
1004    /// (ind, size)
1005    pub fields_size: Vec<(usize, u64)>,
1006}
1007
1008impl AdtVariantInfo {
1009    /// Returns ADT variants ordered by size
1010    pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: GenericArgsRef<'tcx>) -> Vec<Self> {
1011        let mut variants_size = adt
1012            .variants()
1013            .iter()
1014            .enumerate()
1015            .map(|(i, variant)| {
1016                let mut fields_size = variant
1017                    .fields
1018                    .iter()
1019                    .enumerate()
1020                    .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst).skip_norm_wip())))
1021                    .collect::<Vec<_>>();
1022                fields_size.sort_by_key(|(_, a_size)| *a_size);
1023
1024                Self {
1025                    ind: i,
1026                    size: fields_size.iter().map(|(_, size)| size).sum(),
1027                    fields_size,
1028                }
1029            })
1030            .collect::<Vec<_>>();
1031        variants_size.sort_by_key(|b| std::cmp::Reverse(b.size));
1032        variants_size
1033    }
1034}
1035
1036/// Gets the struct or enum variant from the given `Res`
1037pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<(AdtDef<'tcx>, &'tcx VariantDef)> {
1038    match res {
1039        Res::Def(DefKind::Struct, id) => {
1040            let adt = cx.tcx.adt_def(id);
1041            Some((adt, adt.non_enum_variant()))
1042        },
1043        Res::Def(DefKind::Variant, id) => {
1044            let adt = cx.tcx.adt_def(cx.tcx.parent(id));
1045            Some((adt, adt.variant_with_id(id)))
1046        },
1047        Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => {
1048            let adt = cx.tcx.adt_def(cx.tcx.parent(id));
1049            Some((adt, adt.non_enum_variant()))
1050        },
1051        Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
1052            let var_id = cx.tcx.parent(id);
1053            let adt = cx.tcx.adt_def(cx.tcx.parent(var_id));
1054            Some((adt, adt.variant_with_id(var_id)))
1055        },
1056        Res::SelfCtor(id) => {
1057            let adt = cx
1058                .tcx
1059                .type_of(id)
1060                .instantiate_identity()
1061                .skip_norm_wip()
1062                .ty_adt_def()
1063                .unwrap();
1064            Some((adt, adt.non_enum_variant()))
1065        },
1066        _ => None,
1067    }
1068}
1069
1070/// Comes up with an "at least" guesstimate for the type's size, not taking into
1071/// account the layout of type parameters.
1072pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
1073    use rustc_middle::ty::layout::LayoutOf as _;
1074    match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
1075        (Ok(size), _) => size,
1076        (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(),
1077        (Err(_), ty::Array(t, n)) => n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t),
1078        (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
1079            .variants()
1080            .iter()
1081            .map(|v| {
1082                v.fields
1083                    .iter()
1084                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst).skip_norm_wip()))
1085                    .sum::<u64>()
1086            })
1087            .sum(),
1088        (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
1089            .variants()
1090            .iter()
1091            .map(|v| {
1092                v.fields
1093                    .iter()
1094                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst).skip_norm_wip()))
1095                    .sum::<u64>()
1096            })
1097            .max()
1098            .unwrap_or_default(),
1099        (Err(_), ty::Adt(def, subst)) if def.is_union() => def
1100            .variants()
1101            .iter()
1102            .map(|v| {
1103                v.fields
1104                    .iter()
1105                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst).skip_norm_wip()))
1106                    .max()
1107                    .unwrap_or_default()
1108            })
1109            .max()
1110            .unwrap_or_default(),
1111        (Err(_), _) => 0,
1112    }
1113}
1114
1115#[cfg(debug_assertions)]
1116/// Asserts that the given arguments match the generic parameters of the given item.
1117fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[GenericArg<'tcx>]) {
1118    use itertools::Itertools as _;
1119    let g = tcx.generics_of(did);
1120    let parent = g.parent.map(|did| tcx.generics_of(did));
1121    let count = g.parent_count + g.own_params.len();
1122    let params = parent
1123        .map_or([].as_slice(), |p| p.own_params.as_slice())
1124        .iter()
1125        .chain(&g.own_params)
1126        .map(|x| &x.kind);
1127
1128    assert!(
1129        count == args.len(),
1130        "wrong number of arguments for `{did:?}`: expected `{count}`, found {}\n\
1131            note: the expected arguments are: `[{}]`\n\
1132            the given arguments are: `{args:#?}`",
1133        args.len(),
1134        params.clone().map(ty::GenericParamDefKind::descr).format(", "),
1135    );
1136
1137    if let Some((idx, (param, arg))) =
1138        params
1139            .clone()
1140            .zip(args.iter().map(|&x| x.kind()))
1141            .enumerate()
1142            .find(|(_, (param, arg))| match (param, arg) {
1143                (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
1144                | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
1145                | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_)) => false,
1146                (
1147                    ty::GenericParamDefKind::Lifetime
1148                    | ty::GenericParamDefKind::Type { .. }
1149                    | ty::GenericParamDefKind::Const { .. },
1150                    _,
1151                ) => true,
1152            })
1153    {
1154        panic!(
1155            "incorrect argument for `{did:?}` at index `{idx}`: expected a {}, found `{arg:?}`\n\
1156                note: the expected arguments are `[{}]`\n\
1157                the given arguments are `{args:#?}`",
1158            param.descr(),
1159            params.clone().map(ty::GenericParamDefKind::descr).format(", "),
1160        );
1161    }
1162}
1163
1164/// Returns whether `ty` is never-like; i.e., `!` (never) or an enum with zero variants.
1165pub fn is_never_like(ty: Ty<'_>) -> bool {
1166    ty.is_never() || (ty.is_enum() && ty.ty_adt_def().is_some_and(|def| def.variants().is_empty()))
1167}
1168
1169/// Makes the projection type for the named associated type in the given impl or trait impl.
1170///
1171/// This function is for associated types which are "known" to exist, and as such, will only return
1172/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions
1173/// enabled this will check that the named associated type exists, the correct number of
1174/// arguments are given, and that the correct kinds of arguments are given (lifetime,
1175/// constant or type). This will not check if type normalization would succeed.
1176pub fn make_projection<'tcx>(
1177    tcx: TyCtxt<'tcx>,
1178    container_id: DefId,
1179    assoc_ty: Symbol,
1180    args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
1181) -> Option<AliasTy<'tcx>> {
1182    fn helper<'tcx>(
1183        tcx: TyCtxt<'tcx>,
1184        container_id: DefId,
1185        assoc_ty: Symbol,
1186        args: GenericArgsRef<'tcx>,
1187    ) -> Option<AliasTy<'tcx>> {
1188        let Some(assoc_item) = tcx.associated_items(container_id).find_by_ident_and_kind(
1189            tcx,
1190            Ident::with_dummy_span(assoc_ty),
1191            AssocTag::Type,
1192            container_id,
1193        ) else {
1194            debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`");
1195            return None;
1196        };
1197        #[cfg(debug_assertions)]
1198        assert_generic_args_match(tcx, assoc_item.def_id, args);
1199
1200        let kind = if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(assoc_item.def_id)) {
1201            ty::AliasTyKind::Inherent {
1202                def_id: assoc_item.def_id,
1203            }
1204        } else {
1205            ty::AliasTyKind::Projection {
1206                def_id: assoc_item.def_id,
1207            }
1208        };
1209
1210        Some(AliasTy::new_from_args(tcx, kind, args))
1211    }
1212    helper(
1213        tcx,
1214        container_id,
1215        assoc_ty,
1216        tcx.mk_args_from_iter(args.into_iter().map(Into::into)),
1217    )
1218}
1219
1220/// Normalizes the named associated type in the given impl or trait impl.
1221///
1222/// This function is for associated types which are "known" to be valid with the given
1223/// arguments, and as such, will only return `None` when debug assertions are disabled in order
1224/// to prevent ICE's. With debug assertions enabled this will check that type normalization
1225/// succeeds as well as everything checked by `make_projection`.
1226pub fn make_normalized_projection<'tcx>(
1227    tcx: TyCtxt<'tcx>,
1228    typing_env: ty::TypingEnv<'tcx>,
1229    container_id: DefId,
1230    assoc_ty: Symbol,
1231    args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
1232) -> Option<Ty<'tcx>> {
1233    fn helper<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
1234        #[cfg(debug_assertions)]
1235        if let Some((i, arg)) = ty
1236            .args
1237            .iter()
1238            .enumerate()
1239            .find(|(_, arg)| arg.has_escaping_bound_vars())
1240        {
1241            debug_assert!(
1242                false,
1243                "args contain late-bound region at index `{i}` which can't be normalized.\n\
1244                    use `TyCtxt::instantiate_bound_regions_with_erased`\n\
1245                    note: arg is `{arg:#?}`",
1246            );
1247            return None;
1248        }
1249        match tcx.try_normalize_erasing_regions(
1250            typing_env,
1251            Unnormalized::new_wip(Ty::new_alias(tcx, ty::IsRigid::No, ty)),
1252        ) {
1253            Ok(ty) => Some(ty),
1254            Err(e) => {
1255                debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
1256                None
1257            },
1258        }
1259    }
1260    helper(tcx, typing_env, make_projection(tcx, container_id, assoc_ty, args)?)
1261}
1262
1263/// Helper to check if given type has inner mutability such as [`std::cell::Cell`] or
1264/// [`std::cell::RefCell`].
1265#[derive(Default, Debug)]
1266pub struct InteriorMut<'tcx> {
1267    ignored_def_ids: FxHashSet<DefId>,
1268    ignore_pointers: bool,
1269    tys: FxHashMap<Ty<'tcx>, Option<&'tcx ty::List<Ty<'tcx>>>>,
1270}
1271
1272impl<'tcx> InteriorMut<'tcx> {
1273    pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self {
1274        let ignored_def_ids = ignore_interior_mutability
1275            .iter()
1276            .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty))
1277            .collect();
1278
1279        Self {
1280            ignored_def_ids,
1281            ..Self::default()
1282        }
1283    }
1284
1285    /// Creates a new [`InteriorMut`] instance that ignores raw pointers.
1286    pub fn without_pointers(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self {
1287        Self {
1288            ignore_pointers: true,
1289            ..Self::new(tcx, ignore_interior_mutability)
1290        }
1291    }
1292
1293    /// Check if given type has interior mutability such as [`std::cell::Cell`] or
1294    /// [`std::cell::RefCell`] etc. and if it does, returns a chain of types that causes
1295    /// this type to be interior mutable.  False negatives may be expected for infinitely recursive
1296    /// types, and `None` will be returned there.
1297    pub fn interior_mut_ty_chain(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx ty::List<Ty<'tcx>>> {
1298        self.interior_mut_ty_chain_inner(cx, ty, 0)
1299    }
1300
1301    fn interior_mut_ty_chain_inner(
1302        &mut self,
1303        cx: &LateContext<'tcx>,
1304        ty: Ty<'tcx>,
1305        depth: usize,
1306    ) -> Option<&'tcx ty::List<Ty<'tcx>>> {
1307        if !cx.tcx.recursion_limit().value_within_limit(depth) {
1308            return None;
1309        }
1310
1311        match self.tys.entry(ty) {
1312            Entry::Occupied(o) => return *o.get(),
1313            // Temporarily insert a `None` to break cycles
1314            Entry::Vacant(v) => v.insert(None),
1315        };
1316        let depth = depth + 1;
1317
1318        let chain = match *ty.kind() {
1319            ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
1320            ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain_inner(cx, inner_ty, depth),
1321            ty::Array(inner_ty, size) if size.try_to_target_usize(cx.tcx) != Some(0) => {
1322                self.interior_mut_ty_chain_inner(cx, inner_ty, depth)
1323            },
1324            ty::Tuple(fields) => fields
1325                .iter()
1326                .find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth)),
1327            ty::Adt(def, _) if def.is_unsafe_cell() => Some(ty::List::empty()),
1328            ty::Adt(def, args) => {
1329                let is_std_collection = matches!(
1330                    cx.tcx.get_diagnostic_name(def.did()),
1331                    Some(
1332                        sym::LinkedList
1333                            | sym::Vec
1334                            | sym::VecDeque
1335                            | sym::BTreeMap
1336                            | sym::BTreeSet
1337                            | sym::HashMap
1338                            | sym::HashSet
1339                            | sym::Arc
1340                            | sym::Rc
1341                    )
1342                );
1343
1344                if is_std_collection || def.is_box() {
1345                    // Include the types from std collections that are behind pointers internally
1346                    args.types()
1347                        .find_map(|ty| self.interior_mut_ty_chain_inner(cx, ty, depth))
1348                } else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() {
1349                    None
1350                } else {
1351                    def.all_fields()
1352                        .find_map(|f| self.interior_mut_ty_chain_inner(cx, f.ty(cx.tcx, args).skip_norm_wip(), depth))
1353                }
1354            },
1355            ty::Alias(
1356                _,
1357                AliasTy {
1358                    kind: ty::Projection { .. },
1359                    ..
1360                },
1361            ) => match cx
1362                .tcx
1363                .try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(ty))
1364            {
1365                Ok(normalized_ty) if ty != normalized_ty => self.interior_mut_ty_chain_inner(cx, normalized_ty, depth),
1366                _ => None,
1367            },
1368            _ => None,
1369        };
1370
1371        chain.map(|chain| {
1372            let list = cx.tcx.mk_type_list_from_iter(chain.iter().chain([ty]));
1373            self.tys.insert(ty, Some(list));
1374            list
1375        })
1376    }
1377
1378    /// Check if given type has interior mutability such as [`std::cell::Cell`] or
1379    /// [`std::cell::RefCell`] etc.
1380    pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
1381        self.interior_mut_ty_chain(cx, ty).is_some()
1382    }
1383}
1384
1385/// Tries to normalize a projection type without erasing regions.
1386///
1387/// Returns `None` if the projection can't be built or normalized.
1388/// With `debug_assertions` enabled, this will panic instead of returning `None`.
1389pub fn make_normalized_projection_with_regions<'tcx>(
1390    tcx: TyCtxt<'tcx>,
1391    typing_env: ty::TypingEnv<'tcx>,
1392    container_id: DefId,
1393    assoc_ty: Symbol,
1394    args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
1395) -> Option<Ty<'tcx>> {
1396    fn helper<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
1397        #[cfg(debug_assertions)]
1398        if let Some((i, arg)) = ty
1399            .args
1400            .iter()
1401            .enumerate()
1402            .find(|(_, arg)| arg.has_escaping_bound_vars())
1403        {
1404            debug_assert!(
1405                false,
1406                "args contain late-bound region at index `{i}` which can't be normalized.\n\
1407                    use `TyCtxt::instantiate_bound_regions_with_erased`\n\
1408                    note: arg is `{arg:#?}`",
1409            );
1410            return None;
1411        }
1412        let cause = ObligationCause::dummy();
1413        let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
1414        match infcx
1415            .at(&cause, param_env)
1416            .query_normalize(Ty::new_alias(tcx, ty::IsRigid::No, ty))
1417        {
1418            Ok(ty) => Some(ty.value),
1419            Err(e) => {
1420                debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
1421                None
1422            },
1423        }
1424    }
1425    helper(tcx, typing_env, make_projection(tcx, container_id, assoc_ty, args)?)
1426}
1427
1428/// Normalizes the given type, without erasing regions.
1429/// If normalization fails it falls back to returning the original un-normalized type.
1430pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
1431    let cause = ObligationCause::dummy();
1432    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
1433    infcx
1434        .at(&cause, param_env)
1435        .query_normalize(ty)
1436        .map_or(ty, |ty| ty.value)
1437}
1438
1439/// Checks if the type is `core::mem::ManuallyDrop<_>`
1440pub fn is_manually_drop(ty: Ty<'_>) -> bool {
1441    ty.ty_adt_def().is_some_and(AdtDef::is_manually_drop)
1442}
1443
1444/// Returns the deref chain of a type, starting with the type itself.
1445pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'cx {
1446    iter::successors(Some(ty), |&ty| {
1447        if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
1448            && implements_trait(cx, ty, deref_did, &[])
1449        {
1450            make_normalized_projection(cx.tcx, cx.typing_env(), deref_did, sym::Target, [ty])
1451        } else {
1452            None
1453        }
1454    })
1455}
1456
1457/// Checks if a Ty<'_> has some inherent method Symbol.
1458///
1459/// This does not look for impls in the type's `Deref::Target` type.
1460/// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
1461pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {
1462    let ty_did = ty.ty_adt_def().map(AdtDef::did)?;
1463    cx.tcx.inherent_impls(ty_did).iter().find_map(|&did| {
1464        cx.tcx
1465            .associated_items(did)
1466            .filter_by_name_unhygienic_and_kind(method_name, AssocTag::Fn)
1467            .next()
1468    })
1469}
1470
1471/// Gets the type of a field by name.
1472pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
1473    match *ty.kind() {
1474        ty::Adt(def, args) if def.is_union() || def.is_struct() => def
1475            .non_enum_variant()
1476            .fields
1477            .iter()
1478            .find(|f| f.name == name)
1479            .map(|f| f.ty(tcx, args).skip_norm_wip()),
1480        ty::Tuple(args) => name.as_str().parse::<usize>().ok().and_then(|i| args.get(i).copied()),
1481        _ => None,
1482    }
1483}
1484
1485/// Attempts to find the field's `DefId` by name.
1486/// Returns `None` if the type is not an ADT or the field is not found.
1487pub fn get_field_def_id_by_name(ty: Ty<'_>, name: Symbol) -> Option<DefId> {
1488    ty.ty_adt_def()?
1489        .all_fields()
1490        .find_map(|field| if field.name == name { Some(field.did) } else { None })
1491}
1492
1493/// Check if `ty` is an `Option` and return its argument type if it is.
1494pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
1495    match *ty.kind() {
1496        ty::Adt(adt, args)
1497            if let [arg] = &**args
1498                && let Some(arg) = arg.as_type()
1499                && adt.is_diag_item(cx, sym::Option) =>
1500        {
1501            Some(arg)
1502        },
1503        _ => None,
1504    }
1505}
1506
1507/// Check if `ty` is an `Option<T>` or a `Result<T, E>` and return its argument type (`T`) if it is.
1508pub fn option_or_result_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
1509    match ty.kind() {
1510        ty::Adt(adt, args) if matches!(adt.opt_diag_name(cx), Some(sym::Option | sym::Result)) => Some(args.type_at(0)),
1511        _ => None,
1512    }
1513}
1514
1515/// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if
1516/// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze`
1517/// types, or `PhantomData` types containing any of the previous. This can be used to check whether
1518/// skipping iterating over an iterator will change its behavior.
1519pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>) -> bool {
1520    fn normalize_ty<'tcx>(cx: &LateContext<'tcx>, ty: Unnormalized<'tcx, Ty<'tcx>>) -> Ty<'tcx> {
1521        cx.tcx
1522            .try_normalize_erasing_regions(cx.typing_env(), ty)
1523            .unwrap_or(ty.skip_norm_wip())
1524    }
1525
1526    /// Check if `ty` contains mutable references or equivalent, which includes:
1527    /// - A mutable reference/pointer.
1528    /// - A reference/pointer to a non-`Freeze` type.
1529    /// - A `PhantomData` type containing any of the previous.
1530    fn has_non_owning_mutable_access_inner<'tcx>(
1531        cx: &LateContext<'tcx>,
1532        visited: &mut FxHashSet<Ty<'tcx>>,
1533        ty: Ty<'tcx>,
1534    ) -> bool {
1535        // Avoid cycles and repeated work by skipping types that have already been visited during this traversal.
1536        if !visited.insert(ty) {
1537            return false;
1538        }
1539        match ty.kind() {
1540            ty::Adt(adt_def, args) if adt_def.is_phantom_data() => args
1541                .types()
1542                .any(|arg_ty| has_non_owning_mutable_access_inner(cx, visited, arg_ty)),
1543            ty::Adt(adt_def, args) => adt_def.all_fields().any(|field| {
1544                has_non_owning_mutable_access_inner(cx, visited, normalize_ty(cx, field.ty(cx.tcx, args)))
1545            }),
1546            ty::Array(elem_ty, _) | ty::Slice(elem_ty) => has_non_owning_mutable_access_inner(cx, visited, *elem_ty),
1547            ty::RawPtr(pointee_ty, mutability) | ty::Ref(_, pointee_ty, mutability) => {
1548                mutability.is_mut() || !pointee_ty.is_freeze(cx.tcx, cx.typing_env())
1549            },
1550            ty::Closure(_, closure_args) => {
1551                matches!(closure_args.types().next_back(),
1552                         Some(captures) if has_non_owning_mutable_access_inner(cx, visited, captures))
1553            },
1554            ty::Tuple(tuple_args) => tuple_args
1555                .iter()
1556                .any(|arg_ty| has_non_owning_mutable_access_inner(cx, visited, arg_ty)),
1557            _ => false,
1558        }
1559    }
1560
1561    has_non_owning_mutable_access_inner(cx, &mut FxHashSet::default(), iter_ty)
1562}
1563
1564/// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec<T>`.
1565pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
1566    ty.is_slice() || ty.is_array() || ty.is_diag_item(cx, sym::Vec)
1567}
1568
1569/// Attempts to find the field's index by name, for unions, structs and tuples.
1570/// Note: for tuples, `name` is just parsed as an index.
1571pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> {
1572    match *ty.kind() {
1573        ty::Adt(def, _) if def.is_union() || def.is_struct() => {
1574            def.non_enum_variant().fields.iter().position(|f| f.name == name)
1575        },
1576        ty::Tuple(_) => name.as_str().parse::<usize>().ok(),
1577        _ => None,
1578    }
1579}
1580
1581/// Checks if the adjustments contain a mutable dereference of a `ManuallyDrop<_>`.
1582pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool {
1583    adjustments.iter().any(|a| {
1584        let ty = mem::replace(&mut ty, a.target);
1585        matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(op)) if op.mutbl == Mutability::Mut)
1586            && is_manually_drop(ty)
1587    })
1588}