1use std::cell::{Ref, RefCell};
6use std::ops::Deref;
7use std::slice::from_ref;
8
9use hir::Expr;
10use hir::def::DefKind;
11use hir::pat_util::EnumerateAndAdjustIterator as _;
12use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
13use rustc_data_structures::fx::FxIndexMap;
14use rustc_hir::def::{CtorOf, Res};
15use rustc_hir::def_id::LocalDefId;
16use rustc_hir::{self as hir, HirId, PatExpr, PatExprKind, PatKind};
17use rustc_lint::LateContext;
18use rustc_middle::hir::place::ProjectionKind;
19pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
21use rustc_middle::mir::FakeReadCause;
22use rustc_middle::ty::{
23 self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
24};
25use rustc_middle::{bug, span_bug};
26use rustc_span::{ErrorGuaranteed, Span};
27use rustc_trait_selection::infer::InferCtxtExt;
28use tracing::{debug, trace};
29
30use crate::fn_ctxt::FnCtxt;
31
32pub trait Delegate<'tcx> {
35 fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);
49
50 fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);
64
65 fn borrow(
68 &mut self,
69 place_with_id: &PlaceWithHirId<'tcx>,
70 diag_expr_id: HirId,
71 bk: ty::BorrowKind,
72 );
73
74 fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
77 self.borrow(place_with_id, diag_expr_id, ty::BorrowKind::Immutable)
80 }
81
82 fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);
85
86 fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
90 self.mutate(binding_place, diag_expr_id)
93 }
94
95 fn fake_read(
97 &mut self,
98 place_with_id: &PlaceWithHirId<'tcx>,
99 cause: FakeReadCause,
100 diag_expr_id: HirId,
101 );
102}
103
104impl<'tcx, D: Delegate<'tcx>> Delegate<'tcx> for &mut D {
105 fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
106 (**self).consume(place_with_id, diag_expr_id)
107 }
108
109 fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
110 (**self).use_cloned(place_with_id, diag_expr_id)
111 }
112
113 fn borrow(
114 &mut self,
115 place_with_id: &PlaceWithHirId<'tcx>,
116 diag_expr_id: HirId,
117 bk: ty::BorrowKind,
118 ) {
119 (**self).borrow(place_with_id, diag_expr_id, bk)
120 }
121
122 fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
123 (**self).copy(place_with_id, diag_expr_id)
124 }
125
126 fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
127 (**self).mutate(assignee_place, diag_expr_id)
128 }
129
130 fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
131 (**self).bind(binding_place, diag_expr_id)
132 }
133
134 fn fake_read(
135 &mut self,
136 place_with_id: &PlaceWithHirId<'tcx>,
137 cause: FakeReadCause,
138 diag_expr_id: HirId,
139 ) {
140 (**self).fake_read(place_with_id, cause, diag_expr_id)
141 }
142}
143
144pub trait TypeInformationCtxt<'tcx> {
145 type TypeckResults<'a>: Deref<Target = ty::TypeckResults<'tcx>>
146 where
147 Self: 'a;
148
149 type Error;
150
151 fn typeck_results(&self) -> Self::TypeckResults<'_>;
152
153 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T;
154
155 fn try_structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
156
157 fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error;
158
159 fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error>;
160
161 fn tainted_by_errors(&self) -> Result<(), Self::Error>;
162
163 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool;
164
165 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool;
166
167 fn body_owner_def_id(&self) -> LocalDefId;
168
169 fn tcx(&self) -> TyCtxt<'tcx>;
170}
171
172impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
173 type TypeckResults<'a>
174 = Ref<'a, ty::TypeckResults<'tcx>>
175 where
176 Self: 'a;
177
178 type Error = ErrorGuaranteed;
179
180 fn typeck_results(&self) -> Self::TypeckResults<'_> {
181 self.typeck_results.borrow()
182 }
183
184 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
185 self.infcx.resolve_vars_if_possible(t)
186 }
187
188 fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
189 (**self).try_structurally_resolve_type(sp, ty)
190 }
191
192 fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error {
193 self.dcx().span_delayed_bug(span, msg.to_string())
194 }
195
196 fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error> {
197 ty.error_reported()
198 }
199
200 fn tainted_by_errors(&self) -> Result<(), ErrorGuaranteed> {
201 if let Some(guar) = self.infcx.tainted_by_errors() { Err(guar) } else { Ok(()) }
202 }
203
204 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
205 self.infcx.type_is_copy_modulo_regions(self.param_env, ty)
206 }
207
208 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
209 self.infcx.type_is_use_cloned_modulo_regions(self.param_env, ty)
210 }
211
212 fn body_owner_def_id(&self) -> LocalDefId {
213 self.body_id
214 }
215
216 fn tcx(&self) -> TyCtxt<'tcx> {
217 self.tcx
218 }
219}
220
221impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
222 type TypeckResults<'a>
223 = &'tcx ty::TypeckResults<'tcx>
224 where
225 Self: 'a;
226
227 type Error = !;
228
229 fn typeck_results(&self) -> Self::TypeckResults<'_> {
230 self.0.maybe_typeck_results().expect("expected typeck results")
231 }
232
233 fn try_structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
234 ty
236 }
237
238 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
239 t
240 }
241
242 fn report_error(&self, span: Span, msg: impl ToString) -> ! {
243 span_bug!(span, "{}", msg.to_string())
244 }
245
246 fn error_reported_in_ty(&self, _ty: Ty<'tcx>) -> Result<(), !> {
247 Ok(())
248 }
249
250 fn tainted_by_errors(&self) -> Result<(), !> {
251 Ok(())
252 }
253
254 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
255 self.0.type_is_copy_modulo_regions(ty)
256 }
257
258 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
259 self.0.type_is_use_cloned_modulo_regions(ty)
260 }
261
262 fn body_owner_def_id(&self) -> LocalDefId {
263 self.1
264 }
265
266 fn tcx(&self) -> TyCtxt<'tcx> {
267 self.0.tcx
268 }
269}
270
271pub struct ExprUseVisitor<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> {
275 cx: Cx,
276 delegate: RefCell<D>,
279 upvars: Option<&'tcx FxIndexMap<HirId, hir::Upvar>>,
280}
281
282impl<'a, 'tcx, D: Delegate<'tcx>> ExprUseVisitor<'tcx, (&'a LateContext<'tcx>, LocalDefId), D> {
283 pub fn for_clippy(cx: &'a LateContext<'tcx>, body_def_id: LocalDefId, delegate: D) -> Self {
284 Self::new((cx, body_def_id), delegate)
285 }
286}
287
288impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
289 pub(crate) fn new(cx: Cx, delegate: D) -> Self {
295 ExprUseVisitor {
296 delegate: RefCell::new(delegate),
297 upvars: cx.tcx().upvars_mentioned(cx.body_owner_def_id()),
298 cx,
299 }
300 }
301
302 pub fn consume_body(&self, body: &hir::Body<'_>) -> Result<(), Cx::Error> {
303 for param in body.params {
304 let param_ty = self.pat_ty_adjusted(param.pat)?;
305 debug!("consume_body: param_ty = {:?}", param_ty);
306
307 let param_place = self.cat_rvalue(param.hir_id, param_ty);
308
309 self.walk_irrefutable_pat(¶m_place, param.pat)?;
310 }
311
312 self.consume_expr(body.value)?;
313
314 Ok(())
315 }
316
317 fn consume_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
318 debug!("delegate_consume(place_with_id={:?})", place_with_id);
319
320 if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
321 self.delegate.borrow_mut().copy(place_with_id, diag_expr_id);
322 } else {
323 self.delegate.borrow_mut().consume(place_with_id, diag_expr_id);
324 }
325 }
326
327 pub fn consume_clone_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
328 debug!("delegate_consume_or_clone(place_with_id={:?})", place_with_id);
329
330 if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
337 self.delegate.borrow_mut().copy(place_with_id, diag_expr_id);
338 } else if self.cx.type_is_use_cloned_modulo_regions(place_with_id.place.ty()) {
339 self.delegate.borrow_mut().use_cloned(place_with_id, diag_expr_id);
340 } else {
341 self.delegate.borrow_mut().consume(place_with_id, diag_expr_id);
342 }
343 }
344
345 fn consume_exprs(&self, exprs: &[hir::Expr<'_>]) -> Result<(), Cx::Error> {
346 for expr in exprs {
347 self.consume_expr(expr)?;
348 }
349
350 Ok(())
351 }
352
353 pub fn consume_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
355 debug!("consume_expr(expr={:?})", expr);
356
357 let place_with_id = self.cat_expr(expr)?;
358 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
359 self.walk_expr(expr)?;
360 Ok(())
361 }
362
363 pub fn consume_or_clone_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
364 debug!("consume_or_clone_expr(expr={:?})", expr);
365
366 let place_with_id = self.cat_expr(expr)?;
367 self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
368 self.walk_expr(expr)?;
369 Ok(())
370 }
371
372 fn mutate_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
373 let place_with_id = self.cat_expr(expr)?;
374 self.delegate.borrow_mut().mutate(&place_with_id, place_with_id.hir_id);
375 self.walk_expr(expr)?;
376 Ok(())
377 }
378
379 fn borrow_expr(&self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) -> Result<(), Cx::Error> {
380 debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk);
381
382 let place_with_id = self.cat_expr(expr)?;
383 self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
384 self.walk_expr(expr)
385 }
386
387 pub fn walk_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
388 debug!("walk_expr(expr={:?})", expr);
389
390 self.walk_adjustment(expr)?;
391
392 match expr.kind {
393 hir::ExprKind::Path(_) => {}
394
395 hir::ExprKind::Type(subexpr, _) => {
396 self.walk_expr(subexpr)?;
397 }
398
399 hir::ExprKind::UnsafeBinderCast(_, subexpr, _) => {
400 self.walk_expr(subexpr)?;
401 }
402
403 hir::ExprKind::Unary(hir::UnOp::Deref, base) => {
404 self.walk_expr(base)?;
406 }
407
408 hir::ExprKind::Field(base, _) => {
409 self.walk_expr(base)?;
411 }
412
413 hir::ExprKind::Index(lhs, rhs, _) => {
414 self.walk_expr(lhs)?;
416 self.consume_expr(rhs)?;
417 }
418
419 hir::ExprKind::Call(callee, args) => {
420 self.consume_expr(callee)?;
422 self.consume_exprs(args)?;
423 }
424
425 hir::ExprKind::Use(expr, _) => {
426 self.consume_or_clone_expr(expr)?;
427 }
428
429 hir::ExprKind::MethodCall(.., receiver, args, _) => {
430 self.consume_expr(receiver)?;
432 self.consume_exprs(args)?;
433 }
434
435 hir::ExprKind::Struct(_, fields, ref opt_with) => {
436 self.walk_struct_expr(fields, opt_with)?;
437 }
438
439 hir::ExprKind::Tup(exprs) => {
440 self.consume_exprs(exprs)?;
441 }
442
443 hir::ExprKind::If(cond_expr, then_expr, ref opt_else_expr) => {
444 self.consume_expr(cond_expr)?;
445 self.consume_expr(then_expr)?;
446 if let Some(else_expr) = *opt_else_expr {
447 self.consume_expr(else_expr)?;
448 }
449 }
450
451 hir::ExprKind::Let(hir::LetExpr { pat, init, .. }) => {
452 self.walk_local(init, pat, None, || self.borrow_expr(init, BorrowKind::Immutable))?;
453 }
454
455 hir::ExprKind::Match(discr, arms, _) => {
456 let discr_place = self.cat_expr(discr)?;
457 self.maybe_read_scrutinee(
458 discr,
459 discr_place.clone(),
460 arms.iter().map(|arm| arm.pat),
461 )?;
462
463 for arm in arms {
465 self.walk_arm(&discr_place, arm)?;
466 }
467 }
468
469 hir::ExprKind::Array(exprs) => {
470 self.consume_exprs(exprs)?;
471 }
472
473 hir::ExprKind::AddrOf(_, m, base) => {
474 let bk = ty::BorrowKind::from_mutbl(m);
478 self.borrow_expr(base, bk)?;
479 }
480
481 hir::ExprKind::InlineAsm(asm) => {
482 for (op, _op_sp) in asm.operands {
483 match op {
484 hir::InlineAsmOperand::In { expr, .. } => {
485 self.consume_expr(expr)?;
486 }
487 hir::InlineAsmOperand::Out { expr: Some(expr), .. }
488 | hir::InlineAsmOperand::InOut { expr, .. } => {
489 self.mutate_expr(expr)?;
490 }
491 hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
492 self.consume_expr(in_expr)?;
493 if let Some(out_expr) = out_expr {
494 self.mutate_expr(out_expr)?;
495 }
496 }
497 hir::InlineAsmOperand::Out { expr: None, .. }
498 | hir::InlineAsmOperand::Const { .. }
499 | hir::InlineAsmOperand::SymFn { .. }
500 | hir::InlineAsmOperand::SymStatic { .. } => {}
501 hir::InlineAsmOperand::Label { block } => {
502 self.walk_block(block)?;
503 }
504 }
505 }
506 }
507
508 hir::ExprKind::Continue(..)
509 | hir::ExprKind::Lit(..)
510 | hir::ExprKind::ConstBlock(..)
511 | hir::ExprKind::OffsetOf(..)
512 | hir::ExprKind::Err(_) => {}
513
514 hir::ExprKind::Loop(blk, ..) => {
515 self.walk_block(blk)?;
516 }
517
518 hir::ExprKind::Unary(_, lhs) => {
519 self.consume_expr(lhs)?;
520 }
521
522 hir::ExprKind::Binary(_, lhs, rhs) => {
523 self.consume_expr(lhs)?;
524 self.consume_expr(rhs)?;
525 }
526
527 hir::ExprKind::Block(blk, _) => {
528 self.walk_block(blk)?;
529 }
530
531 hir::ExprKind::Break(_, ref opt_expr) | hir::ExprKind::Ret(ref opt_expr) => {
532 if let Some(expr) = *opt_expr {
533 self.consume_expr(expr)?;
534 }
535 }
536
537 hir::ExprKind::Become(call) => {
538 self.consume_expr(call)?;
539 }
540
541 hir::ExprKind::Assign(lhs, rhs, _) => {
542 self.mutate_expr(lhs)?;
543 self.consume_expr(rhs)?;
544 }
545
546 hir::ExprKind::Cast(base, _) => {
547 self.consume_expr(base)?;
548 }
549
550 hir::ExprKind::DropTemps(expr) => {
551 self.consume_expr(expr)?;
552 }
553
554 hir::ExprKind::AssignOp(_, lhs, rhs) => {
555 if self.cx.typeck_results().is_method_call(expr) {
556 self.consume_expr(lhs)?;
557 } else {
558 self.mutate_expr(lhs)?;
559 }
560 self.consume_expr(rhs)?;
561 }
562
563 hir::ExprKind::Repeat(base, _) => {
564 self.consume_expr(base)?;
565 }
566
567 hir::ExprKind::Closure(closure) => {
568 self.walk_captures(closure)?;
569 }
570
571 hir::ExprKind::Yield(value, _) => {
572 self.consume_expr(value)?;
573 }
574 }
575
576 Ok(())
577 }
578
579 fn walk_stmt(&self, stmt: &hir::Stmt<'_>) -> Result<(), Cx::Error> {
580 match stmt.kind {
581 hir::StmtKind::Let(hir::LetStmt { pat, init: Some(expr), els, .. }) => {
582 self.walk_local(expr, pat, *els, || Ok(()))?;
583 }
584
585 hir::StmtKind::Let(_) => {}
586
587 hir::StmtKind::Item(_) => {
588 }
591
592 hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) => {
593 self.consume_expr(expr)?;
594 }
595 }
596
597 Ok(())
598 }
599
600 fn maybe_read_scrutinee<'t>(
601 &self,
602 discr: &Expr<'_>,
603 discr_place: PlaceWithHirId<'tcx>,
604 pats: impl Iterator<Item = &'t hir::Pat<'t>>,
605 ) -> Result<(), Cx::Error> {
606 let mut needs_to_be_read = false;
611 for pat in pats {
612 self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
613 match &pat.kind {
614 PatKind::Binding(.., opt_sub_pat) => {
615 if opt_sub_pat.is_none() {
618 needs_to_be_read = true;
619 }
620 }
621 PatKind::Never => {
622 needs_to_be_read = true;
625 }
626 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
627 let res = self.cx.typeck_results().qpath_res(qpath, *hir_id);
631 match res {
632 Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
633 needs_to_be_read = true;
638 }
639 _ => {
640 needs_to_be_read |=
643 self.is_multivariant_adt(place.place.ty(), *span);
644 }
645 }
646 }
647 PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
648 let place_ty = place.place.ty();
655 needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span);
656 }
657 PatKind::Expr(_) | PatKind::Range(..) => {
658 needs_to_be_read = true;
661 }
662 PatKind::Slice(lhs, wild, rhs) => {
663 if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
665 || place.place.ty().peel_refs().is_array()
668 {
669 } else {
670 needs_to_be_read = true;
671 }
672 }
673 PatKind::Or(_)
674 | PatKind::Box(_)
675 | PatKind::Deref(_)
676 | PatKind::Ref(..)
677 | PatKind::Guard(..)
678 | PatKind::Wild
679 | PatKind::Err(_) => {
680 }
685 }
686
687 Ok(())
688 })?
689 }
690
691 if needs_to_be_read {
692 self.borrow_expr(discr, BorrowKind::Immutable)?;
693 } else {
694 let closure_def_id = match discr_place.place.base {
695 PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
696 _ => None,
697 };
698
699 self.delegate.borrow_mut().fake_read(
700 &discr_place,
701 FakeReadCause::ForMatchedPlace(closure_def_id),
702 discr_place.hir_id,
703 );
704
705 self.walk_expr(discr)?;
708 }
709 Ok(())
710 }
711
712 fn walk_local<F>(
713 &self,
714 expr: &hir::Expr<'_>,
715 pat: &hir::Pat<'_>,
716 els: Option<&hir::Block<'_>>,
717 mut f: F,
718 ) -> Result<(), Cx::Error>
719 where
720 F: FnMut() -> Result<(), Cx::Error>,
721 {
722 self.walk_expr(expr)?;
723 let expr_place = self.cat_expr(expr)?;
724 f()?;
725 if let Some(els) = els {
726 self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter())?;
728 self.walk_block(els)?;
729 }
730 self.walk_irrefutable_pat(&expr_place, pat)?;
731 Ok(())
732 }
733
734 fn walk_block(&self, blk: &hir::Block<'_>) -> Result<(), Cx::Error> {
737 debug!("walk_block(blk.hir_id={})", blk.hir_id);
738
739 for stmt in blk.stmts {
740 self.walk_stmt(stmt)?;
741 }
742
743 if let Some(tail_expr) = blk.expr {
744 self.consume_expr(tail_expr)?;
745 }
746
747 Ok(())
748 }
749
750 fn walk_struct_expr<'hir>(
751 &self,
752 fields: &[hir::ExprField<'_>],
753 opt_with: &hir::StructTailExpr<'hir>,
754 ) -> Result<(), Cx::Error> {
755 for field in fields {
757 self.consume_expr(field.expr)?;
758
759 if self.cx.typeck_results().opt_field_index(field.hir_id).is_none() {
761 self.cx
762 .tcx()
763 .dcx()
764 .span_delayed_bug(field.span, "couldn't resolve index for field");
765 }
766 }
767
768 let with_expr = match *opt_with {
769 hir::StructTailExpr::Base(w) => &*w,
770 hir::StructTailExpr::DefaultFields(_) | hir::StructTailExpr::None => {
771 return Ok(());
772 }
773 };
774
775 let with_place = self.cat_expr(with_expr)?;
776
777 match self.cx.try_structurally_resolve_type(with_expr.span, with_place.place.ty()).kind() {
780 ty::Adt(adt, args) if adt.is_struct() => {
781 for (f_index, with_field) in adt.non_enum_variant().fields.iter_enumerated() {
783 let is_mentioned = fields.iter().any(|f| {
784 self.cx.typeck_results().opt_field_index(f.hir_id) == Some(f_index)
785 });
786 if !is_mentioned {
787 let field_place = self.cat_projection(
788 with_expr.hir_id,
789 with_place.clone(),
790 with_field.ty(self.cx.tcx(), args),
791 ProjectionKind::Field(f_index, FIRST_VARIANT),
792 );
793 self.consume_or_copy(&field_place, field_place.hir_id);
794 }
795 }
796 }
797 _ => {
798 if self.cx.tainted_by_errors().is_ok() {
803 span_bug!(with_expr.span, "with expression doesn't evaluate to a struct");
804 }
805 }
806 }
807
808 self.walk_expr(with_expr)?;
811
812 Ok(())
813 }
814
815 fn walk_adjustment(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
819 let typeck_results = self.cx.typeck_results();
820 let adjustments = typeck_results.expr_adjustments(expr);
821 let mut place_with_id = self.cat_expr_unadjusted(expr)?;
822 for adjustment in adjustments {
823 debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
824 match adjustment.kind {
825 adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => {
826 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
829 }
830
831 adjustment::Adjust::Deref(None) => {}
832
833 adjustment::Adjust::Deref(Some(ref deref)) => {
839 let bk = ty::BorrowKind::from_mutbl(deref.mutbl);
840 self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
841 }
842
843 adjustment::Adjust::Borrow(ref autoref) => {
844 self.walk_autoref(expr, &place_with_id, autoref);
845 }
846
847 adjustment::Adjust::ReborrowPin(mutbl) => {
848 let bk = match mutbl {
851 ty::Mutability::Not => ty::BorrowKind::Immutable,
852 ty::Mutability::Mut => ty::BorrowKind::Mutable,
853 };
854 self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
855 }
856 }
857 place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
858 }
859
860 Ok(())
861 }
862
863 fn walk_autoref(
867 &self,
868 expr: &hir::Expr<'_>,
869 base_place: &PlaceWithHirId<'tcx>,
870 autoref: &adjustment::AutoBorrow,
871 ) {
872 debug!(
873 "walk_autoref(expr.hir_id={} base_place={:?} autoref={:?})",
874 expr.hir_id, base_place, autoref
875 );
876
877 match *autoref {
878 adjustment::AutoBorrow::Ref(m) => {
879 self.delegate.borrow_mut().borrow(
880 base_place,
881 base_place.hir_id,
882 ty::BorrowKind::from_mutbl(m.into()),
883 );
884 }
885
886 adjustment::AutoBorrow::RawPtr(m) => {
887 debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place);
888
889 self.delegate.borrow_mut().borrow(
890 base_place,
891 base_place.hir_id,
892 ty::BorrowKind::from_mutbl(m),
893 );
894 }
895 }
896 }
897
898 fn walk_arm(
899 &self,
900 discr_place: &PlaceWithHirId<'tcx>,
901 arm: &hir::Arm<'_>,
902 ) -> Result<(), Cx::Error> {
903 let closure_def_id = match discr_place.place.base {
904 PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
905 _ => None,
906 };
907
908 self.delegate.borrow_mut().fake_read(
909 discr_place,
910 FakeReadCause::ForMatchedPlace(closure_def_id),
911 discr_place.hir_id,
912 );
913 self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?;
914
915 if let Some(ref e) = arm.guard {
916 self.consume_expr(e)?;
917 }
918
919 self.consume_expr(arm.body)?;
920 Ok(())
921 }
922
923 fn walk_irrefutable_pat(
926 &self,
927 discr_place: &PlaceWithHirId<'tcx>,
928 pat: &hir::Pat<'_>,
929 ) -> Result<(), Cx::Error> {
930 let closure_def_id = match discr_place.place.base {
931 PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
932 _ => None,
933 };
934
935 self.delegate.borrow_mut().fake_read(
936 discr_place,
937 FakeReadCause::ForLet(closure_def_id),
938 discr_place.hir_id,
939 );
940 self.walk_pat(discr_place, pat, false)?;
941 Ok(())
942 }
943
944 fn walk_pat(
946 &self,
947 discr_place: &PlaceWithHirId<'tcx>,
948 pat: &hir::Pat<'_>,
949 has_guard: bool,
950 ) -> Result<(), Cx::Error> {
951 debug!("walk_pat(discr_place={:?}, pat={:?}, has_guard={:?})", discr_place, pat, has_guard);
952
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 let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern);
1005 let mutability =
1006 if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1007 let bk = ty::BorrowKind::from_mutbl(mutability);
1008 self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
1009 }
1010 PatKind::Never => {
1011 self.delegate.borrow_mut().borrow(
1014 place,
1015 discr_place.hir_id,
1016 BorrowKind::Immutable,
1017 );
1018 }
1019 _ => {}
1020 }
1021
1022 Ok(())
1023 })
1024 }
1025
1026 fn walk_captures(&self, closure_expr: &hir::Closure<'_>) -> Result<(), Cx::Error> {
1046 fn upvar_is_local_variable(
1047 upvars: Option<&FxIndexMap<HirId, hir::Upvar>>,
1048 upvar_id: HirId,
1049 body_owner_is_closure: bool,
1050 ) -> bool {
1051 upvars.map(|upvars| !upvars.contains_key(&upvar_id)).unwrap_or(body_owner_is_closure)
1052 }
1053
1054 debug!("walk_captures({:?})", closure_expr);
1055
1056 let tcx = self.cx.tcx();
1057 let closure_def_id = closure_expr.def_id;
1058 let body_owner_is_closure = matches!(
1060 tcx.hir_body_owner_kind(self.cx.body_owner_def_id()),
1061 hir::BodyOwnerKind::Closure
1062 );
1063
1064 if let Some(fake_reads) = self.cx.typeck_results().closure_fake_reads.get(&closure_def_id) {
1067 for (fake_read, cause, hir_id) in fake_reads.iter() {
1068 match fake_read.base {
1069 PlaceBase::Upvar(upvar_id) => {
1070 if upvar_is_local_variable(
1071 self.upvars,
1072 upvar_id.var_path.hir_id,
1073 body_owner_is_closure,
1074 ) {
1075 continue;
1092 }
1093 }
1094 _ => {
1095 bug!(
1096 "Do not know how to get HirId out of Rvalue and StaticItem {:?}",
1097 fake_read.base
1098 );
1099 }
1100 };
1101 self.delegate.borrow_mut().fake_read(
1102 &PlaceWithHirId { place: fake_read.clone(), hir_id: *hir_id },
1103 *cause,
1104 *hir_id,
1105 );
1106 }
1107 }
1108
1109 if let Some(min_captures) =
1110 self.cx.typeck_results().closure_min_captures.get(&closure_def_id)
1111 {
1112 for (var_hir_id, min_list) in min_captures.iter() {
1113 if self
1114 .upvars
1115 .map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id))
1116 {
1117 continue;
1121 }
1122 for captured_place in min_list {
1123 let place = &captured_place.place;
1124 let capture_info = captured_place.info;
1125
1126 let place_base = if body_owner_is_closure {
1127 PlaceBase::Upvar(ty::UpvarId::new(*var_hir_id, self.cx.body_owner_def_id()))
1129 } else {
1130 PlaceBase::Local(*var_hir_id)
1133 };
1134 let closure_hir_id = tcx.local_def_id_to_hir_id(closure_def_id);
1135 let place_with_id = PlaceWithHirId::new(
1136 capture_info
1137 .path_expr_id
1138 .unwrap_or(capture_info.capture_kind_expr_id.unwrap_or(closure_hir_id)),
1139 place.base_ty,
1140 place_base,
1141 place.projections.clone(),
1142 );
1143
1144 match capture_info.capture_kind {
1145 ty::UpvarCapture::ByValue => {
1146 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
1147 }
1148 ty::UpvarCapture::ByUse => {
1149 self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
1150 }
1151 ty::UpvarCapture::ByRef(upvar_borrow) => {
1152 self.delegate.borrow_mut().borrow(
1153 &place_with_id,
1154 place_with_id.hir_id,
1155 upvar_borrow,
1156 );
1157 }
1158 }
1159 }
1160 }
1161 }
1162
1163 Ok(())
1164 }
1165}
1166
1167impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
1215 fn resolve_type_vars_or_error(
1216 &self,
1217 id: HirId,
1218 ty: Option<Ty<'tcx>>,
1219 ) -> Result<Ty<'tcx>, Cx::Error> {
1220 match ty {
1221 Some(ty) => {
1222 let ty = self.cx.resolve_vars_if_possible(ty);
1223 self.cx.error_reported_in_ty(ty)?;
1224 if ty.is_ty_var() {
1225 debug!("resolve_type_vars_or_error: infer var from {:?}", ty);
1226 Err(self
1227 .cx
1228 .report_error(self.cx.tcx().hir().span(id), "encountered type variable"))
1229 } else {
1230 Ok(ty)
1231 }
1232 }
1233 None => {
1234 self.cx.tainted_by_errors()?;
1236 bug!(
1237 "no type for node {} in mem_categorization",
1238 self.cx.tcx().hir_id_to_string(id)
1239 );
1240 }
1241 }
1242 }
1243
1244 fn node_ty(&self, hir_id: HirId) -> Result<Ty<'tcx>, Cx::Error> {
1245 self.resolve_type_vars_or_error(hir_id, self.cx.typeck_results().node_type_opt(hir_id))
1246 }
1247
1248 fn expr_ty(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1249 self.resolve_type_vars_or_error(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr))
1250 }
1251
1252 fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1253 self.resolve_type_vars_or_error(
1254 expr.hir_id,
1255 self.cx.typeck_results().expr_ty_adjusted_opt(expr),
1256 )
1257 }
1258
1259 fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1270 if let Some(vec) = self.cx.typeck_results().pat_adjustments().get(pat.hir_id) {
1275 if let Some(first_ty) = vec.first() {
1276 debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty);
1277 return Ok(*first_ty);
1278 }
1279 } else if let PatKind::Ref(subpat, _) = pat.kind
1280 && self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id)
1281 {
1282 return self.pat_ty_adjusted(subpat);
1283 }
1284
1285 self.pat_ty_unadjusted(pat)
1286 }
1287
1288 fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1290 let base_ty = self.node_ty(pat.hir_id)?;
1291 trace!(?base_ty);
1292
1293 match pat.kind {
1296 PatKind::Binding(..) => {
1297 let bm = *self
1298 .cx
1299 .typeck_results()
1300 .pat_binding_modes()
1301 .get(pat.hir_id)
1302 .expect("missing binding mode");
1303
1304 if matches!(bm.0, hir::ByRef::Yes(_)) {
1305 match self
1309 .cx
1310 .try_structurally_resolve_type(pat.span, base_ty)
1311 .builtin_deref(false)
1312 {
1313 Some(ty) => Ok(ty),
1314 None => {
1315 debug!("By-ref binding of non-derefable type");
1316 Err(self
1317 .cx
1318 .report_error(pat.span, "by-ref binding of non-derefable type"))
1319 }
1320 }
1321 } else {
1322 Ok(base_ty)
1323 }
1324 }
1325 _ => Ok(base_ty),
1326 }
1327 }
1328
1329 fn cat_expr(&self, expr: &hir::Expr<'_>) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1330 self.cat_expr_(expr, self.cx.typeck_results().expr_adjustments(expr))
1331 }
1332
1333 fn cat_expr_(
1336 &self,
1337 expr: &hir::Expr<'_>,
1338 adjustments: &[adjustment::Adjustment<'tcx>],
1339 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1340 match adjustments.split_last() {
1341 None => self.cat_expr_unadjusted(expr),
1342 Some((adjustment, previous)) => {
1343 self.cat_expr_adjusted_with(expr, || self.cat_expr_(expr, previous), adjustment)
1344 }
1345 }
1346 }
1347
1348 fn cat_expr_adjusted(
1349 &self,
1350 expr: &hir::Expr<'_>,
1351 previous: PlaceWithHirId<'tcx>,
1352 adjustment: &adjustment::Adjustment<'tcx>,
1353 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1354 self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
1355 }
1356
1357 fn cat_expr_adjusted_with<F>(
1358 &self,
1359 expr: &hir::Expr<'_>,
1360 previous: F,
1361 adjustment: &adjustment::Adjustment<'tcx>,
1362 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error>
1363 where
1364 F: FnOnce() -> Result<PlaceWithHirId<'tcx>, Cx::Error>,
1365 {
1366 let target = self.cx.resolve_vars_if_possible(adjustment.target);
1367 match adjustment.kind {
1368 adjustment::Adjust::Deref(overloaded) => {
1369 let base = if let Some(deref) = overloaded {
1371 let ref_ty = Ty::new_ref(
1372 self.cx.tcx(),
1373 self.cx.tcx().lifetimes.re_erased,
1374 target,
1375 deref.mutbl,
1376 );
1377 self.cat_rvalue(expr.hir_id, ref_ty)
1378 } else {
1379 previous()?
1380 };
1381 self.cat_deref(expr.hir_id, base)
1382 }
1383
1384 adjustment::Adjust::NeverToAny
1385 | adjustment::Adjust::Pointer(_)
1386 | adjustment::Adjust::Borrow(_)
1387 | adjustment::Adjust::ReborrowPin(..) => {
1388 Ok(self.cat_rvalue(expr.hir_id, target))
1390 }
1391 }
1392 }
1393
1394 fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1395 let expr_ty = self.expr_ty(expr)?;
1396 match expr.kind {
1397 hir::ExprKind::Unary(hir::UnOp::Deref, e_base) => {
1398 if self.cx.typeck_results().is_method_call(expr) {
1399 self.cat_overloaded_place(expr, e_base)
1400 } else {
1401 let base = self.cat_expr(e_base)?;
1402 self.cat_deref(expr.hir_id, base)
1403 }
1404 }
1405
1406 hir::ExprKind::Field(base, _) => {
1407 let base = self.cat_expr(base)?;
1408 debug!(?base);
1409
1410 let field_idx = self
1411 .cx
1412 .typeck_results()
1413 .field_indices()
1414 .get(expr.hir_id)
1415 .cloned()
1416 .expect("Field index not found");
1417
1418 Ok(self.cat_projection(
1419 expr.hir_id,
1420 base,
1421 expr_ty,
1422 ProjectionKind::Field(field_idx, FIRST_VARIANT),
1423 ))
1424 }
1425
1426 hir::ExprKind::Index(base, _, _) => {
1427 if self.cx.typeck_results().is_method_call(expr) {
1428 self.cat_overloaded_place(expr, base)
1434 } else {
1435 let base = self.cat_expr(base)?;
1436 Ok(self.cat_projection(expr.hir_id, base, expr_ty, ProjectionKind::Index))
1437 }
1438 }
1439
1440 hir::ExprKind::Path(ref qpath) => {
1441 let res = self.cx.typeck_results().qpath_res(qpath, expr.hir_id);
1442 self.cat_res(expr.hir_id, expr.span, expr_ty, res)
1443 }
1444
1445 hir::ExprKind::Type(e, _) => self.cat_expr(e),
1448 hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e),
1449
1450 hir::ExprKind::AddrOf(..)
1451 | hir::ExprKind::Call(..)
1452 | hir::ExprKind::Use(..)
1453 | hir::ExprKind::Assign(..)
1454 | hir::ExprKind::AssignOp(..)
1455 | hir::ExprKind::Closure { .. }
1456 | hir::ExprKind::Ret(..)
1457 | hir::ExprKind::Become(..)
1458 | hir::ExprKind::Unary(..)
1459 | hir::ExprKind::Yield(..)
1460 | hir::ExprKind::MethodCall(..)
1461 | hir::ExprKind::Cast(..)
1462 | hir::ExprKind::DropTemps(..)
1463 | hir::ExprKind::Array(..)
1464 | hir::ExprKind::If(..)
1465 | hir::ExprKind::Tup(..)
1466 | hir::ExprKind::Binary(..)
1467 | hir::ExprKind::Block(..)
1468 | hir::ExprKind::Let(..)
1469 | hir::ExprKind::Loop(..)
1470 | hir::ExprKind::Match(..)
1471 | hir::ExprKind::Lit(..)
1472 | hir::ExprKind::ConstBlock(..)
1473 | hir::ExprKind::Break(..)
1474 | hir::ExprKind::Continue(..)
1475 | hir::ExprKind::Struct(..)
1476 | hir::ExprKind::Repeat(..)
1477 | hir::ExprKind::InlineAsm(..)
1478 | hir::ExprKind::OffsetOf(..)
1479 | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr_ty)),
1480 }
1481 }
1482
1483 fn cat_res(
1484 &self,
1485 hir_id: HirId,
1486 span: Span,
1487 expr_ty: Ty<'tcx>,
1488 res: Res,
1489 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1490 match res {
1491 Res::Def(
1492 DefKind::Ctor(..)
1493 | DefKind::Const
1494 | DefKind::ConstParam
1495 | DefKind::AssocConst
1496 | DefKind::Fn
1497 | DefKind::AssocFn,
1498 _,
1499 )
1500 | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, expr_ty)),
1501
1502 Res::Def(DefKind::Static { .. }, _) => {
1503 Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new()))
1504 }
1505
1506 Res::Local(var_id) => {
1507 if self.upvars.is_some_and(|upvars| upvars.contains_key(&var_id)) {
1508 self.cat_upvar(hir_id, var_id)
1509 } else {
1510 Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new()))
1511 }
1512 }
1513
1514 def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def),
1515 }
1516 }
1517
1518 fn cat_upvar(&self, hir_id: HirId, var_id: HirId) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1524 let closure_expr_def_id = self.cx.body_owner_def_id();
1525
1526 let upvar_id = ty::UpvarId {
1527 var_path: ty::UpvarPath { hir_id: var_id },
1528 closure_expr_id: closure_expr_def_id,
1529 };
1530 let var_ty = self.node_ty(var_id)?;
1531
1532 Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()))
1533 }
1534
1535 fn cat_rvalue(&self, hir_id: HirId, expr_ty: Ty<'tcx>) -> PlaceWithHirId<'tcx> {
1536 PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new())
1537 }
1538
1539 fn cat_projection(
1540 &self,
1541 node: HirId,
1542 base_place: PlaceWithHirId<'tcx>,
1543 ty: Ty<'tcx>,
1544 kind: ProjectionKind,
1545 ) -> PlaceWithHirId<'tcx> {
1546 let place_ty = base_place.place.ty();
1547 let mut projections = base_place.place.projections;
1548
1549 let node_ty = self.cx.typeck_results().node_type(node);
1550 if node_ty != place_ty
1554 && self
1555 .cx
1556 .try_structurally_resolve_type(
1557 self.cx.tcx().hir().span(base_place.hir_id),
1558 place_ty,
1559 )
1560 .is_impl_trait()
1561 {
1562 projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
1563 }
1564 projections.push(Projection { kind, ty });
1565 PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections)
1566 }
1567
1568 fn cat_overloaded_place(
1569 &self,
1570 expr: &hir::Expr<'_>,
1571 base: &hir::Expr<'_>,
1572 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1573 let place_ty = self.expr_ty(expr)?;
1577 let base_ty = self.expr_ty_adjusted(base)?;
1578
1579 let ty::Ref(region, _, mutbl) =
1580 *self.cx.try_structurally_resolve_type(base.span, base_ty).kind()
1581 else {
1582 span_bug!(expr.span, "cat_overloaded_place: base is not a reference");
1583 };
1584 let ref_ty = Ty::new_ref(self.cx.tcx(), region, place_ty, mutbl);
1585
1586 let base = self.cat_rvalue(expr.hir_id, ref_ty);
1587 self.cat_deref(expr.hir_id, base)
1588 }
1589
1590 fn cat_deref(
1591 &self,
1592 node: HirId,
1593 base_place: PlaceWithHirId<'tcx>,
1594 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1595 let base_curr_ty = base_place.place.ty();
1596 let deref_ty = match self
1597 .cx
1598 .try_structurally_resolve_type(
1599 self.cx.tcx().hir().span(base_place.hir_id),
1600 base_curr_ty,
1601 )
1602 .builtin_deref(true)
1603 {
1604 Some(ty) => ty,
1605 None => {
1606 debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
1607 return Err(self.cx.report_error(
1608 self.cx.tcx().hir().span(node),
1609 "explicit deref of non-derefable type",
1610 ));
1611 }
1612 };
1613 let mut projections = base_place.place.projections;
1614 projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty });
1615
1616 Ok(PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections))
1617 }
1618
1619 fn variant_index_for_adt(
1622 &self,
1623 qpath: &hir::QPath<'_>,
1624 pat_hir_id: HirId,
1625 span: Span,
1626 ) -> Result<VariantIdx, Cx::Error> {
1627 let res = self.cx.typeck_results().qpath_res(qpath, pat_hir_id);
1628 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1629 let ty::Adt(adt_def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() else {
1630 return Err(self
1631 .cx
1632 .report_error(span, "struct or tuple struct pattern not applied to an ADT"));
1633 };
1634
1635 match res {
1636 Res::Def(DefKind::Variant, variant_id) => Ok(adt_def.variant_index_with_id(variant_id)),
1637 Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
1638 Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id))
1639 }
1640 Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
1641 | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
1642 | Res::SelfCtor(..)
1643 | Res::SelfTyParam { .. }
1644 | Res::SelfTyAlias { .. } => {
1645 Ok(FIRST_VARIANT)
1647 }
1648 _ => bug!("expected ADT path, found={:?}", res),
1649 }
1650 }
1651
1652 fn total_fields_in_adt_variant(
1655 &self,
1656 pat_hir_id: HirId,
1657 variant_index: VariantIdx,
1658 span: Span,
1659 ) -> Result<usize, Cx::Error> {
1660 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1661 match self.cx.try_structurally_resolve_type(span, ty).kind() {
1662 ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()),
1663 _ => {
1664 self.cx
1665 .tcx()
1666 .dcx()
1667 .span_bug(span, "struct or tuple struct pattern not applied to an ADT");
1668 }
1669 }
1670 }
1671
1672 fn total_fields_in_tuple(&self, pat_hir_id: HirId, span: Span) -> Result<usize, Cx::Error> {
1675 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1676 match self.cx.try_structurally_resolve_type(span, ty).kind() {
1677 ty::Tuple(args) => Ok(args.len()),
1678 _ => Err(self.cx.report_error(span, "tuple pattern not applied to a tuple")),
1679 }
1680 }
1681
1682 fn cat_pattern<F>(
1689 &self,
1690 mut place_with_id: PlaceWithHirId<'tcx>,
1691 pat: &hir::Pat<'_>,
1692 op: &mut F,
1693 ) -> Result<(), Cx::Error>
1694 where
1695 F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>) -> Result<(), Cx::Error>,
1696 {
1697 for _ in
1730 0..self.cx.typeck_results().pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len())
1731 {
1732 debug!("applying adjustment to place_with_id={:?}", place_with_id);
1733 place_with_id = self.cat_deref(pat.hir_id, place_with_id)?;
1734 }
1735 let place_with_id = place_with_id; debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
1737
1738 op(&place_with_id, pat)?;
1748
1749 match pat.kind {
1750 PatKind::Tuple(subpats, dots_pos) => {
1751 let total_fields = self.total_fields_in_tuple(pat.hir_id, pat.span)?;
1753
1754 for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1755 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1756 let projection_kind =
1757 ProjectionKind::Field(FieldIdx::from_usize(i), FIRST_VARIANT);
1758 let sub_place = self.cat_projection(
1759 pat.hir_id,
1760 place_with_id.clone(),
1761 subpat_ty,
1762 projection_kind,
1763 );
1764 self.cat_pattern(sub_place, subpat, op)?;
1765 }
1766 }
1767
1768 PatKind::TupleStruct(ref qpath, subpats, dots_pos) => {
1769 let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?;
1771 let total_fields =
1772 self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?;
1773
1774 for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1775 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1776 let projection_kind =
1777 ProjectionKind::Field(FieldIdx::from_usize(i), variant_index);
1778 let sub_place = self.cat_projection(
1779 pat.hir_id,
1780 place_with_id.clone(),
1781 subpat_ty,
1782 projection_kind,
1783 );
1784 self.cat_pattern(sub_place, subpat, op)?;
1785 }
1786 }
1787
1788 PatKind::Struct(ref qpath, field_pats, _) => {
1789 let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?;
1792
1793 for fp in field_pats {
1794 let field_ty = self.pat_ty_adjusted(fp.pat)?;
1795 let field_index = self
1796 .cx
1797 .typeck_results()
1798 .field_indices()
1799 .get(fp.hir_id)
1800 .cloned()
1801 .expect("no index for a field");
1802
1803 let field_place = self.cat_projection(
1804 pat.hir_id,
1805 place_with_id.clone(),
1806 field_ty,
1807 ProjectionKind::Field(field_index, variant_index),
1808 );
1809 self.cat_pattern(field_place, fp.pat, op)?;
1810 }
1811 }
1812
1813 PatKind::Or(pats) => {
1814 for pat in pats {
1815 self.cat_pattern(place_with_id.clone(), pat, op)?;
1816 }
1817 }
1818
1819 PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => {
1820 self.cat_pattern(place_with_id, subpat, op)?;
1821 }
1822
1823 PatKind::Ref(subpat, _)
1824 if self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) =>
1825 {
1826 self.cat_pattern(place_with_id, subpat, op)?;
1827 }
1828
1829 PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
1830 let subplace = self.cat_deref(pat.hir_id, place_with_id)?;
1834 self.cat_pattern(subplace, subpat, op)?;
1835 }
1836 PatKind::Deref(subpat) => {
1837 let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat);
1838 let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not };
1839 let re_erased = self.cx.tcx().lifetimes.re_erased;
1840 let ty = self.pat_ty_adjusted(subpat)?;
1841 let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability);
1842 let base = self.cat_rvalue(pat.hir_id, ty);
1844 let place = self.cat_deref(pat.hir_id, base)?;
1845 self.cat_pattern(place, subpat, op)?;
1846 }
1847
1848 PatKind::Slice(before, ref slice, after) => {
1849 let Some(element_ty) = self
1850 .cx
1851 .try_structurally_resolve_type(pat.span, place_with_id.place.ty())
1852 .builtin_index()
1853 else {
1854 debug!("explicit index of non-indexable type {:?}", place_with_id);
1855 return Err(self
1856 .cx
1857 .report_error(pat.span, "explicit index of non-indexable type"));
1858 };
1859 let elt_place = self.cat_projection(
1860 pat.hir_id,
1861 place_with_id.clone(),
1862 element_ty,
1863 ProjectionKind::Index,
1864 );
1865 for before_pat in before {
1866 self.cat_pattern(elt_place.clone(), before_pat, op)?;
1867 }
1868 if let Some(slice_pat) = *slice {
1869 let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?;
1870 let slice_place = self.cat_projection(
1871 pat.hir_id,
1872 place_with_id,
1873 slice_pat_ty,
1874 ProjectionKind::Subslice,
1875 );
1876 self.cat_pattern(slice_place, slice_pat, op)?;
1877 }
1878 for after_pat in after {
1879 self.cat_pattern(elt_place.clone(), after_pat, op)?;
1880 }
1881 }
1882
1883 PatKind::Binding(.., None)
1884 | PatKind::Expr(..)
1885 | PatKind::Range(..)
1886 | PatKind::Never
1887 | PatKind::Wild
1888 | PatKind::Err(_) => {
1889 }
1891 }
1892
1893 Ok(())
1894 }
1895
1896 fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
1897 if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() {
1898 def.variants().len() > 1 || def.variant_list_has_applicable_non_exhaustive()
1903 } else {
1904 false
1905 }
1906 }
1907}