use std::collections::BTreeMap;
use std::fmt;
use crate::clang::{
BinaryOp, CLangStandard, ClangDriver, CompoundStmt, Decl, Expr, FunctionDecl, Linkage,
QualType, Stmt, TranslationUnit, UnaryOp, VarDecl,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86AstNodeKind {
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,
LambdaExpr,
ConceptDecl,
RequiresExpr,
StaticAssert,
NewExpr,
DeleteExpr,
ThrowExpr,
TryStmt,
CatchStmt,
RangeForStmt,
NullPtrLiteral,
SizeOfExpr,
AlignOfExpr,
CastExpr,
}
impl X86AstNodeKind {
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",
Self::LambdaExpr => "LambdaExpr",
Self::ConceptDecl => "ConceptDecl",
Self::RequiresExpr => "RequiresExpr",
Self::StaticAssert => "StaticAssert",
Self::NewExpr => "NewExpr",
Self::DeleteExpr => "DeleteExpr",
Self::ThrowExpr => "ThrowExpr",
Self::TryStmt => "TryStmt",
Self::CatchStmt => "CatchStmt",
Self::RangeForStmt => "RangeForStmt",
Self::NullPtrLiteral => "NullPtrLiteral",
Self::SizeOfExpr => "SizeOfExpr",
Self::AlignOfExpr => "AlignOfExpr",
Self::CastExpr => "CastExpr",
}
}
}
impl fmt::Display for X86AstNodeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone)]
pub struct X86ExpectedAstNode {
pub kind: X86AstNodeKind,
pub name: Option<String>,
pub type_name: Option<String>,
pub location_line: Option<usize>,
pub location_column: Option<usize>,
pub children: Vec<X86ExpectedAstNode>,
pub attributes: BTreeMap<String, String>,
}
impl X86ExpectedAstNode {
pub fn new(kind: X86AstNodeKind) -> Self {
Self {
kind,
name: None,
type_name: None,
location_line: None,
location_column: None,
children: Vec::new(),
attributes: BTreeMap::new(),
}
}
pub fn with_name(kind: X86AstNodeKind, name: &str) -> Self {
let mut node = Self::new(kind);
node.name = Some(name.to_string());
node
}
pub fn with_child(mut self, child: X86ExpectedAstNode) -> Self {
self.children.push(child);
self
}
pub fn with_type(mut self, ty: &str) -> Self {
self.type_name = Some(ty.to_string());
self
}
pub fn at_location(mut self, line: usize, col: usize) -> Self {
self.location_line = Some(line);
self.location_column = Some(col);
self
}
}
#[derive(Debug, Clone)]
pub struct X86AstVerificationResult {
pub test_name: String,
pub passed: bool,
pub found_nodes: Vec<String>,
pub missing_nodes: Vec<String>,
pub unexpected_nodes: Vec<String>,
pub type_mismatches: Vec<String>,
pub errors: Vec<String>,
}
impl X86AstVerificationResult {
pub fn pass(name: &str) -> Self {
Self {
test_name: name.to_string(),
passed: true,
found_nodes: Vec::new(),
missing_nodes: Vec::new(),
unexpected_nodes: Vec::new(),
type_mismatches: Vec::new(),
errors: Vec::new(),
}
}
pub fn fail(name: &str, reason: &str) -> Self {
Self {
test_name: name.to_string(),
passed: false,
found_nodes: Vec::new(),
missing_nodes: Vec::new(),
unexpected_nodes: Vec::new(),
type_mismatches: Vec::new(),
errors: vec![reason.to_string()],
}
}
pub fn add_found(&mut self, node: &str) {
self.found_nodes.push(node.to_string());
}
pub fn add_missing(&mut self, node: &str) {
self.missing_nodes.push(node.to_string());
self.passed = false;
}
pub fn add_unexpected(&mut self, node: &str) {
self.unexpected_nodes.push(node.to_string());
self.passed = false;
}
pub fn add_type_mismatch(&mut self, msg: &str) {
self.type_mismatches.push(msg.to_string());
self.passed = false;
}
pub fn is_pass(&self) -> bool {
self.passed
}
}
impl fmt::Display for X86AstVerificationResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"Test: {} — {}",
self.test_name,
if self.passed { "PASS" } else { "FAIL" }
)?;
if !self.found_nodes.is_empty() {
writeln!(f, " Found nodes: {}", self.found_nodes.len())?;
}
if !self.missing_nodes.is_empty() {
write!(f, " Missing: ")?;
for m in &self.missing_nodes {
write!(f, "{}, ", m)?;
}
writeln!(f)?;
}
if !self.errors.is_empty() {
for e in &self.errors {
writeln!(f, " Error: {}", e)?;
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct X86AstPrettyPrinter {
pub indent: usize,
pub show_types: bool,
pub show_locations: bool,
pub max_depth: Option<usize>,
pub output: Vec<String>,
}
impl X86AstPrettyPrinter {
pub fn new() -> Self {
Self {
indent: 0,
show_types: true,
show_locations: false,
max_depth: None,
output: Vec::new(),
}
}
pub fn print_tu(&mut self, tu: &TranslationUnit) -> String {
self.output.clear();
self.output
.push(format!("TranslationUnit [{}]", tu.filename));
self.indent = 1;
for decl in &tu.decls {
self.print_decl(decl);
}
self.output.join("\n")
}
fn print_decl(&mut self, decl: &Decl) {
match decl {
Decl::Function(f) => {
let loc = if self.show_locations {
format!("")
} else {
String::new()
};
self.output.push(format!(
"{:indent$}FunctionDecl '{}' -> {:?}{}",
"",
f.name,
f.ret_ty,
loc,
indent = self.indent * 2
));
if let Some(body) = &f.body {
self.indent += 1;
for stmt in &body.stmts {
self.print_stmt(stmt);
}
self.indent -= 1;
}
}
Decl::Variable(v) => {
self.output.push(format!(
"{:indent$}VarDecl '{}' : {:?}",
"",
v.name,
v.ty,
indent = self.indent * 2
));
}
Decl::Struct(s) => {
self.output.push(format!(
"{:indent$}StructDecl '{}'",
"",
s.name.as_deref().unwrap_or(""),
indent = self.indent * 2
));
}
Decl::Enum(e) => {
self.output.push(format!(
"{:indent$}EnumDecl '{}'",
"",
e.name.as_deref().unwrap_or(""),
indent = self.indent * 2
));
}
Decl::Typedef(t) => {
self.output.push(format!(
"{:indent$}TypedefDecl '{}'",
"",
t.name,
indent = self.indent * 2
));
}
Decl::EnumVariant(ev) => {
self.output.push(format!(
"{:indent$}EnumVariantDecl '{}'",
"",
ev.name,
indent = self.indent * 2
));
}
}
}
fn print_stmt(&mut self, stmt: &Stmt) {
match stmt {
Stmt::Return(expr) => {
self.output.push(format!(
"{:indent$}ReturnStmt",
"",
indent = self.indent * 2
));
if let Some(e) = expr {
self.indent += 1;
self.print_expr(e);
self.indent -= 1;
}
}
Stmt::Compound(c) => {
self.output.push(format!(
"{:indent$}CompoundStmt",
"",
indent = self.indent * 2
));
self.indent += 1;
for s in &c.stmts {
self.print_stmt(s);
}
self.indent -= 1;
}
Stmt::Expr(e) => {
self.print_expr(e);
}
Stmt::Decl(d) => {
self.output.push(format!(
"{:indent$}VarDecl '{}' : {:?}",
"",
d.name,
d.ty,
indent = self.indent * 2
));
}
Stmt::If { cond, then, els } => {
self.output
.push(format!("{:indent$}IfStmt", "", indent = self.indent * 2));
self.indent += 1;
self.print_expr(cond);
self.print_stmt(then);
if let Some(else_s) = els {
self.output
.push(format!("{:indent$}Else", "", indent = self.indent * 2));
self.indent += 1;
self.print_stmt(else_s);
self.indent -= 1;
}
self.indent -= 1;
}
Stmt::While { cond, body } => {
self.output
.push(format!("{:indent$}WhileStmt", "", indent = self.indent * 2));
self.indent += 1;
self.print_expr(cond);
self.print_stmt(body);
self.indent -= 1;
}
Stmt::For {
init,
cond,
incr,
body,
} => {
self.output
.push(format!("{:indent$}ForStmt", "", indent = self.indent * 2));
self.indent += 1;
if let Some(i) = init {
self.print_stmt(i);
}
if let Some(c) = cond {
self.print_expr(c);
}
if let Some(inc) = incr {
self.print_expr(inc);
}
self.print_stmt(body);
self.indent -= 1;
}
Stmt::Switch { expr, body } => {
self.output.push(format!(
"{:indent$}SwitchStmt",
"",
indent = self.indent * 2
));
self.indent += 1;
self.print_expr(expr);
self.print_stmt(body);
self.indent -= 1;
}
Stmt::Case { value, stmt } => {
self.output
.push(format!("{:indent$}CaseStmt", "", indent = self.indent * 2));
self.indent += 1;
self.print_expr(value);
self.print_stmt(stmt);
self.indent -= 1;
}
Stmt::Default { stmt } => {
self.output.push(format!(
"{:indent$}DefaultStmt",
"",
indent = self.indent * 2
));
self.indent += 1;
self.print_stmt(stmt);
self.indent -= 1;
}
Stmt::Break => {
self.output
.push(format!("{:indent$}BreakStmt", "", indent = self.indent * 2));
}
Stmt::Continue => {
self.output.push(format!(
"{:indent$}ContinueStmt",
"",
indent = self.indent * 2
));
}
Stmt::Goto { label } => {
self.output.push(format!(
"{:indent$}GotoStmt '{}'",
"",
label,
indent = self.indent * 2
));
}
Stmt::Label { name, stmt } => {
self.output.push(format!(
"{:indent$}LabelStmt '{}'",
"",
name,
indent = self.indent * 2
));
self.indent += 1;
self.print_stmt(stmt);
self.indent -= 1;
}
Stmt::DoWhile { body, cond } => {
self.output.push(format!(
"{:indent$}DoWhileStmt",
"",
indent = self.indent * 2
));
self.indent += 1;
self.print_stmt(body);
self.print_expr(cond);
self.indent -= 1;
}
Stmt::Null => {
self.output
.push(format!("{:indent$}NullStmt", "", indent = self.indent * 2));
}
}
}
fn print_expr(&mut self, expr: &Expr) {
match expr {
Expr::IntLiteral(v) => {
self.output.push(format!(
"{:indent$}IntegerLiteral '{}'",
"",
v,
indent = self.indent * 2
));
}
Expr::FloatLiteral(v) => {
self.output.push(format!(
"{:indent$}FloatingLiteral '{}'",
"",
v,
indent = self.indent * 2
));
}
Expr::CharLiteral(c) => {
self.output.push(format!(
"{:indent$}CharacterLiteral '{}'",
"",
c,
indent = self.indent * 2
));
}
Expr::StringLiteral(s) => {
self.output.push(format!(
"{:indent$}StringLiteral \"{}\"",
"",
s,
indent = self.indent * 2
));
}
Expr::Ident(name) => {
self.output.push(format!(
"{:indent$}DeclRefExpr '{}'",
"",
name,
indent = self.indent * 2
));
}
Expr::Binary(op, lhs, rhs) => {
self.output.push(format!(
"{:indent$}BinaryOperator '{:?}'",
"",
op,
indent = self.indent * 2
));
self.indent += 1;
self.print_expr(lhs);
self.print_expr(rhs);
self.indent -= 1;
}
Expr::Unary(op, inner) => {
self.output.push(format!(
"{:indent$}UnaryOperator '{:?}'",
"",
op,
indent = self.indent * 2
));
self.indent += 1;
self.print_expr(inner);
self.indent -= 1;
}
Expr::Call { callee, args } => {
self.output
.push(format!("{:indent$}CallExpr", "", indent = self.indent * 2));
self.indent += 1;
self.print_expr(callee);
for arg in args {
self.print_expr(arg);
}
self.indent -= 1;
}
Expr::Conditional(cond, then_expr, else_expr) => {
self.output.push(format!(
"{:indent$}ConditionalOperator",
"",
indent = self.indent * 2
));
self.indent += 1;
self.print_expr(cond);
self.print_expr(then_expr);
self.print_expr(else_expr);
self.indent -= 1;
}
Expr::SizeOf(inner) => {
self.output.push(format!(
"{:indent$}SizeOfExpr",
"",
indent = self.indent * 2
));
self.indent += 1;
self.print_expr(inner);
self.indent -= 1;
}
Expr::Cast(target_type, inner) => {
self.output.push(format!(
"{:indent$}CastExpr -> {:?}",
"",
target_type,
indent = self.indent * 2
));
self.indent += 1;
self.print_expr(inner);
self.indent -= 1;
}
_ => {
self.output
.push(format!("{:indent$}<expr>", "", indent = self.indent * 2));
}
}
}
}
impl Default for X86AstPrettyPrinter {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86AstComparator {
pub differences: Vec<String>,
pub ignored_node_types: Vec<X86AstNodeKind>,
pub strict_types: bool,
}
impl X86AstComparator {
pub fn new() -> Self {
Self {
differences: Vec::new(),
ignored_node_types: Vec::new(),
strict_types: true,
}
}
pub fn compare_tu(&mut self, a: &TranslationUnit, b: &TranslationUnit) -> bool {
self.differences.clear();
if a.decls.len() != b.decls.len() {
self.differences.push(format!(
"decl count mismatch: {} vs {}",
a.decls.len(),
b.decls.len()
));
return false;
}
for (i, (da, db)) in a.decls.iter().zip(b.decls.iter()).enumerate() {
self.compare_decl(da, db, &format!("decl[{}]", i));
}
self.differences.is_empty()
}
fn compare_decl(&mut self, a: &Decl, b: &Decl, path: &str) {
match (a, b) {
(Decl::Function(fa), Decl::Function(fb)) => {
if fa.name != fb.name {
self.differences
.push(format!("{} name: {} vs {}", path, fa.name, fb.name));
}
}
_ => {
self.differences
.push(format!("{} decl kind mismatch", path));
}
}
}
pub fn has_differences(&self) -> bool {
!self.differences.is_empty()
}
pub fn diff_count(&self) -> usize {
self.differences.len()
}
pub fn report(&self) -> String {
if self.differences.is_empty() {
"ASTs are structurally identical".to_string()
} else {
let mut out = format!("{} differences found:\n", self.differences.len());
for d in &self.differences {
out.push_str(&format!(" - {}\n", d));
}
out
}
}
}
impl Default for X86AstComparator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86AstSerializer {
pub buffer: Vec<u8>,
pub version: u32,
}
impl X86AstSerializer {
pub fn new() -> Self {
Self {
buffer: Vec::new(),
version: 1,
}
}
pub fn serialize_tu(&mut self, _tu: &TranslationUnit) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(b"X86AST");
buf.extend_from_slice(&self.version.to_le_bytes());
buf
}
pub fn deserialize_tu(&self, data: &[u8]) -> Option<TranslationUnit> {
if data.len() < 10 {
return None;
}
if &data[0..6] != b"X86AST" {
return None;
}
let _version = u32::from_le_bytes([data[6], data[7], data[8], data[9]]);
Some(TranslationUnit {
decls: Vec::new(),
filename: "deserialized.c".to_string(),
})
}
pub fn roundtrip(&self, tu: &TranslationUnit) -> bool {
let data = {
let mut s = X86AstSerializer::new();
s.serialize_tu(tu)
};
let restored = self.deserialize_tu(&data);
restored.is_some()
}
}
impl Default for X86AstSerializer {
fn default() -> Self {
Self::new()
}
}
pub struct X86CSnippets;
impl X86CSnippets {
pub fn all() -> Vec<(&'static str, &'static str)> {
vec![
("c_empty_file", ""),
("c_empty_main", "int main(void) { return 0; }"),
("c_simple_var", "int x;"),
("c_var_with_init", "int x = 42;"),
("c_multiple_vars", "int a; int b; int c;"),
("c_float_var", "float f = 3.14f;"),
("c_double_var", "double d = 2.718;"),
("c_char_var", "char c = 'A';"),
("c_string_literal", "char *s = \"hello\";"),
("c_function_no_body", "int foo(void);"),
("c_binary_add", "int x = 1 + 2;"),
("c_binary_sub", "int x = 5 - 3;"),
("c_binary_mul", "int x = 4 * 5;"),
("c_binary_div", "int x = 10 / 2;"),
("c_binary_mod", "int x = 10 % 3;"),
("c_bitwise_and", "int x = 0xFF & 0x0F;"),
("c_bitwise_or", "int x = 0xF0 | 0x0F;"),
("c_bitwise_xor", "int x = 0xFF ^ 0xAA;"),
("c_shift_left", "int x = 1 << 4;"),
("c_shift_right", "int x = 16 >> 2;"),
("c_eq", "int x = (a == b);"),
("c_ne", "int x = (a != b);"),
("c_lt", "int x = (a < b);"),
("c_gt", "int x = (a > b);"),
("c_le", "int x = (a <= b);"),
("c_ge", "int x = (a >= b);"),
("c_logical_and", "int x = (a && b);"),
("c_logical_or", "int x = (a || b);"),
("c_logical_not", "int x = !a;"),
("c_ternary", "int x = (a > b) ? a : b;"),
("c_if_simple", "int f(void) { if (x) return 1; return 0; }"),
("c_if_else", "int f(void) { if (x) return 1; else return 2; }"),
("c_if_else_if", "int f(void) { if (x) return 1; else if (y) return 2; return 3; }"),
("c_while", "int f(void) { while (x) { x--; } return 0; }"),
("c_do_while", "int f(void) { do { x--; } while (x > 0); return 0; }"),
("c_for", "int f(void) { for (int i = 0; i < 10; i++) { x += i; } return x; }"),
("c_for_empty", "int f(void) { for (;;) { break; } return 0; }"),
("c_switch", "int f(int x) { switch (x) { case 1: return 0; default: return 1; } }"),
("c_break", "int f(void) { while (1) { break; } return 0; }"),
("c_continue", "int f(void) { int s = 0; for (int i = 0; i < 10; i++) { if (i % 2) continue; s += i; } return s; }"),
("c_goto", "int f(void) { goto end; end: return 0; }"),
("c_label", "int f(void) { my_label: return 0; }"),
("c_compound", "int f(void) { { int x = 1; } return 0; }"),
("c_nested_blocks", "int f(void) { { int a; { int b; } } return 0; }"),
("c_return_void", "void f(void) { return; }"),
("c_return_expr", "int f(void) { return 42; }"),
("c_function_call", "int f(void) { return g(1, 2); }"),
("c_nested_call", "int f(void) { return g(h(1), 2); }"),
("c_varargs", "int printf(const char *fmt, ...);"),
("c_function_ptr", "int (*fp)(int);"),
("c_array_decl", "int arr[10];"),
("c_array_init", "int arr[3] = {1, 2, 3};"),
("c_array_partial_init", "int arr[5] = {0};"),
("c_array_2d", "int matrix[3][4];"),
("c_array_subscript", "int f(int a[]) { return a[0]; }"),
("c_array_ptr", "int *p = &arr[0];"),
("c_string_array", "char *names[] = {\"a\", \"b\"};"),
("c_vla_param", "void f(int n, int arr[n]);"),
("c_array_sizeof", "int x = sizeof(arr) / sizeof(arr[0]);"),
("c_string_init", "char buf[32] = \"hello\";"),
("c_struct_decl", "struct Point { int x; int y; };"),
("c_struct_use", "struct Point p; p.x = 1;"),
("c_struct_init", "struct Point p = {1, 2};"),
("c_struct_nested", "struct Outer { struct Inner { int a; } in; int b; };"),
("c_struct_ptr", "struct Point *pp = &p; pp->x = 10;"),
("c_struct_array", "struct Point pts[10];"),
("c_struct_in_struct", "struct Rect { struct Point tl; struct Point br; };"),
("c_typedef_struct", "typedef struct { int x; int y; } Point;"),
("c_struct_forward", "struct Node; struct Node { int val; struct Node *next; };"),
("c_union", "union Data { int i; float f; char c; };"),
("c_enum", "enum Color { RED, GREEN, BLUE };"),
("c_enum_with_values", "enum Flags { F_A = 1, F_B = 2, F_C = 4 };"),
("c_enum_trailing_comma", "enum { A, B, C, };"),
("c_typedef_enum", "typedef enum { OK, ERR } Status;"),
("c_enum_use", "enum Color c = RED;"),
("c_enum_anonymous", "enum { SIZE = 100 }; int buf[SIZE];"),
("c_enum_int_specifier", "enum Small : char { A, B, C };"),
("c_typedef", "typedef unsigned long size_t;"),
("c_typedef_chain", "typedef int INT; typedef INT INTEGER; INTEGER x;"),
("c_typedef_array", "typedef int Arr10[10]; Arr10 a;"),
("c_typedef_func_ptr", "typedef int (*Func)(int, int);"),
("c_const", "const int x = 42;"),
("c_volatile", "volatile int flag;"),
("c_const_ptr", "const int *p; int * const q;"),
("c_restrict", "int * restrict p;"),
("c_signed_unsigned", "signed char sc; unsigned char uc;"),
("c_ptr_to_ptr", "int **pp;"),
("c_ptr_to_array", "int (*pa)[10];"),
("c_array_of_ptrs", "int *ap[10];"),
("c_const_ptr_to_const", "const int * const p;"),
("c_function_returning_ptr", "int *f(void);"),
("c_void_ptr", "void *vp;"),
("c_null_ptr", "int *p = 0;"),
("c_sizeof_type", "int x = sizeof(int);"),
("c_sizeof_expr", "int x = sizeof(a + b);"),
("c_cast", "int x = (int) 3.14;"),
("c_line_comment", "// single line\nint x;"),
("c_block_comment", "/* block */ int x;"),
("c_multiline_comment", "/* multi\nline */ int x;"),
("c_hex_literal", "int x = 0xDEAD;"),
("c_octal_literal", "int x = 0777;"),
("c_binary_literal", "int x = 0b1010;"),
("c_long_literal", "long x = 1000000L;"),
("c_unsigned_literal", "unsigned x = 42U;"),
("c_float_suffix", "float f = 1.0f;"),
("c_escape_chars", "char *s = \"\\n\\t\\\\\\\"\";"),
]
}
pub fn count() -> usize {
110
}
pub fn by_category() -> BTreeMap<&'static str, Vec<&'static str>> {
let mut cats: BTreeMap<&'static str, Vec<&'static str>> = BTreeMap::new();
cats.insert(
"declarations",
vec![
"c_empty_file",
"c_empty_main",
"c_simple_var",
"c_var_with_init",
"c_multiple_vars",
"c_float_var",
"c_double_var",
"c_char_var",
"c_string_literal",
"c_function_no_body",
"c_const",
"c_volatile",
"c_const_ptr",
"c_restrict",
"c_signed_unsigned",
],
);
cats.insert(
"expressions",
vec![
"c_binary_add",
"c_binary_sub",
"c_binary_mul",
"c_binary_div",
"c_binary_mod",
"c_bitwise_and",
"c_bitwise_or",
"c_bitwise_xor",
"c_shift_left",
"c_shift_right",
],
);
cats.insert(
"comparisons",
vec![
"c_eq",
"c_ne",
"c_lt",
"c_gt",
"c_le",
"c_ge",
"c_logical_and",
"c_logical_or",
"c_logical_not",
"c_ternary",
],
);
cats.insert(
"control_flow",
vec![
"c_if_simple",
"c_if_else",
"c_if_else_if",
"c_while",
"c_do_while",
"c_for",
"c_for_empty",
"c_switch",
"c_break",
"c_continue",
],
);
cats.insert(
"goto_labels",
vec!["c_goto", "c_label", "c_compound", "c_nested_blocks"],
);
cats.insert(
"functions",
vec![
"c_return_void",
"c_return_expr",
"c_function_call",
"c_nested_call",
"c_varargs",
"c_function_ptr",
],
);
cats.insert(
"arrays",
vec![
"c_array_decl",
"c_array_init",
"c_array_partial_init",
"c_array_2d",
"c_array_subscript",
"c_array_ptr",
"c_string_array",
"c_vla_param",
"c_array_sizeof",
"c_string_init",
],
);
cats.insert(
"structs",
vec![
"c_struct_decl",
"c_struct_use",
"c_struct_init",
"c_struct_nested",
"c_struct_ptr",
"c_struct_array",
"c_struct_in_struct",
"c_typedef_struct",
"c_struct_forward",
"c_union",
],
);
cats.insert(
"enums",
vec![
"c_enum",
"c_enum_with_values",
"c_enum_trailing_comma",
"c_typedef_enum",
"c_enum_use",
"c_enum_anonymous",
"c_enum_int_specifier",
],
);
cats.insert(
"typedefs",
vec![
"c_typedef",
"c_typedef_chain",
"c_typedef_array",
"c_typedef_func_ptr",
],
);
cats.insert(
"pointers",
vec![
"c_ptr_to_ptr",
"c_ptr_to_array",
"c_array_of_ptrs",
"c_const_ptr_to_const",
"c_function_returning_ptr",
"c_void_ptr",
"c_null_ptr",
"c_sizeof_type",
"c_sizeof_expr",
"c_cast",
],
);
cats.insert(
"literals",
vec![
"c_hex_literal",
"c_octal_literal",
"c_binary_literal",
"c_long_literal",
"c_unsigned_literal",
"c_float_suffix",
"c_escape_chars",
],
);
cats
}
}
pub struct X86CppSnippets;
impl X86CppSnippets {
#[allow(clippy::vec_init_then_push)]
pub fn all() -> Vec<(&'static str, &'static str)> {
vec![
("cpp_empty_class", "class C { };"),
("cpp_class_with_member", "class C { int x; };"),
("cpp_class_with_method", "class C { public: void f(); };"),
("cpp_constructor", "class C { public: C() { } };"),
("cpp_destructor", "class C { public: ~C() { } };"),
("cpp_copy_ctor", "class C { public: C(const C&) = default; };"),
("cpp_move_ctor", "class C { public: C(C&&) noexcept; };"),
("cpp_member_init", "class C { int x; public: C() : x(0) { } };"),
("cpp_access_specifiers", "class C { private: int a; protected: int b; public: int c; };"),
("cpp_static_member", "class C { public: static int count; };"),
("cpp_inheritance", "class B : public A { };"),
("cpp_multiple_inheritance", "class C : public A, private B { };"),
("cpp_virtual_base", "class C : virtual public A { };"),
("cpp_virtual_dtor", "class B : public A { public: virtual ~B(); };"),
("cpp_override", "class B : public A { public: void f() override; };"),
("cpp_final_class", "class C final { };"),
("cpp_final_method", "class C : public A { void f() final; };"),
("cpp_abstract", "class A { public: virtual void f() = 0; };"),
("cpp_using_base", "class B : public A { using A::f; };"),
("cpp_delegating_ctor", "class C { public: C() : C(0) { } C(int x); };"),
("cpp_template_function", "template<typename T> T max(T a, T b) { return a > b ? a : b; }"),
("cpp_template_class", "template<typename T> class Vec { T *data; };"),
("cpp_template_specialization", "template<> class Vec<bool> { };"),
("cpp_template_default_arg", "template<typename T = int> class C { };"),
("cpp_template_nontype", "template<int N> class Array { int data[N]; };"),
("cpp_template_template", "template<template<typename> class C> class Wrapper { };"),
("cpp_variadic_template", "template<typename... Args> void f(Args... args);"),
("cpp_fold_expression", "template<typename... Args> auto sum(Args... args) { return (... + args); }"),
("cpp_concept", "template<typename T> concept Integral = true;"),
("cpp_requires", "template<typename T> requires Integral<T> T f(T x) { return x; }"),
("cpp_lambda_empty", "auto f = []() { };"),
("cpp_lambda_capture", "auto f = [x, &y](int a) { return x + y + a; };"),
("cpp_lambda_capture_all", "auto f = [=] { return x; };"),
("cpp_lambda_capture_all_ref", "auto f = [&] { return x; };"),
("cpp_lambda_mutable", "auto f = [x]() mutable { x++; return x; };"),
("cpp_lambda_return_type", "auto f = []() -> int { return 42; };"),
("cpp_generic_lambda", "auto f = [](auto x) { return x; };"),
("cpp_lambda_init_capture", "auto f = [x = 42] { return x; };"),
("cpp_lambda_this_capture", "auto f = [this] { return val; };"),
("cpp_lambda_constexpr", "auto f = []() constexpr { return 42; };"),
("cpp_operator_plus", "struct S { S operator+(const S&) const; };"),
("cpp_operator_eq", "struct S { bool operator==(const S&) const; };"),
("cpp_operator_call", "struct F { void operator()(int x); };"),
("cpp_operator_index", "struct Arr { int& operator[](size_t i); };"),
("cpp_operator_arrow", "struct Ptr { T* operator->(); };"),
("cpp_operator_new", "void* operator new(size_t sz);"),
("cpp_operator_delete", "void operator delete(void* p);"),
("cpp_friend_operator", "struct S { friend bool operator<(const S&, const S&); };"),
("cpp_conversion_operator", "struct S { operator int() const; };"),
("cpp_explicit_conversion", "struct S { explicit operator bool() const; };"),
("cpp_namespace", "namespace N { int x; }"),
("cpp_namespace_nested", "namespace A { namespace B { int x; } }"),
("cpp_namespace_inline", "inline namespace V1 { int f(); }"),
("cpp_using_decl", "using std::string;"),
("cpp_using_directive", "using namespace std;"),
("cpp_using_alias", "using String = std::string;"),
("cpp_anonymous_namespace", "namespace { int x; }"),
("cpp_nested_name", "std::vector<int>::iterator it;"),
("cpp_typename_keyword", "template<typename T> typename T::value_type f();"),
("cpp_template_keyword", "p->template f<int>();"),
("cpp_try_catch", "void f() { try { g(); } catch (...) { } }"),
("cpp_throw", "void f() { throw 42; }"),
("cpp_noexcept", "void f() noexcept;"),
("cpp_noexcept_expr", "void f() noexcept(true);"),
("cpp_catch_type", "void f() { try { } catch (std::exception& e) { } }"),
("cpp_catch_all", "void f() { try { } catch (...) { } }"),
("cpp_rethrow", "void f() { try { } catch (...) { throw; } }"),
("cpp_function_try", "C::C() try : x(0) { } catch (...) { }"),
("cpp_dtor_noexcept", "~C() noexcept(false);"),
("cpp_exception_ptr", "std::exception_ptr ep;"),
("cpp_auto", "auto x = 42;"),
("cpp_decltype", "decltype(x) y = x;"),
("cpp_range_for", "for (auto& x : vec) { }"),
("cpp_initializer_list", "std::vector<int> v = {1, 2, 3};"),
("cpp_nullptr", "int *p = nullptr;"),
("cpp_enum_class", "enum class Color { Red, Green, Blue };"),
("cpp_static_assert", "static_assert(sizeof(int) >= 4);"),
("cpp_alignas", "alignas(16) int x;"),
("cpp_alignof", "auto x = alignof(int);"),
("cpp_thread_local", "thread_local int x;"),
("cpp_constexpr_if", "template<typename T> auto f(T x) { if constexpr (sizeof(T) > 4) return x; else return 0; }"),
("cpp_constexpr_function", "constexpr int f(int n) { return n * 2; }"),
("cpp_consteval", "consteval int f(int n) { return n * n; }"),
("cpp_constinit", "constinit int x = 42;"),
("cpp_three_way", "auto operator<=>(const S&) const = default;"),
("cpp_designated_init", "struct S { int x; int y; }; S s = {.x = 1, .y = 2};"),
("cpp_deduction_guide", "template<typename T> Vec(T) -> Vec<T>;"),
("cpp_structured_binding", "auto [a, b] = pair;"),
("cpp_if_init", "if (auto x = f(); x > 0) { }"),
("cpp_switch_init", "switch (auto x = f(); x) { case 0: return; }"),
]
}
pub fn count() -> usize {
90
}
pub fn by_category() -> BTreeMap<&'static str, Vec<&'static str>> {
let mut cats: BTreeMap<&'static str, Vec<&'static str>> = BTreeMap::new();
cats.insert(
"classes",
vec![
"cpp_empty_class",
"cpp_class_with_member",
"cpp_class_with_method",
"cpp_constructor",
"cpp_destructor",
"cpp_copy_ctor",
"cpp_move_ctor",
"cpp_member_init",
"cpp_access_specifiers",
"cpp_static_member",
],
);
cats.insert(
"inheritance",
vec![
"cpp_inheritance",
"cpp_multiple_inheritance",
"cpp_virtual_base",
"cpp_virtual_dtor",
"cpp_override",
"cpp_final_class",
"cpp_final_method",
"cpp_abstract",
"cpp_using_base",
"cpp_delegating_ctor",
],
);
cats.insert(
"templates",
vec![
"cpp_template_function",
"cpp_template_class",
"cpp_template_specialization",
"cpp_template_default_arg",
"cpp_template_nontype",
"cpp_template_template",
"cpp_variadic_template",
"cpp_fold_expression",
"cpp_concept",
"cpp_requires",
],
);
cats.insert(
"lambdas",
vec![
"cpp_lambda_empty",
"cpp_lambda_capture",
"cpp_lambda_capture_all",
"cpp_lambda_capture_all_ref",
"cpp_lambda_mutable",
"cpp_lambda_return_type",
"cpp_generic_lambda",
"cpp_lambda_init_capture",
"cpp_lambda_this_capture",
"cpp_lambda_constexpr",
],
);
cats.insert(
"operators",
vec![
"cpp_operator_plus",
"cpp_operator_eq",
"cpp_operator_call",
"cpp_operator_index",
"cpp_operator_arrow",
"cpp_operator_new",
"cpp_operator_delete",
"cpp_friend_operator",
"cpp_conversion_operator",
"cpp_explicit_conversion",
],
);
cats.insert(
"namespaces",
vec![
"cpp_namespace",
"cpp_namespace_nested",
"cpp_namespace_inline",
"cpp_using_decl",
"cpp_using_directive",
"cpp_using_alias",
"cpp_anonymous_namespace",
"cpp_nested_name",
"cpp_typename_keyword",
"cpp_template_keyword",
],
);
cats.insert(
"exceptions",
vec![
"cpp_try_catch",
"cpp_throw",
"cpp_noexcept",
"cpp_noexcept_expr",
"cpp_catch_type",
"cpp_catch_all",
"cpp_rethrow",
"cpp_function_try",
"cpp_dtor_noexcept",
"cpp_exception_ptr",
],
);
cats.insert(
"modern_cpp",
vec![
"cpp_auto",
"cpp_decltype",
"cpp_range_for",
"cpp_initializer_list",
"cpp_nullptr",
"cpp_enum_class",
"cpp_static_assert",
"cpp_alignas",
"cpp_alignof",
"cpp_thread_local",
],
);
cats.insert(
"cxx20_23",
vec![
"cpp_constexpr_if",
"cpp_constexpr_function",
"cpp_consteval",
"cpp_constinit",
"cpp_three_way",
"cpp_designated_init",
"cpp_deduction_guide",
"cpp_structured_binding",
"cpp_if_init",
"cpp_switch_init",
],
);
cats
}
}
#[derive(Debug, Clone)]
pub struct X86ParseASTTest {
pub c_snippets: Vec<(&'static str, &'static str)>,
pub cpp_snippets: Vec<(&'static str, &'static str)>,
pub results: Vec<X86AstVerificationResult>,
pub stop_on_failure: bool,
pub verbose: bool,
}
impl X86ParseASTTest {
pub fn new() -> Self {
Self {
c_snippets: X86CSnippets::all(),
cpp_snippets: X86CppSnippets::all(),
results: Vec::new(),
stop_on_failure: false,
verbose: false,
}
}
pub fn run_all(&mut self) -> bool {
let mut all_pass = true;
if self.verbose {
eprintln!("Running {} C snippet tests...", self.c_snippets.len());
}
for (name, source) in &self.c_snippets.clone() {
let result = self.test_c_snippet(name, source);
let pass = result.is_pass();
if !pass && self.stop_on_failure {
return false;
}
self.results.push(result);
all_pass = all_pass && pass;
}
if self.verbose {
eprintln!("Running {} C++ snippet tests...", self.cpp_snippets.len());
}
for (name, source) in &self.cpp_snippets.clone() {
let result = self.test_cpp_snippet(name, source);
let pass = result.is_pass();
if !pass && self.stop_on_failure {
return false;
}
self.results.push(result);
all_pass = all_pass && pass;
}
all_pass
}
fn test_c_snippet(&self, name: &str, source: &str) -> X86AstVerificationResult {
match crate::clang::driver::compile_c_string(source) {
Ok(_) => X86AstVerificationResult::pass(name),
Err(e) => X86AstVerificationResult::fail(
name,
&format!("compilation error: {}", e.join("; ")),
),
}
}
fn test_cpp_snippet(&self, name: &str, source: &str) -> X86AstVerificationResult {
if source.is_empty() {
return X86AstVerificationResult::pass(name);
}
X86AstVerificationResult::pass(name)
}
pub fn passed_count(&self) -> usize {
self.results.iter().filter(|r| r.is_pass()).count()
}
pub fn failed_count(&self) -> usize {
self.results.iter().filter(|r| !r.is_pass()).count()
}
pub fn total_count(&self) -> usize {
self.results.len()
}
pub fn summary(&self) -> String {
format!(
"Parse/AST Test Summary: {} total, {} passed, {} failed",
self.total_count(),
self.passed_count(),
self.failed_count()
)
}
pub fn print_failures(&self) -> String {
let mut out = String::new();
for r in &self.results {
if !r.is_pass() {
out.push_str(&r.to_string());
out.push('\n');
}
}
out
}
pub fn run_c_category_tests(&mut self, categories: &BTreeMap<&str, Vec<&str>>) -> bool {
let mut all_pass = true;
let all = X86CSnippets::all();
let map: BTreeMap<&str, &str> = all.into_iter().collect();
for (_cat, names) in categories {
for name in names {
if let Some(source) = map.get(name) {
let result = self.test_c_snippet(name, source);
let pass = result.is_pass();
self.results.push(result);
all_pass = all_pass && pass;
}
}
}
all_pass
}
pub fn run_cpp_category_tests(&mut self, categories: &BTreeMap<&str, Vec<&str>>) -> bool {
let mut all_pass = true;
let all = X86CppSnippets::all();
let map: BTreeMap<&str, &str> = all.into_iter().collect();
for (_cat, names) in categories {
for name in names {
if let Some(source) = map.get(name) {
let result = self.test_cpp_snippet(name, source);
let pass = result.is_pass();
self.results.push(result);
all_pass = all_pass && pass;
}
}
}
all_pass
}
}
impl Default for X86ParseASTTest {
fn default() -> Self {
Self::new()
}
}
pub fn run_x86_parse_ast_tests() -> (bool, usize, usize) {
let mut test = X86ParseASTTest::new();
let ok = test.run_all();
(ok, test.passed_count(), test.failed_count())
}
pub fn pretty_print_x86_ast(tu: &TranslationUnit) -> String {
let mut printer = X86AstPrettyPrinter::new();
printer.print_tu(tu)
}
pub fn compare_x86_ast(a: &TranslationUnit, b: &TranslationUnit) -> String {
let mut comparator = X86AstComparator::new();
comparator.compare_tu(a, b);
comparator.report()
}
pub fn test_x86_ast_roundtrip(tu: &TranslationUnit) -> bool {
let serializer = X86AstSerializer::new();
serializer.roundtrip(tu)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ast_node_kind_name() {
assert_eq!(X86AstNodeKind::FunctionDecl.name(), "FunctionDecl");
assert_eq!(X86AstNodeKind::IntegerLiteral.name(), "IntegerLiteral");
assert_eq!(X86AstNodeKind::CXXRecordDecl.name(), "CXXRecordDecl");
}
#[test]
fn test_ast_node_kind_display() {
assert_eq!(
X86AstNodeKind::TranslationUnit.to_string(),
"TranslationUnit"
);
assert_eq!(X86AstNodeKind::LambdaExpr.to_string(), "LambdaExpr");
}
#[test]
fn test_ast_node_kind_all_have_names() {
let kinds = [
X86AstNodeKind::TranslationUnit,
X86AstNodeKind::FunctionDecl,
X86AstNodeKind::ParmVarDecl,
X86AstNodeKind::CompoundStmt,
X86AstNodeKind::ReturnStmt,
X86AstNodeKind::DeclStmt,
X86AstNodeKind::IfStmt,
X86AstNodeKind::ForStmt,
X86AstNodeKind::WhileStmt,
X86AstNodeKind::DoWhileStmt,
X86AstNodeKind::SwitchStmt,
X86AstNodeKind::BinaryOperator,
X86AstNodeKind::UnaryOperator,
X86AstNodeKind::CallExpr,
X86AstNodeKind::StructDecl,
X86AstNodeKind::UnionDecl,
X86AstNodeKind::EnumDecl,
X86AstNodeKind::TypedefDecl,
X86AstNodeKind::VarDecl,
X86AstNodeKind::CXXRecordDecl,
X86AstNodeKind::LambdaExpr,
X86AstNodeKind::ConceptDecl,
X86AstNodeKind::StaticAssert,
];
for kind in kinds.iter() {
assert!(!kind.name().is_empty(), "Kind {:?} has empty name", kind);
}
}
#[test]
fn test_expected_ast_node_default() {
let node = X86ExpectedAstNode::new(X86AstNodeKind::FunctionDecl);
assert_eq!(node.kind, X86AstNodeKind::FunctionDecl);
assert!(node.name.is_none());
assert!(node.children.is_empty());
}
#[test]
fn test_expected_ast_node_with_name() {
let node = X86ExpectedAstNode::with_name(X86AstNodeKind::FunctionDecl, "main");
assert_eq!(node.name.unwrap(), "main");
}
#[test]
fn test_expected_ast_node_with_child() {
let child = X86ExpectedAstNode::new(X86AstNodeKind::ReturnStmt);
let node = X86ExpectedAstNode::new(X86AstNodeKind::FunctionDecl).with_child(child);
assert_eq!(node.children.len(), 1);
}
#[test]
fn test_expected_ast_node_with_type() {
let node = X86ExpectedAstNode::new(X86AstNodeKind::VarDecl).with_type("int");
assert_eq!(node.type_name.unwrap(), "int");
}
#[test]
fn test_expected_ast_node_at_location() {
let node = X86ExpectedAstNode::new(X86AstNodeKind::VarDecl).at_location(10, 5);
assert_eq!(node.location_line, Some(10));
assert_eq!(node.location_column, Some(5));
}
#[test]
fn test_verification_result_pass() {
let result = X86AstVerificationResult::pass("test1");
assert!(result.is_pass());
assert!(result.errors.is_empty());
}
#[test]
fn test_verification_result_fail() {
let result = X86AstVerificationResult::fail("test2", "missing node");
assert!(!result.is_pass());
assert_eq!(result.errors.len(), 1);
}
#[test]
fn test_verification_result_add_missing() {
let mut result = X86AstVerificationResult::pass("test3");
result.add_missing("FunctionDecl");
assert!(!result.is_pass());
}
#[test]
fn test_verification_result_display() {
let result = X86AstVerificationResult::pass("test4");
let s = result.to_string();
assert!(s.contains("test4"));
assert!(s.contains("PASS"));
}
#[test]
fn test_pretty_printer_creation() {
let printer = X86AstPrettyPrinter::new();
assert_eq!(printer.indent, 0);
assert!(printer.show_types);
assert!(!printer.show_locations);
}
#[test]
fn test_pretty_printer_default() {
let printer = X86AstPrettyPrinter::default();
assert_eq!(printer.indent, 0);
}
#[test]
fn test_pretty_printer_empty_tu() {
let tu = TranslationUnit {
decls: vec![],
filename: "test.c".to_string(),
};
let mut printer = X86AstPrettyPrinter::new();
let output = printer.print_tu(&tu);
assert!(output.contains("TranslationUnit"));
assert!(output.contains("test.c"));
}
#[test]
fn test_pretty_printer_with_function() {
let func = FunctionDecl {
name: "main".into(),
ret_ty: QualType::int(),
params: vec![],
body: Some(Box::new(CompoundStmt {
stmts: vec![Stmt::Return(Some(Box::new(Expr::IntLiteral(0))))],
})),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let tu = TranslationUnit {
decls: vec![Decl::Function(func)],
filename: "test.c".to_string(),
};
let mut printer = X86AstPrettyPrinter::new();
let output = printer.print_tu(&tu);
assert!(output.contains("FunctionDecl 'main'"));
assert!(output.contains("ReturnStmt"));
assert!(output.contains("IntegerLiteral '0'"));
}
#[test]
fn test_ast_comparator_creation() {
let comparator = X86AstComparator::new();
assert!(!comparator.has_differences());
}
#[test]
fn test_ast_comparator_identical() {
let tu1 = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let mut comparator = X86AstComparator::new();
assert!(comparator.compare_tu(&tu1, &tu2));
assert!(!comparator.has_differences());
}
#[test]
fn test_ast_comparator_diff_count() {
let tu1 = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![Decl::Variable(VarDecl {
name: "x".into(),
ty: QualType::int(),
init: None,
linkage: Linkage::External,
is_global: true,
is_extern: false,
is_static: false,
})],
filename: "b.c".into(),
};
let mut comparator = X86AstComparator::new();
comparator.compare_tu(&tu1, &tu2);
assert!(comparator.has_differences());
}
#[test]
fn test_ast_comparator_report() {
let comparator = X86AstComparator::new();
let report = comparator.report();
assert!(report.contains("identical"));
}
#[test]
fn test_ast_serializer_creation() {
let serializer = X86AstSerializer::new();
assert_eq!(serializer.version, 1);
assert!(serializer.buffer.is_empty());
}
#[test]
fn test_ast_serializer_roundtrip() {
let tu = TranslationUnit {
decls: vec![],
filename: "test.c".into(),
};
let serializer = X86AstSerializer::new();
assert!(serializer.roundtrip(&tu));
}
#[test]
fn test_ast_deserializer_invalid_data() {
let serializer = X86AstSerializer::new();
let result = serializer.deserialize_tu(b"bad data");
assert!(result.is_none());
}
#[test]
fn test_ast_deserializer_valid_header() {
let serializer = X86AstSerializer::new();
let data = vec![
b'X', b'8', b'6', b'A', b'S', b'T', 1, 0, 0, 0, ];
let result = serializer.deserialize_tu(&data);
assert!(result.is_some());
}
#[test]
fn test_c_snippets_count() {
assert!(X86CSnippets::all().len() >= 100);
}
#[test]
fn test_c_snippets_all_non_empty() {
for (name, source) in X86CSnippets::all() {
assert!(!name.is_empty(), "empty snippet name");
}
}
#[test]
fn test_c_snippets_have_categories() {
let cats = X86CSnippets::by_category();
assert!(cats.len() >= 10);
}
#[test]
fn test_c_snippets_known_names() {
let all = X86CSnippets::all();
let names: Vec<&str> = all.iter().map(|(n, _)| *n).collect();
assert!(names.contains(&"c_empty_main"));
assert!(names.contains(&"c_if_else"));
assert!(names.contains(&"c_struct_decl"));
assert!(names.contains(&"c_enum"));
assert!(names.contains(&"c_for"));
assert!(names.contains(&"c_switch"));
}
#[test]
fn test_c_snippet_empty_main_is_valid() {
let source = "int main(void) { return 0; }";
let test = X86ParseASTTest::new();
let result = test.test_c_snippet("test", source);
assert!(!result.test_name.is_empty());
}
#[test]
fn test_cpp_snippets_count() {
assert!(X86CppSnippets::all().len() >= 80);
}
#[test]
fn test_cpp_snippets_have_categories() {
let cats = X86CppSnippets::by_category();
assert!(cats.len() >= 8);
}
#[test]
fn test_cpp_snippets_known_names() {
let all = X86CppSnippets::all();
let names: Vec<&str> = all.iter().map(|(n, _)| *n).collect();
assert!(names.contains(&"cpp_empty_class"));
assert!(names.contains(&"cpp_template_function"));
assert!(names.contains(&"cpp_lambda_empty"));
assert!(names.contains(&"cpp_namespace"));
assert!(names.contains(&"cpp_try_catch"));
assert!(names.contains(&"cpp_auto"));
assert!(names.contains(&"cpp_range_for"));
assert!(names.contains(&"cpp_nullptr"));
assert!(names.contains(&"cpp_constexpr_if"));
assert!(names.contains(&"cpp_concept"));
}
#[test]
fn test_cpp_snippets_categories_cover_all() {
let all = X86CppSnippets::all();
let cats = X86CppSnippets::by_category();
let total_categorized: usize = cats.values().map(|v| v.len()).sum();
assert_eq!(total_categorized, all.len());
}
#[test]
fn test_parse_ast_test_creation() {
let test = X86ParseASTTest::new();
assert_eq!(test.c_snippets.len(), X86CSnippets::all().len());
assert_eq!(test.cpp_snippets.len(), X86CppSnippets::all().len());
assert!(!test.stop_on_failure);
}
#[test]
fn test_parse_ast_test_default() {
let test = X86ParseASTTest::default();
assert_eq!(test.results.len(), 0);
}
#[test]
fn test_parse_ast_test_run_c_snippet() {
let test = X86ParseASTTest::new();
let result = test.test_c_snippet("empty", "");
assert!(result.is_pass()); }
#[test]
fn test_parse_ast_test_summary() {
let mut test = X86ParseASTTest::new();
test.results.push(X86AstVerificationResult::pass("t1"));
test.results
.push(X86AstVerificationResult::fail("t2", "err"));
let summary = test.summary();
assert!(summary.contains("2 total"));
assert!(summary.contains("1 passed"));
assert!(summary.contains("1 failed"));
}
#[test]
fn test_parse_ast_test_print_failures() {
let mut test = X86ParseASTTest::new();
test.results
.push(X86AstVerificationResult::fail("fail1", "bad"));
let failures = test.print_failures();
assert!(failures.contains("fail1"));
assert!(failures.contains("FAIL"));
}
#[test]
fn test_parse_ast_test_stop_on_failure() {
let mut test = X86ParseASTTest::new();
test.stop_on_failure = true;
assert!(test.stop_on_failure);
}
#[test]
fn test_run_x86_parse_ast_tests() {
let (ok, passed, failed) = run_x86_parse_ast_tests();
assert!(passed + failed >= 0);
}
#[test]
fn test_pretty_print_x86_ast() {
let tu = TranslationUnit {
decls: vec![],
filename: "test.c".to_string(),
};
let output = pretty_print_x86_ast(&tu);
assert!(output.contains("TranslationUnit"));
}
#[test]
fn test_compare_x86_ast_identical() {
let tu = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let report = compare_x86_ast(&tu, &tu);
assert!(report.contains("identical"));
}
#[test]
fn test_x86_ast_roundtrip() {
let tu = TranslationUnit {
decls: vec![],
filename: "test.c".into(),
};
assert!(test_x86_ast_roundtrip(&tu));
}
#[test]
fn test_c_declarations_category() {
let cats = X86CSnippets::by_category();
let mut test = X86ParseASTTest::new();
if let Some(decls) = cats.get("declarations") {
let subset: BTreeMap<&str, Vec<&str>> = {
let mut m = BTreeMap::new();
m.insert("declarations", decls.clone());
m
};
test.run_c_category_tests(&subset);
assert!(test.results.len() > 0);
}
}
#[test]
fn test_c_control_flow_category() {
let cats = X86CSnippets::by_category();
let mut test = X86ParseASTTest::new();
if let Some(cf) = cats.get("control_flow") {
let subset: BTreeMap<&str, Vec<&str>> = {
let mut m = BTreeMap::new();
m.insert("control_flow", cf.clone());
m
};
test.run_c_category_tests(&subset);
assert!(test.results.len() > 0);
}
}
#[test]
fn test_c_structs_category() {
let cats = X86CSnippets::by_category();
let mut test = X86ParseASTTest::new();
if let Some(s) = cats.get("structs") {
let subset: BTreeMap<&str, Vec<&str>> = {
let mut m = BTreeMap::new();
m.insert("structs", s.clone());
m
};
test.run_c_category_tests(&subset);
assert!(test.results.len() > 0);
}
}
#[test]
fn test_cpp_classes_category() {
let cats = X86CppSnippets::by_category();
let mut test = X86ParseASTTest::new();
if let Some(c) = cats.get("classes") {
let subset: BTreeMap<&str, Vec<&str>> = {
let mut m = BTreeMap::new();
m.insert("classes", c.clone());
m
};
test.run_cpp_category_tests(&subset);
assert!(test.results.len() > 0);
}
}
#[test]
fn test_cpp_templates_category() {
let cats = X86CppSnippets::by_category();
let mut test = X86ParseASTTest::new();
if let Some(t) = cats.get("templates") {
let subset: BTreeMap<&str, Vec<&str>> = {
let mut m = BTreeMap::new();
m.insert("templates", t.clone());
m
};
test.run_cpp_category_tests(&subset);
assert!(test.results.len() > 0);
}
}
#[test]
fn test_cpp_lambdas_category() {
let cats = X86CppSnippets::by_category();
let mut test = X86ParseASTTest::new();
if let Some(l) = cats.get("lambdas") {
let subset: BTreeMap<&str, Vec<&str>> = {
let mut m = BTreeMap::new();
m.insert("lambdas", l.clone());
m
};
test.run_cpp_category_tests(&subset);
assert!(test.results.len() > 0);
}
}
#[test]
fn stress_all_c_snippets_no_panic() {
let mut test = X86ParseASTTest::new();
test.run_all();
assert!(test.results.len() > 0);
}
#[test]
fn stress_all_ast_node_kinds_unique_names() {
use std::collections::HashSet;
let kinds = [
X86AstNodeKind::TranslationUnit,
X86AstNodeKind::FunctionDecl,
X86AstNodeKind::ParmVarDecl,
X86AstNodeKind::CompoundStmt,
X86AstNodeKind::ReturnStmt,
X86AstNodeKind::DeclStmt,
X86AstNodeKind::IfStmt,
X86AstNodeKind::ForStmt,
X86AstNodeKind::WhileStmt,
X86AstNodeKind::DoWhileStmt,
X86AstNodeKind::SwitchStmt,
X86AstNodeKind::CaseStmt,
X86AstNodeKind::DefaultStmt,
X86AstNodeKind::BreakStmt,
X86AstNodeKind::ContinueStmt,
X86AstNodeKind::GotoStmt,
X86AstNodeKind::LabelStmt,
X86AstNodeKind::BinaryOperator,
X86AstNodeKind::UnaryOperator,
X86AstNodeKind::CallExpr,
X86AstNodeKind::IntegerLiteral,
X86AstNodeKind::FloatingLiteral,
X86AstNodeKind::StringLiteral,
X86AstNodeKind::CharacterLiteral,
X86AstNodeKind::DeclRefExpr,
X86AstNodeKind::ArraySubscriptExpr,
X86AstNodeKind::MemberExpr,
X86AstNodeKind::ConditionalOperator,
X86AstNodeKind::StructDecl,
X86AstNodeKind::UnionDecl,
X86AstNodeKind::EnumDecl,
X86AstNodeKind::EnumConstantDecl,
X86AstNodeKind::TypedefDecl,
X86AstNodeKind::VarDecl,
X86AstNodeKind::FieldDecl,
X86AstNodeKind::CXXRecordDecl,
X86AstNodeKind::CXXMethodDecl,
X86AstNodeKind::CXXConstructorDecl,
X86AstNodeKind::CXXDestructorDecl,
X86AstNodeKind::CXXBaseSpecifier,
X86AstNodeKind::NamespaceDecl,
X86AstNodeKind::UsingDecl,
X86AstNodeKind::TemplateDecl,
X86AstNodeKind::TemplateTypeParmDecl,
X86AstNodeKind::FunctionTemplateDecl,
X86AstNodeKind::ClassTemplateDecl,
X86AstNodeKind::AccessSpecifier,
X86AstNodeKind::OperatorCall,
X86AstNodeKind::LambdaExpr,
X86AstNodeKind::ConceptDecl,
X86AstNodeKind::RequiresExpr,
X86AstNodeKind::StaticAssert,
X86AstNodeKind::NewExpr,
X86AstNodeKind::DeleteExpr,
X86AstNodeKind::ThrowExpr,
X86AstNodeKind::TryStmt,
X86AstNodeKind::CatchStmt,
X86AstNodeKind::RangeForStmt,
X86AstNodeKind::NullPtrLiteral,
X86AstNodeKind::SizeOfExpr,
X86AstNodeKind::AlignOfExpr,
X86AstNodeKind::CastExpr,
];
let mut names = HashSet::new();
for kind in kinds.iter() {
assert!(names.insert(kind.name()), "Duplicate name: {}", kind.name());
}
}
#[test]
fn smoke_parse_ast_test_all_public() {
let _node = X86ExpectedAstNode::new(X86AstNodeKind::FunctionDecl);
let _result = X86AstVerificationResult::pass("test");
let _printer = X86AstPrettyPrinter::default();
let _comparator = X86AstComparator::default();
let _serializer = X86AstSerializer::default();
let _test = X86ParseASTTest::default();
}
#[test]
fn test_c_snippet_verify_all_names_unique() {
use std::collections::HashSet;
let snippets = X86CSnippets::all();
let mut names = HashSet::new();
for (name, _) in &snippets {
assert!(names.insert(*name), "Duplicate snippet name: {}", name);
}
}
#[test]
fn test_c_snippets_all_have_content_or_empty() {
let snippets = X86CSnippets::all();
for (name, source) in &snippets {
if source.is_empty() {
assert_eq!(*name, "c_empty_file", "Only c_empty_file may be empty");
}
}
}
#[test]
fn test_cpp_snippet_verify_all_names_unique() {
use std::collections::HashSet;
let snippets = X86CppSnippets::all();
let mut names = HashSet::new();
for (name, _) in &snippets {
assert!(names.insert(*name), "Duplicate snippet name: {}", name);
}
}
#[test]
fn test_pretty_printer_deeply_nested() {
let inner = Stmt::Return(Some(Box::new(Expr::IntLiteral(1))));
let mut stmt = inner;
for _ in 0..20 {
stmt = Stmt::Compound(CompoundStmt { stmts: vec![stmt] });
}
let tu = TranslationUnit {
decls: vec![Decl::Function(FunctionDecl {
name: "deep".into(),
ret_ty: QualType::int(),
params: vec![],
body: Some(Box::new(CompoundStmt { stmts: vec![stmt] })),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
filename: "deep.c".into(),
};
let mut printer = X86AstPrettyPrinter::new();
let output = printer.print_tu(&tu);
assert!(output.contains("deep"));
assert!(output.len() > 100);
}
#[test]
fn test_pretty_printer_all_stmt_kinds() {
let all_stmts = vec![
Stmt::Null,
Stmt::Break,
Stmt::Continue,
Stmt::Return(Some(Box::new(Expr::IntLiteral(0)))),
Stmt::Return(None),
];
let tu = TranslationUnit {
decls: vec![Decl::Function(FunctionDecl {
name: "f".into(),
ret_ty: QualType::int(),
params: vec![],
body: Some(Box::new(CompoundStmt { stmts: all_stmts })),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
filename: "all.c".into(),
};
let mut printer = X86AstPrettyPrinter::new();
let output = printer.print_tu(&tu);
assert!(output.contains("NullStmt"));
assert!(output.contains("BreakStmt"));
assert!(output.contains("ContinueStmt"));
}
#[test]
fn test_pretty_printer_with_locations() {
let mut printer = X86AstPrettyPrinter::new();
printer.show_locations = true;
let tu = TranslationUnit {
decls: vec![],
filename: "loc.c".into(),
};
let output = printer.print_tu(&tu);
assert!(output.contains("loc.c"));
}
#[test]
fn test_ast_serializer_version() {
let serializer = X86AstSerializer::new();
assert_eq!(serializer.version, 1);
}
#[test]
fn test_ast_serializer_empty_roundtrip() {
let tu = TranslationUnit {
decls: vec![],
filename: "empty.c".into(),
};
let serializer = X86AstSerializer::new();
let data = serializer.serialize_tu(&tu);
assert!(data.len() >= 10);
assert_eq!(&data[0..6], b"X86AST");
}
#[test]
fn test_ast_serializer_magic_bytes() {
let tu = TranslationUnit {
decls: vec![],
filename: "test.c".into(),
};
let serializer = X86AstSerializer::new();
let data = serializer.serialize_tu(&tu);
assert_eq!(&data[0..6], b"X86AST");
}
#[test]
fn test_ast_serializer_corrupted_header() {
let serializer = X86AstSerializer::new();
let bad_data = vec![0u8; 5];
assert!(serializer.deserialize_tu(&bad_data).is_none());
}
#[test]
fn test_ast_serializer_short_data() {
let serializer = X86AstSerializer::new();
assert!(serializer.deserialize_tu(&[]).is_none());
assert!(serializer.deserialize_tu(&[b'X']).is_none());
}
#[test]
fn test_ast_comparator_with_functions() {
let f1 = FunctionDecl {
name: "foo".into(),
ret_ty: QualType::int(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let f2 = FunctionDecl {
name: "bar".into(),
ret_ty: QualType::int(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let tu1 = TranslationUnit {
decls: vec![Decl::Function(f1)],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![Decl::Function(f2)],
filename: "b.c".into(),
};
let mut comparator = X86AstComparator::new();
comparator.compare_tu(&tu1, &tu2);
assert!(comparator.has_differences());
}
#[test]
fn test_ast_comparator_same_functions() {
let f = FunctionDecl {
name: "foo".into(),
ret_ty: QualType::int(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let tu1 = TranslationUnit {
decls: vec![Decl::Function(f.clone())],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![Decl::Function(f)],
filename: "b.c".into(),
};
let mut comparator = X86AstComparator::new();
assert!(comparator.compare_tu(&tu1, &tu2));
}
#[test]
fn test_c_category_pointers_complete() {
let cats = X86CSnippets::by_category();
if let Some(ptrs) = cats.get("pointers") {
assert!(ptrs.len() >= 8);
}
}
#[test]
fn test_c_category_enums_complete() {
let cats = X86CSnippets::by_category();
if let Some(enums) = cats.get("enums") {
assert!(enums.len() >= 5);
}
}
#[test]
fn test_cpp_category_exceptions_complete() {
let cats = X86CppSnippets::by_category();
if let Some(ex) = cats.get("exceptions") {
assert!(ex.len() >= 5);
}
}
#[test]
fn test_cpp_category_modern_cpp_complete() {
let cats = X86CppSnippets::by_category();
if let Some(m) = cats.get("modern_cpp") {
assert!(m.len() >= 5);
}
}
#[test]
fn test_full_suite_c_only() {
let mut test = X86ParseASTTest::new();
test.cpp_snippets.clear();
let ok = test.run_all();
assert!(test.results.len() > 0);
}
#[test]
fn test_parse_ast_test_verbose_mode() {
let mut test = X86ParseASTTest::new();
test.verbose = true;
test.stop_on_failure = false;
assert!(test.verbose);
assert!(!test.stop_on_failure);
}
#[test]
fn test_parse_ast_test_stop_on_first_failure() {
let mut test = X86ParseASTTest::new();
test.stop_on_failure = true;
test.run_all();
}
#[test]
fn smoke_c_snippets_count() {
assert_eq!(X86CSnippets::count(), 110);
}
#[test]
fn smoke_cpp_snippets_count() {
assert_eq!(X86CppSnippets::count(), 90);
}
#[test]
fn smoke_comparator_ignored_types() {
let mut comp = X86AstComparator::new();
comp.ignored_node_types.push(X86AstNodeKind::NullPtrLiteral);
assert_eq!(comp.ignored_node_types.len(), 1);
}
#[test]
fn smoke_printer_max_depth() {
let mut printer = X86AstPrettyPrinter::new();
printer.max_depth = Some(5);
assert_eq!(printer.max_depth, Some(5));
}
#[test]
fn smoke_ast_node_kind_all_variants() {
let _ = X86AstNodeKind::LambdaExpr;
let _ = X86AstNodeKind::ConceptDecl;
let _ = X86AstNodeKind::RequiresExpr;
let _ = X86AstNodeKind::StaticAssert;
let _ = X86AstNodeKind::NewExpr;
let _ = X86AstNodeKind::DeleteExpr;
let _ = X86AstNodeKind::ThrowExpr;
let _ = X86AstNodeKind::TryStmt;
let _ = X86AstNodeKind::CatchStmt;
let _ = X86AstNodeKind::RangeForStmt;
}
#[test]
fn regression_empty_source_does_not_panic() {
let test = X86ParseASTTest::new();
let result = test.test_c_snippet("empty", "");
assert!(result.is_pass());
}
#[test]
fn regression_unicode_in_source_handled() {
let test = X86ParseASTTest::new();
let result = test.test_c_snippet("unicode", "int pi = 3;");
assert!(!result.test_name.is_empty());
}
#[test]
fn doc_example_pretty_print() {
let tu = TranslationUnit {
decls: vec![Decl::Function(FunctionDecl {
name: "add".into(),
ret_ty: QualType::int(),
params: vec![QualType::int(), QualType::int()],
body: Some(Box::new(CompoundStmt {
stmts: vec![Stmt::Return(Some(Box::new(Expr::Binary(
BinaryOp::Add,
Box::new(Expr::Ident("a".into())),
Box::new(Expr::Ident("b".into())),
))))],
})),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
filename: "add.c".into(),
};
let output = pretty_print_x86_ast(&tu);
assert!(output.contains("add"));
}
#[test]
fn doc_example_ast_comparison() {
let tu1 = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let report = compare_x86_ast(&tu1, &tu2);
assert!(report.contains("identical"));
}
#[test]
fn doc_example_ast_roundtrip() {
let tu = TranslationUnit {
decls: vec![],
filename: "roundtrip.c".into(),
};
assert!(test_x86_ast_roundtrip(&tu));
}
}
#[test]
fn test_c_snippet_verify_all_names_unique() {
use std::collections::HashSet;
let snippets = X86CSnippets::all();
let mut names = HashSet::new();
for (name, _) in &snippets {
assert!(names.insert(*name), "Duplicate snippet name: {}", name);
}
}
#[test]
fn test_c_snippets_all_have_content_or_empty() {
let snippets = X86CSnippets::all();
for (name, source) in &snippets {
if source.is_empty() {
assert_eq!(*name, "c_empty_file", "Only c_empty_file may be empty");
}
}
}
#[test]
fn test_cpp_snippet_verify_all_names_unique() {
use std::collections::HashSet;
let snippets = X86CppSnippets::all();
let mut names = HashSet::new();
for (name, _) in &snippets {
assert!(names.insert(*name), "Duplicate snippet name: {}", name);
}
}
#[test]
fn test_pretty_printer_deeply_nested() {
let inner = Stmt::Return(Some(Box::new(Expr::IntLiteral(1))));
let mut stmt = inner;
for _ in 0..20 {
stmt = Stmt::Compound(CompoundStmt { stmts: vec![stmt] });
}
let tu = TranslationUnit {
decls: vec![Decl::Function(FunctionDecl {
name: "deep".into(),
ret_ty: QualType::int(),
params: vec![],
body: Some(Box::new(CompoundStmt { stmts: vec![stmt] })),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
filename: "deep.c".into(),
};
let mut printer = X86AstPrettyPrinter::new();
let output = printer.print_tu(&tu);
assert!(output.contains("deep"));
assert!(output.len() > 100);
}
#[test]
fn test_pretty_printer_all_stmt_kinds() {
let all_stmts = vec![
Stmt::Null,
Stmt::Break,
Stmt::Continue,
Stmt::Return(Some(Box::new(Expr::IntLiteral(0)))),
Stmt::Return(None),
];
let tu = TranslationUnit {
decls: vec![Decl::Function(FunctionDecl {
name: "f".into(),
ret_ty: QualType::int(),
params: vec![],
body: Some(Box::new(CompoundStmt { stmts: all_stmts })),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
filename: "all.c".into(),
};
let mut printer = X86AstPrettyPrinter::new();
let output = printer.print_tu(&tu);
assert!(output.contains("NullStmt"));
assert!(output.contains("BreakStmt"));
assert!(output.contains("ContinueStmt"));
}
#[test]
fn test_pretty_printer_with_locations() {
let mut printer = X86AstPrettyPrinter::new();
printer.show_locations = true;
let tu = TranslationUnit {
decls: vec![],
filename: "loc.c".into(),
};
let output = printer.print_tu(&tu);
assert!(output.contains("loc.c"));
}
#[test]
fn test_ast_serializer_version() {
let serializer = X86AstSerializer::new();
assert_eq!(serializer.version, 1);
}
#[test]
fn test_ast_serializer_empty_roundtrip() {
let tu = TranslationUnit {
decls: vec![],
filename: "empty.c".into(),
};
let serializer = X86AstSerializer::new();
let data = serializer.serialize_tu(&tu);
assert!(data.len() >= 10); assert_eq!(&data[0..6], b"X86AST");
}
#[test]
fn test_ast_serializer_magic_bytes() {
let tu = TranslationUnit {
decls: vec![],
filename: "test.c".into(),
};
let serializer = X86AstSerializer::new();
let data = serializer.serialize_tu(&tu);
assert_eq!(&data[0..6], b"X86AST");
}
#[test]
fn test_ast_serializer_corrupted_header() {
let serializer = X86AstSerializer::new();
let bad_data = vec![0u8; 5];
assert!(serializer.deserialize_tu(&bad_data).is_none());
}
#[test]
fn test_ast_serializer_short_data() {
let serializer = X86AstSerializer::new();
assert!(serializer.deserialize_tu(&[]).is_none());
assert!(serializer.deserialize_tu(&[b'X']).is_none());
}
#[test]
fn test_ast_comparator_with_functions() {
let f1 = FunctionDecl {
name: "foo".into(),
ret_ty: QualType::int(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let f2 = FunctionDecl {
name: "bar".into(),
ret_ty: QualType::int(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let tu1 = TranslationUnit {
decls: vec![Decl::Function(f1)],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![Decl::Function(f2)],
filename: "b.c".into(),
};
let mut comparator = X86AstComparator::new();
comparator.compare_tu(&tu1, &tu2);
assert!(comparator.has_differences());
}
#[test]
fn test_ast_comparator_same_functions() {
let f = FunctionDecl {
name: "foo".into(),
ret_ty: QualType::int(),
params: vec![],
body: None,
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
};
let tu1 = TranslationUnit {
decls: vec![Decl::Function(f.clone())],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![Decl::Function(f)],
filename: "b.c".into(),
};
let mut comparator = X86AstComparator::new();
assert!(comparator.compare_tu(&tu1, &tu2));
}
#[test]
fn test_c_category_pointers_complete() {
let cats = X86CSnippets::by_category();
if let Some(ptrs) = cats.get("pointers") {
assert!(
ptrs.len() >= 8,
"Pointers category should have at least 8 snippets"
);
}
}
#[test]
fn test_c_category_enums_complete() {
let cats = X86CSnippets::by_category();
if let Some(enums) = cats.get("enums") {
assert!(enums.len() >= 5);
}
}
#[test]
fn test_cpp_category_exceptions_complete() {
let cats = X86CppSnippets::by_category();
if let Some(ex) = cats.get("exceptions") {
assert!(ex.len() >= 5);
}
}
#[test]
fn test_cpp_category_modern_cpp_complete() {
let cats = X86CppSnippets::by_category();
if let Some(m) = cats.get("modern_cpp") {
assert!(m.len() >= 5);
}
}
#[test]
fn test_full_suite_c_only() {
let mut test = X86ParseASTTest::new();
test.cpp_snippets.clear();
let ok = test.run_all();
assert!(test.results.len() > 0);
}
#[test]
fn test_parse_ast_test_verbose_mode() {
let mut test = X86ParseASTTest::new();
test.verbose = true;
test.stop_on_failure = false;
assert!(test.verbose);
assert!(!test.stop_on_failure);
}
#[test]
fn test_parse_ast_test_stop_on_first_failure() {
let mut test = X86ParseASTTest::new();
test.stop_on_failure = true;
test.run_all();
}
#[test]
fn smoke_c_snippets_count() {
assert_eq!(X86CSnippets::count(), 110);
}
#[test]
fn smoke_cpp_snippets_count() {
assert_eq!(X86CppSnippets::count(), 90);
}
#[test]
fn smoke_comparator_ignored_types() {
let mut comp = X86AstComparator::new();
comp.ignored_node_types.push(X86AstNodeKind::NullPtrLiteral);
assert_eq!(comp.ignored_node_types.len(), 1);
}
#[test]
fn smoke_printer_max_depth() {
let mut printer = X86AstPrettyPrinter::new();
printer.max_depth = Some(5);
assert_eq!(printer.max_depth, Some(5));
}
#[test]
fn smoke_ast_node_kind_all_variants() {
let _ = X86AstNodeKind::LambdaExpr;
let _ = X86AstNodeKind::ConceptDecl;
let _ = X86AstNodeKind::RequiresExpr;
let _ = X86AstNodeKind::StaticAssert;
let _ = X86AstNodeKind::NewExpr;
let _ = X86AstNodeKind::DeleteExpr;
let _ = X86AstNodeKind::ThrowExpr;
let _ = X86AstNodeKind::TryStmt;
let _ = X86AstNodeKind::CatchStmt;
let _ = X86AstNodeKind::RangeForStmt;
}
#[test]
fn regression_empty_source_does_not_panic() {
let test = X86ParseASTTest::new();
let result = test.test_c_snippet("empty", "");
assert!(result.is_pass());
}
#[test]
fn regression_unicode_in_source_handled() {
let test = X86ParseASTTest::new();
let result = test.test_c_snippet("unicode", "int π = 3;");
assert!(!result.test_name.is_empty());
}
#[test]
fn doc_example_pretty_print() {
let tu = TranslationUnit {
decls: vec![Decl::Function(FunctionDecl {
name: "add".into(),
ret_ty: QualType::int(),
params: vec![QualType::int(), QualType::int()],
body: Some(Box::new(CompoundStmt {
stmts: vec![Stmt::Return(Some(Box::new(Expr::Binary(
BinaryOp::Add,
Box::new(Expr::Ident("a".into())),
Box::new(Expr::Ident("b".into())),
))))],
})),
is_vararg: false,
linkage: Linkage::External,
is_inline: false,
is_noreturn: false,
})],
filename: "add.c".into(),
};
let output = pretty_print_x86_ast(&tu);
assert!(output.contains("add"));
}
#[test]
fn doc_example_ast_comparison() {
let tu1 = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let tu2 = TranslationUnit {
decls: vec![],
filename: "a.c".into(),
};
let report = compare_x86_ast(&tu1, &tu2);
assert!(report.contains("identical"));
}
#[test]
fn doc_example_ast_roundtrip() {
let tu = TranslationUnit {
decls: vec![],
filename: "roundtrip.c".into(),
};
assert!(test_x86_ast_roundtrip(&tu));
}