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 schema = ty.properties();
444
445 for (name, subty) in schema {
446 self.track_record_field(&RecordField {
447 name: Identifier::new(name),
448 r#type: subty,
449 span: Span::DUMMY,
450 });
451 }
452 }
453
454 pub fn resolve(&self, name: &str) -> Option<Symbol> {
455 if let Some(symbol) = self.symbols.get(name) {
456 Some(symbol.clone())
457 } else if let Some(parent) = &self.parent {
458 parent.resolve(name)
459 } else {
460 None
461 }
462 }
463}
464
465pub trait Analyzable {
470 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport;
478
479 fn is_resolved(&self) -> bool;
481}
482
483impl<T: Analyzable> Analyzable for Option<T> {
484 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
485 if let Some(item) = self {
486 item.analyze(parent)
487 } else {
488 AnalyzeReport::default()
489 }
490 }
491
492 fn is_resolved(&self) -> bool {
493 self.as_ref().is_none_or(|x| x.is_resolved())
494 }
495}
496
497impl<T: Analyzable> Analyzable for Box<T> {
498 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
499 self.as_mut().analyze(parent)
500 }
501
502 fn is_resolved(&self) -> bool {
503 self.as_ref().is_resolved()
504 }
505}
506
507impl<T: Analyzable> Analyzable for Vec<T> {
508 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
509 self.iter_mut()
510 .map(|item| item.analyze(parent.clone()))
511 .collect()
512 }
513
514 fn is_resolved(&self) -> bool {
515 self.iter().all(|x| x.is_resolved())
516 }
517}
518
519impl Analyzable for PartyDef {
520 fn analyze(&mut self, _parent: Option<Rc<Scope>>) -> AnalyzeReport {
521 AnalyzeReport::default()
522 }
523
524 fn is_resolved(&self) -> bool {
525 true
526 }
527}
528
529impl Analyzable for PolicyField {
530 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
531 match self {
532 PolicyField::Hash(x) => x.analyze(parent),
533 PolicyField::Script(x) => x.analyze(parent),
534 PolicyField::Ref(x) => x.analyze(parent),
535 }
536 }
537
538 fn is_resolved(&self) -> bool {
539 match self {
540 PolicyField::Hash(x) => x.is_resolved(),
541 PolicyField::Script(x) => x.is_resolved(),
542 PolicyField::Ref(x) => x.is_resolved(),
543 }
544 }
545}
546impl Analyzable for PolicyConstructor {
547 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
548 self.fields.analyze(parent)
549 }
550
551 fn is_resolved(&self) -> bool {
552 self.fields.is_resolved()
553 }
554}
555
556impl Analyzable for PolicyDef {
557 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
558 match &mut self.value {
559 PolicyValue::Constructor(x) => x.analyze(parent),
560 PolicyValue::Assign(_) => AnalyzeReport::default(),
561 }
562 }
563
564 fn is_resolved(&self) -> bool {
565 match &self.value {
566 PolicyValue::Constructor(x) => x.is_resolved(),
567 PolicyValue::Assign(_) => true,
568 }
569 }
570}
571
572impl Analyzable for AddOp {
573 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
574 let left = self.lhs.analyze(parent.clone());
575 let right = self.rhs.analyze(parent.clone());
576
577 left + right
578 }
579
580 fn is_resolved(&self) -> bool {
581 self.lhs.is_resolved() && self.rhs.is_resolved()
582 }
583}
584
585impl Analyzable for ConcatOp {
586 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
587 let left = self.lhs.analyze(parent.clone());
588 let right = self.rhs.analyze(parent.clone());
589
590 left + right
591 }
592
593 fn is_resolved(&self) -> bool {
594 self.lhs.is_resolved() && self.rhs.is_resolved()
595 }
596}
597
598impl Analyzable for SubOp {
599 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
600 let left = self.lhs.analyze(parent.clone());
601 let right = self.rhs.analyze(parent.clone());
602
603 left + right
604 }
605
606 fn is_resolved(&self) -> bool {
607 self.lhs.is_resolved() && self.rhs.is_resolved()
608 }
609}
610
611impl Analyzable for MulOp {
612 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
613 let left = self.lhs.analyze(parent.clone());
614 let right = self.rhs.analyze(parent.clone());
615
616 left + right
617 }
618
619 fn is_resolved(&self) -> bool {
620 self.lhs.is_resolved() && self.rhs.is_resolved()
621 }
622}
623
624impl Analyzable for DivOp {
625 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
626 let left = self.lhs.analyze(parent.clone());
627 let right = self.rhs.analyze(parent.clone());
628
629 left + right
630 }
631
632 fn is_resolved(&self) -> bool {
633 self.lhs.is_resolved() && self.rhs.is_resolved()
634 }
635}
636
637impl Analyzable for NegateOp {
638 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
639 self.operand.analyze(parent)
640 }
641
642 fn is_resolved(&self) -> bool {
643 self.operand.is_resolved()
644 }
645}
646
647impl Analyzable for RecordConstructorField {
648 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
649 let name = self.name.analyze(parent.clone());
650
651 let outer = parent.as_ref().and_then(|p| p.parent.clone());
654 let value = self.value.analyze(outer);
655
656 name + value
657 }
658
659 fn is_resolved(&self) -> bool {
660 self.name.is_resolved() && self.value.is_resolved()
661 }
662}
663
664impl Analyzable for VariantCaseConstructor {
665 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
666 let name = if self.name.symbol.is_some() {
667 AnalyzeReport::default()
668 } else {
669 self.name.analyze(parent.clone())
670 };
671
672 let mut scope = Scope::new(parent);
673
674 let case = match &self.name.symbol {
675 Some(Symbol::VariantCase(x)) => x,
676 Some(x) => bail_report!(Error::invalid_symbol("VariantCase", x, &self.name)),
677 None => bail_report!(Error::not_in_scope(self.name.value.clone(), &self.name)),
678 };
679
680 for field in case.fields.iter() {
681 scope.track_record_field(field);
682 }
683
684 self.scope = Some(Rc::new(scope));
685
686 let fields = self.fields.analyze(self.scope.clone());
687
688 let spread = self.spread.analyze(self.scope.clone());
689
690 name + fields + spread
691 }
692
693 fn is_resolved(&self) -> bool {
694 self.name.is_resolved() && self.fields.is_resolved() && self.spread.is_resolved()
695 }
696}
697
698impl Analyzable for StructConstructor {
699 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
700 let r#type = self.r#type.analyze(parent.clone());
701
702 let mut scope = Scope::new(parent);
703
704 let type_def = match &self.r#type.symbol {
705 Some(Symbol::TypeDef(type_def)) => type_def.as_ref(),
706 Some(Symbol::AliasDef(alias_def)) => match alias_def.resolve_alias_chain() {
707 Some(resolved_type_def) => resolved_type_def,
708 None => {
709 bail_report!(Error::invalid_symbol(
710 "struct type",
711 &Symbol::AliasDef(alias_def.clone()),
712 &self.r#type
713 ));
714 }
715 },
716 Some(symbol) => {
717 bail_report!(Error::invalid_symbol("struct type", symbol, &self.r#type));
718 }
719 None => {
720 bail_report!(Error::not_in_scope(
721 self.r#type.value.clone(),
722 &self.r#type
723 ));
724 }
725 };
726
727 for case in type_def.cases.iter() {
728 scope.track_variant_case(case);
729 }
730
731 self.scope = Some(Rc::new(scope));
732
733 let case = self.case.analyze(self.scope.clone());
734
735 r#type + case
736 }
737
738 fn is_resolved(&self) -> bool {
739 self.r#type.is_resolved() && self.case.is_resolved()
740 }
741}
742
743impl Analyzable for ListConstructor {
744 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
745 self.elements.analyze(parent)
746 }
747
748 fn is_resolved(&self) -> bool {
749 self.elements.is_resolved()
750 }
751}
752
753impl Analyzable for TupleConstructor {
754 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
755 self.elements.analyze(parent)
756 }
757
758 fn is_resolved(&self) -> bool {
759 self.elements.is_resolved()
760 }
761}
762
763impl Analyzable for MapField {
764 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
765 self.key.analyze(parent.clone()) + self.value.analyze(parent.clone())
766 }
767
768 fn is_resolved(&self) -> bool {
769 self.key.is_resolved() && self.value.is_resolved()
770 }
771}
772
773impl Analyzable for MapConstructor {
774 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
775 self.fields.analyze(parent)
776 }
777
778 fn is_resolved(&self) -> bool {
779 self.fields.is_resolved()
780 }
781}
782
783impl Analyzable for DataExpr {
784 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
785 match self {
786 DataExpr::StructConstructor(x) => x.analyze(parent),
787 DataExpr::ListConstructor(x) => x.analyze(parent),
788 DataExpr::MapConstructor(x) => x.analyze(parent),
789 DataExpr::TupleConstructor(x) => x.analyze(parent),
790 DataExpr::Identifier(x) => x.analyze(parent),
791 DataExpr::AddOp(x) => x.analyze(parent),
792 DataExpr::SubOp(x) => x.analyze(parent),
793 DataExpr::MulOp(x) => x.analyze(parent),
794 DataExpr::DivOp(x) => x.analyze(parent),
795 DataExpr::NegateOp(x) => x.analyze(parent),
796 DataExpr::PropertyOp(x) => x.analyze(parent),
797 DataExpr::AnyAssetConstructor(x) => x.analyze(parent),
798 DataExpr::FnCall(x) => x.analyze(parent),
799 DataExpr::ConcatOp(x) => x.analyze(parent),
800 _ => AnalyzeReport::default(),
801 }
802 }
803
804 fn is_resolved(&self) -> bool {
805 match self {
806 DataExpr::StructConstructor(x) => x.is_resolved(),
807 DataExpr::ListConstructor(x) => x.is_resolved(),
808 DataExpr::MapConstructor(x) => x.is_resolved(),
809 DataExpr::TupleConstructor(x) => x.is_resolved(),
810 DataExpr::Identifier(x) => x.is_resolved(),
811 DataExpr::AddOp(x) => x.is_resolved(),
812 DataExpr::SubOp(x) => x.is_resolved(),
813 DataExpr::MulOp(x) => x.is_resolved(),
814 DataExpr::DivOp(x) => x.is_resolved(),
815 DataExpr::NegateOp(x) => x.is_resolved(),
816 DataExpr::PropertyOp(x) => x.is_resolved(),
817 DataExpr::AnyAssetConstructor(x) => x.is_resolved(),
818 DataExpr::FnCall(x) => x.is_resolved(),
819 DataExpr::ConcatOp(x) => x.is_resolved(),
820 _ => true,
821 }
822 }
823}
824
825impl Analyzable for crate::ast::FnCall {
826 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
827 let callee = self.callee.analyze(parent.clone());
828
829 let mut args_report = AnalyzeReport::default();
830
831 for arg in &mut self.args {
832 args_report = args_report + arg.analyze(parent.clone());
833 }
834
835 let mut report = callee + args_report;
836
837 let signature = self
842 .callee
843 .symbol
844 .as_ref()
845 .and_then(|s| s.as_fn_def())
846 .map(|fn_def| (fn_def.name.value.clone(), fn_def.parameters.parameters.len()));
847
848 if let Some((name, expected)) = signature {
849 let got = self.args.len();
850 if expected != got {
851 report = report + Error::arity(name, expected, got, self).into();
852 }
853 }
854
855 report
856 }
857
858 fn is_resolved(&self) -> bool {
859 self.callee.is_resolved() && self.args.iter().all(|arg| arg.is_resolved())
860 }
861}
862
863impl Analyzable for AnyAssetConstructor {
864 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
865 let policy = self.policy.analyze(parent.clone());
866 let asset_name = self.asset_name.analyze(parent.clone());
867 let amount = self.amount.analyze(parent.clone());
868
869 policy + asset_name + amount
870 }
871
872 fn is_resolved(&self) -> bool {
873 self.policy.is_resolved() && self.asset_name.is_resolved() && self.amount.is_resolved()
874 }
875}
876
877impl Analyzable for PropertyOp {
878 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
879 let object = self.operand.analyze(parent.clone());
880
881 let mut scope = Scope::new(parent);
882
883 if let Some(ty) = self.operand.target_type() {
884 scope.track_record_fields_for_type(&ty);
885 }
886
887 self.scope = Some(Rc::new(scope));
888
889 let path = self.property.analyze(self.scope.clone());
890
891 object + path
892 }
893
894 fn is_resolved(&self) -> bool {
895 self.operand.is_resolved() && self.property.is_resolved()
896 }
897}
898
899impl Analyzable for AddressExpr {
900 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
901 match self {
902 AddressExpr::Identifier(x) => x.analyze(parent),
903 _ => AnalyzeReport::default(),
904 }
905 }
906
907 fn is_resolved(&self) -> bool {
908 match self {
909 AddressExpr::Identifier(x) => x.is_resolved(),
910 _ => true,
911 }
912 }
913}
914
915impl Analyzable for AssetDef {
916 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
917 let policy = self.policy.analyze(parent.clone());
918 let asset_name = self.asset_name.analyze(parent.clone());
919
920 let policy_type = AnalyzeReport::expect_data_expr_type(&self.policy, &Type::Bytes);
921 let asset_name_type = AnalyzeReport::expect_data_expr_type(&self.asset_name, &Type::Bytes);
922
923 policy + asset_name + policy_type + asset_name_type
924 }
925
926 fn is_resolved(&self) -> bool {
927 self.policy.is_resolved() && self.asset_name.is_resolved()
928 }
929}
930
931impl Analyzable for Identifier {
932 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
933 let symbol = parent.and_then(|p| p.resolve(&self.value));
934
935 if symbol.is_none() {
936 bail_report!(Error::not_in_scope(self.value.clone(), self));
937 }
938
939 self.symbol = symbol;
940
941 AnalyzeReport::default()
942 }
943
944 fn is_resolved(&self) -> bool {
945 self.symbol.is_some()
946 }
947}
948
949impl Analyzable for Type {
950 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
951 match self {
952 Type::Custom(x) => x.analyze(parent),
953 Type::List(x) => x.analyze(parent),
954 Type::Map(key_type, value_type) => {
955 key_type.analyze(parent.clone()) + value_type.analyze(parent)
956 }
957 Type::Tuple(elements) => elements.analyze(parent),
958 _ => AnalyzeReport::default(),
959 }
960 }
961
962 fn is_resolved(&self) -> bool {
963 match self {
964 Type::Custom(x) => x.is_resolved(),
965 Type::List(x) => x.is_resolved(),
966 Type::Map(key_type, value_type) => key_type.is_resolved() && value_type.is_resolved(),
967 Type::Tuple(elements) => elements.iter().all(|t| t.is_resolved()),
968 _ => true,
969 }
970 }
971}
972
973impl Analyzable for InputBlockField {
974 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
975 match self {
976 InputBlockField::From(x) => x.analyze(parent),
977 InputBlockField::DatumIs(x) => x.analyze(parent),
978 InputBlockField::MinAmount(x) => x.analyze(parent),
979 InputBlockField::Redeemer(x) => x.analyze(parent),
980 InputBlockField::Ref(x) => x.analyze(parent),
981 }
982 }
983
984 fn is_resolved(&self) -> bool {
985 match self {
986 InputBlockField::From(x) => x.is_resolved(),
987 InputBlockField::DatumIs(x) => x.is_resolved(),
988 InputBlockField::MinAmount(x) => x.is_resolved(),
989 InputBlockField::Redeemer(x) => x.is_resolved(),
990 InputBlockField::Ref(x) => x.is_resolved(),
991 }
992 }
993}
994
995impl Analyzable for InputBlock {
996 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
997 self.fields.analyze(parent)
998 }
999
1000 fn is_resolved(&self) -> bool {
1001 self.fields.is_resolved()
1002 }
1003}
1004
1005fn validate_metadata_value_size(expr: &DataExpr) -> Result<(), MetadataSizeLimitError> {
1006 match expr {
1007 DataExpr::String(string_literal) => {
1008 let utf8_bytes = string_literal.value.as_bytes();
1009 if utf8_bytes.len() > METADATA_MAX_SIZE_BYTES {
1010 return Err(MetadataSizeLimitError {
1011 size: utf8_bytes.len(),
1012 src: None,
1013 span: string_literal.span.clone(),
1014 });
1015 }
1016 }
1017 DataExpr::HexString(hex_literal) => {
1018 let hex_str = &hex_literal.value;
1019 let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
1020 let byte_length = hex_str.len() / 2;
1021
1022 if byte_length > METADATA_MAX_SIZE_BYTES {
1023 return Err(MetadataSizeLimitError {
1024 size: byte_length,
1025 src: None,
1026 span: hex_literal.span.clone(),
1027 });
1028 }
1029 }
1030 _ => {}
1031 }
1032 Ok(())
1033}
1034
1035fn validate_metadata_key_type(expr: &DataExpr) -> Result<(), MetadataInvalidKeyTypeError> {
1036 match expr {
1037 DataExpr::Number(_) => Ok(()),
1038 DataExpr::Identifier(id) => match id.target_type() {
1039 Some(Type::Int) => Ok(()),
1040 Some(other_type) => Err(MetadataInvalidKeyTypeError {
1041 key_type: format!("identifier of type {}", other_type),
1042 src: None,
1043 span: id.span().clone(),
1044 }),
1045 None => Err(MetadataInvalidKeyTypeError {
1046 key_type: "unresolved identifier".to_string(),
1047 src: None,
1048 span: id.span().clone(),
1049 }),
1050 },
1051 _ => {
1052 let key_type = match expr {
1053 DataExpr::String(_) => "string",
1054 DataExpr::HexString(_) => "hex string",
1055 DataExpr::ListConstructor(_) => "list",
1056 DataExpr::MapConstructor(_) => "map",
1057 DataExpr::StructConstructor(_) => "struct",
1058 _ => "unknown",
1059 };
1060
1061 Err(MetadataInvalidKeyTypeError {
1062 key_type: key_type.to_string(),
1063 src: None,
1064 span: expr.span().clone(),
1065 })
1066 }
1067 }
1068}
1069
1070impl Analyzable for MetadataBlockField {
1071 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1072 let mut report = self.key.analyze(parent.clone()) + self.value.analyze(parent.clone());
1073
1074 validate_metadata_key_type(&self.key)
1075 .map_err(Error::MetadataInvalidKeyType)
1076 .err()
1077 .map(|e| report.errors.push(e));
1078
1079 validate_metadata_value_size(&self.value)
1080 .map_err(Error::MetadataSizeLimitExceeded)
1081 .err()
1082 .map(|e| report.errors.push(e));
1083
1084 report
1085 }
1086
1087 fn is_resolved(&self) -> bool {
1088 self.key.is_resolved() && self.value.is_resolved()
1089 }
1090}
1091
1092impl Analyzable for MetadataBlock {
1093 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1094 self.fields.analyze(parent)
1095 }
1096
1097 fn is_resolved(&self) -> bool {
1098 self.fields.is_resolved()
1099 }
1100}
1101
1102impl Analyzable for ValidityBlockField {
1103 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1104 match self {
1105 ValidityBlockField::SinceSlot(x) => x.analyze(parent),
1106 ValidityBlockField::UntilSlot(x) => x.analyze(parent),
1107 }
1108 }
1109 fn is_resolved(&self) -> bool {
1110 match self {
1111 ValidityBlockField::SinceSlot(x) => x.is_resolved(),
1112 ValidityBlockField::UntilSlot(x) => x.is_resolved(),
1113 }
1114 }
1115}
1116
1117impl Analyzable for ValidityBlock {
1118 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1119 self.fields.analyze(parent)
1120 }
1121
1122 fn is_resolved(&self) -> bool {
1123 self.fields.is_resolved()
1124 }
1125}
1126
1127impl Analyzable for OutputBlockField {
1128 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1129 match self {
1130 OutputBlockField::To(x) => x.analyze(parent),
1131 OutputBlockField::Amount(x) => x.analyze(parent),
1132 OutputBlockField::Datum(x) => x.analyze(parent),
1133 }
1134 }
1135
1136 fn is_resolved(&self) -> bool {
1137 match self {
1138 OutputBlockField::To(x) => x.is_resolved(),
1139 OutputBlockField::Amount(x) => x.is_resolved(),
1140 OutputBlockField::Datum(x) => x.is_resolved(),
1141 }
1142 }
1143}
1144
1145impl Analyzable for OutputBlock {
1146 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1147 validate_optional_output(self)
1148 .map(AnalyzeReport::from)
1149 .unwrap_or_else(|| AnalyzeReport::default())
1150 + self.fields.analyze(parent)
1151 }
1152
1153 fn is_resolved(&self) -> bool {
1154 self.fields.is_resolved()
1155 }
1156}
1157
1158fn validate_optional_output(output: &OutputBlock) -> Option<Error> {
1159 if output.optional {
1160 if let Some(_field) = output.find("datum") {
1161 return Some(Error::InvalidOptionalOutput(OptionalOutputError {
1162 name: output
1163 .name
1164 .as_ref()
1165 .map(|i| i.value.clone())
1166 .unwrap_or_else(|| "<anonymous>".to_string()),
1167 src: None,
1168 span: output.span.clone(),
1169 }));
1170 }
1171 }
1172
1173 None
1174}
1175
1176impl Analyzable for RecordField {
1177 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1178 self.r#type.analyze(parent)
1179 }
1180
1181 fn is_resolved(&self) -> bool {
1182 self.r#type.is_resolved()
1183 }
1184}
1185
1186impl Analyzable for VariantCase {
1187 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1188 self.fields.analyze(parent)
1189 }
1190
1191 fn is_resolved(&self) -> bool {
1192 self.fields.is_resolved()
1193 }
1194}
1195
1196impl Analyzable for AliasDef {
1197 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1198 self.alias_type.analyze(parent)
1199 }
1200
1201 fn is_resolved(&self) -> bool {
1202 self.alias_type.is_resolved() && self.is_alias_chain_resolved()
1203 }
1204}
1205
1206impl Analyzable for TypeDef {
1207 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1208 self.cases.analyze(parent)
1209 }
1210
1211 fn is_resolved(&self) -> bool {
1212 self.cases.is_resolved()
1213 }
1214}
1215
1216impl Analyzable for MintBlockField {
1217 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1218 match self {
1219 MintBlockField::Amount(x) => x.analyze(parent),
1220 MintBlockField::Redeemer(x) => x.analyze(parent),
1221 }
1222 }
1223
1224 fn is_resolved(&self) -> bool {
1225 match self {
1226 MintBlockField::Amount(x) => x.is_resolved(),
1227 MintBlockField::Redeemer(x) => x.is_resolved(),
1228 }
1229 }
1230}
1231
1232impl Analyzable for MintBlock {
1233 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1234 self.fields.analyze(parent)
1235 }
1236
1237 fn is_resolved(&self) -> bool {
1238 self.fields.is_resolved()
1239 }
1240}
1241
1242impl Analyzable for SignersBlock {
1243 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1244 self.signers.analyze(parent)
1245 }
1246
1247 fn is_resolved(&self) -> bool {
1248 self.signers.is_resolved()
1249 }
1250}
1251
1252impl Analyzable for ReferenceBlock {
1253 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1254 self.r#ref.analyze(parent.clone()) + self.datum_is.analyze(parent)
1255 }
1256
1257 fn is_resolved(&self) -> bool {
1258 self.r#ref.is_resolved() && self.datum_is.is_resolved()
1259 }
1260}
1261
1262impl Analyzable for CollateralBlockField {
1263 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1264 match self {
1265 CollateralBlockField::From(x) => x.analyze(parent),
1266 CollateralBlockField::MinAmount(x) => x.analyze(parent),
1267 CollateralBlockField::Ref(x) => x.analyze(parent),
1268 }
1269 }
1270
1271 fn is_resolved(&self) -> bool {
1272 match self {
1273 CollateralBlockField::From(x) => x.is_resolved(),
1274 CollateralBlockField::MinAmount(x) => x.is_resolved(),
1275 CollateralBlockField::Ref(x) => x.is_resolved(),
1276 }
1277 }
1278}
1279
1280impl Analyzable for CollateralBlock {
1281 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1282 self.fields.analyze(parent)
1283 }
1284
1285 fn is_resolved(&self) -> bool {
1286 self.fields.is_resolved()
1287 }
1288}
1289
1290impl Analyzable for ChainSpecificBlock {
1291 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1292 match self {
1293 ChainSpecificBlock::Cardano(x) => x.analyze(parent),
1294 }
1295 }
1296
1297 fn is_resolved(&self) -> bool {
1298 match self {
1299 ChainSpecificBlock::Cardano(x) => x.is_resolved(),
1300 }
1301 }
1302}
1303
1304impl Analyzable for LocalsAssign {
1305 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1306 self.value.analyze(parent)
1307 }
1308
1309 fn is_resolved(&self) -> bool {
1310 self.value.is_resolved()
1311 }
1312}
1313
1314impl Analyzable for LocalsBlock {
1315 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1316 self.assigns.analyze(parent)
1317 }
1318
1319 fn is_resolved(&self) -> bool {
1320 self.assigns.is_resolved()
1321 }
1322}
1323
1324impl Analyzable for LetBinding {
1325 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1326 self.value.analyze(parent)
1327 }
1328
1329 fn is_resolved(&self) -> bool {
1330 self.value.is_resolved()
1331 }
1332}
1333
1334impl Analyzable for FnBody {
1335 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1336 let mut report = AnalyzeReport::default();
1337
1338 for binding in &mut self.let_bindings {
1339 report = report + binding.analyze(parent.clone());
1340 }
1341
1342 report = report + self.result.analyze(parent);
1343
1344 report
1345 }
1346
1347 fn is_resolved(&self) -> bool {
1348 self.let_bindings.is_resolved() && self.result.is_resolved()
1349 }
1350}
1351
1352impl Analyzable for FnDef {
1353 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1354 let params_report = self.parameters.analyze(parent.clone());
1355 let return_type_report = self.return_type.analyze(parent.clone());
1356
1357 let body = match &mut self.body {
1359 Some(body) => body,
1360 None => return params_report + return_type_report,
1361 };
1362
1363 let mut scope = Scope::new(parent);
1364
1365 for param in self.parameters.parameters.iter() {
1366 scope.track_param_var(¶m.name.value, param.r#type.clone());
1367 }
1368
1369 let mut current_scope = Rc::new(scope);
1371
1372 let mut bindings_report = AnalyzeReport::default();
1373
1374 for binding in &mut body.let_bindings {
1375 bindings_report = bindings_report + binding.analyze(Some(current_scope.clone()));
1376 let mut next_scope = Scope::new(Some(current_scope));
1378 next_scope.track_local_expr(&binding.name.value, binding.value.clone());
1379 current_scope = Rc::new(next_scope);
1380 }
1381
1382 let result_report = body.result.analyze(Some(current_scope.clone()));
1383
1384 self.scope = Some(current_scope);
1385
1386 params_report + return_type_report + bindings_report + result_report
1387 }
1388
1389 fn is_resolved(&self) -> bool {
1390 self.parameters.is_resolved()
1391 && self.body.as_ref().map_or(true, |b| b.is_resolved())
1392 }
1393}
1394
1395impl Analyzable for ParamDef {
1396 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1397 self.r#type.analyze(parent)
1398 }
1399
1400 fn is_resolved(&self) -> bool {
1401 self.r#type.is_resolved()
1402 }
1403}
1404
1405impl Analyzable for ParameterList {
1406 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1407 self.parameters.analyze(parent)
1408 }
1409
1410 fn is_resolved(&self) -> bool {
1411 self.parameters.is_resolved()
1412 }
1413}
1414
1415impl TxDef {
1416 fn best_effort_analyze_circular_dependencies(&mut self, mut scope: Scope) -> Scope {
1418 if let Some(locals) = &self.locals {
1419 for assign in locals.assigns.iter() {
1420 scope.track_local_expr(&assign.name.value, assign.value.clone());
1421 }
1422 }
1423
1424 for (_, input) in self.inputs.iter().enumerate() {
1425 scope.track_input(&input.name, input.clone())
1426 }
1427
1428 for reference in self.references.iter() {
1429 scope.track_reference(&reference.name, reference.clone());
1430 }
1431
1432 for (index, output) in self.outputs.iter().enumerate() {
1433 scope.track_output(index, output.clone())
1434 }
1435
1436 let scope_snapshot = Rc::new(scope);
1437 let _ = self.locals.analyze(Some(scope_snapshot.clone()));
1438 let _ = self.references.analyze(Some(scope_snapshot.clone()));
1439 let _ = self.inputs.analyze(Some(scope_snapshot.clone()));
1440 let _ = self.outputs.analyze(Some(scope_snapshot.clone()));
1441
1442 Scope::new(Some(scope_snapshot))
1443 }
1444}
1445
1446impl Analyzable for TxDef {
1447 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1448 let params = self.parameters.analyze(parent.clone());
1450
1451 let mut scope = Scope::new(parent.clone());
1454
1455 scope.symbols.insert("fees".to_string(), Symbol::Fees);
1456
1457 for param in self.parameters.parameters.iter() {
1458 scope.track_param_var(¶m.name.value, param.r#type.clone());
1459 }
1460
1461 for _ in 0..9 {
1462 scope = self.best_effort_analyze_circular_dependencies(scope);
1463 }
1464
1465 let final_scope = Rc::new(scope);
1466
1467 let locals = self.locals.analyze(Some(final_scope.clone()));
1468 let inputs = self.inputs.analyze(Some(final_scope.clone()));
1469 let outputs = self.outputs.analyze(Some(final_scope.clone()));
1470 let mints = self.mints.analyze(Some(final_scope.clone()));
1471 let burns = self.burns.analyze(Some(final_scope.clone()));
1472 let adhoc = self.adhoc.analyze(Some(final_scope.clone()));
1473 let validity = self.validity.analyze(Some(final_scope.clone()));
1474 let metadata = self.metadata.analyze(Some(final_scope.clone()));
1475 let signers = self.signers.analyze(Some(final_scope.clone()));
1476 let references = self.references.analyze(Some(final_scope.clone()));
1477 let collateral = self.collateral.analyze(Some(final_scope.clone()));
1478
1479 self.scope = Some(final_scope);
1480
1481 params
1482 + locals
1483 + inputs
1484 + outputs
1485 + mints
1486 + burns
1487 + adhoc
1488 + validity
1489 + metadata
1490 + signers
1491 + references
1492 + collateral
1493 }
1494
1495 fn is_resolved(&self) -> bool {
1496 self.inputs.is_resolved()
1497 && self.outputs.is_resolved()
1498 && self.mints.is_resolved()
1499 && self.locals.is_resolved()
1500 && self.adhoc.is_resolved()
1501 && self.validity.is_resolved()
1502 && self.metadata.is_resolved()
1503 && self.signers.is_resolved()
1504 && self.references.is_resolved()
1505 && self.collateral.is_resolved()
1506 }
1507}
1508
1509fn ada_asset_def() -> AssetDef {
1510 AssetDef {
1511 name: Identifier {
1512 value: "Ada".to_string(),
1513 symbol: None,
1514 span: Span::DUMMY,
1515 },
1516 policy: DataExpr::None,
1517 asset_name: DataExpr::None,
1518 span: Span::DUMMY,
1519 }
1520}
1521
1522fn resolve_types_and_aliases(
1523 scope_rc: &mut Rc<Scope>,
1524 types: &mut Vec<TypeDef>,
1525 aliases: &mut Vec<AliasDef>,
1526) -> (AnalyzeReport, AnalyzeReport) {
1527 let mut types_report = AnalyzeReport::default();
1528 let mut aliases_report = AnalyzeReport::default();
1529
1530 let mut pass_count = 0usize;
1531 let max_passes = 100usize; while pass_count < max_passes && !(types.is_resolved() && aliases.is_resolved()) {
1534 pass_count += 1;
1535
1536 let scope = Rc::get_mut(scope_rc).expect("scope should be unique during resolution");
1537
1538 for type_def in types.iter() {
1539 scope.track_type_def(type_def);
1540 }
1541 for alias_def in aliases.iter() {
1542 scope.track_alias_def(alias_def);
1543 }
1544
1545 types_report = types.analyze(Some(scope_rc.clone()));
1546 aliases_report = aliases.analyze(Some(scope_rc.clone()));
1547 }
1548
1549 (types_report, aliases_report)
1550}
1551
1552impl Analyzable for Program {
1553 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
1554 let mut scope = Scope::new(parent);
1555
1556 if let Some(env) = self.env.as_ref() {
1557 for field in env.fields.iter() {
1558 scope.track_env_var(&field.name, field.r#type.clone());
1559 }
1560 }
1561
1562 for party in self.parties.iter() {
1563 scope.track_party_def(party);
1564 }
1565
1566 for policy in self.policies.iter() {
1567 scope.track_policy_def(policy);
1568 }
1569
1570 scope.track_asset_def(&ada_asset_def());
1571
1572 for asset in self.assets.iter() {
1573 scope.track_asset_def(asset);
1574 }
1575
1576 for type_def in self.types.iter() {
1577 scope.track_type_def(type_def);
1578 }
1579
1580 for alias_def in self.aliases.iter() {
1581 scope.track_alias_def(alias_def);
1582 }
1583
1584 for builtin in crate::builtins::all() {
1585 scope.track_fn_def(&builtin.definition());
1586 }
1587
1588 for fn_def in self.functions.iter() {
1589 scope.track_fn_def(fn_def);
1590 }
1591
1592 self.scope = Some(Rc::new(scope));
1593
1594 let parties = self.parties.analyze(self.scope.clone());
1595
1596 let policies = self.policies.analyze(self.scope.clone());
1597
1598 let assets = self.assets.analyze(self.scope.clone());
1599
1600 let mut types = self.types.clone();
1601 let mut aliases = self.aliases.clone();
1602
1603 let scope_rc = self.scope.as_mut().unwrap();
1604
1605 {
1611 let scope =
1612 Rc::get_mut(scope_rc).expect("scope should be unique during resolution");
1613 for policy in self.policies.iter() {
1614 scope.track_policy_def(policy);
1615 }
1616 }
1617
1618 let (types, aliases) = resolve_types_and_aliases(scope_rc, &mut types, &mut aliases);
1619
1620 let program_scope = self.scope.clone();
1629 let mut functions = AnalyzeReport::default();
1630 for _ in 0..self.functions.len() {
1631 let mut fn_scope = Scope::new(program_scope.clone());
1632 for fn_def in self.functions.iter() {
1633 fn_scope.track_fn_def(fn_def);
1634 }
1635 functions = self.functions.analyze(Some(Rc::new(fn_scope)));
1636 }
1637
1638 let mut fn_scope = Scope::new(program_scope);
1640 for fn_def in self.functions.iter() {
1641 fn_scope.track_fn_def(fn_def);
1642 }
1643 self.scope = Some(Rc::new(fn_scope));
1644
1645 let txs = self.txs.analyze(self.scope.clone());
1646
1647 parties + policies + types + aliases + functions + txs + assets
1648 }
1649
1650 fn is_resolved(&self) -> bool {
1651 self.policies.is_resolved()
1652 && self.types.is_resolved()
1653 && self.aliases.is_resolved()
1654 && self.functions.is_resolved()
1655 && self.txs.is_resolved()
1656 && self.assets.is_resolved()
1657 }
1658}
1659
1660pub fn analyze(ast: &mut Program) -> AnalyzeReport {
1674 ast.analyze(None)
1675}
1676
1677#[cfg(test)]
1678mod tests {
1679 use crate::parsing::{parse_string, parse_well_known_example};
1680
1681 use super::*;
1682
1683 #[test]
1688 fn policy_def_fields_resolved_in_symbol_table() {
1689 let mut program = parse_string(
1690 r#"
1691 env {
1692 policy_hash: Bytes,
1693 script_ref: UtxoRef,
1694 }
1695
1696 policy P {
1697 hash: policy_hash,
1698 ref: script_ref,
1699 }
1700 "#,
1701 )
1702 .unwrap();
1703
1704 analyze(&mut program).ok().unwrap();
1705
1706 let symbol = program
1707 .scope
1708 .as_ref()
1709 .unwrap()
1710 .resolve("P")
1711 .expect("policy P should be in scope");
1712
1713 match symbol {
1714 Symbol::PolicyDef(policy) => assert!(
1715 policy.is_resolved(),
1716 "policy stored in the symbol table should have resolved fields"
1717 ),
1718 other => panic!("expected PolicyDef, got {other:?}"),
1719 }
1720 }
1721
1722 #[test]
1723 fn test_program_with_semantic_errors() {
1724 let mut ast = parse_well_known_example("semantic_errors");
1725
1726 let report = analyze(&mut ast);
1727
1728 assert_eq!(report.errors.len(), 3);
1729
1730 assert_eq!(
1731 report.errors[0],
1732 Error::NotInScope(NotInScopeError {
1733 name: "missing_symbol".to_string(),
1734 src: None,
1735 span: Span::DUMMY,
1736 })
1737 );
1738
1739 assert_eq!(
1740 report.errors[1],
1741 Error::InvalidTargetType(InvalidTargetTypeError {
1742 expected: "Bytes".to_string(),
1743 got: "Int".to_string(),
1744 src: None,
1745 span: Span::DUMMY,
1746 })
1747 );
1748
1749 assert_eq!(
1750 report.errors[2],
1751 Error::InvalidTargetType(InvalidTargetTypeError {
1752 expected: "Bytes".to_string(),
1753 got: "Int".to_string(),
1754 src: None,
1755 span: Span::DUMMY,
1756 })
1757 );
1758 }
1759
1760 #[test]
1761 fn test_min_utxo_analysis() {
1762 let mut ast = crate::parsing::parse_string(
1763 r#"
1764 party Alice;
1765 tx test() {
1766 output my_output {
1767 to: Alice,
1768 amount: min_utxo(my_output),
1769 }
1770 }
1771 "#,
1772 )
1773 .unwrap();
1774
1775 let result = analyze(&mut ast);
1776 assert!(result.errors.is_empty());
1777 }
1778
1779 #[test]
1780 fn test_alias_undefined_type_error() {
1781 let mut ast = crate::parsing::parse_string(
1782 r#"
1783 type MyAlias = UndefinedType;
1784 "#,
1785 )
1786 .unwrap();
1787
1788 let result = analyze(&mut ast);
1789
1790 assert!(!result.errors.is_empty());
1791 assert!(result
1792 .errors
1793 .iter()
1794 .any(|e| matches!(e, Error::NotInScope(_))));
1795 }
1796
1797 #[test]
1798 fn test_alias_valid_type_success() {
1799 let mut ast = crate::parsing::parse_string(
1800 r#"
1801 type Address = Bytes;
1802 type Amount = Int;
1803 type ValidAlias = Address;
1804 "#,
1805 )
1806 .unwrap();
1807
1808 let result = analyze(&mut ast);
1809
1810 assert!(result.errors.is_empty());
1811 }
1812
1813 #[test]
1814 fn test_min_utxo_undefined_output_error() {
1815 let mut ast = crate::parsing::parse_string(
1816 r#"
1817 party Alice;
1818 tx test() {
1819 output {
1820 to: Alice,
1821 amount: min_utxo(nonexistent_output),
1822 }
1823 }
1824 "#,
1825 )
1826 .unwrap();
1827
1828 let result = analyze(&mut ast);
1829 assert!(!result.errors.is_empty());
1830 }
1831
1832 #[test]
1833 fn test_time_and_slot_conversion() {
1834 let mut ast = crate::parsing::parse_string(
1835 r#"
1836 party Sender;
1837
1838 type TimestampDatum {
1839 slot_time: Int,
1840 time: Int,
1841 }
1842
1843 tx create_timestamp_tx() {
1844 input source {
1845 from: Sender,
1846 min_amount: Ada(2000000),
1847 }
1848
1849 output timestamp_output {
1850 to: Sender,
1851 amount: source - fees,
1852 datum: TimestampDatum {
1853 slot_time: time_to_slot(1666716638000),
1854 time: slot_to_time(60638),
1855 },
1856 }
1857 }
1858 "#,
1859 )
1860 .unwrap();
1861
1862 let result = analyze(&mut ast);
1863 assert!(result.errors.is_empty());
1864 }
1865
1866 #[test]
1867 fn test_optional_output_with_datum_error() {
1868 let mut ast = crate::parsing::parse_string(
1869 r#"
1870 party Alice;
1871 type MyDatum {
1872 field1: Int,
1873 }
1874 tx test() {
1875 output ? my_output {
1876 to: Alice,
1877 amount: Ada(1),
1878 datum: MyDatum { field1: 1, },
1879 }
1880 }
1881 "#,
1882 )
1883 .unwrap();
1884
1885 let report = analyze(&mut ast);
1886
1887 assert!(!report.errors.is_empty());
1888 assert!(report
1889 .errors
1890 .iter()
1891 .any(|e| matches!(e, Error::InvalidOptionalOutput(_))));
1892 }
1893
1894 #[test]
1895 fn test_optional_output_ok() {
1896 let mut ast = crate::parsing::parse_string(
1897 r#"
1898 party Alice;
1899
1900 tx test() {
1901 output ? my_output {
1902 to: Alice,
1903 amount: Ada(0),
1904 }
1905 }
1906 "#,
1907 )
1908 .unwrap();
1909
1910 let report = analyze(&mut ast);
1911 assert!(report.errors.is_empty());
1912 }
1913
1914 #[test]
1915 fn test_fn_call_too_few_args() {
1916 let mut ast = crate::parsing::parse_string(
1917 r#"
1918 party Alice;
1919
1920 fn double(x: Int) -> Int {
1921 x + x
1922 }
1923
1924 tx t() {
1925 input source {
1926 from: Alice,
1927 min_amount: Ada(2),
1928 }
1929 output {
1930 to: Alice,
1931 amount: Ada(double()),
1932 }
1933 }
1934 "#,
1935 )
1936 .unwrap();
1937
1938 let report = analyze(&mut ast);
1939
1940 assert!(report.errors.iter().any(|e| matches!(
1941 e,
1942 Error::Arity(a) if a.name == "double" && a.expected == 1 && a.got == 0
1943 )));
1944 }
1945
1946 #[test]
1947 fn test_fn_call_too_many_args() {
1948 let mut ast = crate::parsing::parse_string(
1949 r#"
1950 party Alice;
1951
1952 fn double(x: Int) -> Int {
1953 x + x
1954 }
1955
1956 tx t() {
1957 input source {
1958 from: Alice,
1959 min_amount: Ada(2),
1960 }
1961 output {
1962 to: Alice,
1963 amount: Ada(double(1, 2)),
1964 }
1965 }
1966 "#,
1967 )
1968 .unwrap();
1969
1970 let report = analyze(&mut ast);
1971
1972 assert!(report.errors.iter().any(|e| matches!(
1973 e,
1974 Error::Arity(a) if a.name == "double" && a.expected == 1 && a.got == 2
1975 )));
1976 }
1977
1978 #[test]
1979 fn test_fn_call_correct_arity_ok() {
1980 let mut ast = crate::parsing::parse_string(
1981 r#"
1982 party Alice;
1983
1984 fn double(x: Int) -> Int {
1985 x + x
1986 }
1987
1988 tx t() {
1989 input source {
1990 from: Alice,
1991 min_amount: Ada(2),
1992 }
1993 output {
1994 to: Alice,
1995 amount: Ada(double(2)),
1996 }
1997 }
1998 "#,
1999 )
2000 .unwrap();
2001
2002 let report = analyze(&mut ast);
2003 assert!(!report.errors.iter().any(|e| matches!(e, Error::Arity(_))));
2004 }
2005
2006 #[test]
2007 fn test_builtin_call_wrong_arity() {
2008 let mut ast = crate::parsing::parse_string(
2009 r#"
2010 party Alice;
2011
2012 tx t() {
2013 input source {
2014 from: Alice,
2015 min_amount: Ada(2),
2016 }
2017 output {
2018 to: Alice,
2019 amount: min_utxo(),
2020 }
2021 }
2022 "#,
2023 )
2024 .unwrap();
2025
2026 let report = analyze(&mut ast);
2027
2028 assert!(report.errors.iter().any(|e| matches!(
2029 e,
2030 Error::Arity(a) if a.name == "min_utxo" && a.expected == 1 && a.got == 0
2031 )));
2032 }
2033
2034 #[test]
2035 fn test_metadata_value_size_validation_string_within_limit() {
2036 let mut ast = crate::parsing::parse_string(
2037 r#"
2038 tx test() {
2039 metadata {
2040 123: "This is a short string that is within the 64-byte limit",
2041 }
2042 }
2043 "#,
2044 )
2045 .unwrap();
2046
2047 let result = analyze(&mut ast);
2048
2049 assert!(
2050 result.errors.is_empty(),
2051 "Expected no errors for string within limit, but got: {:?}",
2052 result.errors
2053 );
2054 }
2055
2056 #[test]
2057 fn test_metadata_value_size_validation_string_exceeds_limit() {
2058 let mut ast = crate::parsing::parse_string(
2059 r#"
2060 tx test() {
2061 metadata {
2062 123: "This is a very long string that definitely exceeds the 64-byte limit here",
2063 }
2064 }
2065 "#,
2066 )
2067 .unwrap();
2068
2069 let result = analyze(&mut ast);
2070 assert_eq!(result.errors.len(), 1);
2071
2072 match &result.errors[0] {
2073 Error::MetadataSizeLimitExceeded(error) => {
2074 assert_eq!(error.size, 73);
2075 }
2076 _ => panic!(
2077 "Expected MetadataSizeLimitExceeded error, got: {:?}",
2078 result.errors[0]
2079 ),
2080 }
2081 }
2082
2083 #[test]
2084 fn test_metadata_value_size_validation_hex_string_within_limit() {
2085 let mut ast = crate::parsing::parse_string(
2086 r#"
2087 tx test() {
2088 metadata {
2089 123: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef,
2090 }
2091 }
2092 "#,
2093 )
2094 .unwrap();
2095
2096 let result = analyze(&mut ast);
2097 assert!(
2098 result.errors.is_empty(),
2099 "Expected no errors for hex string within limit, but got: {:?}",
2100 result.errors
2101 );
2102 }
2103
2104 #[test]
2105 fn test_metadata_value_size_validation_hex_string_exceeds_limit() {
2106 let mut ast = crate::parsing::parse_string(
2107 r#"
2108 tx test() {
2109 metadata {
2110 123: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12,
2111 }
2112 }
2113 "#,
2114 )
2115 .unwrap();
2116
2117 let result = analyze(&mut ast);
2118 assert_eq!(result.errors.len(), 1);
2119
2120 match &result.errors[0] {
2121 Error::MetadataSizeLimitExceeded(error) => {
2122 assert_eq!(error.size, 65);
2123 }
2124 _ => panic!(
2125 "Expected MetadataSizeLimitExceeded error, got: {:?}",
2126 result.errors[0]
2127 ),
2128 }
2129 }
2130
2131 #[test]
2132 fn test_metadata_value_size_validation_multiple_fields() {
2133 let mut ast = crate::parsing::parse_string(
2134 r#"
2135 tx test() {
2136 metadata {
2137 123: "Short string",
2138 456: "This is a very long string that definitely exceeds the 64-byte limit here",
2139 789: "Another short one",
2140 }
2141 }
2142 "#,
2143 )
2144 .unwrap();
2145
2146 let result = analyze(&mut ast);
2147 assert_eq!(result.errors.len(), 1);
2148
2149 match &result.errors[0] {
2150 Error::MetadataSizeLimitExceeded(error) => {
2151 assert_eq!(error.size, 73);
2152 }
2153 _ => panic!(
2154 "Expected MetadataSizeLimitExceeded error, got: {:?}",
2155 result.errors[0]
2156 ),
2157 }
2158 }
2159
2160 #[test]
2161 fn test_metadata_value_size_validation_non_literal_expression() {
2162 let mut ast = crate::parsing::parse_string(
2163 r#"
2164 party Alice;
2165
2166 tx test(my_param: Bytes) {
2167 metadata {
2168 123: my_param,
2169 }
2170 }
2171 "#,
2172 )
2173 .unwrap();
2174
2175 let result = analyze(&mut ast);
2176 let metadata_errors: Vec<_> = result
2177 .errors
2178 .iter()
2179 .filter(|e| matches!(e, Error::MetadataSizeLimitExceeded(_)))
2180 .collect();
2181 assert!(
2182 metadata_errors.is_empty(),
2183 "Expected no metadata size errors for non-literal expressions"
2184 );
2185 }
2186
2187 #[test]
2188 fn test_metadata_key_type_validation_string_key() {
2189 let mut ast = crate::parsing::parse_string(
2190 r#"
2191 tx test() {
2192 metadata {
2193 "invalid_key": "some value",
2194 }
2195 }
2196 "#,
2197 )
2198 .unwrap();
2199
2200 let result = analyze(&mut ast);
2201 assert_eq!(result.errors.len(), 1);
2202
2203 match &result.errors[0] {
2204 Error::MetadataInvalidKeyType(error) => {
2205 assert_eq!(error.key_type, "string");
2206 }
2207 _ => panic!(
2208 "Expected MetadataInvalidKeyType error, got: {:?}",
2209 result.errors[0]
2210 ),
2211 }
2212 }
2213
2214 #[test]
2215 fn test_metadata_key_type_validation_identifier_with_int_type() {
2216 let mut ast = crate::parsing::parse_string(
2217 r#"
2218 tx test(my_key: Int) {
2219 metadata {
2220 my_key: "valid value",
2221 }
2222 }
2223 "#,
2224 )
2225 .unwrap();
2226
2227 let result = analyze(&mut ast);
2228 let key_type_errors: Vec<_> = result
2229 .errors
2230 .iter()
2231 .filter(|e| matches!(e, Error::MetadataInvalidKeyType(_)))
2232 .collect();
2233 assert!(
2234 key_type_errors.is_empty(),
2235 "Expected no key type errors for Int parameter used as key"
2236 );
2237 }
2238
2239 #[test]
2240 fn test_metadata_key_type_validation_identifier_with_wrong_type() {
2241 let mut ast = crate::parsing::parse_string(
2242 r#"
2243 tx test(my_key: Bytes) {
2244 metadata {
2245 my_key: "some value",
2246 }
2247 }
2248 "#,
2249 )
2250 .unwrap();
2251
2252 let result = analyze(&mut ast);
2253 assert_eq!(result.errors.len(), 1);
2254
2255 match &result.errors[0] {
2256 Error::MetadataInvalidKeyType(error) => {
2257 assert!(error.key_type.contains("identifier of type Bytes"));
2258 }
2259 _ => panic!(
2260 "Expected MetadataInvalidKeyType error, got: {:?}",
2261 result.errors[0]
2262 ),
2263 }
2264 }
2265}