difftreelog
feat(ir) source url
in: master
7 files changed
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -35,14 +35,14 @@
pub use ctx::*;
pub use dynamic::*;
-pub use error::{Error, ErrorKind::*, Result, ResultExt};
+pub use error::{Error, ErrorKind::*, Result, ResultExt, StackTraceElement};
pub use evaluate::ensure_sufficient_stack;
use function::CallLocation;
pub use import::*;
use jrsonnet_gcmodule::{Cc, Trace, cc_dyn};
pub use jrsonnet_interner::{IBytes, IStr};
use jrsonnet_ir::Expr;
-pub use jrsonnet_ir::{NumValue, Source, SourcePath, Span};
+pub use jrsonnet_ir::{NumValue, Source, SourcePath, SourceUrl, SourceVirtual, Span};
#[doc(hidden)]
pub use jrsonnet_macros;
@@ -396,9 +396,17 @@
file.parsed = Some(
parse_jsonnet(&code, file_name.clone())
.map(Rc::new)
- .map_err(|e| ImportSyntaxError {
- path: file_name.clone(),
- error: Box::new(e),
+ .map_err(|e| {
+ let span = e.location.clone();
+ let mut err = Error::from(ImportSyntaxError {
+ path: file_name.clone(),
+ error: Box::new(e),
+ });
+ err.trace_mut().0.push(StackTraceElement {
+ location: Some(span),
+ desc: "parse imported".to_string(),
+ });
+ err
})?,
);
}
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -82,18 +82,24 @@
) -> Result<(), std::fmt::Error> {
if start.line == end.line {
if start.column == end.column {
- write!(out, "{}:{}", start.line, end.column.saturating_sub(1))?;
+ write!(out, "{}:{}", start.line, start.column)?;
} else {
- write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;
+ write!(
+ out,
+ "{}:{}-{}",
+ start.line,
+ start.column,
+ end.column.saturating_sub(1)
+ )?;
}
} else {
write!(
out,
"{}:{}-{}:{}",
- start.line,
- end.column.saturating_sub(1),
start.line,
- end.column
+ start.column,
+ end.line,
+ end.column.saturating_sub(1)
)?;
}
Ok(())
@@ -131,22 +137,13 @@
|| path.source_path().to_string(),
|r| self.resolver.resolve(r),
);
- let mut offset = error.location.1 as usize;
- let is_eof = if offset >= path.code().len() {
- offset = path.code().len().saturating_sub(1);
- true
- } else {
- false
- };
+ let offset = (error.location.1 as usize).min(path.code().len());
#[expect(clippy::cast_possible_truncation, reason = "code is limited by 4gb")]
- let mut location = path
+ let location = path
.map_source_locations(&[offset as u32])
.into_iter()
.next()
.unwrap();
- if is_eof {
- location.column += 1;
- }
write!(n, ":").unwrap();
print_code_location(&mut n, &location, &location).unwrap();
crates/jrsonnet-formatter/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/lib.rs
+++ b/crates/jrsonnet-formatter/src/lib.rs
@@ -895,10 +895,16 @@
}
}
+#[derive(Default)]
pub struct FormatOptions {
// 0 for hard tabs, otherwise number of spaces
pub indent: u8,
}
+impl FormatOptions {
+ pub fn new() -> Self {
+ Self::default()
+ }
+}
#[allow(
clippy::result_large_err,
crates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir-parser/src/lib.rs
+++ b/crates/jrsonnet-ir-parser/src/lib.rs
@@ -220,10 +220,7 @@
fn ident(p: &mut Parser<'_>) -> Result<IStr> {
if !p.at(SyntaxKind::IDENT) {
- return Err(p.error(format!(
- "expected identifier, got {}",
- p.current_desc()
- )));
+ return Err(p.error(format!("expected identifier, got {}", p.current_desc())));
}
let text = p.text();
p.eat_any();
crates/jrsonnet-ir/src/lib.rsdiffbeforeafterboth1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]23mod expr;4use std::{cmp::Ordering, fmt, ops::Deref};56pub use expr::*;7use jrsonnet_gcmodule::Acyclic;8pub use jrsonnet_interner::IStr;9pub mod function;10mod location;11mod source;12pub mod unescape;13pub mod visit;1415pub use location::CodeLocation;16pub use source::{17 Source, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,18 SourcePathT, SourceVirtual,19};2021// It seels to be a wrong place for this kind of stuff, but as it would also be used for static analysis and22// is already wanted for NumValue, I don't know a better place.23#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]24pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;25#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]26pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;2728/// Represents jsonnet number29/// Jsonnet numbers are finite f64, with NaNs disallowed30#[derive(Acyclic, Clone, Copy)]31pub struct NumValue(f64);32impl NumValue {33 /// Creates a [`NumValue`], if value is finite and not NaN34 pub fn new(v: f64) -> Option<Self> {35 if !v.is_finite() {36 return None;37 }38 Some(Self(v))39 }40 #[inline]41 pub const fn get(&self) -> f64 {42 self.043 }44 pub fn truncate_for_bitwise(self) -> Result<i64, ConvertNumValueError> {45 if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {46 return Err(ConvertNumValueError::BitwiseSafeRange);47 }48 #[expect(clippy::cast_possible_truncation, reason = "intended")]49 Ok(self.0 as i64)50 }51}52impl PartialEq for NumValue {53 fn eq(&self, other: &Self) -> bool {54 self.0 == other.055 }56}57impl Eq for NumValue {}58impl Ord for NumValue {59 #[inline]60 fn cmp(&self, other: &Self) -> Ordering {61 // Can't use `total_cmp`: its behavior for `-0` and `0`62 // is not following wanted.63 unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }64 }65}66impl PartialOrd for NumValue {67 #[inline]68 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {69 Some(self.cmp(other))70 }71}72impl fmt::Debug for NumValue {73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {74 fmt::Debug::fmt(&self.0, f)75 }76}77impl fmt::Display for NumValue {78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {79 fmt::Display::fmt(&self.0, f)80 }81}82impl Deref for NumValue {83 type Target = f64;8485 #[inline]86 fn deref(&self) -> &Self::Target {87 &self.088 }89}90macro_rules! impl_num {91 ($($ty:ty),+) => {$(92 impl From<$ty> for NumValue {93 #[inline]94 fn from(value: $ty) -> Self {95 Self(value.into())96 }97 }98 )+};99}100impl_num!(i8, u8, i16, u16, i32, u32);101102#[derive(Clone, Copy, Debug, thiserror::Error, Acyclic)]103pub enum ConvertNumValueError {104 #[error("overflow")]105 Overflow,106 #[error("underflow")]107 Underflow,108 #[error("non-finite")]109 NonFinite,110 #[error("float out of safe int range")]111 BitwiseSafeRange,112}113114macro_rules! impl_try_num {115 ($($ty:ty),+) => {$(116 impl TryFrom<$ty> for NumValue {117 type Error = ConvertNumValueError;118 #[inline]119 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {120 #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]121 let value = value as f64;122 if value < MIN_SAFE_INTEGER {123 return Err(ConvertNumValueError::Underflow)124 } else if value > MAX_SAFE_INTEGER {125 return Err(ConvertNumValueError::Overflow)126 }127 // Number is finite.128 Ok(Self(value))129 }130 }131 )+};132}133impl_try_num!(usize, isize, i64, u64);134135impl TryFrom<f64> for NumValue {136 type Error = ConvertNumValueError;137138 #[inline]139 fn try_from(value: f64) -> Result<Self, Self::Error> {140 Self::new(value).ok_or(ConvertNumValueError::NonFinite)141 }142}143impl TryFrom<f32> for NumValue {144 type Error = ConvertNumValueError;145146 #[inline]147 fn try_from(value: f32) -> Result<Self, Self::Error> {148 Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)149 }150}1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]23mod expr;4use std::{cmp::Ordering, fmt, ops::Deref};56pub use expr::*;7use jrsonnet_gcmodule::Acyclic;8pub use jrsonnet_interner::IStr;9pub mod function;10mod location;11mod source;12pub mod unescape;13pub mod visit;1415pub use location::CodeLocation;16pub use source::{17 Source, SourceDefaultIgnoreJpath, SourceDirectory, SourceFifo, SourceFile, SourcePath,18 SourcePathT, SourceUrl, SourceVirtual,19};2021// It seels to be a wrong place for this kind of stuff, but as it would also be used for static analysis and22// is already wanted for NumValue, I don't know a better place.23#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]24pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;25#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]26pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;2728/// Represents jsonnet number29/// Jsonnet numbers are finite f64, with NaNs disallowed30#[derive(Acyclic, Clone, Copy)]31pub struct NumValue(f64);32impl NumValue {33 /// Creates a [`NumValue`], if value is finite and not NaN34 pub fn new(v: f64) -> Option<Self> {35 if !v.is_finite() {36 return None;37 }38 Some(Self(v))39 }40 #[inline]41 pub const fn get(&self) -> f64 {42 self.043 }44 pub fn truncate_for_bitwise(self) -> Result<i64, ConvertNumValueError> {45 if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {46 return Err(ConvertNumValueError::BitwiseSafeRange);47 }48 #[expect(clippy::cast_possible_truncation, reason = "intended")]49 Ok(self.0 as i64)50 }51}52impl PartialEq for NumValue {53 fn eq(&self, other: &Self) -> bool {54 self.0 == other.055 }56}57impl Eq for NumValue {}58impl Ord for NumValue {59 #[inline]60 fn cmp(&self, other: &Self) -> Ordering {61 // Can't use `total_cmp`: its behavior for `-0` and `0`62 // is not following wanted.63 unsafe { self.0.partial_cmp(&other.0).unwrap_unchecked() }64 }65}66impl PartialOrd for NumValue {67 #[inline]68 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {69 Some(self.cmp(other))70 }71}72impl fmt::Debug for NumValue {73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {74 fmt::Debug::fmt(&self.0, f)75 }76}77impl fmt::Display for NumValue {78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {79 fmt::Display::fmt(&self.0, f)80 }81}82impl Deref for NumValue {83 type Target = f64;8485 #[inline]86 fn deref(&self) -> &Self::Target {87 &self.088 }89}90macro_rules! impl_num {91 ($($ty:ty),+) => {$(92 impl From<$ty> for NumValue {93 #[inline]94 fn from(value: $ty) -> Self {95 Self(value.into())96 }97 }98 )+};99}100impl_num!(i8, u8, i16, u16, i32, u32);101102#[derive(Clone, Copy, Debug, thiserror::Error, Acyclic)]103pub enum ConvertNumValueError {104 #[error("overflow")]105 Overflow,106 #[error("underflow")]107 Underflow,108 #[error("non-finite")]109 NonFinite,110 #[error("float out of safe int range")]111 BitwiseSafeRange,112}113114macro_rules! impl_try_num {115 ($($ty:ty),+) => {$(116 impl TryFrom<$ty> for NumValue {117 type Error = ConvertNumValueError;118 #[inline]119 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {120 #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]121 let value = value as f64;122 if value < MIN_SAFE_INTEGER {123 return Err(ConvertNumValueError::Underflow)124 } else if value > MAX_SAFE_INTEGER {125 return Err(ConvertNumValueError::Overflow)126 }127 // Number is finite.128 Ok(Self(value))129 }130 }131 )+};132}133impl_try_num!(usize, isize, i64, u64);134135impl TryFrom<f64> for NumValue {136 type Error = ConvertNumValueError;137138 #[inline]139 fn try_from(value: f64) -> Result<Self, Self::Error> {140 Self::new(value).ok_or(ConvertNumValueError::NonFinite)141 }142}143impl TryFrom<f32> for NumValue {144 type Error = ConvertNumValueError;145146 #[inline]147 fn try_from(value: f32) -> Result<Self, Self::Error> {148 Self::new(f64::from(value)).ok_or(ConvertNumValueError::NonFinite)149 }150}crates/jrsonnet-ir/src/location.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir/src/location.rs
+++ b/crates/jrsonnet-ir/src/location.rs
@@ -29,7 +29,7 @@
return [CodeLocation::default(); S];
}
let mut line = 1;
- let mut column = 1;
+ let mut column = 0;
let max_offset = *offsets.iter().max().expect("offsets is not empty");
let mut offset_map = offsets
@@ -63,7 +63,7 @@
}
if ch == '\n' {
line += 1;
- column = 1;
+ column = 0;
for idx in with_no_known_line_ending.drain(..) {
out[idx].line_end_offset = pos;
@@ -98,14 +98,14 @@
CodeLocation {
offset: 0,
line: 1,
- column: 2,
+ column: 1,
line_start_offset: 0,
line_end_offset: 11,
},
CodeLocation {
offset: 14,
line: 2,
- column: 4,
+ column: 3,
line_start_offset: 12,
line_end_offset: 67
}
crates/jrsonnet-ir/src/source.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir/src/source.rs
+++ b/crates/jrsonnet-ir/src/source.rs
@@ -8,6 +8,7 @@
use jrsonnet_gcmodule::Acyclic;
use jrsonnet_interner::{IBytes, IStr};
+use url::Url;
use crate::location::{CodeLocation, location_to_offset, offset_to_location};
@@ -186,6 +187,28 @@
any_ext_impl!(SourcePathT);
}
+#[derive(Acyclic, Hash, PartialEq, Eq, Debug)]
+pub struct SourceUrl(Url);
+impl SourceUrl {
+ pub fn new(url: Url) -> Self {
+ Self(url)
+ }
+}
+impl Display for SourceUrl {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+impl SourcePathT for SourceUrl {
+ fn is_default(&self) -> bool {
+ false
+ }
+ fn path(&self) -> Option<&Path> {
+ None
+ }
+ any_ext_impl!(SourcePathT);
+}
+
/// Represents path to the directory on the disk
///
/// See also [`SourceFile`]