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_data_structures::fx::FxIndexMap;
17use rustc_hir::def::{CtorOf, Res};
18use rustc_hir::def_id::LocalDefId;
19use rustc_hir::{self as hir, HirId, PatExpr, PatExprKind, PatKind};
20use rustc_lint::LateContext;
21use rustc_middle::hir::place::ProjectionKind;
22pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
24use rustc_middle::mir::FakeReadCause;
25use rustc_middle::ty::{
26 self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
27};
28use rustc_middle::{bug, span_bug};
29use rustc_span::{ErrorGuaranteed, Span};
30use rustc_trait_selection::infer::InferCtxtExt;
31use tracing::{debug, instrument, trace};
32
33use crate::fn_ctxt::FnCtxt;
34
35pub trait Delegate<'tcx> {
38 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) {
81 self.borrow(place_with_id, diag_expr_id, ty::BorrowKind::Immutable)
84 }
85
86 fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId);
89
90 fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
94 self.mutate(binding_place, diag_expr_id)
97 }
98
99 fn fake_read(
101 &mut self,
102 place_with_id: &PlaceWithHirId<'tcx>,
103 cause: FakeReadCause,
104 diag_expr_id: HirId,
105 );
106}
107
108impl<'tcx, D: Delegate<'tcx>> Delegate<'tcx> for &mut D {
109 fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
110 (**self).consume(place_with_id, diag_expr_id)
111 }
112
113 fn use_cloned(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
114 (**self).use_cloned(place_with_id, diag_expr_id)
115 }
116
117 fn borrow(
118 &mut self,
119 place_with_id: &PlaceWithHirId<'tcx>,
120 diag_expr_id: HirId,
121 bk: ty::BorrowKind,
122 ) {
123 (**self).borrow(place_with_id, diag_expr_id, bk)
124 }
125
126 fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
127 (**self).copy(place_with_id, diag_expr_id)
128 }
129
130 fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
131 (**self).mutate(assignee_place, diag_expr_id)
132 }
133
134 fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
135 (**self).bind(binding_place, diag_expr_id)
136 }
137
138 fn fake_read(
139 &mut self,
140 place_with_id: &PlaceWithHirId<'tcx>,
141 cause: FakeReadCause,
142 diag_expr_id: HirId,
143 ) {
144 (**self).fake_read(place_with_id, cause, diag_expr_id)
145 }
146}
147
148pub trait TypeInformationCtxt<'tcx> {
151 type TypeckResults<'a>: Deref<Target = ty::TypeckResults<'tcx>>
152 where
153 Self: 'a;
154
155 type Error;
156
157 fn typeck_results(&self) -> Self::TypeckResults<'_>;
158
159 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T;
160
161 fn structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
162
163 fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error;
164
165 fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error>;
166
167 fn tainted_by_errors(&self) -> Result<(), Self::Error>;
168
169 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool;
170
171 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool;
172
173 fn body_owner_def_id(&self) -> LocalDefId;
174
175 fn tcx(&self) -> TyCtxt<'tcx>;
176}
177
178impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
179 type TypeckResults<'a>
180 = Ref<'a, ty::TypeckResults<'tcx>>
181 where
182 Self: 'a;
183
184 type Error = ErrorGuaranteed;
185
186 fn typeck_results(&self) -> Self::TypeckResults<'_> {
187 self.typeck_results.borrow()
188 }
189
190 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
191 self.infcx.resolve_vars_if_possible(t)
192 }
193
194 fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
195 (**self).structurally_resolve_type(sp, ty)
196 }
197
198 fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error {
199 self.dcx().span_delayed_bug(span, msg.to_string())
200 }
201
202 fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error> {
203 ty.error_reported()
204 }
205
206 fn tainted_by_errors(&self) -> Result<(), ErrorGuaranteed> {
207 if let Some(guar) = self.infcx.tainted_by_errors() { Err(guar) } else { Ok(()) }
208 }
209
210 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
211 self.infcx.type_is_copy_modulo_regions(self.param_env, ty)
212 }
213
214 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
215 self.infcx.type_is_use_cloned_modulo_regions(self.param_env, ty)
216 }
217
218 fn body_owner_def_id(&self) -> LocalDefId {
219 self.body_id
220 }
221
222 fn tcx(&self) -> TyCtxt<'tcx> {
223 self.tcx
224 }
225}
226
227impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
228 type TypeckResults<'a>
229 = &'tcx ty::TypeckResults<'tcx>
230 where
231 Self: 'a;
232
233 type Error = !;
234
235 fn typeck_results(&self) -> Self::TypeckResults<'_> {
236 self.0.maybe_typeck_results().expect("expected typeck results")
237 }
238
239 fn structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
240 ty
242 }
243
244 fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
245 t
246 }
247
248 fn report_bug(&self, span: Span, msg: impl ToString) -> ! {
249 span_bug!(span, "{}", msg.to_string())
250 }
251
252 fn error_reported_in_ty(&self, _ty: Ty<'tcx>) -> Result<(), !> {
253 Ok(())
254 }
255
256 fn tainted_by_errors(&self) -> Result<(), !> {
257 Ok(())
258 }
259
260 fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
261 self.0.type_is_copy_modulo_regions(ty)
262 }
263
264 fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
265 self.0.type_is_use_cloned_modulo_regions(ty)
266 }
267
268 fn body_owner_def_id(&self) -> LocalDefId {
269 self.1
270 }
271
272 fn tcx(&self) -> TyCtxt<'tcx> {
273 self.0.tcx
274 }
275}
276
277pub struct ExprUseVisitor<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> {
281 cx: Cx,
282 delegate: RefCell<D>,
285 upvars: Option<&'tcx FxIndexMap<HirId, hir::Upvar>>,
286}
287
288impl<'a, 'tcx, D: Delegate<'tcx>> ExprUseVisitor<'tcx, (&'a LateContext<'tcx>, LocalDefId), D> {
289 pub fn for_clippy(cx: &'a LateContext<'tcx>, body_def_id: LocalDefId, delegate: D) -> Self {
290 Self::new((cx, body_def_id), delegate)
291 }
292}
293
294impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
295 pub(crate) fn new(cx: Cx, delegate: D) -> Self {
301 ExprUseVisitor {
302 delegate: RefCell::new(delegate),
303 upvars: cx.tcx().upvars_mentioned(cx.body_owner_def_id()),
304 cx,
305 }
306 }
307
308 pub fn consume_body(&self, body: &hir::Body<'_>) -> Result<(), Cx::Error> {
309 for param in body.params {
310 let param_ty = self.pat_ty_adjusted(param.pat)?;
311 debug!("consume_body: param_ty = {:?}", param_ty);
312
313 let param_place = self.cat_rvalue(param.hir_id, param_ty);
314
315 self.walk_irrefutable_pat(¶m_place, param.pat)?;
316 }
317
318 self.consume_expr(body.value)?;
319
320 Ok(())
321 }
322
323 #[instrument(skip(self), level = "debug")]
324 fn consume_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
325 if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
326 self.delegate.borrow_mut().copy(place_with_id, diag_expr_id);
327 } else {
328 self.delegate.borrow_mut().consume(place_with_id, diag_expr_id);
329 }
330 }
331
332 #[instrument(skip(self), level = "debug")]
333 pub fn consume_clone_or_copy(&self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
334 if self.cx.type_is_copy_modulo_regions(place_with_id.place.ty()) {
341 self.delegate.borrow_mut().copy(place_with_id, diag_expr_id);
342 } else if self.cx.type_is_use_cloned_modulo_regions(place_with_id.place.ty()) {
343 self.delegate.borrow_mut().use_cloned(place_with_id, diag_expr_id);
344 } else {
345 self.delegate.borrow_mut().consume(place_with_id, diag_expr_id);
346 }
347 }
348
349 fn consume_exprs(&self, exprs: &[hir::Expr<'_>]) -> Result<(), Cx::Error> {
350 for expr in exprs {
351 self.consume_expr(expr)?;
352 }
353
354 Ok(())
355 }
356
357 #[instrument(skip(self), level = "debug")]
359 pub fn consume_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
360 let place_with_id = self.cat_expr(expr)?;
361 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
362 self.walk_expr(expr)?;
363 Ok(())
364 }
365
366 #[instrument(skip(self), level = "debug")]
367 pub fn consume_or_clone_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
368 let place_with_id = self.cat_expr(expr)?;
369 self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
370 self.walk_expr(expr)?;
371 Ok(())
372 }
373
374 fn mutate_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
375 let place_with_id = self.cat_expr(expr)?;
376 self.delegate.borrow_mut().mutate(&place_with_id, place_with_id.hir_id);
377 self.walk_expr(expr)?;
378 Ok(())
379 }
380
381 #[instrument(skip(self), level = "debug")]
382 fn borrow_expr(&self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) -> Result<(), Cx::Error> {
383 let place_with_id = self.cat_expr(expr)?;
384 self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
385 self.walk_expr(expr)
386 }
387
388 #[instrument(skip(self), level = "debug")]
389 pub fn walk_expr(&self, expr: &hir::Expr<'_>) -> Result<(), Cx::Error> {
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::Missing => unreachable!(),
615 PatKind::Binding(.., opt_sub_pat) => {
616 if opt_sub_pat.is_none() {
619 needs_to_be_read = true;
620 }
621 }
622 PatKind::Never => {
623 needs_to_be_read = true;
626 }
627 PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => {
628 let res = self.cx.typeck_results().qpath_res(qpath, *hir_id);
632 match res {
633 Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => {
634 needs_to_be_read = true;
639 }
640 _ => {
641 needs_to_be_read |=
644 self.is_multivariant_adt(place.place.ty(), *span);
645 }
646 }
647 }
648 PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => {
649 let place_ty = place.place.ty();
656 needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span);
657 }
658 PatKind::Expr(_) | PatKind::Range(..) => {
659 needs_to_be_read = true;
662 }
663 PatKind::Slice(lhs, wild, rhs) => {
664 if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
666 || place.place.ty().peel_refs().is_array()
669 {
670 } else {
671 needs_to_be_read = true;
672 }
673 }
674 PatKind::Or(_)
675 | PatKind::Box(_)
676 | PatKind::Deref(_)
677 | PatKind::Ref(..)
678 | PatKind::Guard(..)
679 | PatKind::Wild
680 | PatKind::Err(_) => {
681 }
686 }
687
688 Ok(())
689 })?
690 }
691
692 if needs_to_be_read {
693 self.borrow_expr(discr, BorrowKind::Immutable)?;
694 } else {
695 let closure_def_id = match discr_place.place.base {
696 PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id),
697 _ => None,
698 };
699
700 self.delegate.borrow_mut().fake_read(
701 &discr_place,
702 FakeReadCause::ForMatchedPlace(closure_def_id),
703 discr_place.hir_id,
704 );
705
706 self.walk_expr(discr)?;
709 }
710 Ok(())
711 }
712
713 fn walk_local<F>(
714 &self,
715 expr: &hir::Expr<'_>,
716 pat: &hir::Pat<'_>,
717 els: Option<&hir::Block<'_>>,
718 mut f: F,
719 ) -> Result<(), Cx::Error>
720 where
721 F: FnMut() -> Result<(), Cx::Error>,
722 {
723 self.walk_expr(expr)?;
724 let expr_place = self.cat_expr(expr)?;
725 f()?;
726 if let Some(els) = els {
727 self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter())?;
729 self.walk_block(els)?;
730 }
731 self.walk_irrefutable_pat(&expr_place, pat)?;
732 Ok(())
733 }
734
735 #[instrument(skip(self), level = "debug")]
738 fn walk_block(&self, blk: &hir::Block<'_>) -> Result<(), Cx::Error> {
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.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 #[instrument(skip(self), level = "debug")]
946 fn walk_pat(
947 &self,
948 discr_place: &PlaceWithHirId<'tcx>,
949 pat: &hir::Pat<'_>,
950 has_guard: bool,
951 ) -> Result<(), Cx::Error> {
952 let tcx = self.cx.tcx();
953 self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| {
954 match pat.kind {
955 PatKind::Binding(_, canonical_id, ..) => {
956 debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
957 let bm = self
958 .cx
959 .typeck_results()
960 .extract_binding_mode(tcx.sess, pat.hir_id, pat.span);
961 debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm);
962
963 let pat_ty = self.node_ty(pat.hir_id)?;
965 debug!("walk_pat: pat_ty={:?}", pat_ty);
966
967 let def = Res::Local(canonical_id);
968 if let Ok(ref binding_place) = self.cat_res(pat.hir_id, pat.span, pat_ty, def) {
969 self.delegate.borrow_mut().bind(binding_place, binding_place.hir_id);
970 }
971
972 if has_guard {
976 self.delegate.borrow_mut().borrow(
977 place,
978 discr_place.hir_id,
979 BorrowKind::Immutable,
980 );
981 }
982
983 match bm.0 {
988 hir::ByRef::Yes(m) => {
989 let bk = ty::BorrowKind::from_mutbl(m);
990 self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
991 }
992 hir::ByRef::No => {
993 debug!("walk_pat binding consuming pat");
994 self.consume_or_copy(place, discr_place.hir_id);
995 }
996 }
997 }
998 PatKind::Deref(subpattern) => {
999 if let hir::ByRef::Yes(mutability) =
1007 self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern)
1008 {
1009 let bk = ty::BorrowKind::from_mutbl(mutability);
1010 self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
1011 }
1012 }
1013 PatKind::Never => {
1014 self.delegate.borrow_mut().borrow(
1017 place,
1018 discr_place.hir_id,
1019 BorrowKind::Immutable,
1020 );
1021 }
1022 _ => {}
1023 }
1024
1025 Ok(())
1026 })
1027 }
1028
1029 #[instrument(skip(self), level = "debug")]
1049 fn walk_captures(&self, closure_expr: &hir::Closure<'_>) -> Result<(), Cx::Error> {
1050 fn upvar_is_local_variable(
1051 upvars: Option<&FxIndexMap<HirId, hir::Upvar>>,
1052 upvar_id: HirId,
1053 body_owner_is_closure: bool,
1054 ) -> bool {
1055 upvars.map(|upvars| !upvars.contains_key(&upvar_id)).unwrap_or(body_owner_is_closure)
1056 }
1057
1058 let tcx = self.cx.tcx();
1059 let closure_def_id = closure_expr.def_id;
1060 let body_owner_is_closure = matches!(
1062 tcx.hir_body_owner_kind(self.cx.body_owner_def_id()),
1063 hir::BodyOwnerKind::Closure
1064 );
1065
1066 if let Some(fake_reads) = self.cx.typeck_results().closure_fake_reads.get(&closure_def_id) {
1069 for (fake_read, cause, hir_id) in fake_reads.iter() {
1070 match fake_read.base {
1071 PlaceBase::Upvar(upvar_id) => {
1072 if upvar_is_local_variable(
1073 self.upvars,
1074 upvar_id.var_path.hir_id,
1075 body_owner_is_closure,
1076 ) {
1077 continue;
1094 }
1095 }
1096 _ => {
1097 bug!(
1098 "Do not know how to get HirId out of Rvalue and StaticItem {:?}",
1099 fake_read.base
1100 );
1101 }
1102 };
1103 self.delegate.borrow_mut().fake_read(
1104 &PlaceWithHirId { place: fake_read.clone(), hir_id: *hir_id },
1105 *cause,
1106 *hir_id,
1107 );
1108 }
1109 }
1110
1111 if let Some(min_captures) =
1112 self.cx.typeck_results().closure_min_captures.get(&closure_def_id)
1113 {
1114 for (var_hir_id, min_list) in min_captures.iter() {
1115 if self
1116 .upvars
1117 .map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id))
1118 {
1119 continue;
1123 }
1124 for captured_place in min_list {
1125 let place = &captured_place.place;
1126 let capture_info = captured_place.info;
1127
1128 let place_base = if body_owner_is_closure {
1129 PlaceBase::Upvar(ty::UpvarId::new(*var_hir_id, self.cx.body_owner_def_id()))
1131 } else {
1132 PlaceBase::Local(*var_hir_id)
1135 };
1136 let closure_hir_id = tcx.local_def_id_to_hir_id(closure_def_id);
1137 let place_with_id = PlaceWithHirId::new(
1138 capture_info
1139 .path_expr_id
1140 .unwrap_or(capture_info.capture_kind_expr_id.unwrap_or(closure_hir_id)),
1141 place.base_ty,
1142 place_base,
1143 place.projections.clone(),
1144 );
1145
1146 match capture_info.capture_kind {
1147 ty::UpvarCapture::ByValue => {
1148 self.consume_or_copy(&place_with_id, place_with_id.hir_id);
1149 }
1150 ty::UpvarCapture::ByUse => {
1151 self.consume_clone_or_copy(&place_with_id, place_with_id.hir_id);
1152 }
1153 ty::UpvarCapture::ByRef(upvar_borrow) => {
1154 self.delegate.borrow_mut().borrow(
1155 &place_with_id,
1156 place_with_id.hir_id,
1157 upvar_borrow,
1158 );
1159 }
1160 }
1161 }
1162 }
1163 }
1164
1165 Ok(())
1166 }
1167}
1168
1169impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
1179 fn expect_and_resolve_type(
1180 &self,
1181 id: HirId,
1182 ty: Option<Ty<'tcx>>,
1183 ) -> Result<Ty<'tcx>, Cx::Error> {
1184 match ty {
1185 Some(ty) => {
1186 let ty = self.cx.resolve_vars_if_possible(ty);
1187 self.cx.error_reported_in_ty(ty)?;
1188 Ok(ty)
1189 }
1190 None => {
1191 self.cx.tainted_by_errors()?;
1193 bug!("no type for node {} in ExprUseVisitor", self.cx.tcx().hir_id_to_string(id));
1194 }
1195 }
1196 }
1197
1198 fn node_ty(&self, hir_id: HirId) -> Result<Ty<'tcx>, Cx::Error> {
1199 self.expect_and_resolve_type(hir_id, self.cx.typeck_results().node_type_opt(hir_id))
1200 }
1201
1202 fn expr_ty(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1203 self.expect_and_resolve_type(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr))
1204 }
1205
1206 fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1207 self.expect_and_resolve_type(
1208 expr.hir_id,
1209 self.cx.typeck_results().expr_ty_adjusted_opt(expr),
1210 )
1211 }
1212
1213 fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1224 if let Some(vec) = self.cx.typeck_results().pat_adjustments().get(pat.hir_id) {
1229 if let Some(first_adjust) = vec.first() {
1230 debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
1231 return Ok(first_adjust.source);
1232 }
1233 } else if let PatKind::Ref(subpat, _) = pat.kind
1234 && self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id)
1235 {
1236 return self.pat_ty_adjusted(subpat);
1237 }
1238
1239 self.pat_ty_unadjusted(pat)
1240 }
1241
1242 fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> Result<Ty<'tcx>, Cx::Error> {
1244 let base_ty = self.node_ty(pat.hir_id)?;
1245 trace!(?base_ty);
1246
1247 match pat.kind {
1250 PatKind::Binding(..) => {
1251 let bm = *self
1252 .cx
1253 .typeck_results()
1254 .pat_binding_modes()
1255 .get(pat.hir_id)
1256 .expect("missing binding mode");
1257
1258 if matches!(bm.0, hir::ByRef::Yes(_)) {
1259 match self.cx.structurally_resolve_type(pat.span, base_ty).builtin_deref(false)
1263 {
1264 Some(ty) => Ok(ty),
1265 None => {
1266 debug!("By-ref binding of non-derefable type");
1267 Err(self
1268 .cx
1269 .report_bug(pat.span, "by-ref binding of non-derefable type"))
1270 }
1271 }
1272 } else {
1273 Ok(base_ty)
1274 }
1275 }
1276 _ => Ok(base_ty),
1277 }
1278 }
1279
1280 fn cat_expr(&self, expr: &hir::Expr<'_>) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1281 self.cat_expr_(expr, self.cx.typeck_results().expr_adjustments(expr))
1282 }
1283
1284 fn cat_expr_(
1287 &self,
1288 expr: &hir::Expr<'_>,
1289 adjustments: &[adjustment::Adjustment<'tcx>],
1290 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1291 match adjustments.split_last() {
1292 None => self.cat_expr_unadjusted(expr),
1293 Some((adjustment, previous)) => {
1294 self.cat_expr_adjusted_with(expr, || self.cat_expr_(expr, previous), adjustment)
1295 }
1296 }
1297 }
1298
1299 fn cat_expr_adjusted(
1300 &self,
1301 expr: &hir::Expr<'_>,
1302 previous: PlaceWithHirId<'tcx>,
1303 adjustment: &adjustment::Adjustment<'tcx>,
1304 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1305 self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment)
1306 }
1307
1308 fn cat_expr_adjusted_with<F>(
1309 &self,
1310 expr: &hir::Expr<'_>,
1311 previous: F,
1312 adjustment: &adjustment::Adjustment<'tcx>,
1313 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error>
1314 where
1315 F: FnOnce() -> Result<PlaceWithHirId<'tcx>, Cx::Error>,
1316 {
1317 let target = self.cx.resolve_vars_if_possible(adjustment.target);
1318 match adjustment.kind {
1319 adjustment::Adjust::Deref(overloaded) => {
1320 let base = if let Some(deref) = overloaded {
1322 let ref_ty = Ty::new_ref(
1323 self.cx.tcx(),
1324 self.cx.tcx().lifetimes.re_erased,
1325 target,
1326 deref.mutbl,
1327 );
1328 self.cat_rvalue(expr.hir_id, ref_ty)
1329 } else {
1330 previous()?
1331 };
1332 self.cat_deref(expr.hir_id, base)
1333 }
1334
1335 adjustment::Adjust::NeverToAny
1336 | adjustment::Adjust::Pointer(_)
1337 | adjustment::Adjust::Borrow(_)
1338 | adjustment::Adjust::ReborrowPin(..) => {
1339 Ok(self.cat_rvalue(expr.hir_id, target))
1341 }
1342 }
1343 }
1344
1345 fn cat_expr_unadjusted(&self, expr: &hir::Expr<'_>) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1346 let expr_ty = self.expr_ty(expr)?;
1347 match expr.kind {
1348 hir::ExprKind::Unary(hir::UnOp::Deref, e_base) => {
1349 if self.cx.typeck_results().is_method_call(expr) {
1350 self.cat_overloaded_place(expr, e_base)
1351 } else {
1352 let base = self.cat_expr(e_base)?;
1353 self.cat_deref(expr.hir_id, base)
1354 }
1355 }
1356
1357 hir::ExprKind::Field(base, _) => {
1358 let base = self.cat_expr(base)?;
1359 debug!(?base);
1360
1361 let field_idx = self
1362 .cx
1363 .typeck_results()
1364 .field_indices()
1365 .get(expr.hir_id)
1366 .cloned()
1367 .expect("Field index not found");
1368
1369 Ok(self.cat_projection(
1370 expr.hir_id,
1371 base,
1372 expr_ty,
1373 ProjectionKind::Field(field_idx, FIRST_VARIANT),
1374 ))
1375 }
1376
1377 hir::ExprKind::Index(base, _, _) => {
1378 if self.cx.typeck_results().is_method_call(expr) {
1379 self.cat_overloaded_place(expr, base)
1385 } else {
1386 let base = self.cat_expr(base)?;
1387 Ok(self.cat_projection(expr.hir_id, base, expr_ty, ProjectionKind::Index))
1388 }
1389 }
1390
1391 hir::ExprKind::Path(ref qpath) => {
1392 let res = self.cx.typeck_results().qpath_res(qpath, expr.hir_id);
1393 self.cat_res(expr.hir_id, expr.span, expr_ty, res)
1394 }
1395
1396 hir::ExprKind::Type(e, _) => self.cat_expr(e),
1399 hir::ExprKind::UnsafeBinderCast(_, e, _) => self.cat_expr(e),
1400
1401 hir::ExprKind::AddrOf(..)
1402 | hir::ExprKind::Call(..)
1403 | hir::ExprKind::Use(..)
1404 | hir::ExprKind::Assign(..)
1405 | hir::ExprKind::AssignOp(..)
1406 | hir::ExprKind::Closure { .. }
1407 | hir::ExprKind::Ret(..)
1408 | hir::ExprKind::Become(..)
1409 | hir::ExprKind::Unary(..)
1410 | hir::ExprKind::Yield(..)
1411 | hir::ExprKind::MethodCall(..)
1412 | hir::ExprKind::Cast(..)
1413 | hir::ExprKind::DropTemps(..)
1414 | hir::ExprKind::Array(..)
1415 | hir::ExprKind::If(..)
1416 | hir::ExprKind::Tup(..)
1417 | hir::ExprKind::Binary(..)
1418 | hir::ExprKind::Block(..)
1419 | hir::ExprKind::Let(..)
1420 | hir::ExprKind::Loop(..)
1421 | hir::ExprKind::Match(..)
1422 | hir::ExprKind::Lit(..)
1423 | hir::ExprKind::ConstBlock(..)
1424 | hir::ExprKind::Break(..)
1425 | hir::ExprKind::Continue(..)
1426 | hir::ExprKind::Struct(..)
1427 | hir::ExprKind::Repeat(..)
1428 | hir::ExprKind::InlineAsm(..)
1429 | hir::ExprKind::OffsetOf(..)
1430 | hir::ExprKind::Err(_) => Ok(self.cat_rvalue(expr.hir_id, expr_ty)),
1431 }
1432 }
1433
1434 fn cat_res(
1435 &self,
1436 hir_id: HirId,
1437 span: Span,
1438 expr_ty: Ty<'tcx>,
1439 res: Res,
1440 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1441 match res {
1442 Res::Def(
1443 DefKind::Ctor(..)
1444 | DefKind::Const
1445 | DefKind::ConstParam
1446 | DefKind::AssocConst
1447 | DefKind::Fn
1448 | DefKind::AssocFn,
1449 _,
1450 )
1451 | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, expr_ty)),
1452
1453 Res::Def(DefKind::Static { .. }, _) => {
1454 Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new()))
1455 }
1456
1457 Res::Local(var_id) => {
1458 if self.upvars.is_some_and(|upvars| upvars.contains_key(&var_id)) {
1459 self.cat_upvar(hir_id, var_id)
1460 } else {
1461 Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new()))
1462 }
1463 }
1464
1465 def => span_bug!(span, "unexpected definition in ExprUseVisitor: {:?}", def),
1466 }
1467 }
1468
1469 fn cat_upvar(&self, hir_id: HirId, var_id: HirId) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1475 let closure_expr_def_id = self.cx.body_owner_def_id();
1476
1477 let upvar_id = ty::UpvarId {
1478 var_path: ty::UpvarPath { hir_id: var_id },
1479 closure_expr_id: closure_expr_def_id,
1480 };
1481 let var_ty = self.node_ty(var_id)?;
1482
1483 Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()))
1484 }
1485
1486 fn cat_rvalue(&self, hir_id: HirId, expr_ty: Ty<'tcx>) -> PlaceWithHirId<'tcx> {
1487 PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new())
1488 }
1489
1490 fn cat_projection(
1491 &self,
1492 node: HirId,
1493 base_place: PlaceWithHirId<'tcx>,
1494 ty: Ty<'tcx>,
1495 kind: ProjectionKind,
1496 ) -> PlaceWithHirId<'tcx> {
1497 let place_ty = base_place.place.ty();
1498 let mut projections = base_place.place.projections;
1499
1500 let node_ty = self.cx.typeck_results().node_type(node);
1501 if !self.cx.tcx().next_trait_solver_globally() {
1502 if node_ty != place_ty
1506 && self
1507 .cx
1508 .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty)
1509 .is_impl_trait()
1510 {
1511 projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
1512 }
1513 }
1514 projections.push(Projection { kind, ty });
1515 PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections)
1516 }
1517
1518 fn cat_overloaded_place(
1519 &self,
1520 expr: &hir::Expr<'_>,
1521 base: &hir::Expr<'_>,
1522 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1523 let place_ty = self.expr_ty(expr)?;
1527 let base_ty = self.expr_ty_adjusted(base)?;
1528
1529 let ty::Ref(region, _, mutbl) =
1530 *self.cx.structurally_resolve_type(base.span, base_ty).kind()
1531 else {
1532 span_bug!(expr.span, "cat_overloaded_place: base is not a reference");
1533 };
1534 let ref_ty = Ty::new_ref(self.cx.tcx(), region, place_ty, mutbl);
1535
1536 let base = self.cat_rvalue(expr.hir_id, ref_ty);
1537 self.cat_deref(expr.hir_id, base)
1538 }
1539
1540 fn cat_deref(
1541 &self,
1542 node: HirId,
1543 base_place: PlaceWithHirId<'tcx>,
1544 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1545 let base_curr_ty = base_place.place.ty();
1546 let deref_ty = match self
1547 .cx
1548 .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), base_curr_ty)
1549 .builtin_deref(true)
1550 {
1551 Some(ty) => ty,
1552 None => {
1553 debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
1554 return Err(self.cx.report_bug(
1555 self.cx.tcx().hir_span(node),
1556 "explicit deref of non-derefable type",
1557 ));
1558 }
1559 };
1560 let mut projections = base_place.place.projections;
1561 projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty });
1562
1563 Ok(PlaceWithHirId::new(node, base_place.place.base_ty, base_place.place.base, projections))
1564 }
1565
1566 fn variant_index_for_adt(
1569 &self,
1570 qpath: &hir::QPath<'_>,
1571 pat_hir_id: HirId,
1572 span: Span,
1573 ) -> Result<VariantIdx, Cx::Error> {
1574 let res = self.cx.typeck_results().qpath_res(qpath, pat_hir_id);
1575 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1576 let ty::Adt(adt_def, _) = self.cx.structurally_resolve_type(span, ty).kind() else {
1577 return Err(self
1578 .cx
1579 .report_bug(span, "struct or tuple struct pattern not applied to an ADT"));
1580 };
1581
1582 match res {
1583 Res::Def(DefKind::Variant, variant_id) => Ok(adt_def.variant_index_with_id(variant_id)),
1584 Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
1585 Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id))
1586 }
1587 Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
1588 | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
1589 | Res::SelfCtor(..)
1590 | Res::SelfTyParam { .. }
1591 | Res::SelfTyAlias { .. } => {
1592 Ok(FIRST_VARIANT)
1594 }
1595 _ => bug!("expected ADT path, found={:?}", res),
1596 }
1597 }
1598
1599 fn total_fields_in_adt_variant(
1602 &self,
1603 pat_hir_id: HirId,
1604 variant_index: VariantIdx,
1605 span: Span,
1606 ) -> Result<usize, Cx::Error> {
1607 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1608 match self.cx.structurally_resolve_type(span, ty).kind() {
1609 ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()),
1610 _ => {
1611 self.cx
1612 .tcx()
1613 .dcx()
1614 .span_bug(span, "struct or tuple struct pattern not applied to an ADT");
1615 }
1616 }
1617 }
1618
1619 fn total_fields_in_tuple(&self, pat_hir_id: HirId, span: Span) -> Result<usize, Cx::Error> {
1622 let ty = self.cx.typeck_results().node_type(pat_hir_id);
1623 match self.cx.structurally_resolve_type(span, ty).kind() {
1624 ty::Tuple(args) => Ok(args.len()),
1625 _ => Err(self.cx.report_bug(span, "tuple pattern not applied to a tuple")),
1626 }
1627 }
1628
1629 fn cat_pattern<F>(
1636 &self,
1637 mut place_with_id: PlaceWithHirId<'tcx>,
1638 pat: &hir::Pat<'_>,
1639 op: &mut F,
1640 ) -> Result<(), Cx::Error>
1641 where
1642 F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>) -> Result<(), Cx::Error>,
1643 {
1644 let typeck_results = self.cx.typeck_results();
1677 let adjustments: &[adjustment::PatAdjustment<'tcx>] =
1678 typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
1679 let mut adjusts = adjustments.iter().peekable();
1680 while let Some(adjust) = adjusts.next() {
1681 debug!("applying adjustment to place_with_id={:?}", place_with_id);
1682 place_with_id = match adjust.kind {
1683 adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?,
1684 adjustment::PatAdjust::OverloadedDeref => {
1685 op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?;
1692 let target_ty = match adjusts.peek() {
1693 Some(&&next_adjust) => next_adjust.source,
1694 None => self.pat_ty_unadjusted(pat)?,
1696 };
1697 self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)?
1698 }
1699 };
1700 }
1701 drop(typeck_results); let place_with_id = place_with_id; debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
1704
1705 op(&place_with_id, pat)?;
1715
1716 match pat.kind {
1717 PatKind::Tuple(subpats, dots_pos) => {
1718 let total_fields = self.total_fields_in_tuple(pat.hir_id, pat.span)?;
1720
1721 for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1722 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1723 let projection_kind =
1724 ProjectionKind::Field(FieldIdx::from_usize(i), FIRST_VARIANT);
1725 let sub_place = self.cat_projection(
1726 pat.hir_id,
1727 place_with_id.clone(),
1728 subpat_ty,
1729 projection_kind,
1730 );
1731 self.cat_pattern(sub_place, subpat, op)?;
1732 }
1733 }
1734
1735 PatKind::TupleStruct(ref qpath, subpats, dots_pos) => {
1736 let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?;
1738 let total_fields =
1739 self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?;
1740
1741 for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
1742 let subpat_ty = self.pat_ty_adjusted(subpat)?;
1743 let projection_kind =
1744 ProjectionKind::Field(FieldIdx::from_usize(i), variant_index);
1745 let sub_place = self.cat_projection(
1746 pat.hir_id,
1747 place_with_id.clone(),
1748 subpat_ty,
1749 projection_kind,
1750 );
1751 self.cat_pattern(sub_place, subpat, op)?;
1752 }
1753 }
1754
1755 PatKind::Struct(ref qpath, field_pats, _) => {
1756 let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?;
1759
1760 for fp in field_pats {
1761 let field_ty = self.pat_ty_adjusted(fp.pat)?;
1762 let field_index = self
1763 .cx
1764 .typeck_results()
1765 .field_indices()
1766 .get(fp.hir_id)
1767 .cloned()
1768 .expect("no index for a field");
1769
1770 let field_place = self.cat_projection(
1771 pat.hir_id,
1772 place_with_id.clone(),
1773 field_ty,
1774 ProjectionKind::Field(field_index, variant_index),
1775 );
1776 self.cat_pattern(field_place, fp.pat, op)?;
1777 }
1778 }
1779
1780 PatKind::Or(pats) => {
1781 for pat in pats {
1782 self.cat_pattern(place_with_id.clone(), pat, op)?;
1783 }
1784 }
1785
1786 PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => {
1787 self.cat_pattern(place_with_id, subpat, op)?;
1788 }
1789
1790 PatKind::Ref(subpat, _)
1791 if self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) =>
1792 {
1793 self.cat_pattern(place_with_id, subpat, op)?;
1794 }
1795
1796 PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
1797 let subplace = self.cat_deref(pat.hir_id, place_with_id)?;
1801 self.cat_pattern(subplace, subpat, op)?;
1802 }
1803 PatKind::Deref(subpat) => {
1804 let ty = self.pat_ty_adjusted(subpat)?;
1805 let place = self.pat_deref_place(pat.hir_id, place_with_id, subpat, ty)?;
1806 self.cat_pattern(place, subpat, op)?;
1807 }
1808
1809 PatKind::Slice(before, ref slice, after) => {
1810 let Some(element_ty) = self
1811 .cx
1812 .structurally_resolve_type(pat.span, place_with_id.place.ty())
1813 .builtin_index()
1814 else {
1815 debug!("explicit index of non-indexable type {:?}", place_with_id);
1816 return Err(self
1817 .cx
1818 .report_bug(pat.span, "explicit index of non-indexable type"));
1819 };
1820 let elt_place = self.cat_projection(
1821 pat.hir_id,
1822 place_with_id.clone(),
1823 element_ty,
1824 ProjectionKind::Index,
1825 );
1826 for before_pat in before {
1827 self.cat_pattern(elt_place.clone(), before_pat, op)?;
1828 }
1829 if let Some(slice_pat) = *slice {
1830 let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?;
1831 let slice_place = self.cat_projection(
1832 pat.hir_id,
1833 place_with_id,
1834 slice_pat_ty,
1835 ProjectionKind::Subslice,
1836 );
1837 self.cat_pattern(slice_place, slice_pat, op)?;
1838 }
1839 for after_pat in after {
1840 self.cat_pattern(elt_place.clone(), after_pat, op)?;
1841 }
1842 }
1843
1844 PatKind::Binding(.., None)
1845 | PatKind::Expr(..)
1846 | PatKind::Range(..)
1847 | PatKind::Never
1848 | PatKind::Missing
1849 | PatKind::Wild
1850 | PatKind::Err(_) => {
1851 }
1853 }
1854
1855 Ok(())
1856 }
1857
1858 fn pat_deref_place(
1860 &self,
1861 hir_id: HirId,
1862 base_place: PlaceWithHirId<'tcx>,
1863 inner: &hir::Pat<'_>,
1864 target_ty: Ty<'tcx>,
1865 ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> {
1866 match self.cx.typeck_results().deref_pat_borrow_mode(base_place.place.ty(), inner) {
1867 hir::ByRef::No => self.cat_deref(hir_id, base_place),
1869 hir::ByRef::Yes(mutability) => {
1871 let re_erased = self.cx.tcx().lifetimes.re_erased;
1872 let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
1873 let base = self.cat_rvalue(hir_id, ty);
1875 self.cat_deref(hir_id, base)
1877 }
1878 }
1879 }
1880
1881 fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
1882 if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() {
1883 def.variants().len() > 1 || def.variant_list_has_applicable_non_exhaustive()
1888 } else {
1889 false
1890 }
1891 }
1892}