Skip to main content

backhopper_xref_graph/
call.rs

1// Copyright (C) 2026 Michael S. Klishin and Contributors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! Call-site, function-reference, and call-kind types.
6
7use std::fmt;
8
9use backhopper_core::{Arity, FunctionName, Mfa, ModuleName};
10use serde::{Deserialize, Serialize};
11
12use crate::loc::Loc;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15pub struct FunctionSig {
16    pub name: FunctionName,
17    pub arity: Arity,
18}
19
20impl FunctionSig {
21    pub fn new(name: FunctionName, arity: Arity) -> Self {
22        Self { name, arity }
23    }
24}
25
26impl fmt::Display for FunctionSig {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(f, "{}/{}", self.name, self.arity)
29    }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
33#[serde(tag = "ref", rename_all = "snake_case")]
34pub enum FunctionRef {
35    Concrete(Mfa),
36    UnresolvedModule {
37        function: FunctionName,
38        arity: Option<Arity>,
39    },
40    UnresolvedFunction {
41        module: ModuleName,
42        arity: Option<Arity>,
43    },
44    UnresolvedBoth {
45        arity: Option<Arity>,
46    },
47}
48
49impl FunctionRef {
50    pub fn as_concrete(&self) -> Option<&Mfa> {
51        if let FunctionRef::Concrete(mfa) = self {
52            Some(mfa)
53        } else {
54            None
55        }
56    }
57
58    pub fn module(&self) -> Option<&ModuleName> {
59        match self {
60            FunctionRef::Concrete(m) => Some(&m.module),
61            FunctionRef::UnresolvedFunction { module, .. } => Some(module),
62            _ => None,
63        }
64    }
65
66    pub fn arity(&self) -> Option<Arity> {
67        match self {
68            FunctionRef::Concrete(m) => Some(m.arity),
69            FunctionRef::UnresolvedModule { arity, .. }
70            | FunctionRef::UnresolvedFunction { arity, .. }
71            | FunctionRef::UnresolvedBoth { arity } => *arity,
72        }
73    }
74}
75
76impl fmt::Display for FunctionRef {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        match self {
79            FunctionRef::Concrete(mfa) => write!(f, "{mfa}"),
80            FunctionRef::UnresolvedModule { function, arity } => {
81                write!(f, "'$M_EXPR':{}/{}", function, fmt_arity(*arity))
82            }
83            FunctionRef::UnresolvedFunction { module, arity } => {
84                write!(f, "{}:'$F_EXPR'/{}", module, fmt_arity(*arity))
85            }
86            FunctionRef::UnresolvedBoth { arity } => {
87                write!(f, "'$M_EXPR':'$F_EXPR'/{}", fmt_arity(*arity))
88            }
89        }
90    }
91}
92
93fn fmt_arity(a: Option<Arity>) -> String {
94    match a {
95        Some(a) => a.to_string(),
96        None => "-1".to_string(),
97    }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
101pub enum LocalFunctionRef {
102    Concrete {
103        function: FunctionName,
104        arity: Arity,
105    },
106    Unresolved {
107        arity: Option<Arity>,
108    },
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
112#[serde(tag = "target", rename_all = "snake_case")]
113pub enum CallTarget {
114    External(FunctionRef),
115    Local(LocalFunctionRef),
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
119#[serde(rename_all = "snake_case")]
120#[non_exhaustive]
121pub enum CallKind {
122    Direct,
123    Apply,
124    Spawn,
125    FunRef,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
129pub struct UnresolvedCallSite {
130    pub caller: Mfa,
131    pub partial: FunctionRef,
132    pub kind: CallKind,
133    pub loc: Loc,
134}