feat: validate if scripts return Bool
This commit is contained in:
parent
28349ca653
commit
4130e0f2c3
|
@ -39,6 +39,16 @@ pub struct Module<Info, Definitions> {
|
||||||
pub kind: ModuleKind,
|
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 {
|
impl UntypedModule {
|
||||||
pub fn dependencies(&self) -> Vec<(String, Span)> {
|
pub fn dependencies(&self) -> Vec<(String, Span)> {
|
||||||
self.definitions()
|
self.definitions()
|
||||||
|
@ -54,14 +64,6 @@ impl UntypedModule {
|
||||||
})
|
})
|
||||||
.collect()
|
.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>;
|
pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
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};
|
use miette::{Diagnostic, EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -50,6 +50,13 @@ pub enum Error {
|
||||||
#[source]
|
#[source]
|
||||||
error: tipo::error::Error,
|
error: tipo::error::Error,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("validator functions must return Bool")]
|
||||||
|
ValidatorMustReturnBool {
|
||||||
|
path: PathBuf,
|
||||||
|
src: String,
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
@ -143,6 +150,7 @@ impl Diagnostic for Error {
|
||||||
Error::Type { .. } => Some(Box::new("aiken::typecheck")),
|
Error::Type { .. } => Some(Box::new("aiken::typecheck")),
|
||||||
Error::StandardIo(_) => None,
|
Error::StandardIo(_) => None,
|
||||||
Error::Format { .. } => 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::Type { error, .. } => error.help(),
|
||||||
Error::StandardIo(_) => None,
|
Error::StandardIo(_) => None,
|
||||||
Error::Format { .. } => 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::Type { error, .. } => error.labels(),
|
||||||
Error::StandardIo(_) => None,
|
Error::StandardIo(_) => None,
|
||||||
Error::Format { .. } => 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::Type { src, .. } => Some(src),
|
||||||
Error::StandardIo(_) => None,
|
Error::StandardIo(_) => None,
|
||||||
Error::Format { .. } => None,
|
Error::Format { .. } => None,
|
||||||
|
Error::ValidatorMustReturnBool { src, .. } => Some(src),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,17 @@ pub mod error;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
pub mod module;
|
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::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
error::{Error, Warning},
|
error::{Error, Warning},
|
||||||
module::{CheckedModule, ParsedModule, ParsedModules},
|
module::{CheckedModule, CheckedModules, ParsedModule, ParsedModules},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -25,6 +30,12 @@ pub struct Source {
|
||||||
pub kind: ModuleKind,
|
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 {
|
pub struct Project {
|
||||||
config: Config,
|
config: Config,
|
||||||
defined_modules: HashMap<String, PathBuf>,
|
defined_modules: HashMap<String, PathBuf>,
|
||||||
|
@ -70,7 +81,9 @@ impl Project {
|
||||||
|
|
||||||
let processing_sequence = parsed_modules.sequence()?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -157,8 +170,8 @@ impl Project {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut parsed_modules: ParsedModules,
|
mut parsed_modules: ParsedModules,
|
||||||
processing_sequence: Vec<String>,
|
processing_sequence: Vec<String>,
|
||||||
) -> Result<Vec<CheckedModule>, Error> {
|
) -> Result<CheckedModules, Error> {
|
||||||
let mut modules = Vec::with_capacity(parsed_modules.len() + 1);
|
let mut modules = HashMap::with_capacity(parsed_modules.len() + 1);
|
||||||
|
|
||||||
for name in processing_sequence {
|
for name in processing_sequence {
|
||||||
if let Some(ParsedModule {
|
if let Some(ParsedModule {
|
||||||
|
@ -199,18 +212,68 @@ impl Project {
|
||||||
self.module_types
|
self.module_types
|
||||||
.insert(name.clone(), ast.type_info.clone());
|
.insert(name.clone(), ast.type_info.clone());
|
||||||
|
|
||||||
modules.push(CheckedModule {
|
modules.insert(
|
||||||
|
name.clone(),
|
||||||
|
CheckedModule {
|
||||||
kind,
|
kind,
|
||||||
// extra,
|
// extra,
|
||||||
name,
|
name,
|
||||||
code,
|
code,
|
||||||
ast,
|
ast,
|
||||||
input_path: path,
|
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> {
|
fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> {
|
||||||
|
|
|
@ -155,7 +155,7 @@ fn find_cycle(
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CheckedModule {
|
pub struct CheckedModule {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub code: String,
|
pub code: String,
|
||||||
|
@ -164,3 +164,44 @@ pub struct CheckedModule {
|
||||||
pub ast: TypedModule,
|
pub ast: TypedModule,
|
||||||
// pub extra: ModuleExtra,
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue