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