git.delta.rocks / jrsonnet / refs/commits / 8885f2169203

difftreelog

feat show full error range instead of just start

sonopmszLach2026-04-04parent: #75beb61.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -15,14 +15,9 @@
 };
 
 #[derive(Debug, Clone)]
-pub struct SyntaxErrorLocation {
-	pub offset: usize,
-}
-
-#[derive(Debug, Clone)]
 pub struct SyntaxError {
 	pub message: String,
-	pub location: SyntaxErrorLocation,
+	pub location: (u32, u32),
 }
 impl fmt::Display for SyntaxError {
 	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -50,7 +50,7 @@
 #[cfg(not(any(feature = "ir-parser", feature = "peg-parser")))]
 compile_error!("at least one of `ir-parser` or `peg-parser` features must be enabled");
 
-pub use error::{SyntaxError, SyntaxErrorLocation};
+pub use error::SyntaxError;
 pub use obj::*;
 pub use rustc_hash;
 use rustc_hash::FxHashMap;
@@ -87,9 +87,7 @@
 	jrsonnet_ir_parser::parse(code, &jrsonnet_ir_parser::ParserSettings { source }).map_err(|e| {
 		SyntaxError {
 			message: e.message,
-			location: SyntaxErrorLocation {
-				offset: e.location.offset,
-			},
+			location: (e.location.0, e.location.1),
 		}
 	})
 }
@@ -107,7 +105,7 @@
 						"expected {}, got {:?}",
 						e.expected,
 						code.chars()
-							.nth(e.location.offset)
+							.nth(e.location.0)
 							.map_or_else(|| "EOF".into(), |c: char| c.to_string())
 					)
 				},
@@ -115,9 +113,7 @@
 			);
 		SyntaxError {
 			message,
-			location: SyntaxErrorLocation {
-				offset: e.location.offset,
-			},
+			location: e.location,
 		}
 	})
 }
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -122,7 +122,7 @@
 				|| path.source_path().to_string(),
 				|r| self.resolver.resolve(r),
 			);
-			let mut offset = error.location.offset;
+			let mut offset = error.location.0 as usize;
 			let is_eof = if offset >= path.code().len() {
 				offset = path.code().len().saturating_sub(1);
 				true
@@ -263,11 +263,15 @@
 		write!(out, "{}", error.error())?;
 		if let ErrorKind::ImportSyntaxError { path, error } = error.error() {
 			writeln!(out)?;
-			let offset = error.location.offset;
+			let mut offset = error.location;
+			// To inclusive range
+			if offset.1 > offset.0 {
+				offset.1 -= 1;
+			}
 			let mut builder = SnippetBuilder::new(path.code());
 			builder
 				.error(Text::fragment("syntax error", Formatting::default()))
-				.range(offset..=offset)
+				.range(offset.0 as usize..=offset.1 as usize)
 				.build();
 			let source = builder.build();
 			let ansi = source_to_ansi(&source);
modifiedcrates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth
7 ImportKind, IndexPart, LiteralType, Member, ObjBody, ObjComp, ObjMembers, Slice, SliceDesc,7 ImportKind, IndexPart, LiteralType, Member, ObjBody, ObjComp, ObjMembers, Slice, SliceDesc,
8 Source, Span, Spanned, UnaryOpType, Visibility, unescape,8 Source, Span, Spanned, UnaryOpType, Visibility, unescape,
9};9};
10use jrsonnet_lexer::{Lexeme, Lexer, SyntaxKind, T, collect_lexed_str_block};10use jrsonnet_lexer::{Lexeme, Lexer, Span as LexSpan, SyntaxKind, T, collect_lexed_str_block};
1111
12pub struct ParserSettings {12pub struct ParserSettings {
13 pub source: Source,13 pub source: Source,
14}14}
1515
16#[derive(Debug, Clone)]16#[derive(Debug, Clone)]
17pub struct ParseErrorLocation {
18 pub offset: usize,
19}
20
21#[derive(Debug, Clone)]
22pub struct ParseError {17pub struct ParseError {
23 pub message: String,18 pub message: String,
24 pub location: ParseErrorLocation,19 pub location: LexSpan,
25}20}
2621
27impl std::fmt::Display for ParseError {22impl std::fmt::Display for ParseError {
131126
132 fn error(&self, message: String) -> ParseError {127 fn error(&self, message: String) -> ParseError {
133 ParseError {128 ParseError {
134 location: ParseErrorLocation {129 location: self.lexemes[self.offset].range,
135 offset: self.span_start() as usize,
136 },
137 message,130 message,
138 }131 }
139 }132 }
143 return Err(self.error(format!("expected identifier, got {}", self.current_desc())));136 return Err(self.error(format!("expected identifier, got {}", self.current_desc())));
144 }137 }
145 let text = self.text();138 let text = self.text();
146 if is_reserved(text) {
147 return Err(self.error(format!("expected identifier, got reserved word '{text}'")));
148 }
149 let s: IStr = text.into();139 let s: IStr = text.into();
150 self.eat_any();140 self.eat_any();
151 Ok(s)141 Ok(s)
154 fn at_ident(&self) -> bool {144 fn at_ident(&self) -> bool {
155 self.at(SyntaxKind::IDENT) && !is_reserved(self.lexemes[self.offset].text)145 self.at(SyntaxKind::IDENT) && !is_reserved(self.lexemes[self.offset].text)
156 }146 }
157}
158
159fn is_reserved(s: &str) -> bool {
160 matches!(
161 s,
162 "assert"
163 | "else" | "error"
164 | "false" | "for"
165 | "function"
166 | "if" | "import"
167 | "importstr"
168 | "importbin"
169 | "in" | "local"
170 | "null" | "tailstrict"
171 | "then" | "self"
172 | "super" | "true"
173 )
174}147}
175148
176fn spanned<T: Acyclic>(149fn spanned<T: Acyclic>(
1040 if let Some(desc) = lexeme.kind.error_description() {1013 if let Some(desc) = lexeme.kind.error_description() {
1041 return Err(ParseError {1014 return Err(ParseError {
1042 message: desc.to_owned(),1015 message: desc.to_owned(),
1043 location: ParseErrorLocation {1016 location: lexeme.range,
1044 offset: lexeme.range.0 as usize,
1045 },
1046 });1017 });
1047 }1018 }
1048 }1019 }