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,
|
||||
}
|
||||
|
||||
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>;
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue