Forbid solo-assignment as the only function body

In the similar spirit to what we did for sequences. Yet, we need to handle the case of body being just an assignment -- or a trace of an assignment which is basically the same thing.
This commit is contained in:
KtorZ 2023-01-21 10:32:06 +01:00
parent 2101bb924d
commit d905f24e7f
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 24 additions and 2 deletions

View File

@ -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.

View File

@ -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<Arc<Type>>,
) -> Result<(Vec<TypedArg>, 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 {