feat: new check for valid purpose names

This commit is contained in:
rvcas 2024-08-15 12:15:45 -04:00 committed by KtorZ
parent 5cf0a4d294
commit c2c4bddfb3
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
9 changed files with 60 additions and 13 deletions

View File

@ -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 { pub fn validator_arity(&self) -> usize {
if self.name == PURPOSE_SPEND if self.name == PURPOSE_SPEND
|| self.name == PURPOSE_PUBLISH || self.name == PURPOSE_PUBLISH
@ -628,6 +637,17 @@ pub struct Validator<T, Arg, Expr> {
} }
impl TypedValidator { impl TypedValidator {
pub fn available_purposes() -> Vec<String> {
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<Located<'_>> { pub fn find_node(&self, byte_index: usize) -> Option<Located<'_>> {
self.params self.params
.iter() .iter()

View File

@ -54,7 +54,7 @@ Validator(
name: "True", name: "True",
}, },
doc: None, doc: None,
location: 26..44, location: 20..44,
name: "spend", name: "spend",
public: true, public: true,
return_annotation: None, return_annotation: None,
@ -96,7 +96,7 @@ Validator(
name: "True", name: "True",
}, },
doc: None, doc: None,
location: 68..79, location: 63..79,
name: "mint", name: "mint",
public: true, public: true,
return_annotation: None, return_annotation: None,

View File

@ -54,7 +54,7 @@ Validator(
name: "True", name: "True",
}, },
doc: None, doc: None,
location: 26..44, location: 20..44,
name: "spend", name: "spend",
public: true, public: true,
return_annotation: None, return_annotation: None,
@ -96,7 +96,7 @@ Validator(
name: "True", name: "True",
}, },
doc: None, doc: None,
location: 68..79, location: 63..79,
name: "mint", name: "mint",
public: true, public: true,
return_annotation: None, return_annotation: None,

View File

@ -54,7 +54,7 @@ Validator(
name: "True", name: "True",
}, },
doc: None, doc: None,
location: 26..44, location: 20..44,
name: "spend", name: "spend",
public: true, public: true,
return_annotation: None, return_annotation: None,

View File

@ -23,16 +23,18 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.then( .then(
select! {Token::Name {name} => name} select! {Token::Name {name} => name}
.then(args_and_body()) .then(args_and_body())
.map(|(name, mut function)| { .map_with_span(|(name, mut function), span| {
function.name = name; function.name = name;
function.location.start = span.start;
function function
}) })
.repeated() .repeated()
.then( .then(
just(Token::Else) 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.name = "else".to_string();
function.location.start = span.start;
function function
})) }))

View File

@ -8,6 +8,7 @@ use crate::{
pretty::Documentable, pretty::Documentable,
}; };
use indoc::formatdoc; use indoc::formatdoc;
use itertools::Itertools;
use miette::{Diagnostic, LabeledSpan}; use miette::{Diagnostic, LabeledSpan};
use ordinal::Ordinal; use ordinal::Ordinal;
use owo_colors::{ use owo_colors::{
@ -1070,6 +1071,21 @@ The best thing to do from here is to remove it."#))]
function: UntypedFunction, function: UntypedFunction,
location: Span, 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<String>,
},
} }
impl ExtraData for Error { impl ExtraData for Error {
@ -1129,6 +1145,7 @@ impl ExtraData for Error {
| Error::UnexpectedMultiPatternAssignment { .. } | Error::UnexpectedMultiPatternAssignment { .. }
| Error::ExpectOnOpaqueType { .. } | Error::ExpectOnOpaqueType { .. }
| Error::ValidatorMustReturnBool { .. } | Error::ValidatorMustReturnBool { .. }
| Error::UnknownPurpose { .. }
| Error::MustInferFirst { .. } => None, | Error::MustInferFirst { .. } => None,
Error::UnknownType { name, .. } Error::UnknownType { name, .. }

View File

@ -9,7 +9,7 @@ use crate::{
ast::{ ast::{
Annotation, ArgName, ArgVia, DataType, Definition, Function, ModuleConstant, ModuleKind, Annotation, ArgName, ArgVia, DataType, Definition, Function, ModuleConstant, ModuleKind,
RecordConstructor, RecordConstructorArg, Tracing, TypeAlias, TypedArg, TypedDefinition, RecordConstructor, RecordConstructorArg, Tracing, TypeAlias, TypedArg, TypedDefinition,
TypedModule, UntypedArg, UntypedDefinition, UntypedModule, Use, Validator, TypedModule, TypedValidator, UntypedArg, UntypedDefinition, UntypedModule, Use, Validator,
}, },
builtins::{self, fuzzer, generic_var}, builtins::{self, fuzzer, generic_var},
tipo::{expr::infer_function, Span, Type, TypeVar}, tipo::{expr::infer_function, Span, Type, TypeVar},
@ -209,7 +209,15 @@ fn infer_definition(
typed_fun.arguments.drain(0..params_length); 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() { if typed_fun.arguments.len() != typed_fun.validator_arity() {
return Err(Error::IncorrectValidatorArity { return Err(Error::IncorrectValidatorArity {
count: typed_fun.arguments.len() as u32, count: typed_fun.arguments.len() as u32,

View File

@ -1,9 +1,9 @@
--- ---
source: crates/aiken-project/src/blueprint/validator.rs 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": { "redeemer": {
"title": "redeemer", "title": "redeemer",
"schema": { "schema": {

View File

@ -1,6 +1,6 @@
--- ---
source: crates/aiken-project/src/blueprint/validator.rs 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 { Schema {
error: Error { error: Error {
@ -16,7 +16,7 @@ Schema {
}, },
], ],
}, },
location: 117..135, location: 120..138,
source_code: NamedSource { source_code: NamedSource {
name: "", name: "",
source: "<redacted>", source: "<redacted>",