use crate::ast::*;
use crate::ast::{diagnostics::*, visitors::*};
use crate::phase::Phase;
#[derive(Default)]
pub struct RejectNotDoLeanDSL;
#[derive(Clone, Copy, Debug)]
enum DoDSLExprKind {
Statement,
Expression,
}
fn dsl_expr_kind(expr_kind: &ExprKind) -> DoDSLExprKind {
match expr_kind {
ExprKind::If { .. } | ExprKind::Match { .. } | ExprKind::Let { .. } => {
DoDSLExprKind::Statement
}
_ => DoDSLExprKind::Expression,
}
}
impl Default for DoDSLExprKind {
fn default() -> Self {
Self::Statement
}
}
#[setup_error_handling_struct]
#[derive(Default)]
pub struct RejectNotDoLeanDSLVisitor {
dsl_expr_kind: DoDSLExprKind,
}
impl VisitorWithContext for RejectNotDoLeanDSLVisitor {
fn context(&self) -> Context {
Context::Phase(stringify!(RejectNotDoLeanDSL).to_string())
}
}
impl AstVisitorMut for RejectNotDoLeanDSLVisitor {
setup_error_handling_impl!();
fn visit_expr(&mut self, expr: &mut Expr) {
use DoDSLExprKind::*;
let parent_dsl_expr_kind = self.dsl_expr_kind;
self.dsl_expr_kind = match (self.dsl_expr_kind, dsl_expr_kind(&expr.kind)) {
(Expression, Statement) => {
self.error(
expr.clone(),
DiagnosticInfoKind::ExplicitRejection {
reason: "This interleaving of expression and statements does not fit in Lean's do-notation DSL.\
\nYou may try hoisting out let-bindings and control-flow.".to_string(),
issue_id: Some(1741),
},
);
Statement
}
(_, _) if matches!(&*expr.kind, ExprKind::Closure { .. }) => Statement,
(_, kind) => kind,
};
self.visit_inner(expr);
self.dsl_expr_kind = parent_dsl_expr_kind;
}
fn visit_ty(&mut self, ty: &mut Ty) {
if let TyKind::Array { length, .. } = ty.kind_mut() {
let parent_dsl_expr_kind = self.dsl_expr_kind;
self.dsl_expr_kind = DoDSLExprKind::Expression;
self.visit_inner(&mut *length);
self.dsl_expr_kind = parent_dsl_expr_kind;
}
}
}
impl Phase for RejectNotDoLeanDSL {
fn apply(&self, items: &mut Vec<Item>) {
RejectNotDoLeanDSLVisitor::default().visit(items)
}
}