diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 4101aaca..66431dfc 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -133,7 +133,7 @@ You can use '{discard}' and numbers to distinguish between similar names. location: Span, }, - #[error("I discovered a function who's ending with an assignment.")] + #[error("I discovered a function which is ending with an assignment.")] #[diagnostic(url("https://aiken-lang.org/language-tour/functions#named-functions"))] #[diagnostic(code("illegal::return"))] #[diagnostic(help(r#"In Aiken, functions must return an explicit result in the form of an expression. While assignments are technically speaking expressions, they aren't allowed to be the last expression of a function because they convey a different meaning and this could be error-prone. diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 65d17d4c..e30f1982 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -200,13 +200,33 @@ impl<'a, 'b> ExprTyper<'a, 'b> { Ok(()) } + #[allow(clippy::only_used_in_recursion)] fn assert_no_assignment(&self, expr: &UntypedExpr) -> Result<(), Error> { match expr { UntypedExpr::Assignment { value, .. } => Err(Error::LastExpressionIsAssignment { location: expr.location(), expr: *value.clone(), }), - _ => Ok(()), + UntypedExpr::Trace { then, .. } => self.assert_no_assignment(then), + UntypedExpr::Fn { .. } + | UntypedExpr::BinOp { .. } + | UntypedExpr::ByteArray { .. } + | UntypedExpr::Call { .. } + | UntypedExpr::ErrorTerm { .. } + | UntypedExpr::FieldAccess { .. } + | UntypedExpr::If { .. } + | UntypedExpr::Int { .. } + | UntypedExpr::List { .. } + | UntypedExpr::PipeLine { .. } + | UntypedExpr::RecordUpdate { .. } + | UntypedExpr::Sequence { .. } + | UntypedExpr::String { .. } + | UntypedExpr::Todo { .. } + | UntypedExpr::Tuple { .. } + | UntypedExpr::TupleIndex { .. } + | UntypedExpr::UnOp { .. } + | UntypedExpr::Var { .. } + | UntypedExpr::When { .. } => Ok(()), } } @@ -1563,6 +1583,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> { body: UntypedExpr, return_type: Option>, ) -> Result<(Vec, TypedExpr), Error> { + self.assert_no_assignment(&body)?; + let (body_rigid_names, body_infer) = self.in_new_scope(|body_typer| { for (arg, t) in args.iter().zip(args.iter().map(|arg| arg.tipo.clone())) { match &arg.arg_name {