1#![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
47pub 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
55pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
57 cx.type_is_copy_modulo_regions(ty)
58}
59
60pub 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
67pub 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
81pub 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
90pub 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 ty::ClauseKind::Trait(trait_predicate)
134 if trait_predicate
135 .trait_ref
136 .args
137 .types()
138 .skip(1) .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) =>
140 {
141 return true;
142 },
143 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 let mut seen = FxHashSet::default();
166 contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen)
167}
168
169pub 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
177pub 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
189pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
191 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
231pub 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
253pub 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
268pub 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 assert!(!ty.has_infer());
279
280 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
319pub 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
327pub fn opt_must_use_path<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<MustUsePath> {
333 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
350pub fn describe_must_use_type(cx: &LateContext<'_>, path: &MustUsePath) -> String {
352 describe_must_use_type_inner(cx, path, "", "", 1)
353}
354
355fn 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
436pub 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
445pub 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
457pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
459 matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
460}
461
462pub 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 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 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 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
512pub 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
517pub 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
531pub 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
546pub 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
579pub 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 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 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 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 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 FieldsShape::Arbitrary { offsets, .. } => {
618 (0..offsets.len()).all(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i)))
619 },
620 FieldsShape::Union(count) => {
622 (0..count.get()).any(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i)))
623 },
624 },
625 Variants::Empty => true,
627 Variants::Multiple { .. } => false,
629 },
630 }
631}
632
633fn 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 ty::Array(component, len) => {
640 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 ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
648 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 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 ty::Adt(adt, _) if adt.is_enum() => false,
678 _ => false,
680 }
681}
682
683pub 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#[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 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 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 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
761pub 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
773pub 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 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 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 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 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
936pub 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
949pub 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
961pub 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 pub fields_size: Vec<(usize, u64)>,
1010}
1011
1012impl AdtVariantInfo {
1013 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
1040pub 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
1074pub 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)]
1120fn 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
1168pub 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
1173pub 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
1224pub 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#[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 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 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 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 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
1436pub fn is_manually_drop(ty: Ty<'_>) -> bool {
1438 ty.ty_adt_def().is_some_and(AdtDef::is_manually_drop)
1439}
1440
1441pub 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
1454pub 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
1469pub 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
1490pub 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
1504pub 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
1512pub 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 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
1561pub 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
1576pub 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}