use crate::TypeTable;
use leo_ast::{AstVisitor, Expression, Function, Intrinsic, Node, Type};
use leo_errors::{Handler, StaticAnalyzerError};
pub fn future_check_function(function: &Function, type_table: &TypeTable, handler: &Handler) {
let mut future_checker = FutureChecker { type_table, handler };
future_checker.visit_block(&function.block);
}
#[derive(Clone, Copy, Debug, Default)]
enum Position {
#[default]
Misc,
Await,
TupleAccess,
Return,
FunctionArgument,
LastTupleLiteral,
Definition,
}
struct FutureChecker<'a> {
type_table: &'a TypeTable,
handler: &'a Handler,
}
impl FutureChecker<'_> {
fn emit_err(&self, err: StaticAnalyzerError) {
self.handler.emit_err(err);
}
}
impl AstVisitor for FutureChecker<'_> {
type AdditionalInput = Position;
type Output = ();
fn visit_expression(&mut self, input: &Expression, additional: &Self::AdditionalInput) -> Self::Output {
use Position::*;
let is_call = matches!(input, Expression::Call(..));
let is_async_block = matches!(input, Expression::Async(..));
match self.type_table.get(&input.id()) {
Some(Type::Future(..)) if is_call | is_async_block => {
if !matches!(additional, Await | Return | FunctionArgument | LastTupleLiteral | Definition) {
self.emit_err(StaticAnalyzerError::misplaced_future(input.span()));
}
}
Some(Type::Future(..)) => {
if !matches!(additional, Await | Return | FunctionArgument | LastTupleLiteral | TupleAccess) {
self.emit_err(StaticAnalyzerError::misplaced_future(input.span()));
}
}
Some(Type::Tuple(tuple)) if !matches!(tuple.elements().last(), Some(Type::Future(_))) => {}
Some(Type::Tuple(..)) if is_call => {
if !matches!(additional, Return | Definition) {
self.emit_err(StaticAnalyzerError::misplaced_future(input.span()));
}
}
Some(Type::Tuple(..)) => {
if !matches!(additional, Return | TupleAccess) {
self.emit_err(StaticAnalyzerError::misplaced_future(input.span()));
}
}
_ => {}
}
match input {
Expression::Array(array) => self.visit_array(array, &Position::Misc),
Expression::ArrayAccess(access) => self.visit_array_access(access, &Position::Misc),
Expression::Intrinsic(intr) => self.visit_intrinsic(intr, &Position::Misc),
Expression::Async(async_) => self.visit_async(async_, &Position::Misc),
Expression::Binary(binary) => self.visit_binary(binary, &Position::Misc),
Expression::Call(call) => self.visit_call(call, &Position::Misc),
Expression::Cast(cast) => self.visit_cast(cast, &Position::Misc),
Expression::Composite(composite) => self.visit_composite_init(composite, &Position::Misc),
Expression::Err(err) => self.visit_err(err, &Position::Misc),
Expression::Path(path) => self.visit_path(path, &Position::Misc),
Expression::Literal(literal) => self.visit_literal(literal, &Position::Misc),
Expression::MemberAccess(access) => self.visit_member_access(access, &Position::Misc),
Expression::Repeat(repeat) => self.visit_repeat(repeat, &Position::Misc),
Expression::Ternary(ternary) => self.visit_ternary(ternary, &Position::Misc),
Expression::Tuple(tuple) => self.visit_tuple(tuple, additional),
Expression::TupleAccess(access) => self.visit_tuple_access(access, &Position::Misc),
Expression::Unary(unary) => self.visit_unary(unary, &Position::Misc),
Expression::Unit(unit) => self.visit_unit(unit, &Position::Misc),
}
}
fn visit_array_access(
&mut self,
input: &leo_ast::ArrayAccess,
_additional: &Self::AdditionalInput,
) -> Self::Output {
self.visit_expression(&input.array, &Position::Misc);
self.visit_expression(&input.index, &Position::Misc);
}
fn visit_member_access(
&mut self,
input: &leo_ast::MemberAccess,
_additional: &Self::AdditionalInput,
) -> Self::Output {
self.visit_expression(&input.inner, &Position::Misc);
}
fn visit_tuple_access(
&mut self,
input: &leo_ast::TupleAccess,
_additional: &Self::AdditionalInput,
) -> Self::Output {
self.visit_expression(&input.tuple, &Position::TupleAccess);
}
fn visit_intrinsic(
&mut self,
input: &leo_ast::IntrinsicExpression,
_additional: &Self::AdditionalInput,
) -> Self::Output {
let position = if let Some(Intrinsic::FutureAwait) = Intrinsic::from_symbol(input.name, &input.type_parameters)
{
Position::Await
} else {
Position::Misc
};
input.arguments.iter().for_each(|arg| {
self.visit_expression(arg, &position);
});
}
fn visit_call(&mut self, input: &leo_ast::CallExpression, _additional: &Self::AdditionalInput) -> Self::Output {
input.arguments.iter().for_each(|expr| {
self.visit_expression(expr, &Position::FunctionArgument);
});
Default::default()
}
fn visit_tuple(&mut self, input: &leo_ast::TupleExpression, additional: &Self::AdditionalInput) -> Self::Output {
let next_position = match additional {
Position::Definition | Position::Return => Position::LastTupleLiteral,
_ => Position::Misc,
};
let mut iter = input.elements.iter().peekable();
while let Some(expr) = iter.next() {
let position = if iter.peek().is_some() { &Position::Misc } else { &next_position };
self.visit_expression(expr, position);
}
Default::default()
}
fn visit_definition(&mut self, input: &leo_ast::DefinitionStatement) {
if let Some(ty) = input.type_.as_ref() {
self.visit_type(ty)
}
self.visit_expression(&input.value, &Position::Definition);
}
fn visit_return(&mut self, input: &leo_ast::ReturnStatement) {
self.visit_expression(&input.expression, &Position::Return);
}
}