git.delta.rocks / jrsonnet / refs/commits / b3f009bb7b2e

difftreelog

source

crates/jrsonnet-evaluator/src/error.rs10.3 KiBsourcehistory
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}7677/// Possible errors78#[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		// Peg has no fancier way to handle critical parsing errors https://github.com/kevinmehall/rust-peg/issues/225164		{.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}238239/// Single stack trace frame240#[derive(Clone, Debug, Trace)]241pub struct StackTraceElement {242	/// Source of this frame243	/// Some frames only act as description, without attached source244	pub location: Option<Span>,245	/// Frame description246	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}