use crate::frontend::ast::{BinaryOp, Expr, ExprKind, Literal, Pattern, Span, UnaryOp};
use proptest::prelude::*;
use proptest::strategy::{BoxedStrategy, Strategy};
const MAX_DEPTH: u32 = 5;
pub fn arb_literal() -> BoxedStrategy<Literal> {
prop_oneof![
(0i64..i64::MAX).prop_map(|n| Literal::Integer(n, None)),
any::<f64>().prop_map(Literal::Float),
any::<bool>().prop_map(Literal::Bool),
"[a-zA-Z0-9 ]{0,20}".prop_map(Literal::String),
Just(Literal::Unit),
]
.boxed()
}
pub fn arb_identifier() -> BoxedStrategy<String> {
"[a-z][a-z0-9_]{0,10}".prop_map(|s| s).boxed()
}
pub fn arb_binary_op() -> BoxedStrategy<BinaryOp> {
prop_oneof![
Just(BinaryOp::Add),
Just(BinaryOp::Subtract),
Just(BinaryOp::Multiply),
Just(BinaryOp::Divide),
Just(BinaryOp::Modulo),
Just(BinaryOp::Power),
Just(BinaryOp::Equal),
Just(BinaryOp::NotEqual),
Just(BinaryOp::Less),
Just(BinaryOp::LessEqual),
Just(BinaryOp::Greater),
Just(BinaryOp::GreaterEqual),
Just(BinaryOp::And),
Just(BinaryOp::Or),
Just(BinaryOp::BitwiseAnd),
Just(BinaryOp::BitwiseOr),
Just(BinaryOp::BitwiseXor),
Just(BinaryOp::LeftShift),
]
.boxed()
}
pub fn arb_unary_op() -> BoxedStrategy<UnaryOp> {
prop_oneof![
Just(UnaryOp::Negate),
Just(UnaryOp::Not),
Just(UnaryOp::BitwiseNot),
Just(UnaryOp::Reference),
]
.boxed()
}
pub fn arb_expr_with_depth(depth: u32) -> BoxedStrategy<Expr> {
if depth >= MAX_DEPTH {
prop_oneof![
arb_literal().prop_map(|lit| Expr::new(ExprKind::Literal(lit), Span::new(0, 0))),
arb_identifier().prop_map(|id| Expr::new(ExprKind::Identifier(id), Span::new(0, 0))),
]
.boxed()
} else {
prop_oneof![
arb_literal().prop_map(|lit| Expr::new(ExprKind::Literal(lit), Span::new(0, 0))),
arb_identifier().prop_map(|id| Expr::new(ExprKind::Identifier(id), Span::new(0, 0))),
(
arb_expr_with_depth(depth + 1),
arb_binary_op(),
arb_expr_with_depth(depth + 1),
)
.prop_map(|(left, op, right)| {
Expr::new(
ExprKind::Binary {
left: Box::new(left),
op,
right: Box::new(right),
},
Span::new(0, 0),
)
}),
(arb_unary_op(), arb_expr_with_depth(depth + 1)).prop_map(|(op, operand)| {
Expr::new(
ExprKind::Unary {
op,
operand: Box::new(operand),
},
Span::new(0, 0),
)
}),
(
arb_expr_with_depth(depth + 1),
arb_expr_with_depth(depth + 1),
prop::option::of(arb_expr_with_depth(depth + 1)),
)
.prop_map(|(condition, then_branch, else_branch)| {
Expr::new(
ExprKind::If {
condition: Box::new(condition),
then_branch: Box::new(then_branch),
else_branch: else_branch.map(Box::new),
},
Span::new(0, 0),
)
}),
]
.boxed()
}
}
pub fn arb_expr() -> BoxedStrategy<Expr> {
arb_expr_with_depth(0)
}
pub fn arb_pattern() -> BoxedStrategy<Pattern> {
prop_oneof![
any::<i64>().prop_map(|i| Pattern::Literal(Literal::Integer(i, None))),
any::<bool>().prop_map(|b| Pattern::Literal(Literal::Bool(b))),
arb_identifier().prop_map(Pattern::Identifier),
Just(Pattern::Wildcard),
]
.boxed()
}
pub fn arb_well_typed_expr() -> BoxedStrategy<Expr> {
prop_oneof![
arb_literal().prop_map(|lit| Expr::new(ExprKind::Literal(lit), Span::new(0, 0))),
arb_identifier().prop_map(|id| Expr::new(ExprKind::Identifier(id), Span::new(0, 0))),
(any::<i64>(), any::<i64>()).prop_map(|(a, b)| {
Expr::new(
ExprKind::Binary {
left: Box::new(Expr::new(
ExprKind::Literal(Literal::Integer(a, None)),
Span::new(0, 0),
)),
op: BinaryOp::Add,
right: Box::new(Expr::new(
ExprKind::Literal(Literal::Integer(b, None)),
Span::new(0, 0),
)),
},
Span::new(0, 0),
)
}),
]
.boxed()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arb_literal_generates_all_variants() {
let strategy = arb_literal();
let runner = proptest::test_runner::TestRunner::default();
for _ in 0..10 {
let _ = strategy.new_tree(&mut runner.clone());
}
}
#[test]
fn test_arb_identifier_generates_valid_identifiers() {
let strategy = arb_identifier();
let mut runner = proptest::test_runner::TestRunner::default();
for _ in 0..10 {
let value = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test")
.current();
assert!(value
.chars()
.next()
.expect("operation should succeed in test")
.is_ascii_lowercase());
assert!(value.chars().all(|c| c.is_ascii_alphanumeric() || c == '_'));
}
}
#[test]
fn test_arb_binary_op_covers_all_ops() {
let _ops = [
BinaryOp::Add,
BinaryOp::Subtract,
BinaryOp::Multiply,
BinaryOp::Divide,
BinaryOp::Modulo,
BinaryOp::Power,
BinaryOp::Equal,
BinaryOp::NotEqual,
BinaryOp::Less,
BinaryOp::LessEqual,
BinaryOp::Greater,
BinaryOp::GreaterEqual,
BinaryOp::And,
BinaryOp::Or,
BinaryOp::BitwiseAnd,
BinaryOp::BitwiseOr,
BinaryOp::BitwiseXor,
BinaryOp::LeftShift,
];
let strategy = arb_binary_op();
let mut runner = proptest::test_runner::TestRunner::default();
for _ in 0..20 {
let _ = strategy.new_tree(&mut runner);
}
}
#[test]
fn test_arb_unary_op_covers_all_ops() {
let _ops = [
UnaryOp::Negate,
UnaryOp::Not,
UnaryOp::BitwiseNot,
UnaryOp::Reference,
];
let strategy = arb_unary_op();
let mut runner = proptest::test_runner::TestRunner::default();
for _ in 0..10 {
let _ = strategy.new_tree(&mut runner);
}
}
#[test]
fn test_arb_expr_with_depth_respects_max_depth() {
let strategy = arb_expr_with_depth(0);
let mut runner = proptest::test_runner::TestRunner::default();
for _ in 0..5 {
let expr = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test")
.current();
assert_eq!(expr.span, Span::new(0, 0));
}
let strategy = arb_expr_with_depth(MAX_DEPTH);
let mut runner = proptest::test_runner::TestRunner::default();
for _ in 0..5 {
let expr = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test")
.current();
match &expr.kind {
ExprKind::Literal(_) | ExprKind::Identifier(_) => {}
_ => panic!("Generated recursive expression at MAX_DEPTH"),
}
}
}
#[test]
fn test_arb_expr_generates_valid_expressions() {
let strategy = arb_expr();
let mut runner = proptest::test_runner::TestRunner::default();
for _ in 0..5 {
let expr = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test")
.current();
assert_eq!(expr.span, Span::new(0, 0));
}
}
#[test]
fn test_arb_pattern_generates_all_variants() {
let strategy = arb_pattern();
let mut runner = proptest::test_runner::TestRunner::default();
let mut has_literal = false;
let mut has_identifier = false;
let mut has_wildcard = false;
for _ in 0..50 {
let pattern = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test")
.current();
match pattern {
Pattern::Literal(_) => has_literal = true,
Pattern::Identifier(_) => has_identifier = true,
Pattern::Wildcard => has_wildcard = true,
_ => {}
}
}
assert!(has_literal || has_identifier || has_wildcard);
}
#[test]
fn test_arb_well_typed_expr_generates_valid() {
let strategy = arb_well_typed_expr();
let mut runner = proptest::test_runner::TestRunner::default();
for _ in 0..10 {
let expr = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test")
.current();
match &expr.kind {
ExprKind::Literal(_) => {}
ExprKind::Identifier(_) => {}
ExprKind::Binary {
op: BinaryOp::Add, ..
} => {}
_ => panic!("Unexpected expression type in well-typed generator"),
}
}
}
#[test]
fn test_max_depth_constant() {
assert_eq!(MAX_DEPTH, 5);
}
#[test]
fn test_literal_variants() {
let _ = Literal::Integer(42, None);
let _ = Literal::Float(3.15);
let _ = Literal::Bool(true);
let _ = Literal::String("test".to_string());
let _ = Literal::Unit;
}
#[test]
fn test_span_creation() {
let span = Span::new(0, 0);
assert_eq!(span, Span::new(0, 0));
}
#[test]
fn test_expr_new() {
let expr = Expr::new(
ExprKind::Literal(Literal::Integer(42, None)),
Span::new(0, 0),
);
assert!(matches!(
expr.kind,
ExprKind::Literal(Literal::Integer(42, None))
));
assert_eq!(expr.span, Span::new(0, 0));
}
#[test]
fn test_pattern_enum_variants() {
let _ = Pattern::Literal(Literal::Integer(1, None));
let _ = Pattern::Identifier("x".to_string());
let _ = Pattern::Wildcard;
}
#[test]
fn test_binary_op_variants_exist() {
let _ = BinaryOp::Add;
let _ = BinaryOp::Subtract;
let _ = BinaryOp::Multiply;
let _ = BinaryOp::Divide;
let _ = BinaryOp::Modulo;
let _ = BinaryOp::Power;
}
#[test]
fn test_unary_op_variants_exist() {
let _ = UnaryOp::Negate;
let _ = UnaryOp::Not;
let _ = UnaryOp::BitwiseNot;
let _ = UnaryOp::Reference;
}
#[test]
fn test_expr_kind_if_variant() {
let cond = Box::new(Expr::new(
ExprKind::Literal(Literal::Bool(true)),
Span::new(0, 0),
));
let then = Box::new(Expr::new(
ExprKind::Literal(Literal::Integer(1, None)),
Span::new(0, 0),
));
let else_b = Some(Box::new(Expr::new(
ExprKind::Literal(Literal::Integer(2, None)),
Span::new(0, 0),
)));
let if_expr = ExprKind::If {
condition: cond,
then_branch: then,
else_branch: else_b,
};
let expr = Expr::new(if_expr, Span::new(0, 0));
assert!(matches!(expr.kind, ExprKind::If { .. }));
}
#[test]
fn test_arb_literal_generation() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_literal();
for _ in 0..10 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let literal = value_tree.current();
match literal {
Literal::Integer(_, _)
| Literal::Float(_)
| Literal::Bool(_)
| Literal::String(_)
| Literal::Unit
| Literal::Char(_)
| Literal::Byte(_)
| Literal::Null
| Literal::Atom(_) => {}
}
}
}
#[test]
fn test_arb_identifier_generation() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_identifier();
for _ in 0..10 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let identifier = value_tree.current();
assert!(!identifier.is_empty());
assert!(identifier
.chars()
.next()
.expect("operation should succeed in test")
.is_alphabetic());
}
}
#[test]
fn test_arb_binary_op_generation() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_binary_op();
for _ in 0..10 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let _op = value_tree.current();
}
}
#[test]
fn test_arb_unary_op_generation() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_unary_op();
for _ in 0..10 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let _op = value_tree.current();
}
}
#[test]
fn test_arb_pattern_generation() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_pattern();
for _ in 0..10 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let pattern = value_tree.current();
match pattern {
Pattern::Literal(_)
| Pattern::Identifier(_)
| Pattern::Wildcard
| Pattern::Tuple(_)
| Pattern::Struct { .. }
| Pattern::QualifiedName(_)
| Pattern::List(_)
| Pattern::TupleVariant { .. }
| Pattern::Range { .. }
| Pattern::Or(_)
| Pattern::Rest
| Pattern::RestNamed(_)
| Pattern::AtBinding { .. }
| Pattern::WithDefault { .. }
| Pattern::Mut(_)
| Pattern::Ok(_)
| Pattern::Err(_)
| Pattern::Some(_)
| Pattern::None => {}
}
}
}
#[test]
fn test_arb_expr_generation() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_expr();
for _ in 0..10 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let expr = value_tree.current();
assert_eq!(expr.span, Span::new(0, 0));
}
}
#[test]
fn test_multiple_expr_generation() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_expr();
for _ in 0..20 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let _expr = value_tree.current();
}
}
#[test]
fn test_expr_kind_variants() {
let _ = ExprKind::Literal(Literal::Integer(42, None));
let _ = ExprKind::Identifier("x".to_string());
let _ = ExprKind::Block(vec![]);
let _ = ExprKind::Return { value: None };
let _ = ExprKind::Break {
label: None,
value: None,
};
let _ = ExprKind::Continue { label: None };
}
#[test]
fn test_span_creation_extended() {
let span = Span::new(10, 20);
assert_eq!(span.start, 10);
assert_eq!(span.end, 20);
let span2 = Span { start: 5, end: 15 };
assert_eq!(span2.start, 5);
assert_eq!(span2.end, 15);
}
#[test]
fn test_literal_char_variant() {
let char_lit = Literal::Char('a');
match char_lit {
Literal::Char(c) => assert_eq!(c, 'a'),
_ => panic!("Expected Char variant"),
}
}
#[test]
fn test_arb_literal_variants() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_literal();
let mut found_variants = std::collections::HashSet::new();
for _ in 0..100 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let literal = value_tree.current();
match literal {
Literal::Integer(_, _) => {
found_variants.insert("Integer");
}
Literal::Float(_) => {
found_variants.insert("Float");
}
Literal::Bool(_) => {
found_variants.insert("Bool");
}
Literal::String(_) => {
found_variants.insert("String");
}
Literal::Unit => {
found_variants.insert("Unit");
}
Literal::Char(_) => {
found_variants.insert("Char");
}
Literal::Byte(_) => {
found_variants.insert("Byte");
}
Literal::Null => {
found_variants.insert("Null");
}
Literal::Atom(_) => {
found_variants.insert("Atom");
}
}
}
assert!(!found_variants.is_empty());
}
#[test]
fn test_arb_identifier_format() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_identifier();
for _ in 0..50 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let identifier = value_tree.current();
assert!(identifier
.chars()
.next()
.expect("operation should succeed in test")
.is_ascii_lowercase());
assert!(identifier.len() <= 11);
assert!(identifier
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_'));
}
}
#[test]
fn test_arb_binary_op_coverage() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_binary_op();
let mut found_ops = std::collections::HashSet::new();
for _ in 0..200 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let op = value_tree.current();
found_ops.insert(format!("{op:?}"));
}
assert!(found_ops.len() >= 5);
assert!(found_ops.contains("Add"));
assert!(found_ops.contains("Subtract"));
assert!(found_ops.contains("Multiply"));
}
#[test]
fn test_arb_unary_op_coverage() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_unary_op();
let mut found_ops = std::collections::HashSet::new();
for _ in 0..100 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let op = value_tree.current();
found_ops.insert(format!("{op:?}"));
}
assert!(found_ops.contains("Negate"));
assert!(found_ops.contains("Not"));
assert!(found_ops.contains("BitwiseNot"));
assert!(found_ops.contains("Reference"));
}
#[test]
fn test_arb_expr_depth_limiting() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let deep_strategy = arb_expr_with_depth(MAX_DEPTH);
let value_tree = deep_strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let expr = value_tree.current();
assert_eq!(expr.span, Span::new(0, 0));
let shallow_strategy = arb_expr_with_depth(MAX_DEPTH + 1);
let value_tree = shallow_strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let expr = value_tree.current();
match expr.kind {
ExprKind::Literal(_) | ExprKind::Identifier(_) => {
}
_ => {
}
}
}
#[test]
fn test_arb_well_typed_expr_consistency() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_well_typed_expr();
for _ in 0..20 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let expr = value_tree.current();
assert_eq!(expr.span, Span::new(0, 0));
match &expr.kind {
ExprKind::Literal(_) => {}
ExprKind::Identifier(_) => {}
ExprKind::Binary { .. } => {}
ExprKind::Unary { .. } => {}
ExprKind::If { .. } => {}
_ => {} }
}
}
#[test]
fn test_pattern_generation_variants() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_pattern();
let mut found_patterns = std::collections::HashSet::new();
for _ in 0..100 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let pattern = value_tree.current();
match &pattern {
Pattern::Literal(_) => {
found_patterns.insert("Literal");
}
Pattern::Identifier(_) => {
found_patterns.insert("Identifier");
}
Pattern::Wildcard => {
found_patterns.insert("Wildcard");
}
Pattern::Tuple(_) => {
found_patterns.insert("Tuple");
}
Pattern::Struct { .. } => {
found_patterns.insert("Struct");
}
_ => {
found_patterns.insert("Other");
}
}
}
assert!(!found_patterns.is_empty());
assert!(found_patterns.contains("Literal") || found_patterns.contains("Identifier"));
}
#[test]
fn test_generator_max_depth_constant() {
const _: () = assert!(MAX_DEPTH > 0);
const _: () = assert!(MAX_DEPTH < 20); }
#[test]
fn test_expr_with_specific_kinds() {
let literal_expr = Expr::new(
ExprKind::Literal(Literal::Integer(42, None)),
Span::new(0, 0),
);
assert!(matches!(literal_expr.kind, ExprKind::Literal(_)));
let id_expr = Expr::new(ExprKind::Identifier("test".to_string()), Span::new(0, 0));
assert!(matches!(id_expr.kind, ExprKind::Identifier(_)));
}
#[test]
fn test_binary_ops_enumeration() {
let all_ops = [
BinaryOp::Add,
BinaryOp::Subtract,
BinaryOp::Multiply,
BinaryOp::Divide,
BinaryOp::Modulo,
BinaryOp::Power,
BinaryOp::Equal,
BinaryOp::NotEqual,
BinaryOp::Less,
BinaryOp::LessEqual,
BinaryOp::Greater,
BinaryOp::GreaterEqual,
BinaryOp::And,
BinaryOp::Or,
BinaryOp::BitwiseAnd,
BinaryOp::BitwiseOr,
BinaryOp::BitwiseXor,
BinaryOp::LeftShift,
];
assert!(!all_ops.is_empty());
assert!(all_ops.len() >= 10);
}
#[test]
fn test_unary_ops_enumeration() {
let all_ops = [
UnaryOp::Negate,
UnaryOp::Not,
UnaryOp::BitwiseNot,
UnaryOp::Reference,
];
assert_eq!(all_ops.len(), 4);
}
#[test]
fn test_literal_variants_complete() {
let literals = vec![
Literal::Integer(42, None),
Literal::Float(3.15),
Literal::Bool(true),
Literal::String("test".to_string()),
Literal::Char('a'),
Literal::Unit,
];
assert_eq!(literals.len(), 6);
for literal in &literals {
match literal {
Literal::Integer(n, _) => assert_eq!(*n, 42),
Literal::Float(f) => assert!((*f - 3.15).abs() < f64::EPSILON),
Literal::Bool(b) => assert!(*b),
Literal::String(s) => assert_eq!(s, "test"),
Literal::Char(c) => assert_eq!(*c, 'a'),
Literal::Byte(_) => {} Literal::Unit => {}
Literal::Null => {}
Literal::Atom(_) => {} }
}
}
#[test]
fn test_generator_strategies_boxed() {
let _lit_strategy: BoxedStrategy<Literal> = arb_literal();
let _id_strategy: BoxedStrategy<String> = arb_identifier();
let _bin_op_strategy: BoxedStrategy<BinaryOp> = arb_binary_op();
let _un_op_strategy: BoxedStrategy<UnaryOp> = arb_unary_op();
let _pattern_strategy: BoxedStrategy<Pattern> = arb_pattern();
let _expr_strategy: BoxedStrategy<Expr> = arb_expr();
let _well_typed_strategy: BoxedStrategy<Expr> = arb_well_typed_expr();
}
#[test]
fn test_string_literal_length_limit() {
use proptest::strategy::ValueTree;
use proptest::test_runner::TestRunner;
let mut runner = TestRunner::default();
let strategy = arb_literal();
for _ in 0..50 {
let value_tree = strategy
.new_tree(&mut runner)
.expect("operation should succeed in test");
let literal = value_tree.current();
if let Literal::String(s) = literal {
assert!(s.len() <= 20, "String literal too long: {}", s.len());
}
}
}
#[test]
fn test_span_default_behavior() {
let span = Span::new(0, 0);
assert_eq!(span.start, 0);
assert_eq!(span.end, 0);
let default_span = Default::default();
let Span { start, end } = default_span;
assert_eq!(start, 0);
assert_eq!(end, 0);
}
#[test]
fn test_expr_construction() {
let expr1 = Expr::new(
ExprKind::Literal(Literal::Integer(123, None)),
Span::new(5, 8),
);
assert_eq!(expr1.span.start, 5);
assert_eq!(expr1.span.end, 8);
let expr2 = Expr::new(
ExprKind::Identifier("variable".to_string()),
Default::default(),
);
assert_eq!(expr2.span.start, 0);
assert_eq!(expr2.span.end, 0);
}
}