1use std::{2 cmp::Ordering,3 convert::Infallible,4 fmt::{self, Debug, Display},5};67use jrsonnet_gcmodule::{Acyclic, Trace};8use jrsonnet_interner::IStr;9use jrsonnet_parser::{BinaryOpType, Source, SourcePath, Span, Spanned, UnaryOpType};10use jrsonnet_types::ValType;11use thiserror::Error;1213use crate::{14 function::{CallLocation, FunctionSignature, ParamDefault, ParamName},15 stdlib::format::FormatError,16 typed::TypeLocError,17 val::ConvertNumValueError,18 ObjValue, ResolvePathOwned,19};2021pub(crate) fn format_found(list: &[IStr], what: &str) -> String {22 if list.is_empty() {23 return String::new();24 }25 let mut out = String::new();26 out.push_str("\nThere ");27 if list.len() > 1 {28 out.push_str("are ");29 } else {30 out.push_str("is a ");31 }32 out.push_str(what);33 if list.len() > 1 {34 out.push('s');35 }36 out.push_str(" with similar name");37 if list.len() > 1 {38 out.push('s');39 }40 out.push_str(" present: ");41 for (i, v) in list.iter().enumerate() {42 if i != 0 {43 out.push_str(", ");44 }45 out.push_str(v as &str);46 }47 out48}4950const fn format_empty_str(str: &str) -> &str {51 if str.is_empty() {52 "\"\" (empty string)"53 } else {54 str55 }56}5758pub(crate) fn suggest_object_fields(v: &ObjValue, key: IStr) -> Vec<IStr> {59 let mut heap = Vec::new();60 for field in v.fields_ex(61 true,62 #[cfg(feature = "exp-preserve-order")]63 false,64 ) {65 let conf = strsim::jaro_winkler(field.as_str(), key.as_str());66 if conf < 0.8 {67 continue;68 }69 assert!(field.as_str() != key.as_str(), "looks like string pooling failure, please write any info regarding this crash to https://github.com/CertainLach/jrsonnet/issues/113, thanks!");7071 heap.push((conf, field));72 }73 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));74 heap.into_iter().map(|v| v.1).collect()75}767778#[allow(missing_docs)]79#[derive(Error, Debug, Clone, Trace)]80#[non_exhaustive]81pub enum ErrorKind {82 #[error("intrinsic not found: {0}")]83 IntrinsicNotFound(IStr),8485 #[error("operator {0} does not operate on type {1}")]86 UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),87 #[error("binary operation {1} {0} {2} is not implemented")]88 BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),8990 #[error("self/super/$ are only usable inside objects")]91 CantUseSelfSupOutsideOfObject,92 #[error("no super found")]93 NoSuperFound,9495 #[error("for loop can only iterate over arrays")]96 InComprehensionCanOnlyIterateOverArray,9798 #[error("array out of bounds: {0} is not within [0,{1})")]99 ArrayBoundsError(isize, usize),100 #[error("string out of bounds: {0} is not within [0,{1})")]101 StringBoundsError(usize, usize),102103 #[error("assert failed: {}", format_empty_str(.0))]104 AssertionFailed(IStr),105106 #[error("local is not defined: {0}{found}", found = format_found(.1, "local"))]107 VariableIsNotDefined(IStr, Vec<IStr>),108 #[error("duplicate local var: {0}")]109 DuplicateLocalVar(IStr),110111 #[error("type mismatch: expected {expected}, got {2} {0}", expected = .1.iter().map(|e| format!("{e}")).collect::<Vec<_>>().join(", "))]112 TypeMismatch(&'static str, Vec<ValType>, ValType),113 #[error("no such field: {}{}", format_empty_str(.0), format_found(.1, "field"))]114 NoSuchField(IStr, Vec<IStr>),115116 #[error("only functions can be called, got {0}")]117 OnlyFunctionsCanBeCalledGot(ValType),118 #[error("parameter {0} is not defined")]119 UnknownFunctionParameter(IStr),120 #[error("argument {0} is already bound")]121 BindingParameterASecondTime(IStr),122 #[error("too many args, function has {0}\nFunction has the following signature: {1}")]123 TooManyArgsFunctionHas(usize, FunctionSignature),124 #[error("function argument is not passed: {0}\nFunction has the following signature: {1}")]125 FunctionParameterNotBoundInCall(ParamName, FunctionSignature),126127 #[error("external variable is not defined: {0}")]128 UndefinedExternalVariable(IStr),129130 #[error("field name should be string, got {0}")]131 FieldMustBeStringGot(ValType),132 #[error("duplicate field name: {}", format_empty_str(.0))]133 DuplicateFieldName(IStr),134135 #[error("attempted to index array with string {}", format_empty_str(.0))]136 AttemptedIndexAnArrayWithString(IStr),137 #[error("{0} index type should be {1}, got {2}")]138 ValueIndexMustBeTypeGot(ValType, ValType, ValType),139 #[error("cant index into {0}")]140 CantIndexInto(ValType),141 #[error("{0} is not indexable")]142 ValueIsNotIndexable(ValType),143144 #[error("super can't be used standalone")]145 StandaloneSuper,146147 #[error("can't resolve {1} from {0}")]148 ImportFileNotFound(SourcePath, ResolvePathOwned),149 #[error("resolved file not found: {:?}", .0)]150 ResolvedFileNotFound(SourcePath),151 #[error("can't import {0}: is a directory")]152 ImportIsADirectory(SourcePath),153 #[error("imported file is not valid utf-8: {0:?}")]154 ImportBadFileUtf8(SourcePath),155 #[error("import io error: {0}")]156 ImportIo(String),157 #[error("tried to import {1} from {0}, but imports are not supported")]158 ImportNotSupported(SourcePath, ResolvePathOwned),159 #[error("can't import from virtual file")]160 CantImportFromVirtualFile,161 #[error(162 "syntax error: {}",163 164 {.error.expected.tokens().find(|t| t.starts_with("!!!")).map_or_else(|| {165 format!(166 "expected {}, got {:?}",167 .error.expected,168 .path.code().chars().nth(error.location.offset)169 .map_or_else(|| "EOF".into(), |c| c.to_string())170 )171 }, |v| v[3..].into())}172 )]173 ImportSyntaxError {174 path: Source,175 #[trace(skip)]176 error: Box<jrsonnet_parser::ParseError>,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}232233impl From<Infallible> for Error {234 fn from(_value: Infallible) -> Self {235 unreachable!()236 }237}238239240#[derive(Clone, Debug, Trace)]241pub struct StackTraceElement {242 243 244 pub location: Option<Span>,245 246 pub desc: String,247}248#[derive(Debug, Clone, Trace)]249pub struct StackTrace(pub Vec<StackTraceElement>);250251#[derive(Clone, Trace)]252pub struct Error(Box<(ErrorKind, StackTrace)>);253impl Error {254 pub fn new(e: ErrorKind) -> Self {255 Self(Box::new((e, StackTrace(vec![]))))256 }257258 pub const fn error(&self) -> &ErrorKind {259 &(self.0).0260 }261 pub fn error_mut(&mut self) -> &mut ErrorKind {262 &mut (self.0).0263 }264 pub const fn trace(&self) -> &StackTrace {265 &(self.0).1266 }267 pub fn trace_mut(&mut self) -> &mut StackTrace {268 &mut (self.0).1269 }270}271impl Display for Error {272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {273 writeln!(f, "{}", self.0 .0)?;274 for el in &self.0 .1 .0 {275 write!(f, "\t{}", el.desc)?;276 if let Some(loc) = &el.location {277 write!(f, "at {}", loc.0 .0 .0)?;278 loc.0.map_source_locations(&[loc.1, loc.2]);279 }280 writeln!(f)?;281 }282 Ok(())283 }284}285impl Debug for Error {286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {287 f.debug_tuple("LocError").field(&self.0).finish()288 }289}290impl std::error::Error for Error {}291292pub trait ErrorSource {293 fn to_location(self) -> Option<Span>;294}295impl<T: Acyclic> ErrorSource for &Spanned<T> {296 fn to_location(self) -> Option<Span> {297 Some(self.span())298 }299}300impl ErrorSource for &Span {301 fn to_location(self) -> Option<Span> {302 Some(self.clone())303 }304}305impl ErrorSource for CallLocation<'_> {306 fn to_location(self) -> Option<Span> {307 self.0.cloned()308 }309}310311pub type Result<V, E = Error> = std::result::Result<V, E>;312pub trait ResultExt: Sized {313 #[must_use]314 fn with_description<O: Into<String>>(self, msg: impl FnOnce() -> O) -> Self;315 #[must_use]316 fn description(self, msg: &str) -> Self {317 self.with_description(|| msg)318 }319320 #[must_use]321 fn with_description_src<O: Into<String>>(322 self,323 src: impl ErrorSource,324 msg: impl FnOnce() -> O,325 ) -> Self;326 #[must_use]327 fn description_src(self, src: impl ErrorSource, msg: &str) -> Self {328 self.with_description_src(src, || msg)329 }330}331impl<T> ResultExt for Result<T, Error> {332 fn with_description<O: Into<String>>(mut self, msg: impl FnOnce() -> O) -> Self {333 if let Err(e) = &mut self {334 let trace = e.trace_mut();335 trace.0.push(StackTraceElement {336 location: None,337 desc: msg().into(),338 });339 }340 self341 }342343 fn with_description_src<O: Into<String>>(344 mut self,345 src: impl ErrorSource,346 msg: impl FnOnce() -> O,347 ) -> Self {348 if let Err(e) = &mut self {349 let trace = e.trace_mut();350 trace.0.push(StackTraceElement {351 location: src.to_location(),352 desc: msg().into(),353 });354 }355 self356 }357}358359#[macro_export]360macro_rules! bail {361 ($w:ident$(::$i:ident)*$(($($tt:tt)*))?) => {362 return Err($w$(::$i)*$(($($tt)*))?.into())363 };364 ($w:ident$(::$i:ident)*$({$($tt:tt)*})?) => {365 return Err($w$(::$i)*$({$($tt)*})?.into())366 };367 ($l:literal$(, $($tt:tt)*)?) => {368 return Err($crate::error::ErrorKind::RuntimeError($crate::jrsonnet_macros::format_istr!($l$(, $($tt)*)?)).into())369 };370}371372#[macro_export]373macro_rules! runtime_error {374 ($l:literal$(, $($tt:tt)*)?) => {375 $crate::error::Error::from($crate::error::ErrorKind::RuntimeError($crate::jrsonnet_macros::format_istr!($l$(, $($tt)*)?)))376 };377}