use std::collections::BTreeMap;
#[derive(Debug, Clone)]
pub struct ExpectedAstNode {
pub kind: AstNodeKind,
pub name: Option<String>,
pub type_name: Option<String>,
pub location: Option<(u32, u32)>,
pub children: Vec<ExpectedAstNode>,
pub attributes: BTreeMap<String, String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AstNodeKind {
TranslationUnit,
FunctionDecl,
ParmVarDecl,
CompoundStmt,
ReturnStmt,
DeclStmt,
IfStmt,
ForStmt,
WhileStmt,
DoWhileStmt,
SwitchStmt,
CaseStmt,
DefaultStmt,
BreakStmt,
ContinueStmt,
GotoStmt,
LabelStmt,
BinaryOperator,
UnaryOperator,
CallExpr,
IntegerLiteral,
FloatingLiteral,
StringLiteral,
CharacterLiteral,
DeclRefExpr,
ArraySubscriptExpr,
MemberExpr,
ConditionalOperator,
StructDecl,
UnionDecl,
EnumDecl,
EnumConstantDecl,
TypedefDecl,
VarDecl,
FieldDecl,
CXXRecordDecl,
CXXMethodDecl,
CXXConstructorDecl,
CXXDestructorDecl,
CXXBaseSpecifier,
NamespaceDecl,
UsingDecl,
TemplateDecl,
TemplateTypeParmDecl,
FunctionTemplateDecl,
ClassTemplateDecl,
AccessSpecifier,
OperatorCall,
}
impl AstNodeKind {
pub fn name(&self) -> &'static str {
match self {
Self::TranslationUnit => "TranslationUnit",
Self::FunctionDecl => "FunctionDecl",
Self::ParmVarDecl => "ParmVarDecl",
Self::CompoundStmt => "CompoundStmt",
Self::ReturnStmt => "ReturnStmt",
Self::DeclStmt => "DeclStmt",
Self::IfStmt => "IfStmt",
Self::ForStmt => "ForStmt",
Self::WhileStmt => "WhileStmt",
Self::DoWhileStmt => "DoWhileStmt",
Self::SwitchStmt => "SwitchStmt",
Self::CaseStmt => "CaseStmt",
Self::DefaultStmt => "DefaultStmt",
Self::BreakStmt => "BreakStmt",
Self::ContinueStmt => "ContinueStmt",
Self::GotoStmt => "GotoStmt",
Self::LabelStmt => "LabelStmt",
Self::BinaryOperator => "BinaryOperator",
Self::UnaryOperator => "UnaryOperator",
Self::CallExpr => "CallExpr",
Self::IntegerLiteral => "IntegerLiteral",
Self::FloatingLiteral => "FloatingLiteral",
Self::StringLiteral => "StringLiteral",
Self::CharacterLiteral => "CharacterLiteral",
Self::DeclRefExpr => "DeclRefExpr",
Self::ArraySubscriptExpr => "ArraySubscriptExpr",
Self::MemberExpr => "MemberExpr",
Self::ConditionalOperator => "ConditionalOperator",
Self::StructDecl => "StructDecl",
Self::UnionDecl => "UnionDecl",
Self::EnumDecl => "EnumDecl",
Self::EnumConstantDecl => "EnumConstantDecl",
Self::TypedefDecl => "TypedefDecl",
Self::VarDecl => "VarDecl",
Self::FieldDecl => "FieldDecl",
Self::CXXRecordDecl => "CXXRecordDecl",
Self::CXXMethodDecl => "CXXMethodDecl",
Self::CXXConstructorDecl => "CXXConstructorDecl",
Self::CXXDestructorDecl => "CXXDestructorDecl",
Self::CXXBaseSpecifier => "CXXBaseSpecifier",
Self::NamespaceDecl => "NamespaceDecl",
Self::UsingDecl => "UsingDecl",
Self::TemplateDecl => "TemplateDecl",
Self::TemplateTypeParmDecl => "TemplateTypeParmDecl",
Self::FunctionTemplateDecl => "FunctionTemplateDecl",
Self::ClassTemplateDecl => "ClassTemplateDecl",
Self::AccessSpecifier => "AccessSpecifier",
Self::OperatorCall => "OperatorCall",
}
}
}
#[derive(Debug, Clone)]
pub struct AstVerificationResult {
pub name: String,
pub passed: bool,
pub found_nodes: Vec<String>,
pub missing_nodes: Vec<String>,
pub unexpected_nodes: Vec<String>,
pub type_results: Vec<TypeCheckResult>,
pub location_results: Vec<LocationCheckResult>,
}
impl AstVerificationResult {
pub fn pass(name: &str, found: Vec<String>) -> Self {
Self {
name: name.to_string(),
passed: true,
found_nodes: found,
missing_nodes: Vec::new(),
unexpected_nodes: Vec::new(),
type_results: Vec::new(),
location_results: Vec::new(),
}
}
pub fn fail(name: &str, missing: Vec<String>, unexpected: Vec<String>) -> Self {
Self {
name: name.to_string(),
passed: false,
found_nodes: Vec::new(),
missing_nodes: missing,
unexpected_nodes: unexpected,
type_results: Vec::new(),
location_results: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct TypeCheckResult {
pub identifier: String,
pub expected_type: String,
pub actual_type: String,
pub matches: bool,
}
#[derive(Debug, Clone)]
pub struct LocationCheckResult {
pub node: String,
pub expected_line: u32,
pub expected_column: u32,
pub actual_line: u32,
pub actual_column: u32,
pub matches: bool,
}
#[derive(Debug, Clone)]
pub struct TypeInfo {
pub name: String,
pub size_bits: u32,
pub align_bits: u32,
pub is_signed: bool,
pub is_integral: bool,
pub is_float: bool,
pub is_pointer: bool,
}
impl TypeInfo {
pub fn int() -> Self {
Self {
name: "int".into(),
size_bits: 32,
align_bits: 32,
is_signed: true,
is_integral: true,
is_float: false,
is_pointer: false,
}
}
pub fn double() -> Self {
Self {
name: "double".into(),
size_bits: 64,
align_bits: 64,
is_signed: true,
is_integral: false,
is_float: true,
is_pointer: false,
}
}
pub fn char_type() -> Self {
Self {
name: "char".into(),
size_bits: 8,
align_bits: 8,
is_signed: true,
is_integral: true,
is_float: false,
is_pointer: false,
}
}
pub fn long() -> Self {
Self {
name: "long".into(),
size_bits: 64,
align_bits: 64,
is_signed: true,
is_integral: true,
is_float: false,
is_pointer: false,
}
}
pub fn pointer_to(pointee: &str) -> Self {
Self {
name: format!("{}*", pointee),
size_bits: 64,
align_bits: 64,
is_signed: false,
is_integral: false,
is_float: false,
is_pointer: true,
}
}
pub fn verify_size(&self, expected_bits: u32) -> bool {
self.size_bits == expected_bits
}
}
#[derive(Debug, Clone, Default)]
pub struct TypeRegistry {
types: BTreeMap<String, TypeInfo>,
}
impl TypeRegistry {
pub fn new() -> Self {
let mut registry = Self::default();
registry.register("int", TypeInfo::int());
registry.register("double", TypeInfo::double());
registry.register("char", TypeInfo::char_type());
registry.register("long", TypeInfo::long());
registry.register(
"short",
TypeInfo {
name: "short".into(),
size_bits: 16,
align_bits: 16,
is_signed: true,
is_integral: true,
is_float: false,
is_pointer: false,
},
);
registry.register(
"float",
TypeInfo {
name: "float".into(),
size_bits: 32,
align_bits: 32,
is_signed: true,
is_integral: false,
is_float: true,
is_pointer: false,
},
);
registry.register(
"void",
TypeInfo {
name: "void".into(),
size_bits: 0,
align_bits: 8,
is_signed: false,
is_integral: false,
is_float: false,
is_pointer: false,
},
);
registry.register(
"unsigned int",
TypeInfo {
name: "unsigned int".into(),
size_bits: 32,
align_bits: 32,
is_signed: false,
is_integral: true,
is_float: false,
is_pointer: false,
},
);
registry
}
pub fn register(&mut self, name: &str, info: TypeInfo) {
self.types.insert(name.to_string(), info);
}
pub fn lookup(&self, name: &str) -> Option<&TypeInfo> {
self.types.get(name)
}
pub fn verify_type_size(&self, type_name: &str, expected_bits: u32) -> bool {
self.lookup(type_name)
.map(|t| t.size_bits == expected_bits)
.unwrap_or(false)
}
}
pub fn c_function_def_source() -> &'static str {
"int add(int a, int b) { return a + b; }"
}
pub fn c_function_def_expected() -> Vec<ExpectedAstNode> {
vec![ExpectedAstNode {
kind: AstNodeKind::FunctionDecl,
name: Some("add".into()),
type_name: Some("int".into()),
location: Some((1, 1)),
children: vec![
ExpectedAstNode {
kind: AstNodeKind::ParmVarDecl,
name: Some("a".into()),
type_name: Some("int".into()),
location: None,
children: vec![],
attributes: BTreeMap::new(),
},
ExpectedAstNode {
kind: AstNodeKind::ParmVarDecl,
name: Some("b".into()),
type_name: Some("int".into()),
location: None,
children: vec![],
attributes: BTreeMap::new(),
},
],
attributes: BTreeMap::new(),
}]
}
pub fn c_struct_source() -> &'static str {
"struct Point { int x; int y; };"
}
pub fn c_struct_expected() -> Vec<ExpectedAstNode> {
vec![ExpectedAstNode {
kind: AstNodeKind::StructDecl,
name: Some("Point".into()),
type_name: None,
location: Some((1, 1)),
children: vec![
ExpectedAstNode {
kind: AstNodeKind::FieldDecl,
name: Some("x".into()),
type_name: Some("int".into()),
location: None,
children: vec![],
attributes: BTreeMap::new(),
},
ExpectedAstNode {
kind: AstNodeKind::FieldDecl,
name: Some("y".into()),
type_name: Some("int".into()),
location: None,
children: vec![],
attributes: BTreeMap::new(),
},
],
attributes: BTreeMap::new(),
}]
}
pub fn c_typedef_source() -> &'static str {
"typedef unsigned long size_t;"
}
pub fn c_typedef_expected() -> Vec<ExpectedAstNode> {
vec![ExpectedAstNode {
kind: AstNodeKind::TypedefDecl,
name: Some("size_t".into()),
type_name: Some("unsigned long".into()),
location: Some((1, 1)),
children: vec![],
attributes: BTreeMap::new(),
}]
}
pub fn c_array_pointer_source() -> &'static str {
"int main() { int arr[10]; int *p = arr; arr[0] = *p; return 0; }"
}
pub fn c_loops_source() -> &'static str {
"int main() { for (int i = 0; i < 10; i++) { ; } while (1) { break; } do { continue; } while (0); return 0; }"
}
pub fn c_loops_expected_kinds() -> Vec<AstNodeKind> {
vec![
AstNodeKind::ForStmt,
AstNodeKind::WhileStmt,
AstNodeKind::DoWhileStmt,
AstNodeKind::BreakStmt,
AstNodeKind::ContinueStmt,
]
}
pub fn c_switch_source() -> &'static str {
"int main() { int x = 2; switch (x) { case 1: return 1; case 2: return 2; default: return 0; } }"
}
pub fn c_switch_expected_kinds() -> Vec<AstNodeKind> {
vec![
AstNodeKind::SwitchStmt,
AstNodeKind::CaseStmt,
AstNodeKind::DefaultStmt,
]
}
pub fn c_goto_source() -> &'static str {
"int main() { goto end; end: return 0; }"
}
pub fn c_goto_expected_kinds() -> Vec<AstNodeKind> {
vec![AstNodeKind::GotoStmt, AstNodeKind::LabelStmt]
}
pub fn c_union_enum_source() -> &'static str {
r#"
union Data { int i; float f; };
enum Color { RED, GREEN, BLUE };
int main() { union Data d; enum Color c = BLUE; return c; }
"#
}
pub fn c_union_enum_expected_kinds() -> Vec<AstNodeKind> {
vec![
AstNodeKind::UnionDecl,
AstNodeKind::EnumDecl,
AstNodeKind::EnumConstantDecl,
]
}
pub fn cpp_class_source() -> &'static str {
r#"
class Calculator {
public:
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
private:
int result;
};
"#
}
pub fn cpp_class_expected_kinds() -> Vec<AstNodeKind> {
vec![
AstNodeKind::CXXRecordDecl,
AstNodeKind::CXXMethodDecl,
AstNodeKind::AccessSpecifier,
AstNodeKind::FieldDecl,
]
}
pub fn cpp_inheritance_source() -> &'static str {
r#"
class Base {
public:
virtual void foo();
};
class Derived : public Base {
public:
void foo() override;
};
"#
}
pub fn cpp_inheritance_expected_kinds() -> Vec<AstNodeKind> {
vec![
AstNodeKind::CXXRecordDecl,
AstNodeKind::CXXBaseSpecifier,
AstNodeKind::CXXMethodDecl,
]
}
pub fn cpp_template_source() -> &'static str {
r#"
template <typename T>
class Vector {
T* data;
int size;
public:
void push_back(T value);
};
"#
}
pub fn cpp_template_expected_kinds() -> Vec<AstNodeKind> {
vec![
AstNodeKind::ClassTemplateDecl,
AstNodeKind::TemplateTypeParmDecl,
AstNodeKind::FieldDecl,
]
}
pub fn cpp_namespace_source() -> &'static str {
r#"
namespace math {
int add(int a, int b) { return a + b; }
}
using math::add;
"#
}
pub fn cpp_namespace_expected_kinds() -> Vec<AstNodeKind> {
vec![AstNodeKind::NamespaceDecl, AstNodeKind::UsingDecl]
}
pub fn cpp_operator_source() -> &'static str {
r#"
struct Vec {
int x, y;
Vec operator+(const Vec& other) { return {x + other.x, y + other.y}; }
};
"#
}
pub fn cpp_operator_expected_kinds() -> Vec<AstNodeKind> {
vec![AstNodeKind::CXXMethodDecl, AstNodeKind::OperatorCall]
}
pub fn cpp_virtual_source() -> &'static str {
r#"
class Animal {
public:
virtual void speak() = 0;
virtual ~Animal() {}
};
class Dog : public Animal {
public:
void speak() override {}
};
"#
}
pub fn cpp_virtual_expected_kinds() -> Vec<AstNodeKind> {
vec![
AstNodeKind::CXXRecordDecl,
AstNodeKind::CXXMethodDecl,
AstNodeKind::CXXDestructorDecl,
]
}
#[derive(Debug, Clone)]
pub struct ErrorRecoveryResult {
pub recovered: bool,
pub error_count: usize,
pub warning_count: usize,
pub partial_ast: bool,
pub errors: Vec<String>,
}
impl ErrorRecoveryResult {
pub fn recovered(errors: Vec<String>) -> Self {
Self {
recovered: true,
error_count: errors.len(),
warning_count: 0,
partial_ast: true,
errors,
}
}
pub fn not_recovered(errors: Vec<String>) -> Self {
Self {
recovered: false,
error_count: errors.len(),
warning_count: 0,
partial_ast: false,
errors,
}
}
}
pub fn error_recovery_sources() -> Vec<(&'static str, &'static str)> {
vec![
("missing_semicolon", "int main() { int x = 5 return 0; }"),
("missing_closing_brace", "int main() { return 0;"),
("missing_closing_paren", "int main( { return 0; }"),
("extra_closing_brace", "int main() { return 0; }}"),
(
"invalid_token_sequence",
"int main() { int 123abc; return 0; }",
),
]
}
#[derive(Debug, Clone)]
pub struct ExpectedSymbol {
pub name: String,
pub kind: SymbolKind,
pub type_name: Option<String>,
pub is_defined: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolKind {
Function,
Variable,
Type,
Label,
EnumConstant,
Parameter,
Field,
}
impl SymbolKind {
pub fn name(&self) -> &'static str {
match self {
Self::Function => "function",
Self::Variable => "variable",
Self::Type => "type",
Self::Label => "label",
Self::EnumConstant => "enum_constant",
Self::Parameter => "parameter",
Self::Field => "field",
}
}
}
pub fn expected_symbols_for_c_basic() -> Vec<ExpectedSymbol> {
vec![
ExpectedSymbol {
name: "main".into(),
kind: SymbolKind::Function,
type_name: Some("int ()".into()),
is_defined: true,
},
ExpectedSymbol {
name: "add".into(),
kind: SymbolKind::Function,
type_name: Some("int (int, int)".into()),
is_defined: true,
},
]
}
pub fn expected_symbols_for_struct() -> Vec<ExpectedSymbol> {
vec![
ExpectedSymbol {
name: "Point".into(),
kind: SymbolKind::Type,
type_name: Some("struct Point".into()),
is_defined: true,
},
ExpectedSymbol {
name: "x".into(),
kind: SymbolKind::Field,
type_name: Some("int".into()),
is_defined: true,
},
ExpectedSymbol {
name: "y".into(),
kind: SymbolKind::Field,
type_name: Some("int".into()),
is_defined: true,
},
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ast_node_kind_name() {
assert_eq!(AstNodeKind::FunctionDecl.name(), "FunctionDecl");
assert_eq!(AstNodeKind::StructDecl.name(), "StructDecl");
assert_eq!(AstNodeKind::CXXRecordDecl.name(), "CXXRecordDecl");
}
#[test]
fn test_ast_verification_result_pass() {
let result = AstVerificationResult::pass("test", vec!["FunctionDecl".into()]);
assert!(result.passed);
assert!(result.missing_nodes.is_empty());
}
#[test]
fn test_ast_verification_result_fail() {
let result = AstVerificationResult::fail(
"test",
vec!["FunctionDecl".into()],
vec!["VarDecl".into()],
);
assert!(!result.passed);
}
#[test]
fn test_type_info_int() {
let ti = TypeInfo::int();
assert_eq!(ti.size_bits, 32);
assert!(ti.is_signed);
assert!(ti.is_integral);
assert!(!ti.is_float);
assert!(ti.verify_size(32));
}
#[test]
fn test_type_info_double() {
let ti = TypeInfo::double();
assert_eq!(ti.size_bits, 64);
assert!(ti.is_float);
assert!(!ti.is_integral);
}
#[test]
fn test_type_info_char() {
let ti = TypeInfo::char_type();
assert_eq!(ti.size_bits, 8);
}
#[test]
fn test_type_info_pointer() {
let ti = TypeInfo::pointer_to("int");
assert_eq!(ti.size_bits, 64);
assert!(ti.is_pointer);
}
#[test]
fn test_type_registry_new() {
let registry = TypeRegistry::new();
assert!(registry.verify_type_size("int", 32));
assert!(registry.verify_type_size("double", 64));
assert!(registry.verify_type_size("char", 8));
assert!(registry.verify_type_size("float", 32));
assert!(!registry.verify_type_size("int", 64));
}
#[test]
fn test_type_registry_lookup() {
let registry = TypeRegistry::new();
assert!(registry.lookup("int").is_some());
assert!(registry.lookup("nonexistent").is_none());
}
#[test]
fn test_c_function_def_source() {
let src = c_function_def_source();
assert!(src.contains("int add"));
assert!(src.contains("a + b"));
}
#[test]
fn test_c_function_def_expected() {
let expected = c_function_def_expected();
assert_eq!(expected.len(), 1);
assert_eq!(expected[0].kind, AstNodeKind::FunctionDecl);
assert_eq!(expected[0].children.len(), 2);
}
#[test]
fn test_c_struct_source() {
let src = c_struct_source();
assert!(src.contains("struct Point"));
assert!(src.contains("int x"));
}
#[test]
fn test_c_struct_expected() {
let expected = c_struct_expected();
assert_eq!(expected[0].kind, AstNodeKind::StructDecl);
assert_eq!(expected[0].children.len(), 2);
}
#[test]
fn test_c_typedef_source() {
let src = c_typedef_source();
assert!(src.contains("typedef"));
assert!(src.contains("size_t"));
}
#[test]
fn test_c_array_pointer_source() {
let src = c_array_pointer_source();
assert!(src.contains("arr[10]"));
assert!(src.contains("*p"));
}
#[test]
fn test_c_loops_source() {
let src = c_loops_source();
assert!(src.contains("for"));
assert!(src.contains("while"));
assert!(src.contains("do"));
}
#[test]
fn test_c_loops_expected_kinds() {
let kinds = c_loops_expected_kinds();
assert!(kinds.contains(&AstNodeKind::ForStmt));
assert!(kinds.contains(&AstNodeKind::WhileStmt));
assert!(kinds.contains(&AstNodeKind::DoWhileStmt));
}
#[test]
fn test_c_switch_source() {
let src = c_switch_source();
assert!(src.contains("switch"));
assert!(src.contains("case"));
assert!(src.contains("default"));
}
#[test]
fn test_c_switch_expected_kinds() {
let kinds = c_switch_expected_kinds();
assert!(kinds.contains(&AstNodeKind::SwitchStmt));
assert!(kinds.contains(&AstNodeKind::CaseStmt));
assert!(kinds.contains(&AstNodeKind::DefaultStmt));
}
#[test]
fn test_c_goto_source() {
let src = c_goto_source();
assert!(src.contains("goto"));
assert!(src.contains("end:"));
}
#[test]
fn test_c_union_enum_source() {
let src = c_union_enum_source();
assert!(src.contains("union Data"));
assert!(src.contains("enum Color"));
}
#[test]
fn test_cpp_class_source() {
let src = cpp_class_source();
assert!(src.contains("class Calculator"));
assert!(src.contains("public:"));
assert!(src.contains("private:"));
}
#[test]
fn test_cpp_class_expected_kinds() {
let kinds = cpp_class_expected_kinds();
assert!(kinds.contains(&AstNodeKind::CXXRecordDecl));
assert!(kinds.contains(&AstNodeKind::CXXMethodDecl));
}
#[test]
fn test_cpp_inheritance_source() {
let src = cpp_inheritance_source();
assert!(src.contains("class Base"));
assert!(src.contains("class Derived"));
assert!(src.contains("public Base"));
}
#[test]
fn test_cpp_template_source() {
let src = cpp_template_source();
assert!(src.contains("template"));
assert!(src.contains("typename T"));
}
#[test]
fn test_cpp_template_expected_kinds() {
let kinds = cpp_template_expected_kinds();
assert!(kinds.contains(&AstNodeKind::ClassTemplateDecl));
assert!(kinds.contains(&AstNodeKind::TemplateTypeParmDecl));
}
#[test]
fn test_cpp_namespace_source() {
let src = cpp_namespace_source();
assert!(src.contains("namespace math"));
assert!(src.contains("using"));
}
#[test]
fn test_cpp_namespace_expected_kinds() {
let kinds = cpp_namespace_expected_kinds();
assert!(kinds.contains(&AstNodeKind::NamespaceDecl));
assert!(kinds.contains(&AstNodeKind::UsingDecl));
}
#[test]
fn test_cpp_operator_source() {
let src = cpp_operator_source();
assert!(src.contains("operator+"));
}
#[test]
fn test_cpp_virtual_source() {
let src = cpp_virtual_source();
assert!(src.contains("virtual"));
assert!(src.contains("override"));
assert!(src.contains("~Animal"));
}
#[test]
fn test_error_recovery_sources_not_empty() {
let sources = error_recovery_sources();
assert!(!sources.is_empty());
}
#[test]
fn test_error_recovery_result_recovered() {
let result = ErrorRecoveryResult::recovered(vec!["expected ';'".into()]);
assert!(result.recovered);
assert_eq!(result.error_count, 1);
assert!(result.partial_ast);
}
#[test]
fn test_error_recovery_result_not_recovered() {
let result = ErrorRecoveryResult::not_recovered(vec!["fatal error".into()]);
assert!(!result.recovered);
assert!(!result.partial_ast);
}
#[test]
fn test_expected_symbols_for_c_basic() {
let syms = expected_symbols_for_c_basic();
assert!(syms.iter().any(|s| s.name == "main"));
assert!(syms.iter().any(|s| s.name == "add"));
assert_eq!(syms[0].kind, SymbolKind::Function);
}
#[test]
fn test_expected_symbols_for_struct() {
let syms = expected_symbols_for_struct();
assert!(syms
.iter()
.any(|s| s.name == "Point" && s.kind == SymbolKind::Type));
assert!(syms
.iter()
.any(|s| s.name == "x" && s.kind == SymbolKind::Field));
}
#[test]
fn test_symbol_kind_name() {
assert_eq!(SymbolKind::Function.name(), "function");
assert_eq!(SymbolKind::Type.name(), "type");
assert_eq!(SymbolKind::Label.name(), "label");
}
#[test]
fn test_expected_ast_node_builder() {
let node = ExpectedAstNode {
kind: AstNodeKind::TranslationUnit,
name: None,
type_name: None,
location: Some((1, 1)),
children: vec![],
attributes: BTreeMap::new(),
};
assert_eq!(node.kind, AstNodeKind::TranslationUnit);
assert_eq!(node.location, Some((1, 1)));
}
#[test]
fn test_expected_ast_node_with_children() {
let child = ExpectedAstNode {
kind: AstNodeKind::ReturnStmt,
name: None,
type_name: None,
location: None,
children: vec![],
attributes: BTreeMap::new(),
};
let parent = ExpectedAstNode {
kind: AstNodeKind::FunctionDecl,
name: Some("f".into()),
type_name: Some("int".into()),
location: Some((1, 1)),
children: vec![child],
attributes: BTreeMap::new(),
};
assert_eq!(parent.children.len(), 1);
}
#[test]
fn test_ast_node_kind_variants() {
let kinds = [
AstNodeKind::TranslationUnit,
AstNodeKind::FunctionDecl,
AstNodeKind::StructDecl,
AstNodeKind::TypedefDecl,
AstNodeKind::IfStmt,
AstNodeKind::BinaryOperator,
AstNodeKind::CallExpr,
AstNodeKind::ArraySubscriptExpr,
];
for k in &kinds {
assert!(!k.name().is_empty());
}
}
}