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