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
14 val::ConvertNumValueError,14 val::ConvertNumValueError,
15};15};
16
17#[derive(Debug, Clone)]
18pub struct SyntaxErrorLocation {
19 pub offset: usize,
20}
2116
22#[derive(Debug, Clone)]17#[derive(Debug, Clone)]
23pub struct SyntaxError {18pub struct SyntaxError {
24 pub message: String,19 pub message: String,
25 pub location: SyntaxErrorLocation,20 pub location: (u32, u32),
26}21}
27impl fmt::Display for SyntaxError {22impl fmt::Display for SyntaxError {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
50#[cfg(not(any(feature = "ir-parser", feature = "peg-parser")))]50#[cfg(not(any(feature = "ir-parser", feature = "peg-parser")))]
51compile_error!("at least one of `ir-parser` or `peg-parser` features must be enabled");51compile_error!("at least one of `ir-parser` or `peg-parser` features must be enabled");
5252
53pub use error::{SyntaxError, SyntaxErrorLocation};53pub use error::SyntaxError;
54pub use obj::*;54pub use obj::*;
55pub use rustc_hash;55pub use rustc_hash;
56use rustc_hash::FxHashMap;56use rustc_hash::FxHashMap;
87 jrsonnet_ir_parser::parse(code, &jrsonnet_ir_parser::ParserSettings { source }).map_err(|e| {87 jrsonnet_ir_parser::parse(code, &jrsonnet_ir_parser::ParserSettings { source }).map_err(|e| {
88 SyntaxError {88 SyntaxError {
89 message: e.message,89 message: e.message,
90 location: SyntaxErrorLocation {90 location: (e.location.0, e.location.1),
91 offset: e.location.offset,
92 },
93 }91 }
94 })92 })
95}93}
107 "expected {}, got {:?}",105 "expected {}, got {:?}",
108 e.expected,106 e.expected,
109 code.chars()107 code.chars()
110 .nth(e.location.offset)108 .nth(e.location.0)
111 .map_or_else(|| "EOF".into(), |c: char| c.to_string())109 .map_or_else(|| "EOF".into(), |c: char| c.to_string())
112 )110 )
113 },111 },
114 |v| v[3..].into(),112 |v| v[3..].into(),
115 );113 );
116 SyntaxError {114 SyntaxError {
117 message,115 message,
118 location: SyntaxErrorLocation {116 location: e.location,
119 offset: e.location.offset,
120 },
121 }117 }
122 })118 })
123}119}
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
122 || path.source_path().to_string(),122 || path.source_path().to_string(),
123 |r| self.resolver.resolve(r),123 |r| self.resolver.resolve(r),
124 );124 );
125 let mut offset = error.location.offset;125 let mut offset = error.location.0 as usize;
126 let is_eof = if offset >= path.code().len() {126 let is_eof = if offset >= path.code().len() {
127 offset = path.code().len().saturating_sub(1);127 offset = path.code().len().saturating_sub(1);
128 true128 true
263 write!(out, "{}", error.error())?;263 write!(out, "{}", error.error())?;
264 if let ErrorKind::ImportSyntaxError { path, error } = error.error() {264 if let ErrorKind::ImportSyntaxError { path, error } = error.error() {
265 writeln!(out)?;265 writeln!(out)?;
266 let offset = error.location.offset;266 let mut offset = error.location;
267 // To inclusive range
268 if offset.1 > offset.0 {
269 offset.1 -= 1;
270 }
267 let mut builder = SnippetBuilder::new(path.code());271 let mut builder = SnippetBuilder::new(path.code());
268 builder272 builder
269 .error(Text::fragment("syntax error", Formatting::default()))273 .error(Text::fragment("syntax error", Formatting::default()))
270 .range(offset..=offset)274 .range(offset.0 as usize..=offset.1 as usize)
271 .build();275 .build();
272 let source = builder.build();276 let source = builder.build();
273 let ansi = source_to_ansi(&source);277 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 }