// Abstract semantic graph (ASG) errors // // Copyright (C) 2014-2023 Ryan Specialty, LLC. // // This file is part of TAME. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Errors resulting from operations on the ASG. use std::{ error::Error, fmt::{self, Display}, }; use crate::{ diagnose::{Annotate, AnnotatedSpan, Diagnostic}, fmt::{DisplayWrapper, TtQuote}, parse::util::SPair, span::Span, }; use super::TransitionError; /// An error from an ASG operation. /// /// /// Note that the user may encounter an equivalent error in the source /// document format /// (e.g. XML via [XIR->NIR lowering](crate::nir)) /// and therefore may never see some of these errors. /// However, /// a source IR _may_ choose to allow certain errors through to ease the /// burden on its maintenance/development, /// or a system may utilize this IR directly. #[derive(Debug, PartialEq)] pub enum AsgError { /// An object could not change state in the manner requested. IdentTransition(TransitionError), /// An identifier was already bound to some object, /// and an attempt was made to bind it to a different one. /// /// This includes an [`SPair`] representing the _original_ definition /// that was already accepted by the system and a [`Span`] /// representing the _duplicate_ definition that triggered this error. /// /// Note that this is different than a _redeclaration_; /// _defining_ an identifier associates it with an object, /// whereas _declaring_ an identifier provides metadata about it. IdentRedefine(SPair, Span), /// Attempted to open a package while defining another package. /// /// Packages cannot be nested. /// The first span represents the location of the second package open, /// and the second span represents the location of the package already /// being defined. NestedPkgOpen(Span, Span), /// Attempted to close a package when not in a package toplevel context. InvalidPkgCloseContext(Span), /// Attempted to open an expression in an invalid context. PkgExpected(Span), /// An expresion is not reachable by any other expression or /// identifier. /// /// A dangling expression has no incoming edge from any other object and /// can therefore not be referenced. /// /// Since the expression is dangling, /// it must be anonymous, /// and can therefore only be identified meaningfully to the user by /// its span. /// The span should encompass the entirety of the expression. DanglingExpr(Span), /// Attempted to close an expression with no corresponding opening /// delimiter. UnbalancedExpr(Span), /// Attempted to close a template with no corresponding opening /// delimiter. UnbalancedTpl(Span), /// Attempted to bind the an identifier to an expression while not in an /// expression context. /// /// Note that the user may encounter an error from a higher-level IR /// instead of this one. InvalidExprBindContext(SPair), /// Attempted to reference an identifier as part of an expression while /// not in an expression context. /// /// Ideally this situation is syntactically invalid in a source IR. InvalidExprRefContext(SPair), /// Attempted to close a template when not in a template toplevel /// context. InvalidTplCloseContext(Span), } impl Display for AsgError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use AsgError::*; match self { IdentTransition(err) => Display::fmt(&err, f), IdentRedefine(spair, _) => { write!(f, "cannot redefine {}", TtQuote::wrap(spair)) } NestedPkgOpen(_, _) => write!(f, "cannot nest packages"), InvalidPkgCloseContext(_) => { write!(f, "invalid context for package close",) } PkgExpected(_) => write!(f, "expected package definition"), DanglingExpr(_) => write!( f, "dangling expression (anonymous expression has no parent)" ), UnbalancedExpr(_) => write!(f, "unbalanced expression"), UnbalancedTpl(_) => write!(f, "unbalanced template definition"), InvalidExprBindContext(_) => { write!(f, "invalid expression identifier binding context") } InvalidExprRefContext(ident) => { write!( f, "invalid context for expression identifier {}", TtQuote::wrap(ident) ) } InvalidTplCloseContext(_) => { write!(f, "invalid context for template close",) } } } } impl Error for AsgError {} impl From for AsgError { fn from(err: TransitionError) -> Self { Self::IdentTransition(err) } } impl Diagnostic for AsgError { fn describe(&self) -> Vec { use AsgError::*; // Before improving the diagnostic messages below, // be sure that you have a use case in mind and that higher-level // IRs do not preempt them in practice; // your efforts may be better focused in those higher IRs. match self { // TODO: need spans IdentTransition(_) => vec![], IdentRedefine(first, span_redecl) => vec![ first.note(format!( "first definition of {} is here", TtQuote::wrap(first) )), span_redecl.error(format!( "attempted to redefine {} here", TtQuote::wrap(first), )), span_redecl.help(format!( "variables in TAME are immutable; {} was previously", TtQuote::wrap(first), )), span_redecl .help(" defined and its definition cannot be changed."), ], NestedPkgOpen(second, first) => vec![ first.note("this package is still being defined"), second.error("attempted to open another package here"), second.help( "close the package to complete its definition before \ attempting to open another", ), ], InvalidPkgCloseContext(span) => vec![ span.error("package close was not expected here"), span.help( "a package must be closed at the same level of nesting \ that it was opened", ), ], PkgExpected(span) => { vec![span.error("a package definition was expected here")] } DanglingExpr(span) => vec![ span.error( "this expression is unreachable and its value \ cannot be used", ), span.help("an expression must either be the child of another "), span.help( " expression or be assigned an identifier, otherwise ", ), span.help(" its value cannot referenced."), ], UnbalancedExpr(span) => { vec![span.error("there is no open expression to close here")] } UnbalancedTpl(span) => { vec![span.error("there is no open template to close here")] } InvalidExprBindContext(span) => vec![ span.error( "there is no active expression to bind this identifier to", ), span.help( "an identifier must be bound to an expression before \ the expression is closed", ), ], InvalidExprRefContext(ident) => vec![ident.error( "cannot reference the value of an expression from outside \ of an expression context", )], InvalidTplCloseContext(span) => vec![ span.error("template close was not expected here"), span.help( "a template must be closed at the same level of nesting \ that it was opened", ), ], } } }