From c2c4bddfb32cc005e40723537d139d530053fb1f Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 15 Aug 2024 12:15:45 -0400 Subject: [PATCH] feat: new check for valid purpose names --- crates/aiken-lang/src/ast.rs | 20 +++++++++++++++++++ .../snapshots/double_validator.snap | 4 ++-- .../parser/definition/snapshots/fallback.snap | 4 ++-- .../definition/snapshots/validator.snap | 2 +- .../src/parser/definition/validator.rs | 6 ++++-- crates/aiken-lang/src/tipo/error.rs | 17 ++++++++++++++++ crates/aiken-lang/src/tipo/infer.rs | 12 +++++++++-- ...ueprint__validator__tests__mint_basic.snap | 4 ++-- ...ests__opaque_singleton_multi_variants.snap | 4 ++-- 9 files changed, 60 insertions(+), 13 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 80ec6eb0..db2ab226 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -277,6 +277,15 @@ impl TypedFunction { }) } + pub fn has_valid_purpose_name(&self) -> bool { + self.name == PURPOSE_SPEND + || self.name == PURPOSE_PUBLISH + || self.name == PURPOSE_PROPOSE + || self.name == PURPOSE_MINT + || self.name == PURPOSE_WITHDRAW + || self.name == PURPOSE_VOTE + } + pub fn validator_arity(&self) -> usize { if self.name == PURPOSE_SPEND || self.name == PURPOSE_PUBLISH @@ -628,6 +637,17 @@ pub struct Validator { } impl TypedValidator { + pub fn available_purposes() -> Vec { + vec![ + PURPOSE_SPEND.to_string(), + PURPOSE_MINT.to_string(), + PURPOSE_WITHDRAW.to_string(), + PURPOSE_PUBLISH.to_string(), + PURPOSE_VOTE.to_string(), + PURPOSE_PROPOSE.to_string(), + ] + } + pub fn find_node(&self, byte_index: usize) -> Option> { self.params .iter() diff --git a/crates/aiken-lang/src/parser/definition/snapshots/double_validator.snap b/crates/aiken-lang/src/parser/definition/snapshots/double_validator.snap index 9d8e5725..b1cbe8d7 100644 --- a/crates/aiken-lang/src/parser/definition/snapshots/double_validator.snap +++ b/crates/aiken-lang/src/parser/definition/snapshots/double_validator.snap @@ -54,7 +54,7 @@ Validator( name: "True", }, doc: None, - location: 26..44, + location: 20..44, name: "spend", public: true, return_annotation: None, @@ -96,7 +96,7 @@ Validator( name: "True", }, doc: None, - location: 68..79, + location: 63..79, name: "mint", public: true, return_annotation: None, diff --git a/crates/aiken-lang/src/parser/definition/snapshots/fallback.snap b/crates/aiken-lang/src/parser/definition/snapshots/fallback.snap index 767b9e8a..aef13342 100644 --- a/crates/aiken-lang/src/parser/definition/snapshots/fallback.snap +++ b/crates/aiken-lang/src/parser/definition/snapshots/fallback.snap @@ -54,7 +54,7 @@ Validator( name: "True", }, doc: None, - location: 26..44, + location: 20..44, name: "spend", public: true, return_annotation: None, @@ -96,7 +96,7 @@ Validator( name: "True", }, doc: None, - location: 68..79, + location: 63..79, name: "mint", public: true, return_annotation: None, diff --git a/crates/aiken-lang/src/parser/definition/snapshots/validator.snap b/crates/aiken-lang/src/parser/definition/snapshots/validator.snap index a37a91f8..e73167a0 100644 --- a/crates/aiken-lang/src/parser/definition/snapshots/validator.snap +++ b/crates/aiken-lang/src/parser/definition/snapshots/validator.snap @@ -54,7 +54,7 @@ Validator( name: "True", }, doc: None, - location: 26..44, + location: 20..44, name: "spend", public: true, return_annotation: None, diff --git a/crates/aiken-lang/src/parser/definition/validator.rs b/crates/aiken-lang/src/parser/definition/validator.rs index ea089369..81f4c176 100644 --- a/crates/aiken-lang/src/parser/definition/validator.rs +++ b/crates/aiken-lang/src/parser/definition/validator.rs @@ -23,16 +23,18 @@ pub fn parser() -> impl Parser name} .then(args_and_body()) - .map(|(name, mut function)| { + .map_with_span(|(name, mut function), span| { function.name = name; + function.location.start = span.start; function }) .repeated() .then( just(Token::Else) - .ignore_then(args_and_body().map(|mut function| { + .ignore_then(args_and_body().map_with_span(|mut function, span| { function.name = "else".to_string(); + function.location.start = span.start; function })) diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 32a19911..905772b7 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -8,6 +8,7 @@ use crate::{ pretty::Documentable, }; use indoc::formatdoc; +use itertools::Itertools; use miette::{Diagnostic, LabeledSpan}; use ordinal::Ordinal; use owo_colors::{ @@ -1070,6 +1071,21 @@ The best thing to do from here is to remove it."#))] function: UntypedFunction, location: Span, }, + + #[error("I found a validator handler referring to an unknown purpose.\n")] + #[diagnostic(code("unknown::purpose"))] + #[diagnostic(help( + "Handler must be named after a known purpose. Here is a list of available purposes:\n{}", + available_purposes + .iter() + .map(|p| format!("-> {}", p.if_supports_color(Stdout, |s| s.green()))) + .join("\n") + ))] + UnknownPurpose { + #[label("unknown purpose")] + location: Span, + available_purposes: Vec, + }, } impl ExtraData for Error { @@ -1129,6 +1145,7 @@ impl ExtraData for Error { | Error::UnexpectedMultiPatternAssignment { .. } | Error::ExpectOnOpaqueType { .. } | Error::ValidatorMustReturnBool { .. } + | Error::UnknownPurpose { .. } | Error::MustInferFirst { .. } => None, Error::UnknownType { name, .. } diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index edb0a4ff..1aa0abbf 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -9,7 +9,7 @@ use crate::{ ast::{ Annotation, ArgName, ArgVia, DataType, Definition, Function, ModuleConstant, ModuleKind, RecordConstructor, RecordConstructorArg, Tracing, TypeAlias, TypedArg, TypedDefinition, - TypedModule, UntypedArg, UntypedDefinition, UntypedModule, Use, Validator, + TypedModule, TypedValidator, UntypedArg, UntypedDefinition, UntypedModule, Use, Validator, }, builtins::{self, fuzzer, generic_var}, tipo::{expr::infer_function, Span, Type, TypeVar}, @@ -209,7 +209,15 @@ fn infer_definition( typed_fun.arguments.drain(0..params_length); - // TODO: the expected number of args comes from the script purpose + if !typed_fun.has_valid_purpose_name() { + return Err(Error::UnknownPurpose { + location: typed_fun + .location + .map(|start, _end| (start, start + typed_fun.name.len())), + available_purposes: TypedValidator::available_purposes(), + }); + } + if typed_fun.arguments.len() != typed_fun.validator_arity() { return Err(Error::IncorrectValidatorArity { count: typed_fun.arguments.len() as u32, diff --git a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__mint_basic.snap b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__mint_basic.snap index 92f879a9..892a3684 100644 --- a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__mint_basic.snap +++ b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__mint_basic.snap @@ -1,9 +1,9 @@ --- source: crates/aiken-project/src/blueprint/validator.rs -description: "Code:\n\nvalidator {\n fn mint(redeemer: Data, ctx: Data) {\n True\n }\n}\n// " +description: "Code:\n\nvalidator thing {\n mint(redeemer: Data, policy_id: Data, transaction: Data) {\n True\n }\n}\n// " --- { - "title": "test_module.mint", + "title": "test_module.thing_mint", "redeemer": { "title": "redeemer", "schema": { diff --git a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__opaque_singleton_multi_variants.snap b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__opaque_singleton_multi_variants.snap index 0b14e043..d3565900 100644 --- a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__opaque_singleton_multi_variants.snap +++ b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__opaque_singleton_multi_variants.snap @@ -1,6 +1,6 @@ --- source: crates/aiken-project/src/blueprint/validator.rs -description: "Code:\n\npub opaque type Rational {\n numerator: Int,\n denominator: Int,\n}\n\nvalidator {\n fn opaque_singleton_multi_variants(redeemer: Rational, ctx: Void) {\n True\n }\n}\n" +description: "Code:\n\npub opaque type Rational {\n numerator: Int,\n denominator: Int,\n}\n\nvalidator opaque_singleton_multi_variants {\n spend(redeemer: Rational, oref: Data, ctx: Void) {\n True\n }\n}\n" --- Schema { error: Error { @@ -16,7 +16,7 @@ Schema { }, ], }, - location: 117..135, + location: 120..138, source_code: NamedSource { name: "", source: "",