1use std::{collections::HashMap, rc::Rc};
7
8use miette::Diagnostic;
9
10use crate::ast::*;
11use crate::parsing::AstNode;
12
13const METADATA_MAX_SIZE_BYTES: usize = 64;
14
15#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq, Clone)]
16#[error("not in scope: {name}")]
17#[diagnostic(code(tx3::not_in_scope))]
18pub struct NotInScopeError {
19 pub name: String,
20
21 #[source_code]
22 src: Option<String>,
23
24 #[label]
25 span: Span,
26}
27
28#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq, Clone)]
29#[error("invalid symbol, expected {expected}, got {got}")]
30#[diagnostic(code(tx3::invalid_symbol))]
31pub struct InvalidSymbolError {
32 pub expected: &'static str,
33 pub got: String,
34
35 #[source_code]
36 src: Option<String>,
37
38 #[label]
39 span: Span,
40}
41
42#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq, Clone)]
43#[error("invalid type ({got}), expected: {expected}")]
44#[diagnostic(code(tx3::invalid_type))]
45pub struct InvalidTargetTypeError {
46 pub expected: String,
47 pub got: String,
48
49 #[source_code]
50 src: Option<String>,
51
52 #[label]
53 span: Span,
54}
55
56#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq, Clone)]
57#[error("function '{name}' expects {expected} argument(s), but got {got}")]
58#[diagnostic(code(tx3::arity_mismatch))]
59pub struct ArityError {
60 pub name: String,
61 pub expected: usize,
62 pub got: usize,
63
64 #[source_code]
65 src: Option<String>,
66
67 #[label]
68 span: Span,
69}
70
71#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq, Clone)]
72#[error("optional output ({name}) cannot have a datum")]
73#[diagnostic(code(tx3::optional_output_datum))]
74pub struct OptionalOutputError {
75 pub name: String,
76
77 #[source_code]
78 src: Option<String>,
79
80 #[label]
81 span: Span,
82}
83
84#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq, Clone)]
85#[error("metadata value exceeds 64 bytes: {size} bytes found")]
86#[diagnostic(code(tx3::metadata_size_limit_exceeded))]
87pub struct MetadataSizeLimitError {
88 pub size: usize,
89
90 #[source_code]
91 src: Option<String>,
92
93 #[label("value too large")]
94 span: Span,
95}
96
97#[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq, Clone)]
98#[error("metadata key must be an integer, got: {key_type}")]
99#[diagnostic(code(tx3::metadata_invalid_key_type))]
100pub struct MetadataInvalidKeyTypeError {
101 pub key_type: String,
102
103 #[source_code]
104 src: Option<String>,
105
106 #[label("expected integer key")]
107 span: Span,
108}
109
110#[derive(thiserror::Error, Debug, miette::Diagnostic, PartialEq, Eq, Clone)]
111pub enum Error {
112 #[error("duplicate definition: {0}")]
113 #[diagnostic(code(tx3::duplicate_definition))]
114 DuplicateDefinition(String),
115
116 #[error(transparent)]
117 #[diagnostic(transparent)]
118 NotInScope(#[from] NotInScopeError),
119
120 #[error("needs parent scope")]
121 #[diagnostic(code(tx3::needs_parent_scope))]
122 NeedsParentScope,
123
124 #[error(transparent)]
125 #[diagnostic(transparent)]
126 InvalidSymbol(#[from] InvalidSymbolError),
127
128 #[error(transparent)]
130 #[diagnostic(transparent)]
131 InvalidTargetType(#[from] InvalidTargetTypeError),
132
133 #[error(transparent)]
134 #[diagnostic(transparent)]
135 MetadataSizeLimitExceeded(#[from] MetadataSizeLimitError),
136
137 #[error(transparent)]
138 #[diagnostic(transparent)]
139 MetadataInvalidKeyType(#[from] MetadataInvalidKeyTypeError),
140
141 #[error(transparent)]
142 #[diagnostic(transparent)]
143 InvalidOptionalOutput(#[from] OptionalOutputError),
144
145 #[error(transparent)]
146 #[diagnostic(transparent)]
147 Arity(#[from] ArityError),
148}
149
150impl Error {
151 pub fn span(&self) -> &Span {
152 match self {
153 Self::NotInScope(x) => &x.span,
154 Self::InvalidSymbol(x) => &x.span,
155 Self::InvalidTargetType(x) => &x.span,
156 Self::MetadataSizeLimitExceeded(x) => &x.span,
157 Self::MetadataInvalidKeyType(x) => &x.span,
158 Self::InvalidOptionalOutput(x) => &x.span,
159 Self::Arity(x) => &x.span,
160 _ => &Span::DUMMY,
161 }
162 }
163
164 pub fn src(&self) -> Option<&str> {
165 match self {
166 Self::NotInScope(x) => x.src.as_deref(),
167 Self::MetadataSizeLimitExceeded(x) => x.src.as_deref(),
168 Self::MetadataInvalidKeyType(x) => x.src.as_deref(),
169 _ => None,
170 }
171 }
172
173 pub fn arity(
174 name: String,
175 expected: usize,
176 got: usize,
177 ast: &impl crate::parsing::AstNode,
178 ) -> Self {
179 Self::Arity(ArityError {
180 name,
181 expected,
182 got,
183 src: None,
184 span: ast.span().clone(),
185 })
186 }
187
188 pub fn not_in_scope(name: String, ast: &impl crate::parsing::AstNode) -> Self {
189 Self::NotInScope(NotInScopeError {
190 name,
191 src: None,
192 span: ast.span().clone(),
193 })
194 }
195
196 fn symbol_type_name(symbol: &Symbol) -> String {
197 match symbol {
198 Symbol::TypeDef(type_def) => format!("TypeDef({})", type_def.name.value),
199 Symbol::AliasDef(alias_def) => format!("AliasDef({})", alias_def.name.value),
200 Symbol::VariantCase(case) => format!("VariantCase({})", case.name.value),
201 Symbol::RecordField(field) => format!("RecordField({})", field.name.value),
202 Symbol::PartyDef(party) => format!("PartyDef({})", party.name.value),
203 Symbol::PolicyDef(policy) => format!("PolicyDef({})", policy.name.value),
204 Symbol::AssetDef(asset) => format!("AssetDef({})", asset.name.value),
205 Symbol::EnvVar(name, _) => format!("EnvVar({})", name),
206 Symbol::ParamVar(name, _) => format!("ParamVar({})", name),
207 Symbol::FunctionDef(fn_def) => format!("FunctionDef({})", fn_def.name.value),
208 Symbol::Input(block) => format!("Input({})", block.name),
209 Symbol::Reference(block) => format!("Reference({})", block.name),
210 Symbol::Output(idx) => format!("Output({})", idx),
211 Symbol::LocalExpr(_) => "LocalExpr".to_string(),
212 Symbol::Fees => "Fees".to_string(),
213 }
214 }
215
216 pub fn invalid_symbol(
217 expected: &'static str,
218 got: &Symbol,
219 ast: &impl crate::parsing::AstNode,
220 ) -> Self {
221 Self::InvalidSymbol(InvalidSymbolError {
222 expected,
223 got: Self::symbol_type_name(got),
224 src: None,
225 span: ast.span().clone(),
226 })
227 }
228
229 pub fn invalid_target_type(
230 expected: &Type,
231 got: &Type,
232 ast: &impl crate::parsing::AstNode,
233 ) -> Self {
234 Self::InvalidTargetType(InvalidTargetTypeError {
235 expected: expected.to_string(),
236 got: got.to_string(),
237 src: None,
238 span: ast.span().clone(),
239 })
240 }
241}
242
243#[derive(Debug, Default, thiserror::Error, Diagnostic, Clone)]
244pub struct AnalyzeReport {
245 #[related]
246 pub errors: Vec<Error>,
247}
248
249impl AnalyzeReport {
250 pub fn is_empty(&self) -> bool {
251 self.errors.is_empty()
252 }
253
254 pub fn ok(self) -> Result<(), Self> {
255 if self.is_empty() {
256 Ok(())
257 } else {
258 Err(self)
259 }
260 }
261
262 pub fn expect_data_expr_type(expr: &DataExpr, expected: &Type) -> Self {
263 if expr.target_type().as_ref() != Some(expected) {
264 Self::from(Error::invalid_target_type(
265 expected,
266 expr.target_type().as_ref().unwrap_or(&Type::Undefined),
267 expr,
268 ))
269 } else {
270 Self::default()
271 }
272 }
273}
274
275impl std::fmt::Display for AnalyzeReport {
276 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 if self.errors.is_empty() {
278 write!(f, "")
279 } else {
280 write!(f, "Failed with {} errors:", self.errors.len())?;
281 for error in &self.errors {
282 write!(f, "\n{} ({:?})", error, error)?;
283 }
284 Ok(())
285 }
286 }
287}
288
289impl std::ops::Add for Error {
290 type Output = AnalyzeReport;
291
292 fn add(self, other: Self) -> Self::Output {
293 Self::Output {
294 errors: vec![self, other],
295 }
296 }
297}
298
299impl From<Error> for AnalyzeReport {
300 fn from(error: Error) -> Self {
301 Self {
302 errors: vec![error],
303 }
304 }
305}
306
307impl From<Vec<Error>> for AnalyzeReport {
308 fn from(errors: Vec<Error>) -> Self {
309 Self { errors }
310 }
311}
312
313impl std::ops::Add for AnalyzeReport {
314 type Output = AnalyzeReport;
315
316 fn add(self, other: Self) -> Self::Output {
317 [self, other].into_iter().collect()
318 }
319}
320
321impl FromIterator<Error> for AnalyzeReport {
322 fn from_iter<T: IntoIterator<Item = Error>>(iter: T) -> Self {
323 Self {
324 errors: iter.into_iter().collect(),
325 }
326 }
327}
328
329impl FromIterator<AnalyzeReport> for AnalyzeReport {
330 fn from_iter<T: IntoIterator<Item = AnalyzeReport>>(iter: T) -> Self {
331 Self {
332 errors: iter.into_iter().flat_map(|r| r.errors).collect(),
333 }
334 }
335}
336
337macro_rules! bail_report {
338 ($($args:expr),*) => {
339 { return AnalyzeReport::from(vec![$($args),*]); }
340 };
341}
342
343impl Scope {
344 pub fn new(parent: Option<Rc<Scope>>) -> Self {
345 Self {
346 symbols: HashMap::new(),
347 parent,
348 }
349 }
350
351 pub fn track_env_var(&mut self, name: &str, ty: Type) {
352 self.symbols.insert(
353 name.to_string(),
354 Symbol::EnvVar(name.to_string(), Box::new(ty)),
355 );
356 }
357
358 pub fn track_type_def(&mut self, type_: &TypeDef) {
359 self.symbols.insert(
360 type_.name.value.clone(),
361 Symbol::TypeDef(Box::new(type_.clone())),
362 );
363 }
364
365 pub fn track_alias_def(&mut self, alias: &AliasDef) {
366 self.symbols.insert(
367 alias.name.value.clone(),
368 Symbol::AliasDef(Box::new(alias.clone())),
369 );
370 }
371
372 pub fn track_variant_case(&mut self, case: &VariantCase) {
373 self.symbols.insert(
374 case.name.value.clone(),
375 Symbol::VariantCase(Box::new(case.clone())),
376 );
377 }
378
379 pub fn track_record_field(&mut self, field: &RecordField) {
380 self.symbols.insert(
381 field.name.value.clone(),
382 Symbol::RecordField(Box::new(field.clone())),
383 );
384 }
385
386 pub fn track_party_def(&mut self, party: &PartyDef) {
387 self.symbols.insert(
388 party.name.value.clone(),
389 Symbol::PartyDef(Box::new(party.clone())),
390 );
391 }
392
393 pub fn track_policy_def(&mut self, policy: &PolicyDef) {
394 self.symbols.insert(
395 policy.name.value.clone(),
396 Symbol::PolicyDef(Box::new(policy.clone())),
397 );
398 }
399
400 pub fn track_asset_def(&mut self, asset: &AssetDef) {
401 self.symbols.insert(
402 asset.name.value.clone(),
403 Symbol::AssetDef(Box::new(asset.clone())),
404 );
405 }
406
407 pub fn track_param_var(&mut self, param: &str, ty: Type) {
408 self.symbols.insert(
409 param.to_string(),
410 Symbol::ParamVar(param.to_string(), Box::new(ty)),
411 );
412 }
413
414 pub fn track_fn_def(&mut self, fn_def: &FnDef) {
415 self.symbols.insert(
416 fn_def.name.value.clone(),
417 Symbol::FunctionDef(Box::new(fn_def.clone())),
418 );
419 }
420
421 pub fn track_local_expr(&mut self, name: &str, expr: DataExpr) {
422 self.symbols
423 .insert(name.to_string(), Symbol::LocalExpr(Box::new(expr)));
424 }
425
426 pub fn track_input(&mut self, name: &str, input: InputBlock) {
427 self.symbols
428 .insert(name.to_string(), Symbol::Input(Box::new(input)));
429 }
430
431 pub fn track_reference(&mut self, name: &str, reference: ReferenceBlock) {
432 self.symbols
433 .insert(name.to_string(), Symbol::Reference(Box::new(reference)));
434 }
435
436 pub fn track_output(&mut self, index: usize, output: OutputBlock) {
437 if let Some(n) = output.name {
438 self.symbols.insert(n.value, Symbol::Output(index));
439 }
440 }
441
442 pub fn track_record_fields_for_type(&mut self, ty: &Type) {
443 let resolved_ty = match ty {
449 Type::Custom(id) if id.symbol.is_none() => {
450 if let Some(symbol) = self.resolve(&id.value) {
451 let mut resolved = ty.clone();
452 if let Type::Custom(cloned_id) = &mut resolved {
453 cloned_id.symbol = Some(symbol);
454 }
455 resolved
456 } else {
457 ty.clone()
458 }
459 }
460 _ => ty.clone(),
461 };
462
463 let schema = resolved_ty.properties();
464
465 for (name, mut subty) in schema {
466 if let Type::Custom(id) = &mut subty {
467 if id.symbol.is_none() {
468 if let Some(symbol) = self.resolve(&id.value) {
469 id.symbol = Some(symbol);
470 }
471 }
472 }
473 self.track_record_field(&RecordField {
474 name: Identifier::new(name),
475 r#type: subty,
476 span: Span::DUMMY,
477 });
478 }
479 }
480
481 pub fn resolve(&self, name: &str) -> Option<Symbol> {
482 if let Some(symbol) = self.symbols.get(name) {
483 Some(symbol.clone())
484 } else if let Some(parent) = &self.parent {
485 parent.resolve(name)
486 } else {
487 None
488 }
489 }
490}
491
492pub trait Analyzable {
497 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport;
505
506 fn is_resolved(&self) -> bool;
508}
509
510impl<T: Analyzable> Analyzable for Option<T> {
511 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
512 if let Some(item) = self {
513 item.analyze(parent)
514 } else {
515 AnalyzeReport::default()
516 }
517 }
518
519 fn is_resolved(&self) -> bool {
520 self.as_ref().is_none_or(|x| x.is_resolved())
521 }
522}
523
524impl<T: Analyzable> Analyzable for Box<T> {
525 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
526 self.as_mut().analyze(parent)
527 }
528
529 fn is_resolved(&self) -> bool {
530 self.as_ref().is_resolved()
531 }
532}
533
534impl<T: Analyzable> Analyzable for Vec<T> {
535 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
536 self.iter_mut()
537 .map(|item| item.analyze(parent.clone()))
538 .collect()
539 }
540
541 fn is_resolved(&self) -> bool {
542 self.iter().all(|x| x.is_resolved())
543 }
544}
545
546impl Analyzable for PartyDef {
547 fn analyze(&mut self, _parent: Option<Rc<Scope>>) -> AnalyzeReport {
548 AnalyzeReport::default()
549 }
550
551 fn is_resolved(&self) -> bool {
552 true
553 }
554}
555
556impl Analyzable for PolicyField {
557 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
558 match self {
559 PolicyField::Hash(x) => x.analyze(parent),
560 PolicyField::Script(x) => x.analyze(parent),
561 PolicyField::Ref(x) => x.analyze(parent),
562 }
563 }
564
565 fn is_resolved(&self) -> bool {
566 match self {
567 PolicyField::Hash(x) => x.is_resolved(),
568 PolicyField::Script(x) => x.is_resolved(),
569 PolicyField::Ref(x) => x.is_resolved(),
570 }
571 }
572}
573impl Analyzable for PolicyConstructor {
574 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
575 self.fields.analyze(parent)
576 }
577
578 fn is_resolved(&self) -> bool {
579 self.fields.is_resolved()
580 }
581}
582
583impl Analyzable for PolicyDef {
584 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
585 match &mut self.value {
586 PolicyValue::Constructor(x) => x.analyze(parent),
587 PolicyValue::Assign(_) => AnalyzeReport::default(),
588 }
589 }
590
591 fn is_resolved(&self) -> bool {
592 match &self.value {
593 PolicyValue::Constructor(x) => x.is_resolved(),
594 PolicyValue::Assign(_) => true,
595 }
596 }
597}
598
599impl Analyzable for AddOp {
600 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
601 let left = self.lhs.analyze(parent.clone());
602 let right = self.rhs.analyze(parent.clone());
603
604 left + right
605 }
606
607 fn is_resolved(&self) -> bool {
608 self.lhs.is_resolved() && self.rhs.is_resolved()
609 }
610}
611
612impl Analyzable for ConcatOp {
613 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
614 let left = self.lhs.analyze(parent.clone());
615 let right = self.rhs.analyze(parent.clone());
616
617 left + right
618 }
619
620 fn is_resolved(&self) -> bool {
621 self.lhs.is_resolved() && self.rhs.is_resolved()
622 }
623}
624
625impl Analyzable for SubOp {
626 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
627 let left = self.lhs.analyze(parent.clone());
628 let right = self.rhs.analyze(parent.clone());
629
630 left + right
631 }
632
633 fn is_resolved(&self) -> bool {
634 self.lhs.is_resolved() && self.rhs.is_resolved()
635 }
636}
637
638impl Analyzable for MulOp {
639 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
640 let left = self.lhs.analyze(parent.clone());
641 let right = self.rhs.analyze(parent.clone());
642
643 left + right
644 }
645
646 fn is_resolved(&self) -> bool {
647 self.lhs.is_resolved() && self.rhs.is_resolved()
648 }
649}
650
651impl Analyzable for DivOp {
652 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
653 let left = self.lhs.analyze(parent.clone());
654 let right = self.rhs.analyze(parent.clone());
655
656 left + right
657 }
658
659 fn is_resolved(&self) -> bool {
660 self.lhs.is_resolved() && self.rhs.is_resolved()
661 }
662}
663
664impl Analyzable for NegateOp {
665 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
666 self.operand.analyze(parent)
667 }
668
669 fn is_resolved(&self) -> bool {
670 self.operand.is_resolved()
671 }
672}
673
674impl Analyzable for RecordConstructorField {
675 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
676 let name = self.name.analyze(parent.clone());
677
678 let outer = parent.as_ref().and_then(|p| p.parent.clone());
681 let value = self.value.analyze(outer);
682
683 name + value
684 }
685
686 fn is_resolved(&self) -> bool {
687 self.name.is_resolved() && self.value.is_resolved()
688 }
689}
690
691impl Analyzable for VariantCaseConstructor {
692 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
693 let name = if self.name.symbol.is_some() {
694 AnalyzeReport::default()
695 } else {
696 self.name.analyze(parent.clone())
697 };
698
699 let mut scope = Scope::new(parent);
700
701 let case = match &self.name.symbol {
702 Some(Symbol::VariantCase(x)) => x,
703 Some(x) => bail_report!(Error::invalid_symbol("VariantCase", x, &self.name)),
704 None => bail_report!(Error::not_in_scope(self.name.value.clone(), &self.name)),
705 };
706
707 for field in case.fields.iter() {
708 scope.track_record_field(field);
709 }
710
711 self.scope = Some(Rc::new(scope));
712
713 let fields = self.fields.analyze(self.scope.clone());
714
715 let spread = self.spread.analyze(self.scope.clone());
716
717 name + fields + spread
718 }
719
720 fn is_resolved(&self) -> bool {
721 self.name.is_resolved() && self.fields.is_resolved() && self.spread.is_resolved()
722 }
723}
724
725impl Analyzable for StructConstructor {
726 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
727 let r#type = self.r#type.analyze(parent.clone());
728
729 let mut scope = Scope::new(parent);
730
731 let type_def = match &self.r#type.symbol {
732 Some(Symbol::TypeDef(type_def)) => type_def.as_ref(),
733 Some(Symbol::AliasDef(alias_def)) => match alias_def.resolve_alias_chain() {
734 Some(resolved_type_def) => resolved_type_def,
735 None => {
736 bail_report!(Error::invalid_symbol(
737 "struct type",
738 &Symbol::AliasDef(alias_def.clone()),
739 &self.r#type
740 ));
741 }
742 },
743 Some(symbol) => {
744 bail_report!(Error::invalid_symbol("struct type", symbol, &self.r#type));
745 }
746 None => {
747 bail_report!(Error::not_in_scope(self.r#type.value.clone(), &self.r#type));
748 }
749 };
750
751 for case in type_def.cases.iter() {
752 scope.track_variant_case(case);
753 }
754
755 self.scope = Some(Rc::new(scope));
756
757 let case = self.case.analyze(self.scope.clone());
758
759 r#type + case
760 }
761
762 fn is_resolved(&self) -> bool {
763 self.r#type.is_resolved() && self.case.is_resolved()
764 }
765}
766
767impl Analyzable for ListConstructor {
768 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
769 self.elements.analyze(parent)
770 }
771
772 fn is_resolved(&self) -> bool {
773 self.elements.is_resolved()
774 }
775}
776
777impl Analyzable for TupleConstructor {
778 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
779 self.elements.analyze(parent)
780 }
781
782 fn is_resolved(&self) -> bool {
783 self.elements.is_resolved()
784 }
785}
786
787impl Analyzable for MapField {
788 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
789 self.key.analyze(parent.clone()) + self.value.analyze(parent.clone())
790 }
791
792 fn is_resolved(&self) -> bool {
793 self.key.is_resolved() && self.value.is_resolved()
794 }
795}
796
797impl Analyzable for MapConstructor {
798 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
799 self.fields.analyze(parent)
800 }
801
802 fn is_resolved(&self) -> bool {
803 self.fields.is_resolved()
804 }
805}
806
807impl Analyzable for DataExpr {
808 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
809 match self {
810 DataExpr::StructConstructor(x) => x.analyze(parent),
811 DataExpr::ListConstructor(x) => x.analyze(parent),
812 DataExpr::MapConstructor(x) => x.analyze(parent),
813 DataExpr::TupleConstructor(x) => x.analyze(parent),
814 DataExpr::Identifier(x) => x.analyze(parent),
815 DataExpr::AddOp(x) => x.analyze(parent),
816 DataExpr::SubOp(x) => x.analyze(parent),
817 DataExpr::MulOp(x) => x.analyze(parent),
818 DataExpr::DivOp(x) => x.analyze(parent),
819 DataExpr::NegateOp(x) => x.analyze(parent),
820 DataExpr::PropertyOp(x) => x.analyze(parent),
821 DataExpr::AnyAssetConstructor(x) => x.analyze(parent),
822 DataExpr::FnCall(x) => x.analyze(parent),
823 DataExpr::ConcatOp(x) => x.analyze(parent),
824 _ => AnalyzeReport::default(),
825 }
826 }
827
828 fn is_resolved(&self) -> bool {
829 match self {
830 DataExpr::StructConstructor(x) => x.is_resolved(),
831 DataExpr::ListConstructor(x) => x.is_resolved(),
832 DataExpr::MapConstructor(x) => x.is_resolved(),
833 DataExpr::TupleConstructor(x) => x.is_resolved(),
834 DataExpr::Identifier(x) => x.is_resolved(),
835 DataExpr::AddOp(x) => x.is_resolved(),
836 DataExpr::SubOp(x) => x.is_resolved(),
837 DataExpr::MulOp(x) => x.is_resolved(),
838 DataExpr::DivOp(x) => x.is_resolved(),
839 DataExpr::NegateOp(x) => x.is_resolved(),
840 DataExpr::PropertyOp(x) => x.is_resolved(),
841 DataExpr::AnyAssetConstructor(x) => x.is_resolved(),
842 DataExpr::FnCall(x) => x.is_resolved(),
843 DataExpr::ConcatOp(x) => x.is_resolved(),
844 _ => true,
845 }
846 }
847}
848
849impl Analyzable for crate::ast::FnCall {
850 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
851 let callee = self.callee.analyze(parent.clone());
852
853 let mut args_report = AnalyzeReport::default();
854
855 for arg in &mut self.args {
856 args_report = args_report + arg.analyze(parent.clone());
857 }
858
859 let mut report = callee + args_report;
860
861 let signature = self
866 .callee
867 .symbol
868 .as_ref()
869 .and_then(|s| s.as_fn_def())
870 .map(|fn_def| {
871 (
872 fn_def.name.value.clone(),
873 fn_def.parameters.parameters.len(),
874 )
875 });
876
877 if let Some((name, expected)) = signature {
878 let got = self.args.len();
879 if expected != got {
880 report = report + Error::arity(name, expected, got, self).into();
881 }
882 }
883
884 report
885 }
886
887 fn is_resolved(&self) -> bool {
888 self.callee.is_resolved() && self.args.iter().all(|arg| arg.is_resolved())
889 }
890}
891
892impl Analyzable for AnyAssetConstructor {
893 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
894 let policy = self.policy.analyze(parent.clone());
895 let asset_name = self.asset_name.analyze(parent.clone());
896 let amount = self.amount.analyze(parent.clone());
897
898 policy + asset_name + amount
899 }
900
901 fn is_resolved(&self) -> bool {
902 self.policy.is_resolved() && self.asset_name.is_resolved() && self.amount.is_resolved()
903 }
904}
905
906impl Analyzable for PropertyOp {
907 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
908 let object = self.operand.analyze(parent.clone());
909
910 let mut scope = Scope::new(parent);
911
912 if let Some(ty) = self.operand.target_type() {
913 scope.track_record_fields_for_type(&ty);
914 }
915
916 self.scope = Some(Rc::new(scope));
917
918 let path = self.property.analyze(self.scope.clone());
919
920 object + path
921 }
922
923 fn is_resolved(&self) -> bool {
924 self.operand.is_resolved() && self.property.is_resolved()
925 }
926}
927
928impl Analyzable for AddressExpr {
929 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
930 match self {
931 AddressExpr::Identifier(x) => x.analyze(parent),
932 _ => AnalyzeReport::default(),
933 }
934 }
935
936 fn is_resolved(&self) -> bool {
937 match self {
938 AddressExpr::Identifier(x) => x.is_resolved(),
939 _ => true,
940 }
941 }
942}
943
944impl Analyzable for AssetDef {
945 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
946 let policy = self.policy.analyze(parent.clone());
947 let asset_name = self.asset_name.analyze(parent.clone());
948
949 let policy_type = AnalyzeReport::expect_data_expr_type(&self.policy, &Type::Bytes);
950 let asset_name_type = AnalyzeReport::expect_data_expr_type(&self.asset_name, &Type::Bytes);
951
952 policy + asset_name + policy_type + asset_name_type
953 }
954
955 fn is_resolved(&self) -> bool {
956 self.policy.is_resolved() && self.asset_name.is_resolved()
957 }
958}
959
960impl Analyzable for Identifier {
961 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
962 let symbol = parent.and_then(|p| p.resolve(&self.value));
963
964 if symbol.is_none() {
965 bail_report!(Error::not_in_scope(self.value.clone(), self));
966 }
967
968 self.symbol = symbol;
969
970 AnalyzeReport::default()
971 }
972
973 fn is_resolved(&self) -> bool {
974 self.symbol.is_some()
975 }
976}
977
978impl Analyzable for Type {
979 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
980 match self {
981 Type::Custom(x) => x.analyze(parent),
982 Type::List(x) => x.analyze(parent),
983 Type::Map(key_type, value_type) => {
984 key_type.analyze(parent.clone()) + value_type.analyze(parent)
985 }
986 Type::Tuple(elements) => elements.analyze(parent),
987 _ => AnalyzeReport::default(),
988 }
989 }
990
991 fn is_resolved(&self) -> bool {
992 match self {
993 Type::Custom(x) => x.is_resolved(),
994 Type::List(x) => x.is_resolved(),
995 Type::Map(key_type, value_type) => key_type.is_resolved() && value_type.is_resolved(),
996 Type::Tuple(elements) => elements.iter().all(|t| t.is_resolved()),
997 _ => true,
998 }
999 }
1000}
1001
1002impl Analyzable for InputBlockField {
1003 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1004 match self {
1005 InputBlockField::From(x) => x.analyze(parent),
1006 InputBlockField::DatumIs(x) => x.analyze(parent),
1007 InputBlockField::MinAmount(x) => x.analyze(parent),
1008 InputBlockField::Redeemer(x) => x.analyze(parent),
1009 InputBlockField::Ref(x) => x.analyze(parent),
1010 }
1011 }
1012
1013 fn is_resolved(&self) -> bool {
1014 match self {
1015 InputBlockField::From(x) => x.is_resolved(),
1016 InputBlockField::DatumIs(x) => x.is_resolved(),
1017 InputBlockField::MinAmount(x) => x.is_resolved(),
1018 InputBlockField::Redeemer(x) => x.is_resolved(),
1019 InputBlockField::Ref(x) => x.is_resolved(),
1020 }
1021 }
1022}
1023
1024impl Analyzable for InputBlock {
1025 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1026 self.fields.analyze(parent)
1027 }
1028
1029 fn is_resolved(&self) -> bool {
1030 self.fields.is_resolved()
1031 }
1032}
1033
1034fn validate_metadata_value_size(expr: &DataExpr) -> Result<(), MetadataSizeLimitError> {
1035 match expr {
1036 DataExpr::String(string_literal) => {
1037 let utf8_bytes = string_literal.value.as_bytes();
1038 if utf8_bytes.len() > METADATA_MAX_SIZE_BYTES {
1039 return Err(MetadataSizeLimitError {
1040 size: utf8_bytes.len(),
1041 src: None,
1042 span: string_literal.span.clone(),
1043 });
1044 }
1045 }
1046 DataExpr::HexString(hex_literal) => {
1047 let hex_str = &hex_literal.value;
1048 let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
1049 let byte_length = hex_str.len() / 2;
1050
1051 if byte_length > METADATA_MAX_SIZE_BYTES {
1052 return Err(MetadataSizeLimitError {
1053 size: byte_length,
1054 src: None,
1055 span: hex_literal.span.clone(),
1056 });
1057 }
1058 }
1059 _ => {}
1060 }
1061 Ok(())
1062}
1063
1064fn validate_metadata_key_type(expr: &DataExpr) -> Result<(), MetadataInvalidKeyTypeError> {
1065 match expr {
1066 DataExpr::Number(_) => Ok(()),
1067 DataExpr::Identifier(id) => match id.target_type() {
1068 Some(Type::Int) => Ok(()),
1069 Some(other_type) => Err(MetadataInvalidKeyTypeError {
1070 key_type: format!("identifier of type {}", other_type),
1071 src: None,
1072 span: id.span().clone(),
1073 }),
1074 None => Err(MetadataInvalidKeyTypeError {
1075 key_type: "unresolved identifier".to_string(),
1076 src: None,
1077 span: id.span().clone(),
1078 }),
1079 },
1080 _ => {
1081 let key_type = match expr {
1082 DataExpr::String(_) => "string",
1083 DataExpr::HexString(_) => "hex string",
1084 DataExpr::ListConstructor(_) => "list",
1085 DataExpr::MapConstructor(_) => "map",
1086 DataExpr::StructConstructor(_) => "struct",
1087 _ => "unknown",
1088 };
1089
1090 Err(MetadataInvalidKeyTypeError {
1091 key_type: key_type.to_string(),
1092 src: None,
1093 span: expr.span().clone(),
1094 })
1095 }
1096 }
1097}
1098
1099impl Analyzable for MetadataBlockField {
1100 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1101 let mut report = self.key.analyze(parent.clone()) + self.value.analyze(parent.clone());
1102
1103 if let Some(e) = validate_metadata_key_type(&self.key)
1104 .map_err(Error::MetadataInvalidKeyType)
1105 .err()
1106 {
1107 report.errors.push(e)
1108 }
1109
1110 if let Some(e) = validate_metadata_value_size(&self.value)
1111 .map_err(Error::MetadataSizeLimitExceeded)
1112 .err()
1113 {
1114 report.errors.push(e)
1115 }
1116
1117 report
1118 }
1119
1120 fn is_resolved(&self) -> bool {
1121 self.key.is_resolved() && self.value.is_resolved()
1122 }
1123}
1124
1125impl Analyzable for MetadataBlock {
1126 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1127 self.fields.analyze(parent)
1128 }
1129
1130 fn is_resolved(&self) -> bool {
1131 self.fields.is_resolved()
1132 }
1133}
1134
1135impl Analyzable for ValidityBlockField {
1136 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1137 match self {
1138 ValidityBlockField::SinceSlot(x) => x.analyze(parent),
1139 ValidityBlockField::UntilSlot(x) => x.analyze(parent),
1140 }
1141 }
1142 fn is_resolved(&self) -> bool {
1143 match self {
1144 ValidityBlockField::SinceSlot(x) => x.is_resolved(),
1145 ValidityBlockField::UntilSlot(x) => x.is_resolved(),
1146 }
1147 }
1148}
1149
1150impl Analyzable for ValidityBlock {
1151 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1152 self.fields.analyze(parent)
1153 }
1154
1155 fn is_resolved(&self) -> bool {
1156 self.fields.is_resolved()
1157 }
1158}
1159
1160impl Analyzable for OutputBlockField {
1161 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1162 match self {
1163 OutputBlockField::To(x) => x.analyze(parent),
1164 OutputBlockField::Amount(x) => x.analyze(parent),
1165 OutputBlockField::Datum(x) => x.analyze(parent),
1166 }
1167 }
1168
1169 fn is_resolved(&self) -> bool {
1170 match self {
1171 OutputBlockField::To(x) => x.is_resolved(),
1172 OutputBlockField::Amount(x) => x.is_resolved(),
1173 OutputBlockField::Datum(x) => x.is_resolved(),
1174 }
1175 }
1176}
1177
1178impl Analyzable for OutputBlock {
1179 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1180 validate_optional_output(self)
1181 .map(AnalyzeReport::from)
1182 .unwrap_or_default()
1183 + self.fields.analyze(parent)
1184 }
1185
1186 fn is_resolved(&self) -> bool {
1187 self.fields.is_resolved()
1188 }
1189}
1190
1191fn validate_optional_output(output: &OutputBlock) -> Option<Error> {
1192 if output.optional {
1193 if let Some(_field) = output.find("datum") {
1194 return Some(Error::InvalidOptionalOutput(OptionalOutputError {
1195 name: output
1196 .name
1197 .as_ref()
1198 .map(|i| i.value.clone())
1199 .unwrap_or_else(|| "<anonymous>".to_string()),
1200 src: None,
1201 span: output.span.clone(),
1202 }));
1203 }
1204 }
1205
1206 None
1207}
1208
1209impl Analyzable for RecordField {
1210 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1211 self.r#type.analyze(parent)
1212 }
1213
1214 fn is_resolved(&self) -> bool {
1215 self.r#type.is_resolved()
1216 }
1217}
1218
1219impl Analyzable for VariantCase {
1220 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1221 self.fields.analyze(parent)
1222 }
1223
1224 fn is_resolved(&self) -> bool {
1225 self.fields.is_resolved()
1226 }
1227}
1228
1229impl Analyzable for AliasDef {
1230 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1231 self.alias_type.analyze(parent)
1232 }
1233
1234 fn is_resolved(&self) -> bool {
1235 self.alias_type.is_resolved() && self.is_alias_chain_resolved()
1236 }
1237}
1238
1239impl Analyzable for TypeDef {
1240 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1241 self.cases.analyze(parent)
1242 }
1243
1244 fn is_resolved(&self) -> bool {
1245 self.cases.is_resolved()
1246 }
1247}
1248
1249impl Analyzable for MintBlockField {
1250 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1251 match self {
1252 MintBlockField::Amount(x) => x.analyze(parent),
1253 MintBlockField::Redeemer(x) => x.analyze(parent),
1254 }
1255 }
1256
1257 fn is_resolved(&self) -> bool {
1258 match self {
1259 MintBlockField::Amount(x) => x.is_resolved(),
1260 MintBlockField::Redeemer(x) => x.is_resolved(),
1261 }
1262 }
1263}
1264
1265impl Analyzable for MintBlock {
1266 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1267 self.fields.analyze(parent)
1268 }
1269
1270 fn is_resolved(&self) -> bool {
1271 self.fields.is_resolved()
1272 }
1273}
1274
1275impl Analyzable for SignersBlock {
1276 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1277 self.signers.analyze(parent)
1278 }
1279
1280 fn is_resolved(&self) -> bool {
1281 self.signers.is_resolved()
1282 }
1283}
1284
1285impl Analyzable for ReferenceBlock {
1286 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1287 self.r#ref.analyze(parent.clone()) + self.datum_is.analyze(parent)
1288 }
1289
1290 fn is_resolved(&self) -> bool {
1291 self.r#ref.is_resolved() && self.datum_is.is_resolved()
1292 }
1293}
1294
1295impl Analyzable for CollateralBlockField {
1296 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1297 match self {
1298 CollateralBlockField::From(x) => x.analyze(parent),
1299 CollateralBlockField::MinAmount(x) => x.analyze(parent),
1300 CollateralBlockField::Ref(x) => x.analyze(parent),
1301 }
1302 }
1303
1304 fn is_resolved(&self) -> bool {
1305 match self {
1306 CollateralBlockField::From(x) => x.is_resolved(),
1307 CollateralBlockField::MinAmount(x) => x.is_resolved(),
1308 CollateralBlockField::Ref(x) => x.is_resolved(),
1309 }
1310 }
1311}
1312
1313impl Analyzable for CollateralBlock {
1314 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1315 self.fields.analyze(parent)
1316 }
1317
1318 fn is_resolved(&self) -> bool {
1319 self.fields.is_resolved()
1320 }
1321}
1322
1323impl Analyzable for ChainSpecificBlock {
1324 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1325 match self {
1326 ChainSpecificBlock::Cardano(x) => x.analyze(parent),
1327 }
1328 }
1329
1330 fn is_resolved(&self) -> bool {
1331 match self {
1332 ChainSpecificBlock::Cardano(x) => x.is_resolved(),
1333 }
1334 }
1335}
1336
1337impl Analyzable for LocalsAssign {
1338 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1339 self.value.analyze(parent)
1340 }
1341
1342 fn is_resolved(&self) -> bool {
1343 self.value.is_resolved()
1344 }
1345}
1346
1347impl Analyzable for LocalsBlock {
1348 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1349 self.assigns.analyze(parent)
1350 }
1351
1352 fn is_resolved(&self) -> bool {
1353 self.assigns.is_resolved()
1354 }
1355}
1356
1357impl Analyzable for LetBinding {
1358 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1359 self.value.analyze(parent)
1360 }
1361
1362 fn is_resolved(&self) -> bool {
1363 self.value.is_resolved()
1364 }
1365}
1366
1367impl Analyzable for FnBody {
1368 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1369 let mut report = AnalyzeReport::default();
1370
1371 for binding in &mut self.let_bindings {
1372 report = report + binding.analyze(parent.clone());
1373 }
1374
1375 report = report + self.result.analyze(parent);
1376
1377 report
1378 }
1379
1380 fn is_resolved(&self) -> bool {
1381 self.let_bindings.is_resolved() && self.result.is_resolved()
1382 }
1383}
1384
1385impl Analyzable for FnDef {
1386 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1387 let params_report = self.parameters.analyze(parent.clone());
1388 let return_type_report = self.return_type.analyze(parent.clone());
1389
1390 let body = match &mut self.body {
1392 Some(body) => body,
1393 None => return params_report + return_type_report,
1394 };
1395
1396 let mut scope = Scope::new(parent);
1397
1398 for param in self.parameters.parameters.iter() {
1399 scope.track_param_var(¶m.name.value, param.r#type.clone());
1400 }
1401
1402 let mut current_scope = Rc::new(scope);
1404
1405 let mut bindings_report = AnalyzeReport::default();
1406
1407 for binding in &mut body.let_bindings {
1408 bindings_report = bindings_report + binding.analyze(Some(current_scope.clone()));
1409 let mut next_scope = Scope::new(Some(current_scope));
1411 next_scope.track_local_expr(&binding.name.value, binding.value.clone());
1412 current_scope = Rc::new(next_scope);
1413 }
1414
1415 let result_report = body.result.analyze(Some(current_scope.clone()));
1416
1417 self.scope = Some(current_scope);
1418
1419 params_report + return_type_report + bindings_report + result_report
1420 }
1421
1422 fn is_resolved(&self) -> bool {
1423 self.parameters.is_resolved() && self.body.as_ref().is_none_or(|b| b.is_resolved())
1424 }
1425}
1426
1427impl Analyzable for ParamDef {
1428 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1429 self.r#type.analyze(parent)
1430 }
1431
1432 fn is_resolved(&self) -> bool {
1433 self.r#type.is_resolved()
1434 }
1435}
1436
1437impl Analyzable for ParameterList {
1438 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1439 self.parameters.analyze(parent)
1440 }
1441
1442 fn is_resolved(&self) -> bool {
1443 self.parameters.is_resolved()
1444 }
1445}
1446
1447impl TxDef {
1448 fn best_effort_analyze_circular_dependencies(&mut self, mut scope: Scope) -> Scope {
1450 if let Some(locals) = &self.locals {
1451 for assign in locals.assigns.iter() {
1452 scope.track_local_expr(&assign.name.value, assign.value.clone());
1453 }
1454 }
1455
1456 for input in self.inputs.iter() {
1457 scope.track_input(&input.name, input.clone())
1458 }
1459
1460 for reference in self.references.iter() {
1461 scope.track_reference(&reference.name, reference.clone());
1462 }
1463
1464 for (index, output) in self.outputs.iter().enumerate() {
1465 scope.track_output(index, output.clone())
1466 }
1467
1468 let scope_snapshot = Rc::new(scope);
1469 let _ = self.locals.analyze(Some(scope_snapshot.clone()));
1470 let _ = self.references.analyze(Some(scope_snapshot.clone()));
1471 let _ = self.inputs.analyze(Some(scope_snapshot.clone()));
1472 let _ = self.outputs.analyze(Some(scope_snapshot.clone()));
1473
1474 Scope::new(Some(scope_snapshot))
1475 }
1476}
1477
1478impl Analyzable for TxDef {
1479 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1480 let params = self.parameters.analyze(parent.clone());
1482
1483 let mut scope = Scope::new(parent.clone());
1486
1487 scope.symbols.insert("fees".to_string(), Symbol::Fees);
1488
1489 for param in self.parameters.parameters.iter() {
1490 scope.track_param_var(¶m.name.value, param.r#type.clone());
1491 }
1492
1493 for _ in 0..9 {
1494 scope = self.best_effort_analyze_circular_dependencies(scope);
1495 }
1496
1497 let final_scope = Rc::new(scope);
1498
1499 let locals = self.locals.analyze(Some(final_scope.clone()));
1500 let inputs = self.inputs.analyze(Some(final_scope.clone()));
1501 let outputs = self.outputs.analyze(Some(final_scope.clone()));
1502 let mints = self.mints.analyze(Some(final_scope.clone()));
1503 let burns = self.burns.analyze(Some(final_scope.clone()));
1504 let adhoc = self.adhoc.analyze(Some(final_scope.clone()));
1505 let validity = self.validity.analyze(Some(final_scope.clone()));
1506 let metadata = self.metadata.analyze(Some(final_scope.clone()));
1507 let signers = self.signers.analyze(Some(final_scope.clone()));
1508 let references = self.references.analyze(Some(final_scope.clone()));
1509 let collateral = self.collateral.analyze(Some(final_scope.clone()));
1510
1511 self.scope = Some(final_scope);
1512
1513 params
1514 + locals
1515 + inputs
1516 + outputs
1517 + mints
1518 + burns
1519 + adhoc
1520 + validity
1521 + metadata
1522 + signers
1523 + references
1524 + collateral
1525 }
1526
1527 fn is_resolved(&self) -> bool {
1528 self.inputs.is_resolved()
1529 && self.outputs.is_resolved()
1530 && self.mints.is_resolved()
1531 && self.locals.is_resolved()
1532 && self.adhoc.is_resolved()
1533 && self.validity.is_resolved()
1534 && self.metadata.is_resolved()
1535 && self.signers.is_resolved()
1536 && self.references.is_resolved()
1537 && self.collateral.is_resolved()
1538 }
1539}
1540
1541fn ada_asset_def() -> AssetDef {
1542 AssetDef {
1543 name: Identifier {
1544 value: "Ada".to_string(),
1545 symbol: None,
1546 span: Span::DUMMY,
1547 },
1548 policy: DataExpr::None,
1549 asset_name: DataExpr::None,
1550 span: Span::DUMMY,
1551 }
1552}
1553
1554fn resolve_types_and_aliases(
1555 scope_rc: &mut Rc<Scope>,
1556 types: &mut Vec<TypeDef>,
1557 aliases: &mut Vec<AliasDef>,
1558) -> (AnalyzeReport, AnalyzeReport) {
1559 let mut types_report = AnalyzeReport::default();
1560 let mut aliases_report = AnalyzeReport::default();
1561
1562 let mut pass_count = 0usize;
1563 let max_passes = 100usize; while pass_count < max_passes && !(types.is_resolved() && aliases.is_resolved()) {
1566 pass_count += 1;
1567
1568 let scope = Rc::get_mut(scope_rc).expect("scope should be unique during resolution");
1569
1570 for type_def in types.iter() {
1571 scope.track_type_def(type_def);
1572 }
1573 for alias_def in aliases.iter() {
1574 scope.track_alias_def(alias_def);
1575 }
1576
1577 types_report = types.analyze(Some(scope_rc.clone()));
1578 aliases_report = aliases.analyze(Some(scope_rc.clone()));
1579 }
1580
1581 (types_report, aliases_report)
1582}
1583
1584impl Analyzable for Program {
1585 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1586 let mut scope = Scope::new(parent);
1587
1588 if let Some(env) = self.env.as_ref() {
1589 for field in env.fields.iter() {
1590 scope.track_env_var(&field.name, field.r#type.clone());
1591 }
1592 }
1593
1594 for party in self.parties.iter() {
1595 scope.track_party_def(party);
1596 }
1597
1598 for policy in self.policies.iter() {
1599 scope.track_policy_def(policy);
1600 }
1601
1602 scope.track_asset_def(&ada_asset_def());
1603
1604 for asset in self.assets.iter() {
1605 scope.track_asset_def(asset);
1606 }
1607
1608 for type_def in self.types.iter() {
1609 scope.track_type_def(type_def);
1610 }
1611
1612 for alias_def in self.aliases.iter() {
1613 scope.track_alias_def(alias_def);
1614 }
1615
1616 for builtin in crate::builtins::all() {
1617 scope.track_fn_def(&builtin.definition());
1618 }
1619
1620 for fn_def in self.functions.iter() {
1621 scope.track_fn_def(fn_def);
1622 }
1623
1624 self.scope = Some(Rc::new(scope));
1625
1626 let parties = self.parties.analyze(self.scope.clone());
1627
1628 let policies = self.policies.analyze(self.scope.clone());
1629
1630 let assets = self.assets.analyze(self.scope.clone());
1631
1632 let mut types = self.types.clone();
1633 let mut aliases = self.aliases.clone();
1634
1635 let scope_rc = self.scope.as_mut().unwrap();
1636
1637 {
1643 let scope = Rc::get_mut(scope_rc).expect("scope should be unique during resolution");
1644 for policy in self.policies.iter() {
1645 scope.track_policy_def(policy);
1646 }
1647 }
1648
1649 let (types, aliases) = resolve_types_and_aliases(scope_rc, &mut types, &mut aliases);
1650
1651 let program_scope = self.scope.clone();
1660 let mut functions = AnalyzeReport::default();
1661 for _ in 0..self.functions.len() {
1662 let mut fn_scope = Scope::new(program_scope.clone());
1663 for fn_def in self.functions.iter() {
1664 fn_scope.track_fn_def(fn_def);
1665 }
1666 functions = self.functions.analyze(Some(Rc::new(fn_scope)));
1667 }
1668
1669 let mut fn_scope = Scope::new(program_scope);
1671 for fn_def in self.functions.iter() {
1672 fn_scope.track_fn_def(fn_def);
1673 }
1674 self.scope = Some(Rc::new(fn_scope));
1675
1676 let txs = self.txs.analyze(self.scope.clone());
1677
1678 parties + policies + types + aliases + functions + txs + assets
1679 }
1680
1681 fn is_resolved(&self) -> bool {
1682 self.policies.is_resolved()
1683 && self.types.is_resolved()
1684 && self.aliases.is_resolved()
1685 && self.functions.is_resolved()
1686 && self.txs.is_resolved()
1687 && self.assets.is_resolved()
1688 }
1689}
1690
1691pub fn analyze(ast: &mut Program) -> AnalyzeReport {
1705 ast.analyze(None)
1706}
1707
1708#[cfg(test)]
1709mod tests {
1710 use crate::parsing::{parse_string, parse_well_known_example};
1711
1712 use super::*;
1713
1714 #[test]
1719 fn policy_def_fields_resolved_in_symbol_table() {
1720 let mut program = parse_string(
1721 r#"
1722 env {
1723 policy_hash: Bytes,
1724 script_ref: UtxoRef,
1725 }
1726
1727 policy P {
1728 hash: policy_hash,
1729 ref: script_ref,
1730 }
1731 "#,
1732 )
1733 .unwrap();
1734
1735 analyze(&mut program).ok().unwrap();
1736
1737 let symbol = program
1738 .scope
1739 .as_ref()
1740 .unwrap()
1741 .resolve("P")
1742 .expect("policy P should be in scope");
1743
1744 match symbol {
1745 Symbol::PolicyDef(policy) => assert!(
1746 policy.is_resolved(),
1747 "policy stored in the symbol table should have resolved fields"
1748 ),
1749 other => panic!("expected PolicyDef, got {other:?}"),
1750 }
1751 }
1752
1753 #[test]
1754 fn test_program_with_semantic_errors() {
1755 let mut ast = parse_well_known_example("semantic_errors");
1756
1757 let report = analyze(&mut ast);
1758
1759 assert_eq!(report.errors.len(), 3);
1760
1761 assert_eq!(
1762 report.errors[0],
1763 Error::NotInScope(NotInScopeError {
1764 name: "missing_symbol".to_string(),
1765 src: None,
1766 span: Span::DUMMY,
1767 })
1768 );
1769
1770 assert_eq!(
1771 report.errors[1],
1772 Error::InvalidTargetType(InvalidTargetTypeError {
1773 expected: "Bytes".to_string(),
1774 got: "Int".to_string(),
1775 src: None,
1776 span: Span::DUMMY,
1777 })
1778 );
1779
1780 assert_eq!(
1781 report.errors[2],
1782 Error::InvalidTargetType(InvalidTargetTypeError {
1783 expected: "Bytes".to_string(),
1784 got: "Int".to_string(),
1785 src: None,
1786 span: Span::DUMMY,
1787 })
1788 );
1789 }
1790
1791 #[test]
1792 fn test_min_utxo_analysis() {
1793 let mut ast = crate::parsing::parse_string(
1794 r#"
1795 party Alice;
1796 tx test() {
1797 output my_output {
1798 to: Alice,
1799 amount: min_utxo(my_output),
1800 }
1801 }
1802 "#,
1803 )
1804 .unwrap();
1805
1806 let result = analyze(&mut ast);
1807 assert!(result.errors.is_empty());
1808 }
1809
1810 #[test]
1811 fn test_alias_undefined_type_error() {
1812 let mut ast = crate::parsing::parse_string(
1813 r#"
1814 type MyAlias = UndefinedType;
1815 "#,
1816 )
1817 .unwrap();
1818
1819 let result = analyze(&mut ast);
1820
1821 assert!(!result.errors.is_empty());
1822 assert!(result
1823 .errors
1824 .iter()
1825 .any(|e| matches!(e, Error::NotInScope(_))));
1826 }
1827
1828 #[test]
1829 fn test_alias_valid_type_success() {
1830 let mut ast = crate::parsing::parse_string(
1831 r#"
1832 type Address = Bytes;
1833 type Amount = Int;
1834 type ValidAlias = Address;
1835 "#,
1836 )
1837 .unwrap();
1838
1839 let result = analyze(&mut ast);
1840
1841 assert!(result.errors.is_empty());
1842 }
1843
1844 #[test]
1845 fn test_min_utxo_undefined_output_error() {
1846 let mut ast = crate::parsing::parse_string(
1847 r#"
1848 party Alice;
1849 tx test() {
1850 output {
1851 to: Alice,
1852 amount: min_utxo(nonexistent_output),
1853 }
1854 }
1855 "#,
1856 )
1857 .unwrap();
1858
1859 let result = analyze(&mut ast);
1860 assert!(!result.errors.is_empty());
1861 }
1862
1863 #[test]
1864 fn test_time_and_slot_conversion() {
1865 let mut ast = crate::parsing::parse_string(
1866 r#"
1867 party Sender;
1868
1869 type TimestampDatum {
1870 slot_time: Int,
1871 time: Int,
1872 }
1873
1874 tx create_timestamp_tx() {
1875 input source {
1876 from: Sender,
1877 min_amount: Ada(2000000),
1878 }
1879
1880 output timestamp_output {
1881 to: Sender,
1882 amount: source - fees,
1883 datum: TimestampDatum {
1884 slot_time: time_to_slot(1666716638000),
1885 time: slot_to_time(60638),
1886 },
1887 }
1888 }
1889 "#,
1890 )
1891 .unwrap();
1892
1893 let result = analyze(&mut ast);
1894 assert!(result.errors.is_empty());
1895 }
1896
1897 #[test]
1898 fn test_optional_output_with_datum_error() {
1899 let mut ast = crate::parsing::parse_string(
1900 r#"
1901 party Alice;
1902 type MyDatum {
1903 field1: Int,
1904 }
1905 tx test() {
1906 output ? my_output {
1907 to: Alice,
1908 amount: Ada(1),
1909 datum: MyDatum { field1: 1, },
1910 }
1911 }
1912 "#,
1913 )
1914 .unwrap();
1915
1916 let report = analyze(&mut ast);
1917
1918 assert!(!report.errors.is_empty());
1919 assert!(report
1920 .errors
1921 .iter()
1922 .any(|e| matches!(e, Error::InvalidOptionalOutput(_))));
1923 }
1924
1925 #[test]
1926 fn test_optional_output_ok() {
1927 let mut ast = crate::parsing::parse_string(
1928 r#"
1929 party Alice;
1930
1931 tx test() {
1932 output ? my_output {
1933 to: Alice,
1934 amount: Ada(0),
1935 }
1936 }
1937 "#,
1938 )
1939 .unwrap();
1940
1941 let report = analyze(&mut ast);
1942 assert!(report.errors.is_empty());
1943 }
1944
1945 #[test]
1946 fn test_fn_call_too_few_args() {
1947 let mut ast = crate::parsing::parse_string(
1948 r#"
1949 party Alice;
1950
1951 fn double(x: Int) -> Int {
1952 x + x
1953 }
1954
1955 tx t() {
1956 input source {
1957 from: Alice,
1958 min_amount: Ada(2),
1959 }
1960 output {
1961 to: Alice,
1962 amount: Ada(double()),
1963 }
1964 }
1965 "#,
1966 )
1967 .unwrap();
1968
1969 let report = analyze(&mut ast);
1970
1971 assert!(report.errors.iter().any(|e| matches!(
1972 e,
1973 Error::Arity(a) if a.name == "double" && a.expected == 1 && a.got == 0
1974 )));
1975 }
1976
1977 #[test]
1978 fn test_fn_call_too_many_args() {
1979 let mut ast = crate::parsing::parse_string(
1980 r#"
1981 party Alice;
1982
1983 fn double(x: Int) -> Int {
1984 x + x
1985 }
1986
1987 tx t() {
1988 input source {
1989 from: Alice,
1990 min_amount: Ada(2),
1991 }
1992 output {
1993 to: Alice,
1994 amount: Ada(double(1, 2)),
1995 }
1996 }
1997 "#,
1998 )
1999 .unwrap();
2000
2001 let report = analyze(&mut ast);
2002
2003 assert!(report.errors.iter().any(|e| matches!(
2004 e,
2005 Error::Arity(a) if a.name == "double" && a.expected == 1 && a.got == 2
2006 )));
2007 }
2008
2009 #[test]
2010 fn test_fn_call_correct_arity_ok() {
2011 let mut ast = crate::parsing::parse_string(
2012 r#"
2013 party Alice;
2014
2015 fn double(x: Int) -> Int {
2016 x + x
2017 }
2018
2019 tx t() {
2020 input source {
2021 from: Alice,
2022 min_amount: Ada(2),
2023 }
2024 output {
2025 to: Alice,
2026 amount: Ada(double(2)),
2027 }
2028 }
2029 "#,
2030 )
2031 .unwrap();
2032
2033 let report = analyze(&mut ast);
2034 assert!(!report.errors.iter().any(|e| matches!(e, Error::Arity(_))));
2035 }
2036
2037 #[test]
2038 fn test_builtin_call_wrong_arity() {
2039 let mut ast = crate::parsing::parse_string(
2040 r#"
2041 party Alice;
2042
2043 tx t() {
2044 input source {
2045 from: Alice,
2046 min_amount: Ada(2),
2047 }
2048 output {
2049 to: Alice,
2050 amount: min_utxo(),
2051 }
2052 }
2053 "#,
2054 )
2055 .unwrap();
2056
2057 let report = analyze(&mut ast);
2058
2059 assert!(report.errors.iter().any(|e| matches!(
2060 e,
2061 Error::Arity(a) if a.name == "min_utxo" && a.expected == 1 && a.got == 0
2062 )));
2063 }
2064
2065 #[test]
2066 fn test_metadata_value_size_validation_string_within_limit() {
2067 let mut ast = crate::parsing::parse_string(
2068 r#"
2069 tx test() {
2070 metadata {
2071 123: "This is a short string that is within the 64-byte limit",
2072 }
2073 }
2074 "#,
2075 )
2076 .unwrap();
2077
2078 let result = analyze(&mut ast);
2079
2080 assert!(
2081 result.errors.is_empty(),
2082 "Expected no errors for string within limit, but got: {:?}",
2083 result.errors
2084 );
2085 }
2086
2087 #[test]
2088 fn test_metadata_value_size_validation_string_exceeds_limit() {
2089 let mut ast = crate::parsing::parse_string(
2090 r#"
2091 tx test() {
2092 metadata {
2093 123: "This is a very long string that definitely exceeds the 64-byte limit here",
2094 }
2095 }
2096 "#,
2097 )
2098 .unwrap();
2099
2100 let result = analyze(&mut ast);
2101 assert_eq!(result.errors.len(), 1);
2102
2103 match &result.errors[0] {
2104 Error::MetadataSizeLimitExceeded(error) => {
2105 assert_eq!(error.size, 73);
2106 }
2107 _ => panic!(
2108 "Expected MetadataSizeLimitExceeded error, got: {:?}",
2109 result.errors[0]
2110 ),
2111 }
2112 }
2113
2114 #[test]
2115 fn test_metadata_value_size_validation_hex_string_within_limit() {
2116 let mut ast = crate::parsing::parse_string(
2117 r#"
2118 tx test() {
2119 metadata {
2120 123: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef,
2121 }
2122 }
2123 "#,
2124 )
2125 .unwrap();
2126
2127 let result = analyze(&mut ast);
2128 assert!(
2129 result.errors.is_empty(),
2130 "Expected no errors for hex string within limit, but got: {:?}",
2131 result.errors
2132 );
2133 }
2134
2135 #[test]
2136 fn test_metadata_value_size_validation_hex_string_exceeds_limit() {
2137 let mut ast = crate::parsing::parse_string(
2138 r#"
2139 tx test() {
2140 metadata {
2141 123: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12,
2142 }
2143 }
2144 "#,
2145 )
2146 .unwrap();
2147
2148 let result = analyze(&mut ast);
2149 assert_eq!(result.errors.len(), 1);
2150
2151 match &result.errors[0] {
2152 Error::MetadataSizeLimitExceeded(error) => {
2153 assert_eq!(error.size, 65);
2154 }
2155 _ => panic!(
2156 "Expected MetadataSizeLimitExceeded error, got: {:?}",
2157 result.errors[0]
2158 ),
2159 }
2160 }
2161
2162 #[test]
2163 fn test_metadata_value_size_validation_multiple_fields() {
2164 let mut ast = crate::parsing::parse_string(
2165 r#"
2166 tx test() {
2167 metadata {
2168 123: "Short string",
2169 456: "This is a very long string that definitely exceeds the 64-byte limit here",
2170 789: "Another short one",
2171 }
2172 }
2173 "#,
2174 )
2175 .unwrap();
2176
2177 let result = analyze(&mut ast);
2178 assert_eq!(result.errors.len(), 1);
2179
2180 match &result.errors[0] {
2181 Error::MetadataSizeLimitExceeded(error) => {
2182 assert_eq!(error.size, 73);
2183 }
2184 _ => panic!(
2185 "Expected MetadataSizeLimitExceeded error, got: {:?}",
2186 result.errors[0]
2187 ),
2188 }
2189 }
2190
2191 #[test]
2192 fn test_metadata_value_size_validation_non_literal_expression() {
2193 let mut ast = crate::parsing::parse_string(
2194 r#"
2195 party Alice;
2196
2197 tx test(my_param: Bytes) {
2198 metadata {
2199 123: my_param,
2200 }
2201 }
2202 "#,
2203 )
2204 .unwrap();
2205
2206 let result = analyze(&mut ast);
2207 let metadata_errors: Vec<_> = result
2208 .errors
2209 .iter()
2210 .filter(|e| matches!(e, Error::MetadataSizeLimitExceeded(_)))
2211 .collect();
2212 assert!(
2213 metadata_errors.is_empty(),
2214 "Expected no metadata size errors for non-literal expressions"
2215 );
2216 }
2217
2218 #[test]
2219 fn test_metadata_key_type_validation_string_key() {
2220 let mut ast = crate::parsing::parse_string(
2221 r#"
2222 tx test() {
2223 metadata {
2224 "invalid_key": "some value",
2225 }
2226 }
2227 "#,
2228 )
2229 .unwrap();
2230
2231 let result = analyze(&mut ast);
2232 assert_eq!(result.errors.len(), 1);
2233
2234 match &result.errors[0] {
2235 Error::MetadataInvalidKeyType(error) => {
2236 assert_eq!(error.key_type, "string");
2237 }
2238 _ => panic!(
2239 "Expected MetadataInvalidKeyType error, got: {:?}",
2240 result.errors[0]
2241 ),
2242 }
2243 }
2244
2245 #[test]
2246 fn test_metadata_key_type_validation_identifier_with_int_type() {
2247 let mut ast = crate::parsing::parse_string(
2248 r#"
2249 tx test(my_key: Int) {
2250 metadata {
2251 my_key: "valid value",
2252 }
2253 }
2254 "#,
2255 )
2256 .unwrap();
2257
2258 let result = analyze(&mut ast);
2259 let key_type_errors: Vec<_> = result
2260 .errors
2261 .iter()
2262 .filter(|e| matches!(e, Error::MetadataInvalidKeyType(_)))
2263 .collect();
2264 assert!(
2265 key_type_errors.is_empty(),
2266 "Expected no key type errors for Int parameter used as key"
2267 );
2268 }
2269
2270 #[test]
2271 fn test_metadata_key_type_validation_identifier_with_wrong_type() {
2272 let mut ast = crate::parsing::parse_string(
2273 r#"
2274 tx test(my_key: Bytes) {
2275 metadata {
2276 my_key: "some value",
2277 }
2278 }
2279 "#,
2280 )
2281 .unwrap();
2282
2283 let result = analyze(&mut ast);
2284 assert_eq!(result.errors.len(), 1);
2285
2286 match &result.errors[0] {
2287 Error::MetadataInvalidKeyType(error) => {
2288 assert!(error.key_type.contains("identifier of type Bytes"));
2289 }
2290 _ => panic!(
2291 "Expected MetadataInvalidKeyType error, got: {:?}",
2292 result.errors[0]
2293 ),
2294 }
2295 }
2296}