difftreelog
fix type errors are acyclic
in: master
1 file changed
crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth1use std::{fmt::Display, rc::Rc};23pub(crate) mod conversions;4pub use conversions::*;5use jrsonnet_gcmodule::Trace;6pub use jrsonnet_types::{ComplexValType, ValType};7use thiserror::Error;89use crate::{10 error::{Error, ErrorKind, Result},11 in_description_frame, Val,12};1314#[derive(Debug, Error, Clone, Trace)]15pub enum TypeError {16 #[error("expected {0}, got {1}")]17 ExpectedGot(ComplexValType, ValType),18 #[error("missing property {0} from {1}")]19 MissingProperty(#[trace(skip)] Rc<str>, ComplexValType),20 #[error("every failed from {0}:\n{1}")]21 UnionFailed(ComplexValType, TypeLocErrorList),22 #[error(23 "number out of bounds: {0} not in {start}..{end}",24 start = .1.map(|v|v.to_string()).unwrap_or_default(),25 end = .2.map(|v|v.to_string()).unwrap_or_default(),26 )]27 BoundsFailed(f64, Option<f64>, Option<f64>),28}29impl From<TypeError> for Error {30 fn from(e: TypeError) -> Self {31 ErrorKind::TypeError(e.into()).into()32 }33}3435#[derive(Debug, Clone, Trace)]36pub struct TypeLocError(Box<TypeError>, ValuePathStack);37impl From<TypeError> for TypeLocError {38 fn from(e: TypeError) -> Self {39 Self(Box::new(e), ValuePathStack(Vec::new()))40 }41}42impl From<TypeLocError> for Error {43 fn from(e: TypeLocError) -> Self {44 ErrorKind::TypeError(e).into()45 }46}47impl Display for TypeLocError {48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {49 write!(f, "{}", self.0)?;50 if !(self.1).0.is_empty() {51 write!(f, " at {}", self.1)?;52 }53 Ok(())54 }55}5657#[derive(Debug, Clone, Trace)]58pub struct TypeLocErrorList(Vec<TypeLocError>);59impl Display for TypeLocErrorList {60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {61 use std::fmt::Write;62 let mut out = String::new();63 for (i, err) in self.0.iter().enumerate() {64 if i != 0 {65 writeln!(f)?;66 }67 out.clear();68 write!(out, "{err}")?;6970 for (i, line) in out.lines().enumerate() {71 if line.trim().is_empty() {72 continue;73 }74 if i == 0 {75 write!(f, " - ")?;76 } else {77 writeln!(f)?;78 write!(f, " ")?;79 }80 write!(f, "{line}")?;81 }82 }83 Ok(())84 }85}8687fn push_type_description(88 error_reason: impl Fn() -> String,89 path: impl Fn() -> ValuePathItem,90 item: impl Fn() -> Result<()>,91) -> Result<()> {92 in_description_frame(error_reason, || match item() {93 Ok(()) => Ok(()),94 Err(mut e) => {95 if let ErrorKind::TypeError(e) = &mut e.error_mut() {96 (e.1).0.push(path());97 }98 Err(e)99 }100 })101}102103// TODO: check_fast for fast path of union type checking104pub trait CheckType {105 fn check(&self, value: &Val) -> Result<()>;106}107108impl CheckType for ValType {109 fn check(&self, value: &Val) -> Result<()> {110 let got = value.value_type();111 if got != *self {112 let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();113 return Err(loc_error.into());114 }115 Ok(())116 }117}118119#[derive(Clone, Debug, Trace)]120enum ValuePathItem {121 Field(#[trace(skip)] Rc<str>),122 Index(u64),123}124impl Display for ValuePathItem {125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {126 match self {127 Self::Field(name) => write!(f, ".{name:?}")?,128 Self::Index(idx) => write!(f, "[{idx}]")?,129 }130 Ok(())131 }132}133134#[derive(Clone, Debug, Trace)]135struct ValuePathStack(Vec<ValuePathItem>);136impl Display for ValuePathStack {137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {138 write!(f, "self")?;139 for elem in self.0.iter().rev() {140 write!(f, "{elem}")?;141 }142 Ok(())143 }144}145146impl CheckType for ComplexValType {147 #[allow(clippy::too_many_lines)]148 fn check(&self, value: &Val) -> Result<()> {149 match self {150 Self::Any => Ok(()),151 Self::Simple(t) => t.check(value),152 Self::Char => match value {153 Val::Str(s) if s.len() == 1 || s.clone().into_flat().chars().count() == 1 => Ok(()),154 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),155 },156 Self::BoundedNumber(from, to) => {157 if let Val::Num(n) = value {158 let n = n.get();159 if from.map(|from| from > n).unwrap_or(false)160 || to.map(|to| to < n).unwrap_or(false)161 {162 return Err(TypeError::BoundsFailed(n, *from, *to).into());163 }164 Ok(())165 } else {166 Err(TypeError::ExpectedGot(self.clone(), value.value_type()).into())167 }168 }169 Self::Array(elem_type) => match value {170 Val::Arr(a) => {171 for (i, item) in a.iter().enumerate() {172 push_type_description(173 || format!("array index {i}"),174 || ValuePathItem::Index(i as u64),175 || elem_type.check(&item.clone()?),176 )?;177 }178 Ok(())179 }180 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),181 },182 Self::ArrayRef(elem_type) => match value {183 Val::Arr(a) => {184 for (i, item) in a.iter().enumerate() {185 push_type_description(186 || format!("array index {i}"),187 || ValuePathItem::Index(i as u64),188 || elem_type.check(&item.clone()?),189 )?;190 }191 Ok(())192 }193 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),194 },195 Self::AttrsOf(a) => match value {196 Val::Obj(o) => {197 for (_key, value) in o.iter(198 #[cfg(feature = "exp-preserve-order")]199 false,200 ) {201 let value = value?;202 a.check(&value)?;203 }204 Ok(())205 }206 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),207 },208 Self::ObjectRef(elems) => match value {209 Val::Obj(obj) => {210 for (k, v) in *elems {211 if let Some(got_v) = obj.get((*k).into())? {212 push_type_description(213 || format!("property {k}"),214 || ValuePathItem::Field((*k).into()),215 || v.check(&got_v),216 )?;217 } else {218 return Err(219 TypeError::MissingProperty((*k).into(), self.clone()).into()220 );221 }222 }223 Ok(())224 }225 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),226 },227 Self::Union(types) => {228 let mut errors = Vec::new();229 for ty in types {230 match ty.check(value) {231 Ok(()) => {232 return Ok(());233 }234 Err(e) => match e.error() {235 ErrorKind::TypeError(e) => errors.push(e.clone()),236 _ => return Err(e),237 },238 }239 }240 Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())241 }242 Self::UnionRef(types) => {243 let mut errors = Vec::new();244 for ty in *types {245 match ty.check(value) {246 Ok(()) => {247 return Ok(());248 }249 Err(e) => match e.error() {250 ErrorKind::TypeError(e) => errors.push(e.clone()),251 _ => return Err(e),252 },253 }254 }255 Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())256 }257 Self::Sum(types) => {258 for ty in types {259 ty.check(value)?;260 }261 Ok(())262 }263 Self::SumRef(types) => {264 for ty in *types {265 ty.check(value)?;266 }267 Ok(())268 }269 Self::Lazy(_lazy) => Ok(()),270 }271 }272}1use std::{fmt::Display, rc::Rc};23pub(crate) mod conversions;4pub use conversions::*;5use jrsonnet_gcmodule::Acyclic;6pub use jrsonnet_types::{ComplexValType, ValType};7use thiserror::Error;89use crate::{10 error::{Error, ErrorKind, Result},11 in_description_frame, Val,12};1314#[derive(Debug, Error, Clone, Acyclic)]15pub enum TypeError {16 #[error("expected {0}, got {1}")]17 ExpectedGot(ComplexValType, ValType),18 #[error("missing property {0} from {1}")]19 MissingProperty(Rc<str>, ComplexValType),20 #[error("every failed from {0}:\n{1}")]21 UnionFailed(ComplexValType, TypeLocErrorList),22 #[error(23 "number out of bounds: {0} not in {start}..{end}",24 start = .1.map(|v|v.to_string()).unwrap_or_default(),25 end = .2.map(|v|v.to_string()).unwrap_or_default(),26 )]27 BoundsFailed(f64, Option<f64>, Option<f64>),28}29impl From<TypeError> for Error {30 fn from(e: TypeError) -> Self {31 ErrorKind::TypeError(e.into()).into()32 }33}3435#[derive(Debug, Clone, Acyclic)]36pub struct TypeLocError(Box<TypeError>, ValuePathStack);37impl From<TypeError> for TypeLocError {38 fn from(e: TypeError) -> Self {39 Self(Box::new(e), ValuePathStack(Vec::new()))40 }41}42impl From<TypeLocError> for Error {43 fn from(e: TypeLocError) -> Self {44 ErrorKind::TypeError(e).into()45 }46}47impl Display for TypeLocError {48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {49 write!(f, "{}", self.0)?;50 if !(self.1).0.is_empty() {51 write!(f, " at {}", self.1)?;52 }53 Ok(())54 }55}5657#[derive(Debug, Clone, Acyclic)]58pub struct TypeLocErrorList(Vec<TypeLocError>);59impl Display for TypeLocErrorList {60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {61 use std::fmt::Write;62 let mut out = String::new();63 for (i, err) in self.0.iter().enumerate() {64 if i != 0 {65 writeln!(f)?;66 }67 out.clear();68 write!(out, "{err}")?;6970 for (i, line) in out.lines().enumerate() {71 if line.trim().is_empty() {72 continue;73 }74 if i == 0 {75 write!(f, " - ")?;76 } else {77 writeln!(f)?;78 write!(f, " ")?;79 }80 write!(f, "{line}")?;81 }82 }83 Ok(())84 }85}8687fn push_type_description(88 error_reason: impl Fn() -> String,89 path: impl Fn() -> ValuePathItem,90 item: impl Fn() -> Result<()>,91) -> Result<()> {92 in_description_frame(error_reason, || match item() {93 Ok(()) => Ok(()),94 Err(mut e) => {95 if let ErrorKind::TypeError(e) = &mut e.error_mut() {96 (e.1).0.push(path());97 }98 Err(e)99 }100 })101}102103// TODO: check_fast for fast path of union type checking104pub trait CheckType {105 fn check(&self, value: &Val) -> Result<()>;106}107108impl CheckType for ValType {109 fn check(&self, value: &Val) -> Result<()> {110 let got = value.value_type();111 if got != *self {112 let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();113 return Err(loc_error.into());114 }115 Ok(())116 }117}118119#[derive(Clone, Debug, Acyclic)]120enum ValuePathItem {121 Field(Rc<str>),122 Index(u64),123}124impl Display for ValuePathItem {125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {126 match self {127 Self::Field(name) => write!(f, ".{name:?}")?,128 Self::Index(idx) => write!(f, "[{idx}]")?,129 }130 Ok(())131 }132}133134#[derive(Clone, Debug, Acyclic)]135struct ValuePathStack(Vec<ValuePathItem>);136impl Display for ValuePathStack {137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {138 write!(f, "self")?;139 for elem in self.0.iter().rev() {140 write!(f, "{elem}")?;141 }142 Ok(())143 }144}145146impl CheckType for ComplexValType {147 #[allow(clippy::too_many_lines)]148 fn check(&self, value: &Val) -> Result<()> {149 match self {150 Self::Any => Ok(()),151 Self::Simple(t) => t.check(value),152 Self::Char => match value {153 Val::Str(s) if s.len() == 1 || s.clone().into_flat().chars().count() == 1 => Ok(()),154 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),155 },156 Self::BoundedNumber(from, to) => {157 if let Val::Num(n) = value {158 let n = n.get();159 if from.map(|from| from > n).unwrap_or(false)160 || to.map(|to| to < n).unwrap_or(false)161 {162 return Err(TypeError::BoundsFailed(n, *from, *to).into());163 }164 Ok(())165 } else {166 Err(TypeError::ExpectedGot(self.clone(), value.value_type()).into())167 }168 }169 Self::Array(elem_type) => match value {170 Val::Arr(a) => {171 for (i, item) in a.iter().enumerate() {172 push_type_description(173 || format!("array index {i}"),174 || ValuePathItem::Index(i as u64),175 || elem_type.check(&item.clone()?),176 )?;177 }178 Ok(())179 }180 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),181 },182 Self::ArrayRef(elem_type) => match value {183 Val::Arr(a) => {184 for (i, item) in a.iter().enumerate() {185 push_type_description(186 || format!("array index {i}"),187 || ValuePathItem::Index(i as u64),188 || elem_type.check(&item.clone()?),189 )?;190 }191 Ok(())192 }193 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),194 },195 Self::AttrsOf(a) => match value {196 Val::Obj(o) => {197 for (_key, value) in o.iter(198 #[cfg(feature = "exp-preserve-order")]199 false,200 ) {201 let value = value?;202 a.check(&value)?;203 }204 Ok(())205 }206 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),207 },208 Self::ObjectRef(elems) => match value {209 Val::Obj(obj) => {210 for (k, v) in *elems {211 if let Some(got_v) = obj.get((*k).into())? {212 push_type_description(213 || format!("property {k}"),214 || ValuePathItem::Field((*k).into()),215 || v.check(&got_v),216 )?;217 } else {218 return Err(219 TypeError::MissingProperty((*k).into(), self.clone()).into()220 );221 }222 }223 Ok(())224 }225 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),226 },227 Self::Union(types) => {228 let mut errors = Vec::new();229 for ty in types {230 match ty.check(value) {231 Ok(()) => {232 return Ok(());233 }234 Err(e) => match e.error() {235 ErrorKind::TypeError(e) => errors.push(e.clone()),236 _ => return Err(e),237 },238 }239 }240 Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())241 }242 Self::UnionRef(types) => {243 let mut errors = Vec::new();244 for ty in *types {245 match ty.check(value) {246 Ok(()) => {247 return Ok(());248 }249 Err(e) => match e.error() {250 ErrorKind::TypeError(e) => errors.push(e.clone()),251 _ => return Err(e),252 },253 }254 }255 Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())256 }257 Self::Sum(types) => {258 for ty in types {259 ty.check(value)?;260 }261 Ok(())262 }263 Self::SumRef(types) => {264 for ty in *types {265 ty.check(value)?;266 }267 Ok(())268 }269 Self::Lazy(_lazy) => Ok(()),270 }271 }272}