about summary refs log tree commit diff
path: root/compiler/rustc_middle/src/util/find_self_call.rs
blob: 0fdd352073860d76adf424860a5cbb994398c4ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
use rustc_span::def_id::DefId;
use rustc_span::source_map::Spanned;
use tracing::debug;

use crate::mir::*;
use crate::ty::{self, GenericArgsRef, TyCtxt};

/// Checks if the specified `local` is used as the `self` parameter of a method call
/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
/// returned.
pub fn find_self_call<'tcx>(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    local: Local,
    block: BasicBlock,
) -> Option<(DefId, GenericArgsRef<'tcx>)> {
    debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator);
    if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
        &body[block].terminator
        && let Operand::Constant(box ConstOperand { const_, .. }) = func
        && let ty::FnDef(def_id, fn_args) = *const_.ty().kind()
        && let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
            tcx.opt_associated_item(def_id)
        && let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] =
            **args
    {
        if self_place.as_local() == Some(local) {
            return Some((def_id, fn_args));
        }

        // Handle the case where `self_place` gets reborrowed.
        // This happens when the receiver is `&T`.
        for stmt in &body[block].statements {
            if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind
                && let Some(reborrow_local) = place.as_local()
                && self_place.as_local() == Some(reborrow_local)
                && let Rvalue::Ref(_, _, deref_place) = rvalue
                && let PlaceRef { local: deref_local, projection: [ProjectionElem::Deref] } =
                    deref_place.as_ref()
                && deref_local == local
            {
                return Some((def_id, fn_args));
            }
        }
    }
    None
}