1use std::{cmp::Ordering, convert::Infallible, fmt};23use jrsonnet_gcmodule::{Acyclic, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_ir::{6 BinaryOpType, ConvertNumValueError, Source, SourcePath, Span, Spanned, UnaryOpType,7};8use jrsonnet_types::ValType;9use thiserror::Error;1011use crate::{12 ObjValue, ResolvePathOwned,13 function::{CallLocation, FunctionSignature, ParamName},14 stdlib::format::FormatError,15 typed::TypeLocError,16};1718#[derive(Debug, Clone)]19pub struct SyntaxError {20 pub message: String,21 pub location: (u32, u32),22}23impl fmt::Display for SyntaxError {24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {25 write!(f, "{}", self.message)26 }27}2829pub(crate) fn format_found(list: &[IStr], what: &str) -> String {30 if list.is_empty() {31 return String::new();32 }33 let mut out = String::new();34 out.push_str("\nThere ");35 if list.len() > 1 {36 out.push_str("are ");37 } else {38 out.push_str("is a ");39 }40 out.push_str(what);41 if list.len() > 1 {42 out.push('s');43 }44 out.push_str(" with similar name");45 if list.len() > 1 {46 out.push('s');47 }48 out.push_str(" present: ");49 for (i, v) in list.iter().enumerate() {50 if i != 0 {51 out.push_str(", ");52 }53 out.push_str(v as &str);54 }55 out56}5758const fn format_empty_str(str: &str) -> &str {59 if str.is_empty() {60 "\"\" (empty string)"61 } else {62 str63 }64}6566pub(crate) fn suggest_object_fields(v: &ObjValue, key: IStr) -> Vec<IStr> {67 let mut heap = Vec::new();68 for field in v.fields_ex(69 true,70 #[cfg(feature = "exp-preserve-order")]71 false,72 ) {73 let conf = strsim::jaro_winkler(field.as_str(), key.as_str());74 if conf < 0.8 {75 continue;76 }77 assert!(78 field.as_str() != key.as_str(),79 "looks like string pooling failure, please write any info regarding this crash to https://github.com/CertainLach/jrsonnet/issues/113, thanks!"80 );8182 heap.push((conf, field));83 }84 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));85 heap.into_iter().map(|v| v.1).collect()86}878889#[allow(missing_docs)]90#[derive(Error, Debug, Clone, Trace)]91#[non_exhaustive]92pub enum ErrorKind {93 #[error("intrinsic not found: {0}")]94 IntrinsicNotFound(IStr),9596 #[error("operator {0} does not operate on type {1}")]97 UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),98 #[error("binary operation {1} {0} {2} is not implemented")]99 BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),100101 #[error("self/super/$ are only usable inside objects")]102 CantUseSelfSupOutsideOfObject,103 #[error("no super found")]104 NoSuperFound,105106 #[error("for loop can only iterate over arrays")]107 InComprehensionCanOnlyIterateOverArray,108109 #[error("array out of bounds: {0} is not within [0,{1})")]110 ArrayBoundsError(isize, usize),111 #[error("string out of bounds: {0} is not within [0,{1})")]112 StringBoundsError(usize, usize),113114 #[error("assert failed: {}", format_empty_str(.0))]115 AssertionFailed(IStr),116117 #[error("local is not defined: {0}{found}", found = format_found(.1, "local"))]118 VariableIsNotDefined(IStr, Vec<IStr>),119 #[error("duplicate local var: {0}")]120 DuplicateLocalVar(IStr),121122 #[error("type mismatch: expected {expected}, got {2} {0}", expected = .1.iter().map(|e| format!("{e}")).collect::<Vec<_>>().join(", "))]123 TypeMismatch(&'static str, Vec<ValType>, ValType),124 #[error("no such field: {}{}", format_empty_str(.0), format_found(.1, "field"))]125 NoSuchField(IStr, Vec<IStr>),126127 #[error("only functions can be called, got {0}")]128 OnlyFunctionsCanBeCalledGot(ValType),129 #[error("parameter {0} is not defined")]130 UnknownFunctionParameter(IStr),131 #[error("argument {0} is already bound")]132 BindingParameterASecondTime(IStr),133 #[error("too many args, function has {0}\nFunction has the following signature: {1}")]134 TooManyArgsFunctionHas(usize, FunctionSignature),135 #[error("function argument is not passed: {0}\nFunction has the following signature: {1}")]136 FunctionParameterNotBoundInCall(ParamName, FunctionSignature),137138 #[error("external variable is not defined: {0}")]139 UndefinedExternalVariable(IStr),140141 #[error("field name should be string, got {0}")]142 FieldMustBeStringGot(ValType),143 #[error("duplicate field name: {}", format_empty_str(.0))]144 DuplicateFieldName(IStr),145146 #[error("attempted to index array with string {}", format_empty_str(.0))]147 AttemptedIndexAnArrayWithString(IStr),148 #[error("{0} index type should be {1}, got {2}")]149 ValueIndexMustBeTypeGot(ValType, ValType, ValType),150 #[error("cant index into {0}")]151 CantIndexInto(ValType),152 #[error("{0} is not indexable")]153 ValueIsNotIndexable(ValType),154155 #[error("super can't be used standalone")]156 StandaloneSuper,157158 #[error("can't resolve {1} from {0}")]159 ImportFileNotFound(SourcePath, ResolvePathOwned),160 #[error("resolved file not found: {:?}", .0)]161 ResolvedFileNotFound(SourcePath),162 #[error("can't import {0}: is a directory")]163 ImportIsADirectory(SourcePath),164 #[error("imported file is not valid utf-8: {0:?}")]165 ImportBadFileUtf8(SourcePath),166 #[error("import io error: {0}")]167 ImportIo(String),168 #[error("tried to import {1} from {0}, but imports are not supported")]169 ImportNotSupported(SourcePath, ResolvePathOwned),170 #[error("can't import from virtual file")]171 CantImportFromVirtualFile,172 #[error("syntax error: {error}")]173 ImportSyntaxError {174 path: Source,175 #[trace(skip)]176 error: Box<SyntaxError>,177 },178179 #[error("runtime error: {}", format_empty_str(.0))]180 RuntimeError(IStr),181 #[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]182 StackOverflow,183 #[error("infinite recursion detected")]184 InfiniteRecursionDetected,185 #[error("tried to index by fractional value")]186 FractionalIndex,187 #[error("attempted to divide by zero")]188 DivisionByZero,189190 #[error("string manifest output is not an string")]191 StringManifestOutputIsNotAString,192 #[error("stream manifest output is not an array")]193 StreamManifestOutputIsNotAArray,194 #[error("multi manifest output is not an object")]195 MultiManifestOutputIsNotAObject,196197 #[error("cant recurse stream manifest")]198 StreamManifestOutputCannotBeRecursed,199 #[error("stream manifest output cannot consist of raw strings")]200 StreamManifestCannotNestString,201202 #[error("{}", format_empty_str(.0))]203 ImportCallbackError(String),204 #[error("invalid unicode codepoint: {0}")]205 InvalidUnicodeCodepointGot(u32),206207 #[error("convert num value: {0}")]208 ConvertNumValue(#[from] ConvertNumValueError),209210 #[error("format error: {0}")]211 Format(#[from] FormatError),212 #[error("type error: {0}")]213 TypeError(TypeLocError),214215 #[cfg(feature = "anyhow-error")]216 #[error(transparent)]217 Other(#[trace(skip)] std::rc::Rc<anyhow::Error>),218}219220#[cfg(feature = "anyhow-error")]221impl From<anyhow::Error> for Error {222 fn from(e: anyhow::Error) -> Self {223 Self::new(ErrorKind::Other(std::rc::Rc::new(e)))224 }225}226227impl From<ErrorKind> for Error {228 fn from(e: ErrorKind) -> Self {229 Self::new(e)230 }231}232impl From<ConvertNumValueError> for Error {233 fn from(e: ConvertNumValueError) -> Self {234 Self::new(ErrorKind::ConvertNumValue(e))235 }236}237238impl From<Infallible> for Error {239 fn from(_value: Infallible) -> Self {240 unreachable!()241 }242}243244245#[derive(Clone, Debug, Trace)]246pub struct StackTraceElement {247 248 249 pub location: Option<Span>,250 251 pub desc: String,252}253#[derive(Debug, Clone, Trace)]254pub struct StackTrace(pub Vec<StackTraceElement>);255256#[derive(Clone, Trace)]257pub struct Error(Box<(ErrorKind, StackTrace)>);258259#[cfg(target_pointer_width = "64")]260static_assertions::assert_eq_size!(Error, usize);261262impl Error {263 pub fn new(e: ErrorKind) -> Self {264 Self(Box::new((e, StackTrace(vec![]))))265 }266267 pub const fn error(&self) -> &ErrorKind {268 &(self.0).0269 }270 pub fn error_mut(&mut self) -> &mut ErrorKind {271 &mut (self.0).0272 }273 pub const fn trace(&self) -> &StackTrace {274 &(self.0).1275 }276 pub fn trace_mut(&mut self) -> &mut StackTrace {277 &mut (self.0).1278 }279}280impl fmt::Display for Error {281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {282 writeln!(f, "{}", self.0.0)?;283 for el in &self.0.1.0 {284 write!(f, "\t{}", el.desc)?;285 if let Some(loc) = &el.location {286 write!(f, "at {}", loc.0.0.0)?;287 loc.0.map_source_locations(&[loc.1, loc.2]);288 }289 writeln!(f)?;290 }291 Ok(())292 }293}294impl fmt::Debug for Error {295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {296 f.debug_tuple("LocError").field(&self.0).finish()297 }298}299impl std::error::Error for Error {}300301pub trait ErrorSource {302 fn to_location(self) -> Option<Span>;303}304impl<T: Acyclic> ErrorSource for &Spanned<T> {305 fn to_location(self) -> Option<Span> {306 Some(self.span.clone())307 }308}309impl ErrorSource for &Span {310 fn to_location(self) -> Option<Span> {311 Some(self.clone())312 }313}314impl ErrorSource for CallLocation<'_> {315 fn to_location(self) -> Option<Span> {316 self.0.cloned()317 }318}319320pub type Result<V, E = Error> = std::result::Result<V, E>;321pub trait ResultExt: Sized {322 #[must_use]323 fn with_description<O: Into<String>>(self, msg: impl FnOnce() -> O) -> Self;324 #[must_use]325 fn description(self, msg: &str) -> Self {326 self.with_description(|| msg)327 }328329 #[must_use]330 fn with_description_src<O: Into<String>>(331 self,332 src: impl ErrorSource,333 msg: impl FnOnce() -> O,334 ) -> Self;335 #[must_use]336 fn description_src(self, src: impl ErrorSource, msg: &str) -> Self {337 self.with_description_src(src, || msg)338 }339}340impl<T> ResultExt for Result<T, Error> {341 fn with_description<O: Into<String>>(mut self, msg: impl FnOnce() -> O) -> Self {342 if let Err(e) = &mut self {343 let trace = e.trace_mut();344 trace.0.push(StackTraceElement {345 location: None,346 desc: msg().into(),347 });348 }349 self350 }351352 fn with_description_src<O: Into<String>>(353 mut self,354 src: impl ErrorSource,355 msg: impl FnOnce() -> O,356 ) -> Self {357 if let Err(e) = &mut self {358 let trace = e.trace_mut();359 trace.0.push(StackTraceElement {360 location: src.to_location(),361 desc: msg().into(),362 });363 }364 self365 }366}367368#[macro_export]369macro_rules! bail {370 ($w:ident$(::$i:ident)*$(($($tt:tt)*))?) => {371 return Err($w$(::$i)*$(($($tt)*))?.into())372 };373 ($w:ident$(::$i:ident)*$({$($tt:tt)*})?) => {374 return Err($w$(::$i)*$({$($tt)*})?.into())375 };376 ($l:literal$(, $($tt:tt)*)?) => {377 return Err($crate::error::ErrorKind::RuntimeError($crate::jrsonnet_macros::format_istr!($l$(, $($tt)*)?)).into())378 };379}380381#[macro_export]382macro_rules! runtime_error {383 ($l:literal$(, $($tt:tt)*)?) => {384 $crate::error::Error::from($crate::error::ErrorKind::RuntimeError($crate::jrsonnet_macros::format_istr!($l$(, $($tt)*)?)))385 };386}