brink-ir 0.0.17

Intermediate representations for inkle's ink narrative scripting language
Documentation
//! `VarDecl` symbol declaration and lowering.

use brink_syntax::ast::{self, AstNode};

use super::super::context::{LowerScope, LowerSink, Lowered};
use super::super::directive::{DirectiveTarget, apply_scope_directives, directives_before};
use super::super::doc_comment::{DocPolicy, parse_doc_comment};
use super::super::expr::LowerExpr;
use super::super::helpers::name_from_ident;
use super::super::types::lower_type_annotation;
use super::DeclareSymbols;
use crate::provenance::NodeClass;
use crate::{DiagnosticCode, Expr, VarDecl};

impl DeclareSymbols for ast::VarDecl {
    type Output = VarDecl;

    fn declare_and_lower(&self, scope: &LowerScope, sink: &mut impl LowerSink) -> Lowered<VarDecl> {
        let range = self.syntax().text_range();
        let ident = self
            .identifier()
            .ok_or_else(|| sink.diagnose(range, DiagnosticCode::E004))?;
        let name =
            name_from_ident(&ident).ok_or_else(|| sink.diagnose(range, DiagnosticCode::E004))?;
        let (doc, issues) = parse_doc_comment(self.syntax(), DocPolicy::VALUE);
        issues.diagnose(sink);

        let value = if let Some(e) = self.value() {
            e.lower_expr(scope, sink).unwrap_or(Expr::Null)
        } else {
            sink.diagnose(range, DiagnosticCode::E005);
            Expr::Null
        };

        // `#@local` and `#@private`/`#@public` directive line(s) immediately
        // above the declaration.
        let dirs = directives_before(self.syntax());
        let is_local = apply_scope_directives(&dirs, DirectiveTarget::Var, sink);
        let mut visibility = None;
        if let Some(vis) = super::super::directive::visibility_from_directives(&dirs, sink) {
            visibility = Some(vis);
        }
        let mut was = None;
        if let Some((old_name, was_range)) =
            super::super::directive::was_from_directives(&dirs, sink)
        {
            if old_name == name.text {
                sink.diagnose(was_range, DiagnosticCode::E095);
            } else {
                was = Some((old_name, was_range));
            }
        }

        let annotation = self
            .type_annotation()
            .and_then(|ta| lower_type_annotation(&ta));

        Ok(VarDecl {
            ptr: scope.prov(NodeClass::VarDecl, self.syntax()),
            name,
            value,
            is_local,
            annotation,
            doc,
            visibility,
            was,
        })
    }
}