gbf_core/decompiler/ast/
member_access.rs

1#![deny(missing_docs)]
2
3use gbf_macros::AstNodeTransform;
4use serde::{Deserialize, Serialize};
5
6use super::{AstKind, AstNodeError, expr::ExprKind, ptr::P, ssa::SsaVersion, visitors::AstVisitor};
7use crate::decompiler::ast::AstVisitable;
8
9/// Represents a member access node in the AST, such as `object.field`.
10#[derive(Debug, Clone, Serialize, Deserialize, Eq, AstNodeTransform)]
11#[convert_to(ExprKind::MemberAccess, AstKind::Expression)]
12pub struct MemberAccessNode {
13    /// The left-hand side of the member access, such as `object`.
14    pub lhs: ExprKind,
15    /// The right-hand side of the member access, such as `field`.
16    pub rhs: ExprKind,
17    /// Represents the SSA version of a variable.
18    pub ssa_version: Option<SsaVersion>,
19}
20
21impl MemberAccessNode {
22    /// Creates a new `MemberAccessNode` after validating `lhs` and `rhs` types.
23    ///
24    /// # Arguments
25    /// - `lhs` - The left-hand side of the member access.
26    /// - `rhs` - The right-hand side of the member access.
27    ///
28    /// # Returns
29    /// A new `MemberAccessNode`.
30    ///
31    /// # Errors
32    /// Returns an `AstNodeError` if `lhs` or `rhs` is of an unsupported type.
33    pub fn new(lhs: ExprKind, rhs: ExprKind) -> Result<Self, AstNodeError> {
34        let new_lhs = lhs.clone();
35        let new_rhs = rhs.clone();
36
37        Ok(Self {
38            lhs: new_lhs,
39            rhs: new_rhs,
40            ssa_version: None,
41        })
42    }
43}
44
45impl AstVisitable for P<MemberAccessNode> {
46    fn accept<V: AstVisitor>(&self, visitor: &mut V) -> V::Output {
47        visitor.visit_member_access(self)
48    }
49}
50
51// == Other implementations for member access ==
52impl PartialEq for MemberAccessNode {
53    fn eq(&self, other: &Self) -> bool {
54        self.lhs == other.lhs && self.rhs == other.rhs
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use crate::decompiler::ast::{AstNodeError, emit, new_id, new_member_access};
61
62    #[test]
63    fn test_member_access_emit() -> Result<(), AstNodeError> {
64        let member = new_member_access(new_id("object"), new_id("field"))?;
65        assert_eq!(emit(member), "object.field");
66        Ok(())
67    }
68
69    #[test]
70    fn test_member_access_nested_emit() -> Result<(), AstNodeError> {
71        let member = new_member_access(
72            new_member_access(new_id("object"), new_id("field"))?,
73            new_id("other"),
74        )?;
75        assert_eq!(emit(member), "object.field.other");
76        Ok(())
77    }
78
79    #[test]
80    fn test_member_access_equality() -> Result<(), AstNodeError> {
81        let member1 = new_member_access(new_id("object"), new_id("field"))?;
82        let member2 = new_member_access(new_id("object"), new_id("field"))?;
83        assert_eq!(member1, member2);
84
85        let member3 = new_member_access(new_id("object"), new_id("other"))?;
86        assert_ne!(member1, member3);
87        Ok(())
88    }
89}