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 {
if self.name == PURPOSE_SPEND
|| self.name == PURPOSE_PUBLISH
@ -628,6 +637,17 @@ pub struct Validator<T, Arg, Expr> {
}
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<'_>> {
self.params
.iter()

View File

@ -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,

View File

@ -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,

View File

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

View File

@ -23,16 +23,18 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.then(
select! {Token::Name {name} => 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
}))

View File

@ -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<String>,
},
}
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, .. }

View File

@ -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,

View File

@ -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": {

View File

@ -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: "<redacted>",