rapx/analysis/core/api_dep/
visitor.rs

1use std::io::Write;
2
3use super::extract::extract_constraints;
4use super::graph::ApiDepGraph;
5use super::graph::{DepEdge, DepNode};
6use crate::rap_debug;
7use rustc_hir::{
8    def_id::{DefId, LocalDefId},
9    intravisit::{FnKind, Visitor},
10    BodyId, FnDecl,
11};
12
13use rustc_middle::ty::{self, TyCtxt};
14use rustc_span::Span;
15
16pub struct FnVisitor<'tcx, 'a> {
17    fn_cnt: usize,
18    tcx: TyCtxt<'tcx>,
19    funcs: Vec<DefId>,
20    current_fn_did: Option<DefId>,
21    graph: &'a mut ApiDepGraph<'tcx>,
22}
23
24impl<'tcx, 'a> FnVisitor<'tcx, 'a> {
25    pub fn new(tcx: TyCtxt<'tcx>, graph: &'a mut ApiDepGraph<'tcx>) -> FnVisitor<'tcx, 'a> {
26        let fn_cnt = 0;
27        let funcs = Vec::new();
28        FnVisitor {
29            fn_cnt,
30            tcx,
31            graph,
32            funcs,
33            current_fn_did: None,
34        }
35    }
36    pub fn fn_cnt(&self) -> usize {
37        self.fn_cnt
38    }
39    pub fn write_funcs<T: Write>(&self, f: &mut T) {
40        for id in &self.funcs {
41            write!(f, "{}\n", self.tcx.def_path_str(id)).expect("fail when write funcs");
42        }
43    }
44}
45
46fn get_bound_var_attr(var: ty::BoundVariableKind) -> (String, bool) {
47    let name: String;
48    let is_lifetime;
49    match var {
50        ty::BoundVariableKind::Ty(bound_ty_kind) => {
51            is_lifetime = false;
52            name = match bound_ty_kind {
53                ty::BoundTyKind::Param(_, sym) => sym.to_string(),
54                _ => "anon".to_string(),
55            }
56        }
57        ty::BoundVariableKind::Region(bound_region_kind) => {
58            is_lifetime = true;
59            name = match bound_region_kind {
60                ty::BoundRegionKind::Named(_, name) => name.to_string(),
61                _ => "anon".to_string(),
62            }
63        }
64        ty::BoundVariableKind::Const => {
65            is_lifetime = false;
66            name = "anon const".to_string();
67        }
68    }
69    (name, is_lifetime)
70}
71
72impl<'tcx, 'a> Visitor<'tcx> for FnVisitor<'tcx, 'a> {
73    fn visit_fn<'v>(
74        &mut self,
75        fk: FnKind<'v>,
76        fd: &'v FnDecl<'v>,
77        b: BodyId,
78        span: Span,
79        id: LocalDefId,
80    ) -> Self::Result {
81        let fn_def_id = id.to_def_id();
82        self.fn_cnt += 1;
83        self.funcs.push(fn_def_id);
84        let api_node = self.graph.get_node(DepNode::api(id));
85
86        let early_fn_sig = self.tcx.fn_sig(fn_def_id);
87        let binder_fn_sig = early_fn_sig.instantiate_identity();
88        let fn_sig = self
89            .tcx
90            .liberate_late_bound_regions(fn_def_id, binder_fn_sig);
91        rap_debug!("visit {}", fn_sig);
92
93        // add generic param def to graph
94        // NOTE: generics_of query only return early bound generics
95        let generics = self.tcx.generics_of(fn_def_id);
96        let early_generic_count = generics.count();
97        rap_debug!("early bound generic count = {}", early_generic_count);
98        for i in 0..early_generic_count {
99            let generic_param_def = generics.param_at(i, self.tcx);
100            rap_debug!("early bound generic#{i}: {:?}", generic_param_def);
101            let node_index = self.graph.get_node(DepNode::generic_param_def(
102                fn_def_id,
103                i,
104                generic_param_def.name,
105                !generic_param_def.kind.is_ty_or_const(),
106            ));
107            self.graph
108                .add_edge_once(api_node, node_index, DepEdge::fn2generic());
109        }
110
111        // add late bound generic
112        rap_debug!(
113            "late bound generic count = {}",
114            binder_fn_sig.bound_vars().len()
115        );
116        for (i, var) in binder_fn_sig.bound_vars().iter().enumerate() {
117            rap_debug!("bound var#{i}: {var:?}");
118            let (name, is_lifetime) = get_bound_var_attr(var);
119            let node_index = self.graph.get_node(DepNode::generic_param_def(
120                fn_def_id,
121                early_generic_count + i,
122                name,
123                is_lifetime,
124            ));
125            self.graph
126                .add_edge_once(api_node, node_index, DepEdge::fn2generic());
127        }
128
129        extract_constraints(fn_def_id, self.tcx);
130
131        // add inputs/output to graph, and compute constraints based on subtyping
132        for (no, input_ty) in fn_sig.inputs().iter().enumerate() {
133            // let free_input_ty = input_ty.fold_with(folder)
134            let input_node = self.graph.get_node(DepNode::ty(*input_ty));
135            self.graph.add_edge(input_node, api_node, DepEdge::arg(no));
136        }
137
138        let output_ty = fn_sig.output();
139        let output_node = self.graph.get_node(DepNode::ty(output_ty));
140        self.graph.add_edge(api_node, output_node, DepEdge::ret());
141        rap_debug!("exit visit_fn");
142    }
143}