pub use bock_errors::{FileId, Span};
pub mod visitor;
pub type NodeId = u32;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ident {
pub name: String,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypePath {
pub segments: Vec<Ident>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModulePath {
pub segments: Vec<Ident>,
pub span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Visibility {
#[default]
Private,
Internal,
Public,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AnnotationArg {
pub label: Option<Ident>,
pub value: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Annotation {
pub id: NodeId,
pub span: Span,
pub name: Ident,
pub args: Vec<AnnotationArg>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct GenericParam {
pub id: NodeId,
pub span: Span,
pub name: Ident,
pub bounds: Vec<TypePath>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypeConstraint {
pub id: NodeId,
pub span: Span,
pub param: Ident,
pub bounds: Vec<TypePath>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeExpr {
Named {
id: NodeId,
span: Span,
path: TypePath,
args: Vec<TypeExpr>,
},
Tuple {
id: NodeId,
span: Span,
elems: Vec<TypeExpr>,
},
Function {
id: NodeId,
span: Span,
params: Vec<TypeExpr>,
ret: Box<TypeExpr>,
effects: Vec<TypePath>,
},
Optional {
id: NodeId,
span: Span,
inner: Box<TypeExpr>,
},
SelfType { id: NodeId, span: Span },
}
impl TypeExpr {
#[must_use]
pub fn node_id(&self) -> NodeId {
match self {
TypeExpr::Named { id, .. }
| TypeExpr::Tuple { id, .. }
| TypeExpr::Function { id, .. }
| TypeExpr::Optional { id, .. }
| TypeExpr::SelfType { id, .. } => *id,
}
}
#[must_use]
pub fn span(&self) -> Span {
match self {
TypeExpr::Named { span, .. }
| TypeExpr::Tuple { span, .. }
| TypeExpr::Function { span, .. }
| TypeExpr::Optional { span, .. }
| TypeExpr::SelfType { span, .. } => *span,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Literal {
Int(String),
Float(String),
Bool(bool),
Char(String),
String(String),
Unit,
}
const TYPE_SUFFIXES: &[&str] = &[
"i128", "i64", "i32", "i16", "i8", "u64", "u32", "u16", "u8", "f64", "f32",
];
#[must_use]
pub fn strip_type_suffix(s: &str) -> (&str, Option<&str>) {
for suffix in TYPE_SUFFIXES {
let with_underscore_len = 1 + suffix.len();
if s.len() > with_underscore_len {
let split = s.len() - with_underscore_len;
if s.as_bytes()[split] == b'_' && &s[split + 1..] == *suffix {
return (&s[..split], Some(suffix));
}
}
}
(s, None)
}
#[derive(Debug, Clone, PartialEq)]
pub enum Pattern {
Wildcard { id: NodeId, span: Span },
Bind { id: NodeId, span: Span, name: Ident },
MutBind { id: NodeId, span: Span, name: Ident },
Literal {
id: NodeId,
span: Span,
lit: Literal,
},
Constructor {
id: NodeId,
span: Span,
path: TypePath,
fields: Vec<Pattern>,
},
Record {
id: NodeId,
span: Span,
path: TypePath,
fields: Vec<RecordPatternField>,
rest: bool,
},
Tuple {
id: NodeId,
span: Span,
elems: Vec<Pattern>,
},
List {
id: NodeId,
span: Span,
elems: Vec<Pattern>,
rest: Option<Box<Pattern>>,
},
Or {
id: NodeId,
span: Span,
alternatives: Vec<Pattern>,
},
Range {
id: NodeId,
span: Span,
lo: Box<Pattern>,
hi: Box<Pattern>,
inclusive: bool,
},
Rest { id: NodeId, span: Span },
}
impl Pattern {
#[must_use]
pub fn node_id(&self) -> NodeId {
match self {
Pattern::Wildcard { id, .. }
| Pattern::Bind { id, .. }
| Pattern::MutBind { id, .. }
| Pattern::Literal { id, .. }
| Pattern::Constructor { id, .. }
| Pattern::Record { id, .. }
| Pattern::Tuple { id, .. }
| Pattern::List { id, .. }
| Pattern::Or { id, .. }
| Pattern::Range { id, .. }
| Pattern::Rest { id, .. } => *id,
}
}
#[must_use]
pub fn span(&self) -> Span {
match self {
Pattern::Wildcard { span, .. }
| Pattern::Bind { span, .. }
| Pattern::MutBind { span, .. }
| Pattern::Literal { span, .. }
| Pattern::Constructor { span, .. }
| Pattern::Record { span, .. }
| Pattern::Tuple { span, .. }
| Pattern::List { span, .. }
| Pattern::Or { span, .. }
| Pattern::Range { span, .. }
| Pattern::Rest { span, .. } => *span,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct RecordPatternField {
pub span: Span,
pub name: Ident,
pub pattern: Option<Pattern>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Rem,
Pow,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
BitAnd,
BitOr,
BitXor,
Compose, Is,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOp {
Neg,
Not,
BitNot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssignOp {
Assign,
AddAssign,
SubAssign,
MulAssign,
DivAssign,
RemAssign,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Arg {
pub span: Span,
pub label: Option<Ident>,
pub mutable: bool,
pub value: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RecordField {
pub span: Span,
pub name: Ident,
pub value: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RecordSpread {
pub span: Span,
pub expr: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MatchArm {
pub id: NodeId,
pub span: Span,
pub pattern: Pattern,
pub guard: Option<Expr>,
pub body: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PropertyBinding {
pub span: Span,
pub name: Ident,
pub ty: TypeExpr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HandlerPair {
pub span: Span,
pub effect: TypePath,
pub handler: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Literal {
id: NodeId,
span: Span,
lit: Literal,
},
Identifier { id: NodeId, span: Span, name: Ident },
Binary {
id: NodeId,
span: Span,
op: BinOp,
left: Box<Expr>,
right: Box<Expr>,
},
Unary {
id: NodeId,
span: Span,
op: UnaryOp,
operand: Box<Expr>,
},
Assign {
id: NodeId,
span: Span,
op: AssignOp,
target: Box<Expr>,
value: Box<Expr>,
},
Call {
id: NodeId,
span: Span,
callee: Box<Expr>,
args: Vec<Arg>,
type_args: Vec<TypeExpr>,
},
MethodCall {
id: NodeId,
span: Span,
receiver: Box<Expr>,
method: Ident,
type_args: Vec<TypeExpr>,
args: Vec<Arg>,
},
FieldAccess {
id: NodeId,
span: Span,
object: Box<Expr>,
field: Ident,
},
Index {
id: NodeId,
span: Span,
object: Box<Expr>,
index: Box<Expr>,
},
Try {
id: NodeId,
span: Span,
expr: Box<Expr>,
},
Lambda {
id: NodeId,
span: Span,
params: Vec<Param>,
body: Box<Expr>,
},
Pipe {
id: NodeId,
span: Span,
left: Box<Expr>,
right: Box<Expr>,
},
Compose {
id: NodeId,
span: Span,
left: Box<Expr>,
right: Box<Expr>,
},
If {
id: NodeId,
span: Span,
let_pattern: Option<Pattern>,
condition: Box<Expr>,
then_block: Block,
else_block: Option<Box<Expr>>,
},
Match {
id: NodeId,
span: Span,
scrutinee: Box<Expr>,
arms: Vec<MatchArm>,
},
Loop { id: NodeId, span: Span, body: Block },
Block {
id: NodeId,
span: Span,
block: Block,
},
RecordConstruct {
id: NodeId,
span: Span,
path: TypePath,
fields: Vec<RecordField>,
spread: Option<Box<RecordSpread>>,
},
ListLiteral {
id: NodeId,
span: Span,
elems: Vec<Expr>,
},
MapLiteral {
id: NodeId,
span: Span,
entries: Vec<(Expr, Expr)>,
},
SetLiteral {
id: NodeId,
span: Span,
elems: Vec<Expr>,
},
TupleLiteral {
id: NodeId,
span: Span,
elems: Vec<Expr>,
},
Range {
id: NodeId,
span: Span,
lo: Box<Expr>,
hi: Box<Expr>,
inclusive: bool,
},
Await {
id: NodeId,
span: Span,
expr: Box<Expr>,
},
Return {
id: NodeId,
span: Span,
value: Option<Box<Expr>>,
},
Break {
id: NodeId,
span: Span,
value: Option<Box<Expr>>,
},
Continue { id: NodeId, span: Span },
Unreachable { id: NodeId, span: Span },
Interpolation {
id: NodeId,
span: Span,
parts: Vec<InterpolationPart>,
},
Placeholder { id: NodeId, span: Span },
Is {
id: NodeId,
span: Span,
expr: Box<Expr>,
type_expr: TypeExpr,
},
}
impl Expr {
#[must_use]
pub fn node_id(&self) -> NodeId {
match self {
Expr::Literal { id, .. }
| Expr::Identifier { id, .. }
| Expr::Binary { id, .. }
| Expr::Unary { id, .. }
| Expr::Assign { id, .. }
| Expr::Call { id, .. }
| Expr::MethodCall { id, .. }
| Expr::FieldAccess { id, .. }
| Expr::Index { id, .. }
| Expr::Try { id, .. }
| Expr::Lambda { id, .. }
| Expr::Pipe { id, .. }
| Expr::Compose { id, .. }
| Expr::If { id, .. }
| Expr::Match { id, .. }
| Expr::Loop { id, .. }
| Expr::Block { id, .. }
| Expr::RecordConstruct { id, .. }
| Expr::ListLiteral { id, .. }
| Expr::MapLiteral { id, .. }
| Expr::SetLiteral { id, .. }
| Expr::TupleLiteral { id, .. }
| Expr::Range { id, .. }
| Expr::Await { id, .. }
| Expr::Return { id, .. }
| Expr::Break { id, .. }
| Expr::Continue { id, .. }
| Expr::Unreachable { id, .. }
| Expr::Interpolation { id, .. }
| Expr::Placeholder { id, .. }
| Expr::Is { id, .. } => *id,
}
}
#[must_use]
pub fn span(&self) -> Span {
match self {
Expr::Literal { span, .. }
| Expr::Identifier { span, .. }
| Expr::Binary { span, .. }
| Expr::Unary { span, .. }
| Expr::Assign { span, .. }
| Expr::Call { span, .. }
| Expr::MethodCall { span, .. }
| Expr::FieldAccess { span, .. }
| Expr::Index { span, .. }
| Expr::Try { span, .. }
| Expr::Lambda { span, .. }
| Expr::Pipe { span, .. }
| Expr::Compose { span, .. }
| Expr::If { span, .. }
| Expr::Match { span, .. }
| Expr::Loop { span, .. }
| Expr::Block { span, .. }
| Expr::RecordConstruct { span, .. }
| Expr::ListLiteral { span, .. }
| Expr::MapLiteral { span, .. }
| Expr::SetLiteral { span, .. }
| Expr::TupleLiteral { span, .. }
| Expr::Range { span, .. }
| Expr::Await { span, .. }
| Expr::Return { span, .. }
| Expr::Break { span, .. }
| Expr::Continue { span, .. }
| Expr::Unreachable { span, .. }
| Expr::Interpolation { span, .. }
| Expr::Placeholder { span, .. }
| Expr::Is { span, .. } => *span,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum InterpolationPart {
Literal(String),
Expr(Expr),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Stmt {
Let(LetStmt),
Expr(Expr),
For(ForLoop),
While(WhileLoop),
Loop(LoopStmt),
Guard(GuardStmt),
Handling(HandlingBlock),
Empty,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub id: NodeId,
pub span: Span,
pub stmts: Vec<Stmt>,
pub tail: Option<Box<Expr>>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LetStmt {
pub id: NodeId,
pub span: Span,
pub pattern: Pattern,
pub ty: Option<TypeExpr>,
pub value: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ForLoop {
pub id: NodeId,
pub span: Span,
pub pattern: Pattern,
pub iterable: Expr,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct WhileLoop {
pub id: NodeId,
pub span: Span,
pub condition: Expr,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LoopStmt {
pub id: NodeId,
pub span: Span,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct GuardStmt {
pub id: NodeId,
pub span: Span,
pub let_pattern: Option<Pattern>,
pub condition: Expr,
pub else_block: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HandlingBlock {
pub id: NodeId,
pub span: Span,
pub handlers: Vec<HandlerPair>,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Param {
pub id: NodeId,
pub span: Span,
pub pattern: Pattern,
pub ty: Option<TypeExpr>,
pub default: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FnDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub is_async: bool,
pub name: Ident,
pub generic_params: Vec<GenericParam>,
pub params: Vec<Param>,
pub return_type: Option<TypeExpr>,
pub effect_clause: Vec<TypePath>,
pub where_clause: Vec<TypeConstraint>,
pub body: Option<Block>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RecordDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub name: Ident,
pub generic_params: Vec<GenericParam>,
pub where_clause: Vec<TypeConstraint>,
pub fields: Vec<RecordDeclField>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RecordDeclField {
pub id: NodeId,
pub span: Span,
pub name: Ident,
pub ty: TypeExpr,
pub default: Option<Expr>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EnumDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub name: Ident,
pub generic_params: Vec<GenericParam>,
pub where_clause: Vec<TypeConstraint>,
pub variants: Vec<EnumVariant>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum EnumVariant {
Unit { id: NodeId, span: Span, name: Ident },
Struct {
id: NodeId,
span: Span,
name: Ident,
fields: Vec<RecordDeclField>,
},
Tuple {
id: NodeId,
span: Span,
name: Ident,
tys: Vec<TypeExpr>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct ClassDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub name: Ident,
pub generic_params: Vec<GenericParam>,
pub base: Option<TypePath>,
pub traits: Vec<TypePath>,
pub where_clause: Vec<TypeConstraint>,
pub fields: Vec<RecordDeclField>,
pub methods: Vec<FnDecl>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TraitDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub is_platform: bool,
pub name: Ident,
pub generic_params: Vec<GenericParam>,
pub supertraits: Vec<TypePath>,
pub associated_types: Vec<AssociatedType>,
pub methods: Vec<FnDecl>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AssociatedType {
pub id: NodeId,
pub span: Span,
pub name: Ident,
pub bounds: Vec<TypePath>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypeAssignment {
pub id: NodeId,
pub span: Span,
pub name: Ident,
pub type_expr: TypeExpr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImplBlock {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub generic_params: Vec<GenericParam>,
pub trait_path: Option<TypePath>,
pub target: TypeExpr,
pub where_clause: Vec<TypeConstraint>,
pub type_assignments: Vec<TypeAssignment>,
pub methods: Vec<FnDecl>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EffectDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub name: Ident,
pub generic_params: Vec<GenericParam>,
pub components: Vec<TypePath>,
pub operations: Vec<FnDecl>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypeAliasDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub name: Ident,
pub generic_params: Vec<GenericParam>,
pub ty: TypeExpr,
pub where_clause: Vec<TypeConstraint>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConstDecl {
pub id: NodeId,
pub span: Span,
pub annotations: Vec<Annotation>,
pub visibility: Visibility,
pub name: Ident,
pub ty: TypeExpr,
pub value: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ModuleHandleDecl {
pub id: NodeId,
pub span: Span,
pub effect: TypePath,
pub handler: Expr,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PropertyTestDecl {
pub id: NodeId,
pub span: Span,
pub name: String,
pub bindings: Vec<PropertyBinding>,
pub body: Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImportDecl {
pub id: NodeId,
pub span: Span,
pub visibility: Visibility,
pub path: ModulePath,
pub items: ImportItems,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ImportItems {
Module,
Named(Vec<ImportedName>),
Glob,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImportedName {
pub span: Span,
pub name: Ident,
pub alias: Option<Ident>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Item {
Fn(FnDecl),
Record(RecordDecl),
Enum(EnumDecl),
Class(ClassDecl),
Trait(TraitDecl),
PlatformTrait(TraitDecl),
Impl(ImplBlock),
Effect(EffectDecl),
TypeAlias(TypeAliasDecl),
Const(ConstDecl),
ModuleHandle(ModuleHandleDecl),
PropertyTest(PropertyTestDecl),
Error {
id: NodeId,
span: Span,
},
}
impl Item {
#[must_use]
pub fn span(&self) -> Span {
match self {
Item::Fn(d) => d.span,
Item::Record(d) => d.span,
Item::Enum(d) => d.span,
Item::Class(d) => d.span,
Item::Trait(d) | Item::PlatformTrait(d) => d.span,
Item::Impl(d) => d.span,
Item::Effect(d) => d.span,
Item::TypeAlias(d) => d.span,
Item::Const(d) => d.span,
Item::ModuleHandle(d) => d.span,
Item::PropertyTest(d) => d.span,
Item::Error { span, .. } => *span,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Module {
pub id: NodeId,
pub span: Span,
pub doc: Vec<String>,
pub path: Option<ModulePath>,
pub imports: Vec<ImportDecl>,
pub items: Vec<Item>,
}
#[cfg(test)]
mod tests {
use super::*;
use bock_errors::FileId;
fn dummy_span() -> Span {
Span {
file: FileId(0),
start: 0,
end: 0,
}
}
fn dummy_ident(name: &str) -> Ident {
Ident {
name: name.to_string(),
span: dummy_span(),
}
}
#[test]
fn module_is_debug() {
let m = Module {
id: 0,
span: dummy_span(),
doc: vec![],
path: None,
imports: vec![],
items: vec![],
};
let s = format!("{m:?}");
assert!(s.contains("Module"));
}
#[test]
fn item_fn_span() {
let fn_decl = FnDecl {
id: 1,
span: Span {
file: FileId(1),
start: 5,
end: 20,
},
annotations: vec![],
visibility: Visibility::Public,
is_async: false,
name: dummy_ident("foo"),
generic_params: vec![],
params: vec![],
return_type: None,
effect_clause: vec![],
where_clause: vec![],
body: Some(Block {
id: 2,
span: Span {
file: FileId(1),
start: 10,
end: 20,
},
stmts: vec![],
tail: None,
}),
};
let item = Item::Fn(fn_decl);
assert_eq!(item.span().start, 5);
}
#[test]
fn item_module_handle_and_property_test_exist() {
let mh = Item::ModuleHandle(ModuleHandleDecl {
id: 10,
span: dummy_span(),
effect: TypePath {
segments: vec![dummy_ident("Log")],
span: dummy_span(),
},
handler: Expr::Identifier {
id: 11,
span: dummy_span(),
name: dummy_ident("console_log"),
},
});
let pt = Item::PropertyTest(PropertyTestDecl {
id: 20,
span: dummy_span(),
name: "addition is commutative".into(),
bindings: vec![],
body: Block {
id: 21,
span: dummy_span(),
stmts: vec![],
tail: None,
},
});
assert!(format!("{mh:?}").contains("ModuleHandle"));
assert!(format!("{pt:?}").contains("PropertyTest"));
}
#[test]
fn expr_node_id_and_span() {
let span = Span {
file: FileId(1),
start: 3,
end: 7,
};
let e = Expr::Literal {
id: 42,
span,
lit: Literal::Int("42".into()),
};
assert_eq!(e.node_id(), 42);
assert_eq!(e.span(), span);
}
#[test]
fn pattern_wildcard_debug() {
let p = Pattern::Wildcard {
id: 0,
span: dummy_span(),
};
assert!(format!("{p:?}").contains("Wildcard"));
}
#[test]
fn type_expr_optional_debug() {
let inner = TypeExpr::Named {
id: 0,
span: dummy_span(),
path: TypePath {
segments: vec![dummy_ident("Int")],
span: dummy_span(),
},
args: vec![],
};
let opt = TypeExpr::Optional {
id: 1,
span: dummy_span(),
inner: Box::new(inner),
};
assert!(format!("{opt:?}").contains("Optional"));
}
#[test]
fn visibility_default_is_private() {
assert_eq!(Visibility::default(), Visibility::Private);
}
#[test]
fn enum_variant_kinds() {
let unit = EnumVariant::Unit {
id: 0,
span: dummy_span(),
name: dummy_ident("A"),
};
let strukt = EnumVariant::Struct {
id: 1,
span: dummy_span(),
name: dummy_ident("B"),
fields: vec![],
};
let tuple = EnumVariant::Tuple {
id: 2,
span: dummy_span(),
name: dummy_ident("C"),
tys: vec![],
};
assert!(format!("{unit:?}").contains("Unit"));
assert!(format!("{strukt:?}").contains("Struct"));
assert!(format!("{tuple:?}").contains("Tuple"));
}
#[test]
fn all_expr_variants_have_span() {
let span = dummy_span();
let exprs: Vec<Expr> = vec![
Expr::Literal {
id: 0,
span,
lit: Literal::Bool(true),
},
Expr::Identifier {
id: 1,
span,
name: dummy_ident("x"),
},
Expr::Continue { id: 2, span },
Expr::Unreachable { id: 3, span },
Expr::Placeholder { id: 4, span },
];
for e in &exprs {
assert_eq!(e.span(), span);
}
}
}