feat: validate if scripts return Bool

This commit is contained in:
rvcas 2022-10-25 21:00:28 -04:00 committed by Lucas
parent 28349ca653
commit 4130e0f2c3
4 changed files with 143 additions and 24 deletions

View File

@ -39,6 +39,16 @@ pub struct Module<Info, Definitions> {
pub kind: ModuleKind,
}
impl<Info, Definitions> Module<Info, Definitions> {
pub fn definitions(&self) -> impl Iterator<Item = &Definitions> {
self.definitions.iter()
}
pub fn into_definitions(self) -> impl Iterator<Item = Definitions> {
self.definitions.into_iter()
}
}
impl UntypedModule {
pub fn dependencies(&self) -> Vec<(String, Span)> {
self.definitions()
@ -54,14 +64,6 @@ impl UntypedModule {
})
.collect()
}
pub fn definitions(&self) -> impl Iterator<Item = &UntypedDefinition> {
self.definitions.iter()
}
pub fn into_definitions(self) -> impl Iterator<Item = UntypedDefinition> {
self.definitions.into_iter()
}
}
pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>;

View File

@ -4,7 +4,7 @@ use std::{
path::{Path, PathBuf},
};
use aiken_lang::{parser::error::ParseError, tipo};
use aiken_lang::{error::ParseError, parser::ast::Span, tipo};
use miette::{Diagnostic, EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
#[allow(dead_code)]
@ -50,6 +50,13 @@ pub enum Error {
#[source]
error: tipo::error::Error,
},
#[error("validator functions must return Bool")]
ValidatorMustReturnBool {
path: PathBuf,
src: String,
location: Span,
},
}
impl Error {
@ -143,6 +150,7 @@ impl Diagnostic for Error {
Error::Type { .. } => Some(Box::new("aiken::typecheck")),
Error::StandardIo(_) => None,
Error::Format { .. } => None,
Error::ValidatorMustReturnBool { .. } => Some(Box::new("aiken::scripts")),
}
}
@ -163,6 +171,7 @@ impl Diagnostic for Error {
Error::Type { error, .. } => error.help(),
Error::StandardIo(_) => None,
Error::Format { .. } => None,
Error::ValidatorMustReturnBool { .. } => Some(Box::new("try annotating the validator's return type with Bool")),
}
}
@ -176,6 +185,9 @@ impl Diagnostic for Error {
Error::Type { error, .. } => error.labels(),
Error::StandardIo(_) => None,
Error::Format { .. } => None,
Error::ValidatorMustReturnBool { location, .. } => Some(Box::new(
vec![LabeledSpan::new_with_span(None, *location)].into_iter(),
)),
}
}
@ -189,6 +201,7 @@ impl Diagnostic for Error {
Error::Type { src, .. } => Some(src),
Error::StandardIo(_) => None,
Error::Format { .. } => None,
Error::ValidatorMustReturnBool { src, .. } => Some(src),
}
}
}

View File

@ -9,12 +9,17 @@ pub mod error;
pub mod format;
pub mod module;
use aiken_lang::{ast::ModuleKind, builtins, tipo::TypeInfo, IdGenerator};
use aiken_lang::{
ast::{Definition, ModuleKind},
builtins,
tipo::{Type, TypeInfo},
IdGenerator,
};
use crate::{
config::Config,
error::{Error, Warning},
module::{CheckedModule, ParsedModule, ParsedModules},
module::{CheckedModule, CheckedModules, ParsedModule, ParsedModules},
};
#[derive(Debug)]
@ -25,6 +30,12 @@ pub struct Source {
pub kind: ModuleKind,
}
pub const SPEND: &str = "spend";
pub const CERT: &str = "cert";
pub const MINT: &str = "mint";
pub const WITHDRAWL: &str = "withdrawl";
pub const VALIDATOR_NAMES: [&str; 4] = [SPEND, CERT, MINT, WITHDRAWL];
pub struct Project {
config: Config,
defined_modules: HashMap<String, PathBuf>,
@ -70,7 +81,9 @@ impl Project {
let processing_sequence = parsed_modules.sequence()?;
let _checked_modules = self.type_check(parsed_modules, processing_sequence)?;
let mut checked_modules = self.type_check(parsed_modules, processing_sequence)?;
let scripts = self.validate_scripts(&mut checked_modules)?;
Ok(())
}
@ -157,8 +170,8 @@ impl Project {
&mut self,
mut parsed_modules: ParsedModules,
processing_sequence: Vec<String>,
) -> Result<Vec<CheckedModule>, Error> {
let mut modules = Vec::with_capacity(parsed_modules.len() + 1);
) -> Result<CheckedModules, Error> {
let mut modules = HashMap::with_capacity(parsed_modules.len() + 1);
for name in processing_sequence {
if let Some(ParsedModule {
@ -199,18 +212,68 @@ impl Project {
self.module_types
.insert(name.clone(), ast.type_info.clone());
modules.push(CheckedModule {
kind,
// extra,
name,
code,
ast,
input_path: path,
});
modules.insert(
name.clone(),
CheckedModule {
kind,
// extra,
name,
code,
ast,
input_path: path,
},
);
}
}
Ok(modules)
Ok(modules.into())
}
fn validate_scripts(
&self,
checked_modules: &mut CheckedModules,
) -> Result<Vec<CheckedModule>, Error> {
let mut errors = Vec::new();
let mut scripts = Vec::new();
for module in checked_modules.scripts() {
scripts.push(module.clone());
for def in module.ast.definitions() {
if let Definition::Fn {
arguments,
location,
name,
return_type,
..
} = def
{
if VALIDATOR_NAMES.contains(&name.as_str()) {
// validators must return a Bool
if !return_type.is_bool() {
errors.push(Error::ValidatorMustReturnBool {
location: *location,
src: module.code.clone(),
path: module.input_path.clone(),
})
}
// depending on name, validate the minimum number of arguments
// if too low, push a new error on to errors
}
}
}
}
if errors.is_empty() {
for script in &scripts {
checked_modules.remove(&script.name);
}
Ok(scripts)
} else {
Err(Error::List(errors))
}
}
fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> {

View File

@ -155,7 +155,7 @@ fn find_cycle(
false
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CheckedModule {
pub name: String,
pub code: String,
@ -164,3 +164,44 @@ pub struct CheckedModule {
pub ast: TypedModule,
// pub extra: ModuleExtra,
}
#[derive(Debug, Clone)]
pub struct CheckedModules(HashMap<String, CheckedModule>);
impl From<HashMap<String, CheckedModule>> for CheckedModules {
fn from(checked_modules: HashMap<String, CheckedModule>) -> Self {
CheckedModules(checked_modules)
}
}
impl From<CheckedModules> for HashMap<String, CheckedModule> {
fn from(checked_modules: CheckedModules) -> Self {
checked_modules.0
}
}
impl CheckedModules {
pub fn scripts(&self) -> impl Iterator<Item = &CheckedModule> {
self.0.values().filter(|module| module.kind.is_script())
}
pub fn into_scripts(self) -> impl Iterator<Item = CheckedModule> {
self.0
.into_values()
.filter(|module| module.kind.is_script())
}
}
impl Deref for CheckedModules {
type Target = HashMap<String, CheckedModule>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for CheckedModules {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}