1use std::cell::{Ref, RefCell};
9use std::ops::Deref;
10use std::slice::from_ref;
11
12use hir::Expr;
13use hir::def::DefKind;
14use hir::pat_util::EnumerateAndAdjustIterator as _;
15use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
16use rustc_ast::UnsafeBinderCastKind;
17use rustc_data_structures::fx::FxIndexMap;
18use rustc_hir::def::{CtorOf, Res};
19use rustc_hir::def_id::LocalDefId;
20use rustc_hir::{self as hir, HirId, PatExpr, PatExprKind, PatKind};
21use rustc_lint::LateContext;
22use rustc_middle::hir::place::ProjectionKind;
23pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
25use rustc_middle::mir::FakeReadCause;
26use rustc_middle::ty::{
27 self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
28};
29use rustc_middle::{bug, span_bug};
30use rustc_span::{ErrorGuaranteed, Span};
31use rustc_trait_selection::infer::InferCtxtExt;
32use tracing::{debug, instrument, trace};
33
34use crate::fn_ctxt::FnCtxt;
35
36pub trait Delegate<'tcx> {
39 fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);
50
51 fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);
65
66 fn borrow(
69 &mut self,
70 place_with_id: &PlaceWithHirId<'tcx>,
71 diag_expr_id: HirId,
72 bk: ty::BorrowKind,
73 );
74
75 fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
82 self.borrow(place_with_id, diag_expr_id, ty::BorrowKind::Immutable)
85 }
86
87 fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);
90
91 fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
95 self.mutate(binding_place, diag_expr_id)
98 }
99
100 fn fake_read(
102 &mut self,
103 place_with_id: &PlaceWithHirId<'tcx>,
104 cause: FakeReadCause,
105 diag_expr_id: HirId,
106 );
107}
108
109impl<'tcx, D: Delegate<'tcx>> Delegate<'tcx> for &mut D {
110 fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
111 (**self).consume(place_with_id, diag_expr_id)
112 }
113
114 fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
115 (**self).use_cloned(place_with_id, diag_expr_id)
116 }
117
118 fn borrow(
119 &mut self,
120 place_with_id: &PlaceWithHirId<'tcx>,
121 diag_expr_id: HirId,
122 bk: ty::BorrowKind,
123 ) {
124 (**self).borrow(place_with_id, diag_expr_id, bk)
125 }
126
127 fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
128 (**self).copy(place_with_id, diag_expr_id)
129 }
130
131 fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
132 (**self).mutate(assignee_place, diag_expr_id)
133 }
134
135 fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
136 (**self).bind(binding_place, diag_expr_id)
137 }
138
139 fn fake_read(
140 &mut self,
141 place_with_id: &PlaceWithHirId<'tcx>,
142 cause: FakeReadCause,
143 diag_expr_id: HirId,
144 ) {
145 (**self).fake_read(place_with_id, cause, diag_expr_id)
146 }
147}
148
149pub trait TypeInformationCtxt<'tcx> {
152 type TypeckResults<'a>: Deref<Target = ty::TypeckResults<'tcx>>
153 where
154 Self: 'a;
155
156 type Error;
157
158 fn typeck_results(&self) -> Self::TypeckResults<'_>;
159
160 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T;
161
162 fn structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
163
164 fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error;
165
166 fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error>;
167
168 fn tainted_by_errors(&self) -> Result<(), Self::Error>;
169
170 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool;
171
172 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool;
173
174 fn body_owner_def_id(&self) -> LocalDefId;
175
176 fn tcx(&self) -> TyCtxt<'tcx>;
177}
178
179impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
180 type TypeckResults<'a>
181 = Ref<'a, ty::TypeckResults<'tcx>>
182 where
183 Self: 'a;
184
185 type Error = ErrorGuaranteed;
186
187 fn typeck_results(&self) -> Self::TypeckResults<'_> {
188 self.typeck_results.borrow()
189 }
190
191 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
192 self.infcx.resolve_vars_if_possible(t)
193 }
194
195 fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
196 (**self).structurally_resolve_type(sp, ty)
197 }
198
199 fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error {
200 self.dcx().span_delayed_bug(span, msg.to_string())
201 }
202
203 fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error> {
204 ty.error_reported()
205 }
206
207 fn tainted_by_errors(&self) -> Result<(), ErrorGuaranteed> {
208 if let Some(guar) = self.infcx.tainted_by_errors() { Err(guar) } else { Ok(()) }
209 }
210
211 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
212 self.infcx.type_is_copy_modulo_regions(self.param_env, ty)
213 }
214
215 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
216 self.infcx.type_is_use_cloned_modulo_regions(self.param_env, ty)
217 }
218
219 fn body_owner_def_id(&self) -> LocalDefId {
220 self.body_id
221 }
222
223 fn tcx(&self) -> TyCtxt<'tcx> {
224 self.tcx
225 }
226}
227
228impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
229 type TypeckResults<'a>
230 = &'tcx ty::TypeckResults<'tcx>
231 where
232 Self: 'a;
233
234 type Error = !;
235
236 fn typeck_results(&self) -> Self::TypeckResults<'_> {
237 self.0.maybe_typeck_results().expect("expected typeck results")
238 }
239
240 fn structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
241 ty
243 }
244
245 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
246 t
247 }
248
249 fn report_bug(&self, span: Span, msg: impl ToString) -> ! {
250 span_bug!(span, "{}", msg.to_string())
251 }
252
253 fn error_reported_in_ty(&self, _ty: Ty<'tcx>) -> Result<(), !> {
254 Ok(())
255 }
256
257 fn tainted_by_errors(&self) -> Result<(), !> {
258 Ok(())
259 }
260
261 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
262 self.0.type_is_copy_modulo_regions(ty)
263 }
264
265 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
266 self.0.type_is_use_cloned_modulo_regions(ty)
267 }
268
269 fn body_owner_def_id(&self) -> LocalDefId {
270 self.1
271 }
272
273 fn tcx(&self) -> TyCtxt<'tcx> {
274 self.0.tcx
275 }
276}
277
278pub struct ExprUseVisitor<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> {
282 cx: Cx,
283 delegate: RefCell<D>,
286 upvars: Option<&'tcx FxIndexMap<HirId, hir::Upvar>>,
287}
288
289impl<'a, 'tcx, D: Delegate<'tcx>> ExprUseVisitor<'tcx, (&'a LateContext<'tcx>, LocalDefId), D> {
290 pub fn for_clippy(cx: &'a LateContext<'tcx>, body_def_id: LocalDefId, delegate: D) -> Self {
291 Self::new((cx, body_def_id), delegate)
292 }
293}
294
295impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
296 pub(crate) fn new(cx: Cx, delegate: D) -> Self {
302 ExprUseVisitor {
303 delegate: RefCell::new(delegate),
304 upvars: cx.tcx().upvars_mentioned(cx.body_owner_def_id()),
305 cx,
306 }
307 }
308
309 pub fn consume_body(&self, body: &hir::Body<'_>) -> Result<(), Cx::Error> {
310 for param in body.params {
311 let param_ty = self.pat_ty_adjusted(param.pat)?;
312 debug!("consume_body: param_ty = {:?}", param_ty);
313
314 let param_place = self.cat_rvalue(param.hir_id, param_ty);
315
316 self.walk_irrefutable_pat(¶m_place, param.pat)?;
317 }
318
319 self.consume_expr(body.value)?;
320
321 Ok(())
322 }
323
324 #[instrument(skip(self), level = "debug")]
325 fn consume_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
326 if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
327 self.delegate.borrow_mut().copy(place_with_id, diag_expr_id);
328 } else {
329 self.delegate.borrow_mut().consume(place_with_id, diag_expr_id);
330 }
331 }
332
333 #[instrument(skip(self), level = "debug")]
334 pub fn consume_clone_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
335 if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
342 self.delegate.borrow_mut().copy(place_with_id, diag_expr_id);
343 } else if self.cx.type_is_use_cloned_modulo_regions(place_with_id.place.ty()) {
344 self.delegate.borrow_mut().use_cloned(place_with_id, diag_expr_id);
345 } else {
346 self.delegate.borrow_mut().consume(place_with_id, diag_expr_id);
347 }
348 }
349
350 fn consume_exprs(&self, exprs: &[hir::Expr<'_>]) -> Result<(), Cx::Error> {
351 for expr in exprs {
352 self.consume_expr(expr)?;
353 }
354
355 Ok(())
356 }
357
358 #[instrument(skip(self), level = "debug")]
360 pub fn consume_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
361 let place_with_id = self.cat_expr(expr)?;
362 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
363 self.walk_expr(expr)?;
364 Ok(())
365 }
366
367 #[instrument(skip(self), level = "debug")]
368 pub fn consume_or_clone_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
369 let place_with_id = self.cat_expr(expr)?;
370 self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
371 self.walk_expr(expr)?;
372 Ok(())
373 }
374
375 fn mutate_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
376 let place_with_id = self.cat_expr(expr)?;
377 self.delegate.borrow_mut().mutate(&place_with_id, place_with_id.hir_id);
378 self.walk_expr(expr)?;
379 Ok(())
380 }
381
382 #[instrument(skip(self), level = "debug")]
383 fn borrow_expr(&self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) -> Result<(), Cx::Error> {
384 let place_with_id = self.cat_expr(expr)?;
385 self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
386 self.walk_expr(expr)
387 }
388
389 #[instrument(skip(self), level = "debug")]
390 pub fn walk_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
391 self.walk_adjustment(expr)?;
392
393 match expr.kind {
394 hir::ExprKind::Path(_) => {}
395
396 hir::ExprKind::Type(subexpr, _) => {
397 self.walk_expr(subexpr)?;
398 }
399
400 hir::ExprKind::UnsafeBinderCast(_, subexpr, _) => {
401 self.walk_expr(subexpr)?;
402 }
403
404 hir::ExprKind::Unary(hir::UnOp::Deref, base) => {
405 self.walk_expr(base)?;
407 }
408
409 hir::ExprKind::Field(base, _) => {
410 self.walk_expr(base)?;
412 }
413
414 hir::ExprKind::Index(lhs, rhs, _) => {
415 self.walk_expr(lhs)?;
417 self.consume_expr(rhs)?;
418 }
419
420 hir::ExprKind::Call(callee, args) => {
421 self.consume_expr(callee)?;
423 self.consume_exprs(args)?;
424 }
425
426 hir::ExprKind::Use(expr, _) => {
427 self.consume_or_clone_expr(expr)?;
428 }
429
430 hir::ExprKind::MethodCall(.., receiver, args, _) => {
431 self.consume_expr(receiver)?;
433 self.consume_exprs(args)?;
434 }
435
436 hir::ExprKind::Struct(_, fields, ref opt_with) => {
437 self.walk_struct_expr(fields, opt_with)?;
438 }
439
440 hir::ExprKind::Tup(exprs) => {
441 self.consume_exprs(exprs)?;
442 }
443
444 hir::ExprKind::If(cond_expr, then_expr, ref opt_else_expr) => {
445 self.consume_expr(cond_expr)?;
446 self.consume_expr(then_expr)?;
447 if let Some(else_expr) = *opt_else_expr {
448 self.consume_expr(else_expr)?;
449 }
450 }
451
452 hir::ExprKind::Let(hir::LetExpr { pat, init, .. }) => {
453 self.walk_local(init, pat, None, || self.borrow_expr(init, BorrowKind::Immutable))?;
454 }
455
456 hir::ExprKind::Match(discr, arms, _) => {
457 let discr_place = self.cat_expr(discr)?;
458 self.maybe_read_scrutinee(
459 discr,
460 discr_place.clone(),
461 arms.iter().map(|arm| arm.pat),
462 )?;
463
464 for arm in arms {
466 self.walk_arm(&discr_place, arm)?;
467 }
468 }
469
470 hir::ExprKind::Array(exprs) => {
471 self.consume_exprs(exprs)?;
472 }
473
474 hir::ExprKind::AddrOf(_, m, base) => {
475 let bk = ty::BorrowKind::from_mutbl(m);
479 self.borrow_expr(base, bk)?;
480 }
481
482 hir::ExprKind::InlineAsm(asm) => {
483 for (op, _op_sp) in asm.operands {
484 match op {
485 hir::InlineAsmOperand::In { expr, .. } => {
486 self.consume_expr(expr)?;
487 }
488 hir::InlineAsmOperand::Out { expr: Some(expr), .. }
489 | hir::InlineAsmOperand::InOut { expr, .. } => {
490 self.mutate_expr(expr)?;
491 }
492 hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
493 self.consume_expr(in_expr)?;
494 if let Some(out_expr) = out_expr {
495 self.mutate_expr(out_expr)?;
496 }
497 }
498 hir::InlineAsmOperand::Out { expr: None, .. }
499 | hir::InlineAsmOperand::Const { .. }
500 | hir::InlineAsmOperand::SymFn { .. }
501 | hir::InlineAsmOperand::SymStatic { .. } => {}
502 hir::InlineAsmOperand::Label { block } => {
503 self.walk_block(block)?;
504 }
505 }
506 }
507 }
508
509 hir::ExprKind::Continue(..)
510 | hir::ExprKind::Lit(..)
511 | hir::ExprKind::ConstBlock(..)
512 | hir::ExprKind::OffsetOf(..)
513 | hir::ExprKind::Err(_) => {}
514
515 hir::ExprKind::Loop(blk, ..) => {
516 self.walk_block(blk)?;
517 }
518
519 hir::ExprKind::Unary(_, lhs) => {
520 self.consume_expr(lhs)?;
521 }
522
523 hir::ExprKind::Binary(_, lhs, rhs) => {
524 self.consume_expr(lhs)?;
525 self.consume_expr(rhs)?;
526 }
527
528 hir::ExprKind::Block(blk, _) => {
529 self.walk_block(blk)?;
530 }
531
532 hir::ExprKind::Break(_, ref opt_expr) | hir::ExprKind::Ret(ref opt_expr) => {
533 if let Some(expr) = *opt_expr {
534 self.consume_expr(expr)?;
535 }
536 }
537
538 hir::ExprKind::Become(call) => {
539 self.consume_expr(call)?;
540 }
541
542 hir::ExprKind::Assign(lhs, rhs, _) => {
543 self.mutate_expr(lhs)?;
544 self.consume_expr(rhs)?;
545 }
546
547 hir::ExprKind::Cast(base, _) => {
548 self.consume_expr(base)?;
549 }
550
551 hir::ExprKind::DropTemps(expr) => {
552 self.consume_expr(expr)?;
553 }
554
555 hir::ExprKind::AssignOp(_, lhs, rhs) => {
556 if self.cx.typeck_results().is_method_call(expr) {
557 self.consume_expr(lhs)?;
558 } else {
559 self.mutate_expr(lhs)?;
560 }
561 self.consume_expr(rhs)?;
562 }
563
564 hir::ExprKind::Repeat(base, _) => {
565 self.consume_expr(base)?;
566 }
567
568 hir::ExprKind::Closure(closure) => {
569 self.walk_captures(closure)?;
570 }
571
572 hir::ExprKind::Yield(value, _) => {
573 self.consume_expr(value)?;
574 }
575 }
576
577 Ok(())
578 }
579
580 fn walk_stmt(&self, stmt: &hir::Stmt<'_>) -> Result<(), Cx::Error> {
581 match stmt.kind {
582 hir::StmtKind::Let(hir::LetStmt { pat, init: Some(expr), els, .. }) => {
583 self.walk_local(expr, pat, *els, || Ok(()))?;
584 }
585
586 hir::StmtKind::Let(_) => {}
587
588 hir::StmtKind::Item(_) => {
589 }
592
593 hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => {
594 self.consume_expr(expr)?;
595 }
596 }
597
598 Ok(())
599 }
600
601 fn maybe_read_scrutinee<'t>(
602 &self,
603 discr: &Expr<'_>,
604 discr_place: PlaceWithHirId<'tcx>,
605 pats: impl Iterator<Item = &'t hir::Pat<'t>>,
606 ) -> Result<(), Cx::Error> {
607 let mut needs_to_be_read = false;
612 for pat in pats {
613 self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
614 match &pat.kind {
615 PatKind::Missing => unreachable!(),
616 PatKind::Binding(.., opt_sub_pat) => {
617 if opt_sub_pat.is_none() {
620 needs_to_be_read = true;
621 }
622 }
623 PatKind::Never => {
624 needs_to_be_read = true;
627 }
628 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
629 let res = self.cx.typeck_results().qpath_res(qpath, *hir_id);
633 match res {
634 Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
635 needs_to_be_read = true;
640 }
641 _ => {
642 needs_to_be_read |=
645 self.is_multivariant_adt(place.place.ty(), *span);
646 }
647 }
648 }
649 PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
650 let place_ty = place.place.ty();
657 needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span);
658 }
659 PatKind::Expr(_) | PatKind::Range(..) => {
660 needs_to_be_read = true;
663 }
664 PatKind::Slice(lhs, wild, rhs) => {
665 if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
667 || place.place.ty().peel_refs().is_array()
670 {
671 } else {
672 needs_to_be_read = true;
673 }
674 }
675 PatKind::Or(_)
676 | PatKind::Box(_)
677 | PatKind::Deref(_)
678 | PatKind::Ref(..)
679 | PatKind::Guard(..)
680 | PatKind::Wild
681 | PatKind::Err(_) => {
682 }
687 }
688
689 Ok(())
690 })?
691 }
692
693 if needs_to_be_read {
694 self.borrow_expr(discr, BorrowKind::Immutable)?;
695 } else {
696 let closure_def_id = match discr_place.place.base {
697 PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
698 _ => None,
699 };
700
701 self.delegate.borrow_mut().fake_read(
702 &discr_place,
703 FakeReadCause::ForMatchedPlace(closure_def_id),
704 discr_place.hir_id,
705 );
706
707 self.walk_expr(discr)?;
710 }
711 Ok(())
712 }
713
714 fn walk_local<F>(
715 &self,
716 expr: &hir::Expr<'_>,
717 pat: &hir::Pat<'_>,
718 els: Option<&hir::Block<'_>>,
719 mut f: F,
720 ) -> Result<(), Cx::Error>
721 where
722 F: FnMut() -> Result<(), Cx::Error>,
723 {
724 self.walk_expr(expr)?;
725 let expr_place = self.cat_expr(expr)?;
726 f()?;
727 if let Some(els) = els {
728 self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter())?;
730 self.walk_block(els)?;
731 }
732 self.walk_irrefutable_pat(&expr_place, pat)?;
733 Ok(())
734 }
735
736 #[instrument(skip(self), level = "debug")]
739 fn walk_block(&self, blk: &hir::Block<'_>) -> Result<(), Cx::Error> {
740 for stmt in blk.stmts {
741 self.walk_stmt(stmt)?;
742 }
743
744 if let Some(tail_expr) = blk.expr {
745 self.consume_expr(tail_expr)?;
746 }
747
748 Ok(())
749 }
750
751 fn walk_struct_expr<'hir>(
752 &self,
753 fields: &[hir::ExprField<'_>],
754 opt_with: &hir::StructTailExpr<'hir>,
755 ) -> Result<(), Cx::Error> {
756 for field in fields {
758 self.consume_expr(field.expr)?;
759
760 if self.cx.typeck_results().opt_field_index(field.hir_id).is_none() {
762 self.cx
763 .tcx()
764 .dcx()
765 .span_delayed_bug(field.span, "couldn't resolve index for field");
766 }
767 }
768
769 let with_expr = match *opt_with {
770 hir::StructTailExpr::Base(w) => &*w,
771 hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => {
772 return Ok(());
773 }
774 };
775
776 let with_place = self.cat_expr(with_expr)?;
777
778 match self.cx.structurally_resolve_type(with_expr.span, with_place.place.ty()).kind() {
781 ty::Adt(adt, args) if adt.is_struct() => {
782 for (f_index, with_field) in adt.non_enum_variant().fields.iter_enumerated() {
784 let is_mentioned = fields.iter().any(|f| {
785 self.cx.typeck_results().opt_field_index(f.hir_id) == Some(f_index)
786 });
787 if !is_mentioned {
788 let field_place = self.cat_projection(
789 with_expr.hir_id,
790 with_place.clone(),
791 with_field.ty(self.cx.tcx(), args),
792 ProjectionKind::Field(f_index, FIRST_VARIANT),
793 );
794 self.consume_or_copy(&field_place, field_place.hir_id);
795 }
796 }
797 }
798 _ => {
799 if self.cx.tainted_by_errors().is_ok() {
804 span_bug!(with_expr.span, "with expression doesn't evaluate to a struct");
805 }
806 }
807 }
808
809 self.walk_expr(with_expr)?;
812
813 Ok(())
814 }
815
816 fn walk_adjustment(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
820 let typeck_results = self.cx.typeck_results();
821 let adjustments = typeck_results.expr_adjustments(expr);
822 let mut place_with_id = self.cat_expr_unadjusted(expr)?;
823 for adjustment in adjustments {
824 debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
825 match adjustment.kind {
826 adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => {
827 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
830 }
831
832 adjustment::Adjust::Deref(None) => {}
833
834 adjustment::Adjust::Deref(Some(ref deref)) => {
840 let bk = ty::BorrowKind::from_mutbl(deref.mutbl);
841 self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
842 }
843
844 adjustment::Adjust::Borrow(ref autoref) => {
845 self.walk_autoref(expr, &place_with_id, autoref);
846 }
847
848 adjustment::Adjust::ReborrowPin(mutbl) => {
849 let bk = match mutbl {
852 ty::Mutability::Not => ty::BorrowKind::Immutable,
853 ty::Mutability::Mut => ty::BorrowKind::Mutable,
854 };
855 self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
856 }
857 }
858 place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
859 }
860
861 Ok(())
862 }
863
864 fn walk_autoref(
868 &self,
869 expr: &hir::Expr<'_>,
870 base_place: &PlaceWithHirId<'tcx>,
871 autoref: &adjustment::AutoBorrow,
872 ) {
873 debug!(
874 "walk_autoref(expr.hir_id={} base_place={:?} autoref={:?})",
875 expr.hir_id, base_place, autoref
876 );
877
878 match *autoref {
879 adjustment::AutoBorrow::Ref(m) => {
880 self.delegate.borrow_mut().borrow(
881 base_place,
882 base_place.hir_id,
883 ty::BorrowKind::from_mutbl(m.into()),
884 );
885 }
886
887 adjustment::AutoBorrow::RawPtr(m) => {
888 debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place);
889
890 self.delegate.borrow_mut().borrow(
891 base_place,
892 base_place.hir_id,
893 ty::BorrowKind::from_mutbl(m),
894 );
895 }
896 }
897 }
898
899 fn walk_arm(
900 &self,
901 discr_place: &PlaceWithHirId<'tcx>,
902 arm: &hir::Arm<'_>,
903 ) -> Result<(), Cx::Error> {
904 let closure_def_id = match discr_place.place.base {
905 PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
906 _ => None,
907 };
908
909 self.delegate.borrow_mut().fake_read(
910 discr_place,
911 FakeReadCause::ForMatchedPlace(closure_def_id),
912 discr_place.hir_id,
913 );
914 self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?;
915
916 if let Some(ref e) = arm.guard {
917 self.consume_expr(e)?;
918 }
919
920 self.consume_expr(arm.body)?;
921 Ok(())
922 }
923
924 fn walk_irrefutable_pat(
927 &self,
928 discr_place: &PlaceWithHirId<'tcx>,
929 pat: &hir::Pat<'_>,
930 ) -> Result<(), Cx::Error> {
931 let closure_def_id = match discr_place.place.base {
932 PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
933 _ => None,
934 };
935
936 self.delegate.borrow_mut().fake_read(
937 discr_place,
938 FakeReadCause::ForLet(closure_def_id),
939 discr_place.hir_id,
940 );
941 self.walk_pat(discr_place, pat, false)?;
942 Ok(())
943 }
944
945 #[instrument(skip(self), level = "debug")]
947 fn walk_pat(
948 &self,
949 discr_place: &PlaceWithHirId<'tcx>,
950 pat: &hir::Pat<'_>,
951 has_guard: bool,
952 ) -> Result<(), Cx::Error> {
953 let tcx = self.cx.tcx();
954 self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
955 match pat.kind {
956 PatKind::Binding(_, canonical_id, ..) => {
957 debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
958 let bm = self
959 .cx
960 .typeck_results()
961 .extract_binding_mode(tcx.sess, pat.hir_id, pat.span);
962 debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm);
963
964 let pat_ty = self.node_ty(pat.hir_id)?;
966 debug!("walk_pat: pat_ty={:?}", pat_ty);
967
968 let def = Res::Local(canonical_id);
969 if let Ok(ref binding_place) = self.cat_res(pat.hir_id, pat.span, pat_ty, def) {
970 self.delegate.borrow_mut().bind(binding_place, binding_place.hir_id);
971 }
972
973 if has_guard {
977 self.delegate.borrow_mut().borrow(
978 place,
979 discr_place.hir_id,
980 BorrowKind::Immutable,
981 );
982 }
983
984 match bm.0 {
989 hir::ByRef::Yes(_, m) => {
990 let bk = ty::BorrowKind::from_mutbl(m);
991 self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
992 }
993 hir::ByRef::No => {
994 debug!("walk_pat binding consuming pat");
995 self.consume_or_copy(place, discr_place.hir_id);
996 }
997 }
998 }
999 PatKind::Deref(subpattern) => {
1000 if let hir::ByRef::Yes(_, mutability) =
1008 self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern)
1009 {
1010 let bk = ty::BorrowKind::from_mutbl(mutability);
1011 self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
1012 }
1013 }
1014 PatKind::Never => {
1015 self.delegate.borrow_mut().borrow(
1018 place,
1019 discr_place.hir_id,
1020 BorrowKind::Immutable,
1021 );
1022 }
1023 _ => {}
1024 }
1025
1026 Ok(())
1027 })
1028 }
1029
1030 #[instrument(skip(self), level = "debug")]
1050 fn walk_captures(&self, closure_expr: &hir::Closure<'_>) -> Result<(), Cx::Error> {
1051 fn upvar_is_local_variable(
1052 upvars: Option<&FxIndexMap<HirId, hir::Upvar>>,
1053 upvar_id: HirId,
1054 body_owner_is_closure: bool,
1055 ) -> bool {
1056 upvars.map(|upvars| !upvars.contains_key(&upvar_id)).unwrap_or(body_owner_is_closure)
1057 }
1058
1059 let tcx = self.cx.tcx();
1060 let closure_def_id = closure_expr.def_id;
1061 let body_owner_is_closure = matches!(
1063 tcx.hir_body_owner_kind(self.cx.body_owner_def_id()),
1064 hir::BodyOwnerKind::Closure
1065 );
1066
1067 if let Some(fake_reads) = self.cx.typeck_results().closure_fake_reads.get(&closure_def_id) {
1070 for (fake_read, cause, hir_id) in fake_reads.iter() {
1071 match fake_read.base {
1072 PlaceBase::Upvar(upvar_id) => {
1073 if upvar_is_local_variable(
1074 self.upvars,
1075 upvar_id.var_path.hir_id,
1076 body_owner_is_closure,
1077 ) {
1078 continue;
1095 }
1096 }
1097 _ => {
1098 bug!(
1099 "Do not know how to get HirId out of Rvalue and StaticItem {:?}",
1100 fake_read.base
1101 );
1102 }
1103 };
1104 self.delegate.borrow_mut().fake_read(
1105 &PlaceWithHirId { place: fake_read.clone(), hir_id: *hir_id },
1106 *cause,
1107 *hir_id,
1108 );
1109 }
1110 }
1111
1112 if let Some(min_captures) =
1113 self.cx.typeck_results().closure_min_captures.get(&closure_def_id)
1114 {
1115 for (var_hir_id, min_list) in min_captures.iter() {
1116 if self
1117 .upvars
1118 .map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id))
1119 {
1120 continue;
1124 }
1125 for captured_place in min_list {
1126 let place = &captured_place.place;
1127 let capture_info = captured_place.info;
1128
1129 let place_base = if body_owner_is_closure {
1130 PlaceBase::Upvar(ty::UpvarId::new(*var_hir_id, self.cx.body_owner_def_id()))
1132 } else {
1133 PlaceBase::Local(*var_hir_id)
1136 };
1137 let closure_hir_id = tcx.local_def_id_to_hir_id(closure_def_id);
1138 let place_with_id = PlaceWithHirId::new(
1139 capture_info
1140 .path_expr_id
1141 .unwrap_or(capture_info.capture_kind_expr_id.unwrap_or(closure_hir_id)),
1142 place.base_ty,
1143 place_base,
1144 place.projections.clone(),
1145 );
1146
1147 match capture_info.capture_kind {
1148 ty::UpvarCapture::ByValue => {
1149 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
1150 }
1151 ty::UpvarCapture::ByUse => {
1152 self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
1153 }
1154 ty::UpvarCapture::ByRef(upvar_borrow) => {
1155 self.delegate.borrow_mut().borrow(
1156 &place_with_id,
1157 place_with_id.hir_id,
1158 upvar_borrow,
1159 );
1160 }
1161 }
1162 }
1163 }
1164 }
1165
1166 Ok(())
1167 }
1168}
1169
1170impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
1180 fn expect_and_resolve_type(
1181 &self,
1182 id: HirId,
1183 ty: Option<Ty<'tcx>>,
1184 ) -> Result<Ty<'tcx>, Cx::Error> {
1185 match ty {
1186 Some(ty) => {
1187 let ty = self.cx.resolve_vars_if_possible(ty);
1188 self.cx.error_reported_in_ty(ty)?;
1189 Ok(ty)
1190 }
1191 None => {
1192 self.cx.tainted_by_errors()?;
1194 bug!("no type for node {} in ExprUseVisitor", self.cx.tcx().hir_id_to_string(id));
1195 }
1196 }
1197 }
1198
1199 fn node_ty(&self, hir_id: HirId) -> Result<Ty<'tcx>, Cx::Error> {
1200 self.expect_and_resolve_type(hir_id, self.cx.typeck_results().node_type_opt(hir_id))
1201 }
1202
1203 fn expr_ty(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1204 self.expect_and_resolve_type(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr))
1205 }
1206
1207 fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1208 self.expect_and_resolve_type(
1209 expr.hir_id,
1210 self.cx.typeck_results().expr_ty_adjusted_opt(expr),
1211 )
1212 }
1213
1214 fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1225 if let Some(vec) = self.cx.typeck_results().pat_adjustments().get(pat.hir_id) {
1230 if let Some(first_adjust) = vec.first() {
1231 debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
1232 return Ok(first_adjust.source);
1233 }
1234 } else if let PatKind::Ref(subpat, _, _) = pat.kind
1235 && self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id)
1236 {
1237 return self.pat_ty_adjusted(subpat);
1238 }
1239
1240 self.pat_ty_unadjusted(pat)
1241 }
1242
1243 fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1245 let base_ty = self.node_ty(pat.hir_id)?;
1246 trace!(?base_ty);
1247
1248 match pat.kind {
1251 PatKind::Binding(..) => {
1252 let bm = *self
1253 .cx
1254 .typeck_results()
1255 .pat_binding_modes()
1256 .get(pat.hir_id)
1257 .expect("missing binding mode");
1258
1259 if let hir::ByRef::Yes(pinnedness, _) = bm.0 {
1260 let base_ty = if pinnedness.is_pinned() {
1261 base_ty.pinned_ty().ok_or_else(|| {
1262 debug!("By-pin-ref binding of non-`Pin` type: {base_ty:?}");
1263 self.cx.report_bug(pat.span, "by-pin-ref binding of non-`Pin` type")
1264 })?
1265 } else {
1266 base_ty
1267 };
1268 match self.cx.structurally_resolve_type(pat.span, base_ty).builtin_deref(false)
1272 {
1273 Some(ty) => Ok(ty),
1274 None => {
1275 debug!("By-ref binding of non-derefable type: {base_ty:?}");
1276 Err(self
1277 .cx
1278 .report_bug(pat.span, "by-ref binding of non-derefable type"))
1279 }
1280 }
1281 } else {
1282 Ok(base_ty)
1283 }
1284 }
1285 _ => Ok(base_ty),
1286 }
1287 }
1288
1289 fn cat_expr(&self, expr: &hir::Expr<'_>) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1290 self.cat_expr_(expr, self.cx.typeck_results().expr_adjustments(expr))
1291 }
1292
1293 fn cat_expr_(
1296 &self,
1297 expr: &hir::Expr<'_>,
1298 adjustments: &[adjustment::Adjustment<'tcx>],
1299 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1300 match adjustments.split_last() {
1301 None => self.cat_expr_unadjusted(expr),
1302 Some((adjustment, previous)) => {
1303 self.cat_expr_adjusted_with(expr, || self.cat_expr_(expr, previous), adjustment)
1304 }
1305 }
1306 }
1307
1308 fn cat_expr_adjusted(
1309 &self,
1310 expr: &hir::Expr<'_>,
1311 previous: PlaceWithHirId<'tcx>,
1312 adjustment: &adjustment::Adjustment<'tcx>,
1313 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1314 self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
1315 }
1316
1317 fn cat_expr_adjusted_with<F>(
1318 &self,
1319 expr: &hir::Expr<'_>,
1320 previous: F,
1321 adjustment: &adjustment::Adjustment<'tcx>,
1322 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error>
1323 where
1324 F: FnOnce() -> Result<PlaceWithHirId<'tcx>, Cx::Error>,
1325 {
1326 let target = self.cx.resolve_vars_if_possible(adjustment.target);
1327 match adjustment.kind {
1328 adjustment::Adjust::Deref(overloaded) => {
1329 let base = if let Some(deref) = overloaded {
1331 let ref_ty = Ty::new_ref(
1332 self.cx.tcx(),
1333 self.cx.tcx().lifetimes.re_erased,
1334 target,
1335 deref.mutbl,
1336 );
1337 self.cat_rvalue(expr.hir_id, ref_ty)
1338 } else {
1339 previous()?
1340 };
1341 self.cat_deref(expr.hir_id, base)
1342 }
1343
1344 adjustment::Adjust::NeverToAny
1345 | adjustment::Adjust::Pointer(_)
1346 | adjustment::Adjust::Borrow(_)
1347 | adjustment::Adjust::ReborrowPin(..) => {
1348 Ok(self.cat_rvalue(expr.hir_id, target))
1350 }
1351 }
1352 }
1353
1354 fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1355 let expr_ty = self.expr_ty(expr)?;
1356 match expr.kind {
1357 hir::ExprKind::Unary(hir::UnOp::Deref, e_base) => {
1358 if self.cx.typeck_results().is_method_call(expr) {
1359 self.cat_overloaded_place(expr, e_base)
1360 } else {
1361 let base = self.cat_expr(e_base)?;
1362 self.cat_deref(expr.hir_id, base)
1363 }
1364 }
1365
1366 hir::ExprKind::Field(base, _) => {
1367 let base = self.cat_expr(base)?;
1368 debug!(?base);
1369
1370 let field_idx = self
1371 .cx
1372 .typeck_results()
1373 .field_indices()
1374 .get(expr.hir_id)
1375 .cloned()
1376 .expect("Field index not found");
1377
1378 Ok(self.cat_projection(
1379 expr.hir_id,
1380 base,
1381 expr_ty,
1382 ProjectionKind::Field(field_idx, FIRST_VARIANT),
1383 ))
1384 }
1385
1386 hir::ExprKind::Index(base, _, _) => {
1387 if self.cx.typeck_results().is_method_call(expr) {
1388 self.cat_overloaded_place(expr, base)
1394 } else {
1395 let base = self.cat_expr(base)?;
1396 Ok(self.cat_projection(expr.hir_id, base, expr_ty, ProjectionKind::Index))
1397 }
1398 }
1399
1400 hir::ExprKind::Path(ref qpath) => {
1401 let res = self.cx.typeck_results().qpath_res(qpath, expr.hir_id);
1402 self.cat_res(expr.hir_id, expr.span, expr_ty, res)
1403 }
1404
1405 hir::ExprKind::Type(e, _) => self.cat_expr(e),
1407
1408 hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Unwrap, e, _) => {
1409 let base = self.cat_expr(e)?;
1410 Ok(self.cat_projection(
1411 expr.hir_id,
1412 base,
1413 expr_ty,
1414 ProjectionKind::UnwrapUnsafeBinder,
1415 ))
1416 }
1417
1418 hir::ExprKind::AddrOf(..)
1419 | hir::ExprKind::Call(..)
1420 | hir::ExprKind::Use(..)
1421 | hir::ExprKind::Assign(..)
1422 | hir::ExprKind::AssignOp(..)
1423 | hir::ExprKind::Closure { .. }
1424 | hir::ExprKind::Ret(..)
1425 | hir::ExprKind::Become(..)
1426 | hir::ExprKind::Unary(..)
1427 | hir::ExprKind::Yield(..)
1428 | hir::ExprKind::MethodCall(..)
1429 | hir::ExprKind::Cast(..)
1430 | hir::ExprKind::DropTemps(..)
1431 | hir::ExprKind::Array(..)
1432 | hir::ExprKind::If(..)
1433 | hir::ExprKind::Tup(..)
1434 | hir::ExprKind::Binary(..)
1435 | hir::ExprKind::Block(..)
1436 | hir::ExprKind::Let(..)
1437 | hir::ExprKind::Loop(..)
1438 | hir::ExprKind::Match(..)
1439 | hir::ExprKind::Lit(..)
1440 | hir::ExprKind::ConstBlock(..)
1441 | hir::ExprKind::Break(..)
1442 | hir::ExprKind::Continue(..)
1443 | hir::ExprKind::Struct(..)
1444 | hir::ExprKind::Repeat(..)
1445 | hir::ExprKind::InlineAsm(..)
1446 | hir::ExprKind::OffsetOf(..)
1447 | hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, ..)
1448 | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr_ty)),
1449 }
1450 }
1451
1452 fn cat_res(
1453 &self,
1454 hir_id: HirId,
1455 span: Span,
1456 expr_ty: Ty<'tcx>,
1457 res: Res,
1458 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1459 match res {
1460 Res::Def(
1461 DefKind::Ctor(..)
1462 | DefKind::Const
1463 | DefKind::ConstParam
1464 | DefKind::AssocConst
1465 | DefKind::Fn
1466 | DefKind::AssocFn,
1467 _,
1468 )
1469 | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, expr_ty)),
1470
1471 Res::Def(DefKind::Static { .. }, _) => {
1472 Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new()))
1473 }
1474
1475 Res::Local(var_id) => {
1476 if self.upvars.is_some_and(|upvars| upvars.contains_key(&var_id)) {
1477 self.cat_upvar(hir_id, var_id)
1478 } else {
1479 Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new()))
1480 }
1481 }
1482
1483 def => span_bug!(span, "unexpected definition in ExprUseVisitor: {:?}", def),
1484 }
1485 }
1486
1487 fn cat_upvar(&self, hir_id: HirId, var_id: HirId) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1493 let closure_expr_def_id = self.cx.body_owner_def_id();
1494
1495 let upvar_id = ty::UpvarId {
1496 var_path: ty::UpvarPath { hir_id: var_id },
1497 closure_expr_id: closure_expr_def_id,
1498 };
1499 let var_ty = self.node_ty(var_id)?;
1500
1501 Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()))
1502 }
1503
1504 fn cat_rvalue(&self, hir_id: HirId, expr_ty: Ty<'tcx>) -> PlaceWithHirId<'tcx> {
1505 PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new())
1506 }
1507
1508 fn cat_projection(
1509 &self,
1510 node: HirId,
1511 base_place: PlaceWithHirId<'tcx>,
1512 ty: Ty<'tcx>,
1513 kind: ProjectionKind,
1514 ) -> PlaceWithHirId<'tcx> {
1515 let place_ty = base_place.place.ty();
1516 let mut projections = base_place.place.projections;
1517
1518 let node_ty = self.cx.typeck_results().node_type(node);
1519 if !self.cx.tcx().next_trait_solver_globally() {
1520 if node_ty != place_ty
1524 && self
1525 .cx
1526 .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty)
1527 .is_impl_trait()
1528 {
1529 projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
1530 }
1531 }
1532 projections.push(Projection { kind, ty });
1533 PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections)
1534 }
1535
1536 fn cat_overloaded_place(
1537 &self,
1538 expr: &hir::Expr<'_>,
1539 base: &hir::Expr<'_>,
1540 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1541 let place_ty = self.expr_ty(expr)?;
1545 let base_ty = self.expr_ty_adjusted(base)?;
1546
1547 let ty::Ref(region, _, mutbl) =
1548 *self.cx.structurally_resolve_type(base.span, base_ty).kind()
1549 else {
1550 span_bug!(expr.span, "cat_overloaded_place: base is not a reference");
1551 };
1552 let ref_ty = Ty::new_ref(self.cx.tcx(), region, place_ty, mutbl);
1553
1554 let base = self.cat_rvalue(expr.hir_id, ref_ty);
1555 self.cat_deref(expr.hir_id, base)
1556 }
1557
1558 fn cat_deref(
1559 &self,
1560 node: HirId,
1561 base_place: PlaceWithHirId<'tcx>,
1562 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1563 let base_curr_ty = base_place.place.ty();
1564 let deref_ty = match self
1565 .cx
1566 .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), base_curr_ty)
1567 .builtin_deref(true)
1568 {
1569 Some(ty) => ty,
1570 None => {
1571 debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
1572 return Err(self.cx.report_bug(
1573 self.cx.tcx().hir_span(node),
1574 "explicit deref of non-derefable type",
1575 ));
1576 }
1577 };
1578 let mut projections = base_place.place.projections;
1579 projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty });
1580
1581 Ok(PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections))
1582 }
1583
1584 fn variant_index_for_adt(
1587 &self,
1588 qpath: &hir::QPath<'_>,
1589 pat_hir_id: HirId,
1590 span: Span,
1591 ) -> Result<VariantIdx, Cx::Error> {
1592 let res = self.cx.typeck_results().qpath_res(qpath, pat_hir_id);
1593 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1594 let ty::Adt(adt_def, _) = self.cx.structurally_resolve_type(span, ty).kind() else {
1595 return Err(self
1596 .cx
1597 .report_bug(span, "struct or tuple struct pattern not applied to an ADT"));
1598 };
1599
1600 match res {
1601 Res::Def(DefKind::Variant, variant_id) => Ok(adt_def.variant_index_with_id(variant_id)),
1602 Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
1603 Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id))
1604 }
1605 Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
1606 | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
1607 | Res::SelfCtor(..)
1608 | Res::SelfTyParam { .. }
1609 | Res::SelfTyAlias { .. } => {
1610 Ok(FIRST_VARIANT)
1612 }
1613 _ => bug!("expected ADT path, found={:?}", res),
1614 }
1615 }
1616
1617 fn total_fields_in_adt_variant(
1620 &self,
1621 pat_hir_id: HirId,
1622 variant_index: VariantIdx,
1623 span: Span,
1624 ) -> Result<usize, Cx::Error> {
1625 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1626 match self.cx.structurally_resolve_type(span, ty).kind() {
1627 ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()),
1628 _ => {
1629 self.cx
1630 .tcx()
1631 .dcx()
1632 .span_bug(span, "struct or tuple struct pattern not applied to an ADT");
1633 }
1634 }
1635 }
1636
1637 fn total_fields_in_tuple(&self, pat_hir_id: HirId, span: Span) -> Result<usize, Cx::Error> {
1640 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1641 match self.cx.structurally_resolve_type(span, ty).kind() {
1642 ty::Tuple(args) => Ok(args.len()),
1643 _ => Err(self.cx.report_bug(span, "tuple pattern not applied to a tuple")),
1644 }
1645 }
1646
1647 fn cat_pattern<F>(
1654 &self,
1655 mut place_with_id: PlaceWithHirId<'tcx>,
1656 pat: &hir::Pat<'_>,
1657 op: &mut F,
1658 ) -> Result<(), Cx::Error>
1659 where
1660 F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>) -> Result<(), Cx::Error>,
1661 {
1662 let typeck_results = self.cx.typeck_results();
1695 let adjustments: &[adjustment::PatAdjustment<'tcx>] =
1696 typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
1697 let mut adjusts = adjustments.iter().peekable();
1698 while let Some(adjust) = adjusts.next() {
1699 debug!("applying adjustment to place_with_id={:?}", place_with_id);
1700 place_with_id = match adjust.kind {
1701 adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
1702 adjustment::PatAdjust::OverloadedDeref => {
1703 op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
1710 let target_ty = match adjusts.peek() {
1711 Some(&&next_adjust) => next_adjust.source,
1712 None => self.pat_ty_unadjusted(pat)?,
1714 };
1715 self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)?
1716 }
1717 adjustment::PatAdjust::PinDeref => {
1718 debug!("`PinDeref` of non-pinned-reference type: {:?}", adjust.source);
1719 let target_ty = adjust.source.pinned_ty().ok_or_else(|| {
1720 self.cx.report_bug(
1721 self.cx.tcx().hir_span(pat.hir_id),
1722 "`PinDeref` of non-pinned-reference type",
1723 )
1724 })?;
1725 let kind = ProjectionKind::Field(FieldIdx::ZERO, FIRST_VARIANT);
1726 place_with_id = self.cat_projection(pat.hir_id, place_with_id, target_ty, kind);
1727 self.cat_deref(pat.hir_id, place_with_id)?
1728 }
1729 };
1730 }
1731 drop(typeck_results); let place_with_id = place_with_id; debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
1734
1735 op(&place_with_id, pat)?;
1745
1746 match pat.kind {
1747 PatKind::Tuple(subpats, dots_pos) => {
1748 let total_fields = self.total_fields_in_tuple(pat.hir_id, pat.span)?;
1750
1751 for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1752 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1753 let projection_kind =
1754 ProjectionKind::Field(FieldIdx::from_usize(i), FIRST_VARIANT);
1755 let sub_place = self.cat_projection(
1756 pat.hir_id,
1757 place_with_id.clone(),
1758 subpat_ty,
1759 projection_kind,
1760 );
1761 self.cat_pattern(sub_place, subpat, op)?;
1762 }
1763 }
1764
1765 PatKind::TupleStruct(ref qpath, subpats, dots_pos) => {
1766 let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?;
1768 let total_fields =
1769 self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?;
1770
1771 for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1772 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1773 let projection_kind =
1774 ProjectionKind::Field(FieldIdx::from_usize(i), variant_index);
1775 let sub_place = self.cat_projection(
1776 pat.hir_id,
1777 place_with_id.clone(),
1778 subpat_ty,
1779 projection_kind,
1780 );
1781 self.cat_pattern(sub_place, subpat, op)?;
1782 }
1783 }
1784
1785 PatKind::Struct(ref qpath, field_pats, _) => {
1786 let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?;
1789
1790 for fp in field_pats {
1791 let field_ty = self.pat_ty_adjusted(fp.pat)?;
1792 let field_index = self
1793 .cx
1794 .typeck_results()
1795 .field_indices()
1796 .get(fp.hir_id)
1797 .cloned()
1798 .expect("no index for a field");
1799
1800 let field_place = self.cat_projection(
1801 pat.hir_id,
1802 place_with_id.clone(),
1803 field_ty,
1804 ProjectionKind::Field(field_index, variant_index),
1805 );
1806 self.cat_pattern(field_place, fp.pat, op)?;
1807 }
1808 }
1809
1810 PatKind::Or(pats) => {
1811 for pat in pats {
1812 self.cat_pattern(place_with_id.clone(), pat, op)?;
1813 }
1814 }
1815
1816 PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => {
1817 self.cat_pattern(place_with_id, subpat, op)?;
1818 }
1819
1820 PatKind::Ref(subpat, _, _)
1821 if self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) =>
1822 {
1823 self.cat_pattern(place_with_id, subpat, op)?;
1824 }
1825
1826 PatKind::Box(subpat) | PatKind::Ref(subpat, _, _) => {
1827 let subplace = self.cat_deref(pat.hir_id, place_with_id)?;
1831 self.cat_pattern(subplace, subpat, op)?;
1832 }
1833 PatKind::Deref(subpat) => {
1834 let ty = self.pat_ty_adjusted(subpat)?;
1835 let place = self.pat_deref_place(pat.hir_id, place_with_id, subpat, ty)?;
1836 self.cat_pattern(place, subpat, op)?;
1837 }
1838
1839 PatKind::Slice(before, ref slice, after) => {
1840 let Some(element_ty) = self
1841 .cx
1842 .structurally_resolve_type(pat.span, place_with_id.place.ty())
1843 .builtin_index()
1844 else {
1845 debug!("explicit index of non-indexable type {:?}", place_with_id);
1846 return Err(self
1847 .cx
1848 .report_bug(pat.span, "explicit index of non-indexable type"));
1849 };
1850 let elt_place = self.cat_projection(
1851 pat.hir_id,
1852 place_with_id.clone(),
1853 element_ty,
1854 ProjectionKind::Index,
1855 );
1856 for before_pat in before {
1857 self.cat_pattern(elt_place.clone(), before_pat, op)?;
1858 }
1859 if let Some(slice_pat) = *slice {
1860 let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?;
1861 let slice_place = self.cat_projection(
1862 pat.hir_id,
1863 place_with_id,
1864 slice_pat_ty,
1865 ProjectionKind::Subslice,
1866 );
1867 self.cat_pattern(slice_place, slice_pat, op)?;
1868 }
1869 for after_pat in after {
1870 self.cat_pattern(elt_place.clone(), after_pat, op)?;
1871 }
1872 }
1873
1874 PatKind::Binding(.., None)
1875 | PatKind::Expr(..)
1876 | PatKind::Range(..)
1877 | PatKind::Never
1878 | PatKind::Missing
1879 | PatKind::Wild
1880 | PatKind::Err(_) => {
1881 }
1883 }
1884
1885 Ok(())
1886 }
1887
1888 fn pat_deref_place(
1890 &self,
1891 hir_id: HirId,
1892 base_place: PlaceWithHirId<'tcx>,
1893 inner: &hir::Pat<'_>,
1894 target_ty: Ty<'tcx>,
1895 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1896 match self.cx.typeck_results().deref_pat_borrow_mode(base_place.place.ty(), inner) {
1897 hir::ByRef::No => self.cat_deref(hir_id, base_place),
1899 hir::ByRef::Yes(_, mutability) => {
1901 let re_erased = self.cx.tcx().lifetimes.re_erased;
1902 let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
1903 let base = self.cat_rvalue(hir_id, ty);
1905 self.cat_deref(hir_id, base)
1907 }
1908 }
1909 }
1910
1911 fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
1912 if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() {
1913 def.variants().len() > 1 || def.variant_list_has_applicable_non_exhaustive()
1918 } else {
1919 false
1920 }
1921 }
1922}