difftreelog
refactor better static analysis error display
in: master
67 files changed
crates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/analyze.rs
+++ b/crates/jrsonnet-evaluator/src/analyze.rs
@@ -472,7 +472,7 @@
match bind {
BindSpec::Field { into, .. } => self.alloc_destruct(into),
BindSpec::Function { name, .. } => {
- let (_, id) = self.define_local(name.clone(), None)?;
+ let (_, id) = self.define_local(name.value.clone(), Some(name.span.clone()))?;
Some(LDestruct::Full(id))
}
}
@@ -1337,18 +1337,18 @@
stack: &mut AnalysisStack,
taint: &mut AnalysisResult,
) -> LExpr {
- if let Expr::Function(params, body) = expr {
- return analyze_function(Some(name), params, body, stack, taint);
+ if let Expr::Function(span, params, body) = expr {
+ return analyze_function(Some(name), &span, ¶ms, body, stack, taint);
}
analyze(expr, stack, taint)
}
#[allow(clippy::too_many_lines)]
pub fn analyze(expr: &Expr, stack: &mut AnalysisStack, taint: &mut AnalysisResult) -> LExpr {
match expr {
- Expr::Literal(l) => match l {
+ Expr::Literal(span, l) => match l {
LiteralType::This => stack.use_this(taint).map_or_else(
|| {
- stack.report_error("`self` used outside of object", None);
+ stack.report_error("`self` used outside of object", Some(span.clone()));
LExpr::BadLocal("self")
},
LExpr::Slot,
@@ -1357,13 +1357,13 @@
if stack.use_super(taint).is_some() {
LExpr::Super
} else {
- stack.report_error("`super` used outside of object", None);
+ stack.report_error("`super` used outside of object", Some(span.clone()));
LExpr::BadLocal("super")
}
}
LiteralType::Dollar => stack.use_dollar(taint).map_or_else(
|| {
- stack.report_error("`$` used outside of object", None);
+ stack.report_error("`$` used outside of object", Some(span.clone()));
LExpr::BadLocal("$")
},
LExpr::Slot,
@@ -1475,7 +1475,9 @@
parts: parts_l,
}
}
- Expr::Function(params, body) => analyze_function(None, params, body, stack, taint),
+ Expr::Function(span, params, body) => {
+ analyze_function(None, span, params, body, stack, taint)
+ }
Expr::IfElse(ifelse) => {
let IfElse {
cond,
@@ -1541,15 +1543,22 @@
) -> LExpr {
match bind {
BindSpec::Field {
- value: Expr::Function(params, value),
+ value: Expr::Function(span, params, value),
into: Destruct::Full(name),
- } => analyze_function(Some(name.value.clone()), params, value, stack, taint),
+ } => analyze_function(Some(name.value.clone()), &span, params, value, stack, taint),
BindSpec::Field { value, .. } => analyze(value, stack, taint),
BindSpec::Function {
params,
value,
name,
- } => analyze_function(Some(name.clone()), params, value, stack, taint),
+ } => analyze_function(
+ Some(name.value.clone()),
+ &name.span,
+ params,
+ value,
+ stack,
+ taint,
+ ),
}
}
@@ -1595,6 +1604,7 @@
fn analyze_function(
name: Option<IStr>,
+ span: &Span,
params: &ExprParams,
body: &Expr,
stack: &mut AnalysisStack,
@@ -1648,15 +1658,15 @@
// function(x) x is an identity function
if l_params.len() == 1 && l_params[0].default.is_none() {
- stack.report_warning(
- "do not define identity functions manually, use std.id instead",
- None,
- );
#[allow(irrefutable_let_patterns, reason = "refutable with exp-destruct")]
if let LDestruct::Full(param_slot) = &l_params[0].destruct
&& let LExpr::Slot(LSlot::Local(s)) = &body_expr
&& s == param_slot
{
+ stack.report_warning(
+ "do not define identity functions manually, use std.id instead",
+ Some(span.clone()),
+ );
return LExpr::IdentityFunction {};
}
}
@@ -1727,7 +1737,14 @@
for (f, name) in fields.iter().zip(field_names) {
let value = stack.in_using_closure(|stack| {
if let Some(params) = &f.params {
- analyze_function(name.function_name(), params, &f.value, stack, taint)
+ analyze_function(
+ name.function_name(),
+ &f.name.span,
+ params,
+ &f.value,
+ stack,
+ taint,
+ )
} else {
analyze(&f.value, stack, taint)
}
@@ -1771,7 +1788,14 @@
process_local_frame(&comp.locals, stack, taint, |stack, taint| {
let value = stack.in_using_closure(|stack| {
if let Some(params) = &comp.field.params {
- analyze_function(None, params, &comp.field.value, stack, taint)
+ analyze_function(
+ None,
+ &comp.field.name.span,
+ params,
+ &comp.field.value,
+ stack,
+ taint,
+ )
} else {
analyze(&comp.field.value, stack, taint)
}
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -7,11 +7,9 @@
};
use jrsonnet_gcmodule::Trace;
-use jrsonnet_ir::CodeLocation;
-#[cfg(feature = "explaining-traces")]
-use jrsonnet_ir::Span;
+use jrsonnet_ir::{CodeLocation, Span};
-use crate::{Error, ResolvePathOwned, error::ErrorKind};
+use crate::{Error, ResolvePathOwned, analyze::DiagLevel, error::ErrorKind};
/// The way paths should be displayed
#[derive(Clone, Trace)]
@@ -72,31 +70,64 @@
fn as_any_mut(&mut self) -> &mut dyn Any;
}
+fn span_label(resolver: &PathResolver, span: &Span) -> String {
+ use std::fmt::Write;
+ let mut path = span
+ .0
+ .source_path()
+ .path()
+ .map_or_else(|| span.0.source_path().to_string(), |p| resolver.resolve(p));
+ #[expect(clippy::cast_possible_truncation, reason = "code is limited by 4gb")]
+ let len = span.0.code().len() as u32;
+ let start = span.1.min(len);
+ let end = span.2.min(len);
+ let (start_loc, end_loc) = if start == end {
+ let [loc] = span.0.map_source_locations(&[start]);
+ (loc, loc)
+ } else {
+ let [s, e] = span.0.map_source_locations(&[start, end]);
+ (s, e)
+ };
+ write!(path, ":").unwrap();
+ print_code_location(&mut path, &start_loc, &end_loc).unwrap();
+ path
+}
+
+#[cfg(feature = "explaining-traces")]
+fn span_render_range(span: &Span) -> Option<std::ops::RangeInclusive<usize>> {
+ let len = span.0.code().len();
+ if len == 0 {
+ return None;
+ }
+ let max = len - 1;
+ let r = span.range();
+ Some((*r.start()).min(max)..=(*r.end()).min(max))
+}
+
+fn diag_level_label(level: DiagLevel) -> &'static str {
+ match level {
+ DiagLevel::Error => "error",
+ DiagLevel::Warning => "warning",
+ }
+}
+
fn print_code_location(
out: &mut impl fmt::Write,
start: &CodeLocation,
end: &CodeLocation,
) -> Result<(), fmt::Error> {
+ let end_col = end.column.saturating_sub(1).max(start.column);
if start.line == end.line {
- if start.column == end.column {
+ if start.column == end_col {
write!(out, "{}:{}", start.line, start.column)?;
} else {
- write!(
- out,
- "{}:{}-{}",
- start.line,
- start.column,
- end.column.saturating_sub(1)
- )?;
+ write!(out, "{}:{}-{}", start.line, start.column, end_col)?;
}
} else {
write!(
out,
"{}:{}-{}:{}",
- start.line,
- start.column,
- end.line,
- end.column.saturating_sub(1)
+ start.line, start.column, end.line, end_col
)?;
}
Ok(())
@@ -121,61 +152,63 @@
impl TraceFormat for CompactFormat {
fn write_trace(&self, out: &mut dyn fmt::Write, error: &Error) -> Result<(), fmt::Error> {
- if let ErrorKind::ImportFileNotFound(from, import) = error.error() {
- let from = from
- .path()
- .map_or_else(|| from.to_string(), |path| self.resolver.resolve(path));
- let import = match import {
- ResolvePathOwned::Str(s) => s.clone(),
- ResolvePathOwned::Path(path_buf) => self.resolver.resolve(path_buf),
- };
- write!(out, "import file not found {import} from {from}")?;
- } else {
- write!(out, "{}", error.error())?;
+ match error.error() {
+ ErrorKind::ImportFileNotFound(from, import) => {
+ let from = from
+ .path()
+ .map_or_else(|| from.to_string(), |path| self.resolver.resolve(path));
+ let import = match import {
+ ResolvePathOwned::Str(s) => s.clone(),
+ ResolvePathOwned::Path(path_buf) => self.resolver.resolve(path_buf),
+ };
+ write!(out, "import file not found {import} from {from}")?;
+ }
+ ErrorKind::StaticAnalysisError(_) => {
+ write!(out, "static analysis errors")?;
+ }
+ _ => {
+ write!(out, "{}", error.error())?;
+ }
}
- if let ErrorKind::ImportSyntaxError { path, error } = error.error() {
- use std::fmt::Write;
+ if let ErrorKind::StaticAnalysisError(diagnostics) = error.error() {
+ let labels: Vec<Option<String>> = diagnostics
+ .iter()
+ .map(|d| d.span.as_ref().map(|s| span_label(&self.resolver, s)))
+ .collect();
+ let align = labels.iter().flatten().map(String::len).max().unwrap_or(0);
+ let cont_indent = " ".repeat(self.padding + align + 1);
+ for (diag, label) in diagnostics.iter().zip(labels.iter()) {
+ writeln!(out)?;
+ let level = diag_level_label(diag.level);
+ let message = diag.message.replace('\n', &format!("\n{cont_indent}"));
+ let label = label.as_deref().unwrap_or("");
+ write!(
+ out,
+ "{:<p$}{label:<w$} {level}: {message}",
+ "",
+ p = self.padding,
+ w = align,
+ )?;
+ }
+ }
+ if let ErrorKind::ImportSyntaxError { error, .. } = error.error() {
writeln!(out)?;
- let mut n = path.source_path().path().map_or_else(
- || path.source_path().to_string(),
- |r| self.resolver.resolve(r),
- );
- let offset = (error.location.1 as usize).min(path.code().len());
- #[expect(clippy::cast_possible_truncation, reason = "code is limited by 4gb")]
- let location = path
- .map_source_locations(&[offset as u32])
- .into_iter()
- .next()
- .unwrap();
-
- write!(n, ":").unwrap();
- print_code_location(&mut n, &location, &location).unwrap();
- write!(out, "{:<p$}{n}", "", p = self.padding)?;
+ let label = span_label(&self.resolver, &error.location);
+ write!(out, "{:<p$}{label}", "", p = self.padding)?;
}
let file_names = error
.trace()
.0
.iter()
- .map(|el| &el.location)
- .map(|location| {
- use std::fmt::Write;
- #[allow(clippy::option_if_let_else)]
- if let Some(location) = location {
- let mut resolved_path = match location.0.source_path().path() {
- Some(r) => self.resolver.resolve(r),
- None => location.0.source_path().to_string(),
- };
- // TODO: Process all trace elements first
- let location = location.0.map_source_locations(&[location.1, location.2]);
- write!(resolved_path, ":").unwrap();
- print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();
- write!(resolved_path, ":").unwrap();
- Some(resolved_path)
- } else {
- None
- }
+ .map(|el| {
+ el.location.as_ref().map(|loc| {
+ use std::fmt::Write;
+ let mut s = span_label(&self.resolver, loc);
+ write!(s, ":").unwrap();
+ s
+ })
})
.collect::<Vec<_>>();
let align = file_names
@@ -265,40 +298,45 @@
}
use hi_doc::{Formatting, SnippetBuilder, Text, source_to_ansi};
- write!(out, "{}", error.error())?;
+ match error.error() {
+ ErrorKind::StaticAnalysisError(_) => write!(out, "static analysis errors")?,
+ _ => write!(out, "{}", error.error())?,
+ }
if let ErrorKind::ImportSyntaxError { path, error } = error.error() {
- writeln!(out)?;
- let mut builder = SnippetBuilder::new(path.code());
- builder
- .error(Text::fragment("syntax error", Formatting::default()))
- .range(error.location.range())
- .build();
- let source = builder.build();
- let ansi = source_to_ansi(&source);
- write!(out, "{ansi}")?;
+ writeln!(out, "\n...at {}", path.source_path())?;
+ if let Some(range) = span_render_range(&error.location) {
+ let mut builder = SnippetBuilder::new(path.code());
+ builder
+ .error(Text::fragment("syntax error", Formatting::default()))
+ .range(range)
+ .build();
+ let ansi = source_to_ansi(&builder.build());
+ write!(out, "{}", ansi.trim_end())?;
+ }
}
if let ErrorKind::StaticAnalysisError(diagnostics) = error.error() {
- use crate::analyze::DiagLevel;
- let mut builder: Option<SnippetBuilder> = None;
- let mut current_src: Option<&str> = None;
- let flush = |builder: Option<SnippetBuilder>,
+ let mut builder: Option<(SnippetBuilder, Span)> = None;
+ let flush = |slot: Option<(SnippetBuilder, Span)>,
out: &mut dyn fmt::Write|
-> Result<(), fmt::Error> {
- if let Some(b) = builder {
+ if let Some((b, anchor)) = slot {
+ writeln!(out, "\n...at {}", anchor.0.source_path())?;
let ansi = source_to_ansi(&b.build());
- write!(out, "\n{}", ansi.trim_end())?;
+ write!(out, "{}", ansi.trim_end())?;
}
Ok(())
};
for diag in diagnostics {
if let Some(span) = &diag.span {
- let src = span.0.code();
- if current_src != Some(src) {
+ let Some(range) = span_render_range(span) else {
+ continue;
+ };
+ let same_src = builder.as_ref().is_some_and(|(_, a)| a.0 == span.0);
+ if !same_src {
flush(builder.take(), out)?;
- builder = Some(SnippetBuilder::new(src));
- current_src = Some(src);
+ builder = Some((SnippetBuilder::new(span.0.code()), span.clone()));
}
- let b = builder.as_mut().unwrap();
+ let b = &mut builder.as_mut().unwrap().0;
let ab = match diag.level {
DiagLevel::Error => {
b.error(Text::fragment(diag.message.clone(), Formatting::default()))
@@ -307,14 +345,10 @@
b.warning(Text::fragment(diag.message.clone(), Formatting::default()))
}
};
- ab.range(span.range()).build();
+ ab.range(range).build();
} else {
flush(builder.take(), out)?;
- current_src = None;
- let prefix = match diag.level {
- DiagLevel::Error => "error",
- DiagLevel::Warning => "warning",
- };
+ let prefix = diag_level_label(diag.level);
write!(out, "\n{prefix}: {}", diag.message)?;
}
}
crates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir-parser/src/lib.rs
+++ b/crates/jrsonnet-ir-parser/src/lib.rs
@@ -77,6 +77,13 @@
self.offset += 1;
}
+ fn eat_any_spanned(&mut self) -> Span {
+ let start = self.span_start();
+ self.eat_any();
+ let end = self.span_end();
+ Span(self.source.clone(), start, end)
+ }
+
fn at_eof(&self) -> bool {
self.offset >= self.lexemes.len()
}
@@ -114,6 +121,12 @@
self.eat_any();
Ok(())
}
+ fn eat_spanned(&mut self, t: SyntaxKind) -> Result<Span> {
+ let start = self.span_start();
+ self.eat(t)?;
+ let end = self.span_end();
+ Ok(Span(self.source.clone(), start, end))
+ }
fn span_start(&self) -> u32 {
if self.at_eof() {
@@ -234,7 +247,7 @@
Ok(IStr::from(text))
}
-fn literal(p: &mut Parser<'_>) -> Option<LiteralType> {
+fn literal(p: &mut Parser<'_>) -> Option<(Span, LiteralType)> {
let t = match p.peek() {
T![self] => LiteralType::This,
T![super] => LiteralType::Super,
@@ -244,8 +257,7 @@
T![false] => LiteralType::False,
_ => return None,
};
- p.eat_any();
- Some(t)
+ Some((p.eat_any_spanned(), t))
}
fn assert_stmt(p: &mut Parser<'_>) -> Result<AssertStmt> {
@@ -482,20 +494,20 @@
});
}
}
- let name_spanned = spanned(p, ident)?;
+ let name = spanned(p, ident)?;
if p.try_eat(T!['(']) {
let ps = params(p)?;
p.eat(T![')'])?;
p.eat(T![=])?;
Ok(BindSpec::Function {
- name: name_spanned.value,
+ name,
params: ps,
value: expr(p)?,
})
} else {
p.eat(T![=])?;
Ok(BindSpec::Field {
- into: Destruct::Full(name_spanned),
+ into: Destruct::Full(name),
value: expr(p)?,
})
}
@@ -676,8 +688,8 @@
#[allow(clippy::too_many_lines)]
fn expr_basic(p: &mut Parser<'_>) -> Result<Expr> {
- if let Some(lit) = literal(p) {
- return Ok(Expr::Literal(lit));
+ if let Some((span, lit)) = literal(p) {
+ return Ok(Expr::Literal(span, lit));
}
match p.peek() {
@@ -755,12 +767,12 @@
T![if] => Ok(Expr::IfElse(Box::new(if_else(p)?))),
T![function] => {
- p.eat(T![function])?;
+ let span = p.eat_spanned(T![function])?;
p.eat(T!['('])?;
let ps = params(p)?;
p.eat(T![')'])?;
let body = expr(p)?;
- Ok(Expr::Function(ps, Box::new(body)))
+ Ok(Expr::Function(span, ps, Box::new(body)))
}
T![assert] => {
@@ -820,7 +832,7 @@
if parts.is_empty() {
return;
}
- let old = std::mem::replace(e, Expr::Literal(LiteralType::Null));
+ let old = std::mem::replace(e, Expr::Str(IStr::empty()));
*e = Expr::Index {
indexable: Box::new(old),
parts: std::mem::take(parts),
crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__function_and_call.snap.newdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__function_and_call.snap.new
@@ -0,0 +1,81 @@
+---
+source: crates/jrsonnet-ir-parser/src/lib.rs
+assertion_line: 1110
+expression: "format!(\"{v:#?}\")"
+---
+LocalExpr(
+ [
+ Function {
+ name: "f" from virtual:<test>:6-7,
+ params: ExprParams {
+ exprs: [
+ ExprParam {
+ destruct: Full(
+ "x" from virtual:<test>:8-9,
+ ),
+ default: None,
+ },
+ ExprParam {
+ destruct: Full(
+ "y" from virtual:<test>:11-12,
+ ),
+ default: Some(
+ Num(
+ 1.0,
+ ),
+ ),
+ },
+ ],
+ signature: FunctionSignature(
+ [
+ ParamParse {
+ name: Named(
+ "x",
+ ),
+ default: None,
+ },
+ ParamParse {
+ name: Named(
+ "y",
+ ),
+ default: Exists,
+ },
+ ],
+ ),
+ binds_len: 2,
+ },
+ value: BinaryOp(
+ BinaryOp {
+ lhs: Var(
+ "x" from virtual:<test>:18-19,
+ ),
+ op: Add,
+ rhs: Var(
+ "y" from virtual:<test>:22-23,
+ ),
+ },
+ ),
+ },
+ ],
+ Apply(
+ Var(
+ "f" from virtual:<test>:25-26,
+ ),
+ ArgsDesc {
+ unnamed: [
+ Num(
+ 2.0,
+ ),
+ ],
+ names: [
+ "y",
+ ],
+ values: [
+ Num(
+ 3.0,
+ ),
+ ],
+ } from virtual:<test>:26-34,
+ false,
+ ),
+)
crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@default_nondefault.jsonnet.snap.newdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-ir-parser/src/snapshots/jrsonnet_ir_parser__tests__peg_snapshots@default_nondefault.jsonnet.snap.new
@@ -0,0 +1,56 @@
+---
+source: crates/jrsonnet-ir-parser/src/lib.rs
+assertion_line: 1176
+expression: v
+input_file: crates/jrsonnet-peg-parser/src/tests/default_nondefault.jsonnet
+---
+LocalExpr(
+ [
+ Function {
+ name: "x" from virtual:<test>:6-7,
+ params: ExprParams {
+ exprs: [
+ ExprParam {
+ destruct: Full(
+ "foo" from virtual:<test>:8-11,
+ ),
+ default: Some(
+ Str(
+ "foo",
+ ),
+ ),
+ },
+ ExprParam {
+ destruct: Full(
+ "bar" from virtual:<test>:21-24,
+ ),
+ default: None,
+ },
+ ],
+ signature: FunctionSignature(
+ [
+ ParamParse {
+ name: Named(
+ "foo",
+ ),
+ default: Exists,
+ },
+ ParamParse {
+ name: Named(
+ "bar",
+ ),
+ default: None,
+ },
+ ],
+ ),
+ binds_len: 2,
+ },
+ value: Literal(
+ Null,
+ ),
+ },
+ ],
+ Literal(
+ Null,
+ ),
+)
crates/jrsonnet-ir/src/expr.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir/src/expr.rs
+++ b/crates/jrsonnet-ir/src/expr.rs
@@ -288,7 +288,7 @@
value: Expr,
},
Function {
- name: IStr,
+ name: Spanned<IStr>,
params: ExprParams,
value: Expr,
},
@@ -404,7 +404,7 @@
/// Syntax base
#[derive(Debug, PartialEq, Acyclic)]
pub enum Expr {
- Literal(LiteralType),
+ Literal(Span, LiteralType),
/// String value: "hello"
Str(IStr),
@@ -454,7 +454,7 @@
parts: Vec<IndexPart>,
},
/// function(x) x
- Function(ExprParams, Box<Expr>),
+ Function(Span, ExprParams, Box<Expr>),
/// if true == false then 1 else 2
IfElse(Box<IfElse>),
Slice(Box<Slice>),
crates/jrsonnet-ir/src/visit.rsdiffbeforeafterboth1use jrsonnet_interner::IStr;23#[cfg(feature = "exp-object-iteration")]4use crate::ForObjSpecData;5use crate::{6 ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BindSpec, CompSpec, Destruct, Expr, ExprParam,7 ExprParams, FieldMember, FieldName, ForSpecData, IfElse, IfSpecData, ImportKind, IndexPart,8 ObjBody, ObjComp, ObjMembers, Slice, SliceDesc,9};1011pub trait Visitor: Sized {12 fn visit_expr(&mut self, e: &Expr) {13 visit_expr(self, e)14 }15 fn visit_import(&mut self, _as_expression: bool, _value: IStr) {}16}1718#[cfg(feature = "exp-destruct")]19pub fn visit_destruct_rest<V: Visitor>(_v: &mut V, destruct: &crate::DestructRest) {20 match destruct {21 crate::DestructRest::Keep(_name) => {}22 crate::DestructRest::Drop => {}23 }24}2526#[allow(unused_variables, reason = "used with exp-destruct")]27pub fn visit_destruct<V: Visitor>(v: &mut V, destruct: &Destruct) {28 match destruct {29 Destruct::Full(_istr) => {}30 #[cfg(feature = "exp-destruct")]31 Destruct::Skip => {}32 #[cfg(feature = "exp-destruct")]33 Destruct::Array { start, rest, end } => {34 for s in start {35 visit_destruct(v, s);36 }37 if let Some(rest) = rest {38 visit_destruct_rest(v, rest);39 }40 for s in end {41 visit_destruct(v, s);42 }43 }44 #[cfg(feature = "exp-destruct")]45 Destruct::Object { fields, rest } => {46 for (_name, into, default) in fields {47 if let Some(into) = into {48 visit_destruct(v, into);49 }50 if let Some(default) = default {51 v.visit_expr(default);52 }53 if let Some(rest) = rest {54 visit_destruct_rest(v, rest);55 }56 }57 }58 }59}6061pub fn visit_if_spec<V: Visitor>(v: &mut V, cond: &IfSpecData) {62 let IfSpecData { span: _, cond } = cond;63 v.visit_expr(cond);64}6566pub fn visit_comp_spec<V: Visitor>(v: &mut V, c: &CompSpec) {67 match c {68 CompSpec::IfSpec(cond) => visit_if_spec(v, cond),69 CompSpec::ForSpec(for_spec_data) => {70 let ForSpecData { destruct, over } = for_spec_data;71 visit_destruct(v, destruct);72 v.visit_expr(over);73 }74 #[cfg(feature = "exp-object-iteration")]75 CompSpec::ForObjSpec(for_obj_spec_data) => {76 let ForObjSpecData {77 key: _,78 visibility: _,79 value,80 over,81 } = for_obj_spec_data;82 visit_destruct(v, value);83 v.visit_expr(over);84 }85 }86}87pub fn visit_params<V: Visitor>(v: &mut V, par: &ExprParams) {88 let ExprParams {89 exprs,90 signature: _,91 binds_len: _,92 } = par;93 for par in &**exprs {94 let ExprParam { destruct, default } = ∥95 visit_destruct(v, destruct);96 if let Some(default) = default {97 v.visit_expr(default);98 }99 }100}101102pub fn visit_bind_spec<V: Visitor>(v: &mut V, bind: &BindSpec) {103 match bind {104 BindSpec::Field { into, value } => {105 visit_destruct(v, into);106 v.visit_expr(value);107 }108 BindSpec::Function {109 name: _,110 params,111 value,112 } => {113 visit_params(v, params);114 v.visit_expr(value);115 }116 }117}118119pub fn visit_field_member<V: Visitor>(v: &mut V, mem: &FieldMember) {120 let FieldMember {121 name,122 plus: _,123 params,124 visibility: _,125 value,126 } = mem;127 match &**name {128 FieldName::Fixed(_istr) => {}129 FieldName::Dyn(expr) => v.visit_expr(expr),130 }131 if let Some(params) = params {132 visit_params(v, params);133 }134 v.visit_expr(value);135}136137pub fn visit_obj_body<V: Visitor>(v: &mut V, obj_body: &ObjBody) {138 match obj_body {139 ObjBody::MemberList(obj_members) => {140 let ObjMembers {141 locals,142 asserts,143 fields,144 } = obj_members;145 for local in &**locals {146 visit_bind_spec(v, local);147 }148 for assert in &**asserts {149 visit_assert_stmt(v, assert);150 }151 for field in fields {152 visit_field_member(v, field);153 }154 }155 ObjBody::ObjComp(obj_comp) => {156 let ObjComp {157 locals,158 field,159 compspecs,160 } = obj_comp;161 for local in &**locals {162 visit_bind_spec(v, local);163 }164 visit_field_member(v, field);165 for compspec in compspecs {166 visit_comp_spec(v, compspec);167 }168 }169 }170}171172pub fn visit_assert_stmt<V: Visitor>(v: &mut V, ass: &AssertStmt) {173 let AssertStmt { assertion, message } = ass;174 v.visit_expr(assertion);175 if let Some(message) = message {176 v.visit_expr(message);177 }178}179pub fn visit_expr<V: Visitor>(v: &mut V, e: &Expr) {180 match e {181 Expr::Literal(_literal_type) => {}182 Expr::Str(_istr) => {}183 Expr::Num(_num) => {}184 Expr::Var(_spanned) => {}185 Expr::Arr(exprs) => {186 for e in &**exprs {187 v.visit_expr(e);188 }189 }190 Expr::ArrComp(expr, comp_specs) => {191 v.visit_expr(expr);192 for ele in comp_specs {193 visit_comp_spec(v, ele);194 }195 }196 Expr::Obj(obj_body) => visit_obj_body(v, obj_body),197 Expr::ObjExtend(expr, obj_body) => {198 v.visit_expr(expr);199 visit_obj_body(v, obj_body);200 }201 Expr::UnaryOp(_unary_op_type, expr) => {202 v.visit_expr(expr);203 }204 Expr::BinaryOp(binary_op) => {205 let BinaryOp { lhs, op: _, rhs } = &**binary_op;206 v.visit_expr(lhs);207 v.visit_expr(rhs);208 }209 Expr::AssertExpr(assert_expr) => {210 let AssertExpr { assert, rest } = &**assert_expr;211 visit_assert_stmt(v, assert);212 v.visit_expr(rest);213 }214 Expr::LocalExpr(bind_specs, expr) => {215 for local in bind_specs {216 visit_bind_spec(v, local);217 }218 v.visit_expr(expr);219 }220 Expr::Import(kind, expr) => {221 v.visit_expr(expr);222223 if let Expr::Str(expr) = &**expr {224 v.visit_import(matches!(**kind, ImportKind::Normal), expr.clone());225 }226 }227 Expr::ErrorStmt(_span, expr) => {228 v.visit_expr(expr);229 }230 Expr::Apply(expr, spanned, _) => {231 v.visit_expr(expr);232 let ArgsDesc {233 unnamed,234 names: _,235 values,236 } = &**spanned;237 for unnamed in unnamed {238 v.visit_expr(unnamed);239 }240 for named in values {241 v.visit_expr(named);242 }243 }244 Expr::Index { indexable, parts } => {245 v.visit_expr(indexable);246247 for part in parts {248 let IndexPart {249 span: _,250 value,251 #[cfg(feature = "exp-null-coaelse")]252 null_coaelse: _,253 } = part;254 v.visit_expr(value);255 }256 }257 Expr::Function(expr_params, expr) => {258 visit_params(v, expr_params);259 v.visit_expr(expr);260 }261 Expr::IfElse(if_else) => {262 let IfElse {263 cond,264 cond_then,265 cond_else,266 } = &**if_else;267 visit_if_spec(v, cond);268 v.visit_expr(cond_then);269 if let Some(cond_else) = cond_else {270 v.visit_expr(cond_else);271 }272 }273 Expr::Slice(slice) => {274 let Slice { value, slice } = &**slice;275 v.visit_expr(value);276 let SliceDesc { start, end, step } = slice;277278 if let Some(start) = start {279 v.visit_expr(start);280 }281 if let Some(end) = end {282 v.visit_expr(end);283 }284 if let Some(step) = step {285 v.visit_expr(step);286 }287 }288 }289}crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -131,7 +131,7 @@
pub rule bind(s: &ParserSettings) -> BindSpec
= into:destruct(s) _ "=" _ value:expr(s) {BindSpec::Field{into, value}}
- / name:id() _ "(" _ params:params(s) _ ")" _ "=" _ value:expr(s) {BindSpec::Function{name, params, value}}
+ / name:spanned(<id()>, s) _ "(" _ params:params(s) _ ")" _ "=" _ value:expr(s) {BindSpec::Function{name, params, value}}
pub rule assertion(s: &ParserSettings) -> AssertStmt
= keyword("assert") _ assertion:spanned(<expr(s)>, s) message:(_ ":" _ e:expr(s) {e})? { AssertStmt{assertion, message} }
@@ -290,14 +290,14 @@
}))}
pub rule literal(s: &ParserSettings) -> Expr
- = v:(
+ = a:position!() v:(
keyword("null") {LiteralType::Null}
/ keyword("true") {LiteralType::True}
/ keyword("false") {LiteralType::False}
/ keyword("self") {LiteralType::This}
/ keyword("$") {LiteralType::Dollar}
/ keyword("super") {LiteralType::Super}
- ) {Expr::Literal(v)}
+ ) b:position!() {Expr::Literal(Span(s.source.clone(), codeidx(a), codeidx(b)), v)}
rule import_kind() -> ImportKind
= keyword("importstr") { ImportKind::Str }
@@ -319,7 +319,7 @@
/ local_expr(s)
/ if_then_else_expr(s)
- / keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, Box::new(expr))}
+ / kw:spanned(<keyword("function")>, s) _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(kw.span, params, Box::new(expr))}
/ assert:assertion(s) _ ";" _ rest:expr(s) { Expr::AssertExpr(Box::new(AssertExpr{
assert, rest
})) }
tests/cpp_test_suite_golden_override/error.03.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.03.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.03.jsonnet.golden
@@ -1,3 +1,3 @@
runtime error: foo
error.03.jsonnet:17:21-25: error statement
- error.03.jsonnet:18:8-8: field <x> access
\ No newline at end of file
+ error.03.jsonnet:18:8: field <x> access
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.07.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.07.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.07.jsonnet.golden
@@ -1,4 +1,4 @@
runtime error: sarcasm
error.07.jsonnet:18:31-35: error statement
- error.07.jsonnet:17:33-33: element <3> access
+ error.07.jsonnet:17:33: element <3> access
error.07.jsonnet:18:20-53: function <third> call
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.args_commafodder.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.args_commafodder.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.args_commafodder.jsonnet.golden
@@ -1 +1,4 @@
-static analysis errors: undefined local: foo; undefined local: bar; undefined local: qux
\ No newline at end of file
+static analysis errors
+ error.args_commafodder.jsonnet:1:1-3 error: undefined local: foo
+ error.args_commafodder.jsonnet:2:3-5 error: undefined local: bar
+ error.args_commafodder.jsonnet:4:7-9 error: undefined local: qux
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.computed_field_scope.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.computed_field_scope.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.computed_field_scope.jsonnet.golden
@@ -1 +1,3 @@
-static analysis errors: undefined local: x; unused local: x
\ No newline at end of file
+static analysis errors
+ error.computed_field_scope.jsonnet:17:21 error: undefined local: x
+ error.computed_field_scope.jsonnet:17:9 warning: unused local: x
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.field_not_exist.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.field_not_exist.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.field_not_exist.jsonnet.golden
@@ -1,2 +1,2 @@
no such field: y
- error.field_not_exist.jsonnet:17:10-10: field <y> access
\ No newline at end of file
+ error.field_not_exist.jsonnet:17:10: field <y> access
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.function_duplicate_param.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.function_duplicate_param.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.function_duplicate_param.jsonnet.golden
@@ -1 +1,3 @@
-static analysis errors: local is already defined in the current frame: x; do not define identity functions manually, use std.id instead
\ No newline at end of file
+static analysis errors
+ error.function_duplicate_param.jsonnet:17:13 error: local is already defined in the current frame: x
+ error.function_duplicate_param.jsonnet:17:1-8 warning: do not define identity functions manually, use std.id instead
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.import_static-check-failure.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.import_static-check-failure.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.import_static-check-failure.jsonnet.golden
@@ -1,2 +1,3 @@
-static analysis errors: undefined local: x
+static analysis errors
+ lib/static_check_failure.jsonnet:2:1 error: undefined local: x
error.import_static-check-failure.jsonnet:1:1-6: import
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.import_syntax-error.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.import_syntax-error.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.import_syntax-error.jsonnet.golden
@@ -1,4 +1,4 @@
syntax error: unterminated double-quoted string
- lib/syntax_error.jsonnet:1:1
- lib/syntax_error.jsonnet:1:1-2:0: parse imported
+ lib/syntax_error.jsonnet:1:1-2:1
+ lib/syntax_error.jsonnet:1:1-2:1: parse imported
error.import_syntax-error.jsonnet:1:1-6: import
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.overflow.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.overflow.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.overflow.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: invalid number value: non-finite
- error.overflow.jsonnet:17:1
+ error.overflow.jsonnet:17:1-5
error.overflow.jsonnet:17:1-5: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.overflow3.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.overflow3.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.overflow3.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: invalid number value: non-finite
- error.overflow3.jsonnet:17:1
+ error.overflow3.jsonnet:17:1-5
error.overflow3.jsonnet:17:1-5: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.array_comma.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.array_comma.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.array_comma.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected ']', got number "3"
error.parse.array_comma.jsonnet:17:7
- error.parse.array_comma.jsonnet:17:7-7: parse imported
\ No newline at end of file
+ error.parse.array_comma.jsonnet:17:7: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.function_arg_positional_after_named.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.function_arg_positional_after_named.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.function_arg_positional_after_named.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: positional argument after named argument
error.parse.function_arg_positional_after_named.jsonnet:19:10
- error.parse.function_arg_positional_after_named.jsonnet:19:10-10: parse imported
\ No newline at end of file
+ error.parse.function_arg_positional_after_named.jsonnet:19:10: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.import_not_literal.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.import_not_literal.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.import_not_literal.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: import path must be a string literal
\ No newline at end of file
+static analysis errors
+ error.parse.import_not_literal.jsonnet:17:1-6 error: import path must be a string literal
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.index_unterminated.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.index_unterminated.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.index_unterminated.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unexpected end of file
error.parse.index_unterminated.jsonnet:17:3
- error.parse.index_unterminated.jsonnet:17:3-0:0: parse imported
\ No newline at end of file
+ error.parse.index_unterminated.jsonnet:17:3: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.method_plus.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.method_plus.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.method_plus.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected ':', got '+'
error.parse.method_plus.jsonnet:17:18
- error.parse.method_plus.jsonnet:17:18-18: parse imported
\ No newline at end of file
+ error.parse.method_plus.jsonnet:17:18: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.object_comma.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.object_comma.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.object_comma.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected '}', got identifier "z"
error.parse.object_comma.jsonnet:17:11
- error.parse.object_comma.jsonnet:17:11-11: parse imported
\ No newline at end of file
+ error.parse.object_comma.jsonnet:17:11: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.object_comprehension_local_clash.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.object_comprehension_local_clash.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.object_comprehension_local_clash.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected '}', got ':'
error.parse.object_comprehension_local_clash.jsonnet:17:29
- error.parse.object_comprehension_local_clash.jsonnet:17:29-29: parse imported
\ No newline at end of file
+ error.parse.object_comprehension_local_clash.jsonnet:17:29: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.object_local_clash.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.object_local_clash.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.object_local_clash.jsonnet.golden
@@ -1 +1,3 @@
-static analysis errors: local is already defined in the current frame: x; unused local: x
\ No newline at end of file
+static analysis errors
+ error.parse.object_local_clash.jsonnet:17:21 error: local is already defined in the current frame: x
+ error.parse.object_local_clash.jsonnet:17:9 warning: unused local: x
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.self_in_computed_field.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.self_in_computed_field.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.self_in_computed_field.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected field name, got 'self'
- error.parse.self_in_computed_field.jsonnet:17:15
+ error.parse.self_in_computed_field.jsonnet:17:15-18
error.parse.self_in_computed_field.jsonnet:17:15-18: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.static_error_bad_number.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.static_error_bad_number.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.static_error_bad_number.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unexpected '.'
error.parse.static_error_bad_number.jsonnet:17:1
- error.parse.static_error_bad_number.jsonnet:17:1-1: parse imported
\ No newline at end of file
+ error.parse.static_error_bad_number.jsonnet:17:1: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: invalid string escape
- error.parse.string.invalid_escape.jsonnet:17:1
+ error.parse.string.invalid_escape.jsonnet:17:1-4
error.parse.string.invalid_escape.jsonnet:17:1-4: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_non_hex.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_non_hex.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_non_hex.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: invalid string escape
- error.parse.string.invalid_escape_unicode_non_hex.jsonnet:17:1
+ error.parse.string.invalid_escape_unicode_non_hex.jsonnet:17:1-8
error.parse.string.invalid_escape_unicode_non_hex.jsonnet:17:1-8: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unterminated double-quoted string
- error.parse.string.invalid_escape_unicode_short.jsonnet:17:1
- error.parse.string.invalid_escape_unicode_short.jsonnet:17:1-18:0: parse imported
\ No newline at end of file
+ error.parse.string.invalid_escape_unicode_short.jsonnet:17:1-18:1
+ error.parse.string.invalid_escape_unicode_short.jsonnet:17:1-18:1: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short2.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short2.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short2.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: invalid string escape
- error.parse.string.invalid_escape_unicode_short2.jsonnet:17:1
+ error.parse.string.invalid_escape_unicode_short2.jsonnet:17:1-7
error.parse.string.invalid_escape_unicode_short2.jsonnet:17:1-7: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short3.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short3.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string.invalid_escape_unicode_short3.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unterminated double-quoted string
- error.parse.string.invalid_escape_unicode_short3.jsonnet:17:1
- error.parse.string.invalid_escape_unicode_short3.jsonnet:17:1-18:0: parse imported
\ No newline at end of file
+ error.parse.string.invalid_escape_unicode_short3.jsonnet:17:1-18:1
+ error.parse.string.invalid_escape_unicode_short3.jsonnet:17:1-18:1: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string.unfinished.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string.unfinished.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string.unfinished.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unterminated double-quoted string
- error.parse.string.unfinished.jsonnet:17:1
- error.parse.string.unfinished.jsonnet:17:1-18:0: parse imported
\ No newline at end of file
+ error.parse.string.unfinished.jsonnet:17:1-18:1
+ error.parse.string.unfinished.jsonnet:17:1-18:1: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string.unfinished2.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string.unfinished2.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string.unfinished2.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unterminated single-quoted string
- error.parse.string.unfinished2.jsonnet:17:1
- error.parse.string.unfinished2.jsonnet:17:1-18:0: parse imported
\ No newline at end of file
+ error.parse.string.unfinished2.jsonnet:17:1-18:1
+ error.parse.string.unfinished2.jsonnet:17:1-18:1: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.string_multi_no_newline.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.string_multi_no_newline.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.string_multi_no_newline.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: text block requires new line after |||
- error.parse.string_multi_no_newline.jsonnet:17:1
- error.parse.string_multi_no_newline.jsonnet:17:1-18:0: parse imported
\ No newline at end of file
+ error.parse.string_multi_no_newline.jsonnet:17:1-18:1
+ error.parse.string_multi_no_newline.jsonnet:17:1-18:1: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.text_block_bad_whitespace.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.text_block_bad_whitespace.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.text_block_bad_whitespace.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unterminated text block
- error.parse.text_block_bad_whitespace.jsonnet:17:1
+ error.parse.text_block_bad_whitespace.jsonnet:17:1-20:3
error.parse.text_block_bad_whitespace.jsonnet:17:1-20:3: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.text_block_eof.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.text_block_eof.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.text_block_eof.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unexpected end of text block
- error.parse.text_block_eof.jsonnet:17:1
+ error.parse.text_block_eof.jsonnet:17:1-18:6
error.parse.text_block_eof.jsonnet:17:1-18:6: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.text_block_indent_spaces.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.text_block_indent_spaces.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.text_block_indent_spaces.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unterminated text block
- error.parse.text_block_indent_spaces.jsonnet:17:1
+ error.parse.text_block_indent_spaces.jsonnet:17:1-20:3
error.parse.text_block_indent_spaces.jsonnet:17:1-20:3: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.parse.text_block_not_terminated.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.parse.text_block_not_terminated.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.parse.text_block_not_terminated.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unexpected end of text block
- error.parse.text_block_not_terminated.jsonnet:17:1
- error.parse.text_block_not_terminated.jsonnet:17:1-19:0: parse imported
\ No newline at end of file
+ error.parse.text_block_not_terminated.jsonnet:17:1-19:1
+ error.parse.text_block_not_terminated.jsonnet:17:1-19:1: parse imported
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.static_error_self.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.static_error_self.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.static_error_self.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: `self` used outside of object
\ No newline at end of file
+static analysis errors
+ error.static_error_self.jsonnet:17:2-5 error: `self` used outside of object
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.static_error_super.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.static_error_super.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.static_error_super.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: `super` used outside of object
\ No newline at end of file
+static analysis errors
+ error.static_error_super.jsonnet:17:2-6 error: `super` used outside of object
\ No newline at end of file
tests/cpp_test_suite_golden_override/error.static_error_var_not_exist.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.static_error_var_not_exist.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.static_error_var_not_exist.jsonnet.golden
@@ -1,2 +1,4 @@
-static analysis errors: undefined local: tmp2
-There is a local with similar name present: tmp; unused local: tmp
\ No newline at end of file
+static analysis errors
+ error.static_error_var_not_exist.jsonnet:17:16-19 error: undefined local: tmp2
+ There is a local with similar name present: tmp
+ error.static_error_var_not_exist.jsonnet:17:7-9 warning: unused local: tmp
\ No newline at end of file
tests/go_testdata_golden_override/arrcomp5.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/arrcomp5.jsonnet.golden
+++ b/tests/go_testdata_golden_override/arrcomp5.jsonnet.golden
@@ -1 +1,3 @@
-static analysis errors: undefined local: x; unused local: x
\ No newline at end of file
+static analysis errors
+ arrcomp5.jsonnet:1:14 error: undefined local: x
+ arrcomp5.jsonnet:1:25 warning: unused local: x
\ No newline at end of file
tests/go_testdata_golden_override/arrcomp_if4.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/arrcomp_if4.jsonnet.golden
+++ b/tests/go_testdata_golden_override/arrcomp_if4.jsonnet.golden
@@ -1 +1,3 @@
-static analysis errors: undefined local: y; local could be hoisted to an outer scope: y
\ No newline at end of file
+static analysis errors
+ arrcomp_if4.jsonnet:1:33 error: undefined local: y
+ arrcomp_if4.jsonnet:1:44 warning: local could be hoisted to an outer scope: y
\ No newline at end of file
tests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.golden
+++ b/tests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.golden
@@ -1,3 +1,3 @@
no such field: x
- builtinObjectRemoveKey_super_assert.jsonnet:2:15-15: field <x> access
+ builtinObjectRemoveKey_super_assert.jsonnet:2:15: field <x> access
builtinObjectRemoveKey_super_assert.jsonnet:2:10-15: assertion condition
\ No newline at end of file
tests/go_testdata_golden_override/dollar_bad.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/dollar_bad.jsonnet.golden
+++ b/tests/go_testdata_golden_override/dollar_bad.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: `$` used outside of object
\ No newline at end of file
+static analysis errors
+ dollar_bad.jsonnet:1:1 error: `$` used outside of object
\ No newline at end of file
tests/go_testdata_golden_override/error_from_array.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/error_from_array.jsonnet.golden
+++ b/tests/go_testdata_golden_override/error_from_array.jsonnet.golden
@@ -1,3 +1,3 @@
runtime error: xxx
- error_from_array.jsonnet:1:2-6: error statement
- error_from_array.jsonnet:1:15-15: element <0> access
\ No newline at end of file
+ error_from_array.jsonnet:1:2-6: error statement
+ error_from_array.jsonnet:1:15: element <0> access
\ No newline at end of file
tests/go_testdata_golden_override/error_hexnumber.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/error_hexnumber.jsonnet.golden
+++ b/tests/go_testdata_golden_override/error_hexnumber.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected end of file, got identifier "x42"
- error_hexnumber.jsonnet:1:2
+ error_hexnumber.jsonnet:1:2-4
error_hexnumber.jsonnet:1:2-4: parse imported
\ No newline at end of file
tests/go_testdata_golden_override/import_computed.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/import_computed.jsonnet.golden
+++ b/tests/go_testdata_golden_override/import_computed.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: import path must be a string literal
\ No newline at end of file
+static analysis errors
+ import_computed.jsonnet:1:1-6 error: import path must be a string literal
\ No newline at end of file
tests/go_testdata_golden_override/import_syntax_error.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/import_syntax_error.jsonnet.golden
+++ b/tests/go_testdata_golden_override/import_syntax_error.jsonnet.golden
@@ -1,4 +1,4 @@
syntax error: unexpected end of file
syntax_error.jsonnet:1:4
- syntax_error.jsonnet:1:4-0:0: parse imported
+ syntax_error.jsonnet:1:4: parse imported
import_syntax_error.jsonnet:1:1-6: import
\ No newline at end of file
tests/go_testdata_golden_override/importbin_computed.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/importbin_computed.jsonnet.golden
+++ b/tests/go_testdata_golden_override/importbin_computed.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: import path must be a string literal
\ No newline at end of file
+static analysis errors
+ importbin_computed.jsonnet:1:1-9 error: import path must be a string literal
\ No newline at end of file
tests/go_testdata_golden_override/importstr_computed.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/importstr_computed.jsonnet.golden
+++ b/tests/go_testdata_golden_override/importstr_computed.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: import path must be a string literal
\ No newline at end of file
+static analysis errors
+ importstr_computed.jsonnet:1:1-9 error: import path must be a string literal
\ No newline at end of file
tests/go_testdata_golden_override/insuper4.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/insuper4.jsonnet.golden
+++ b/tests/go_testdata_golden_override/insuper4.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: `super` used outside of object
\ No newline at end of file
+static analysis errors
+ insuper4.jsonnet:1:8-12 error: `super` used outside of object
\ No newline at end of file
tests/go_testdata_golden_override/insuper6.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/insuper6.jsonnet.golden
+++ b/tests/go_testdata_golden_override/insuper6.jsonnet.golden
@@ -1 +1,2 @@
-static analysis errors: undefined local: undeclared
\ No newline at end of file
+static analysis errors
+ insuper6.jsonnet:1:10-19 error: undefined local: undeclared
\ No newline at end of file
tests/go_testdata_golden_override/number_leading_zero.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/number_leading_zero.jsonnet.golden
+++ b/tests/go_testdata_golden_override/number_leading_zero.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected end of file, got number "42"
- number_leading_zero.jsonnet:1:2
+ number_leading_zero.jsonnet:1:2-3
number_leading_zero.jsonnet:1:2-3: parse imported
\ No newline at end of file
tests/go_testdata_golden_override/object_comp_assert.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/object_comp_assert.jsonnet.golden
+++ b/tests/go_testdata_golden_override/object_comp_assert.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: asserts are unsupported in object comprehension
object_comp_assert.jsonnet:1:46
- object_comp_assert.jsonnet:1:46-46: parse imported
\ No newline at end of file
+ object_comp_assert.jsonnet:1:46: parse imported
\ No newline at end of file
tests/go_testdata_golden_override/object_comp_illegal.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/object_comp_illegal.jsonnet.golden
+++ b/tests/go_testdata_golden_override/object_comp_illegal.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: missing object comprehension field
object_comp_illegal.jsonnet:1:34
- object_comp_illegal.jsonnet:1:34-34: parse imported
\ No newline at end of file
+ object_comp_illegal.jsonnet:1:34: parse imported
\ No newline at end of file
tests/go_testdata_golden_override/object_invariant11.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/object_invariant11.jsonnet.golden
+++ b/tests/go_testdata_golden_override/object_invariant11.jsonnet.golden
@@ -1,3 +1,3 @@
assert failed: null
object_invariant11.jsonnet:1:10-14: assertion failure
- object_invariant11.jsonnet:1:18-18: field <x> access
\ No newline at end of file
+ object_invariant11.jsonnet:1:18: field <x> access
\ No newline at end of file
tests/go_testdata_golden_override/static_error_eof.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/static_error_eof.jsonnet.golden
+++ b/tests/go_testdata_golden_override/static_error_eof.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected ';', got end of file
static_error_eof.jsonnet:1:12
- static_error_eof.jsonnet:1:12-0:0: parse imported
\ No newline at end of file
+ static_error_eof.jsonnet:1:12: parse imported
\ No newline at end of file
tests/go_testdata_golden_override/std.codepoint7.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/std.codepoint7.jsonnet.golden
+++ b/tests/go_testdata_golden_override/std.codepoint7.jsonnet.golden
@@ -1,3 +1,3 @@
type error: expected char, got string
argument <str> evaluation
- std.codepoint7.jsonnet:2:14-0:0: function <builtin_codepoint> call
\ No newline at end of file
+ std.codepoint7.jsonnet:2:14-0:14: function <builtin_codepoint> call
\ No newline at end of file
tests/go_testdata_golden_override/syntax_error.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/syntax_error.jsonnet.golden
+++ b/tests/go_testdata_golden_override/syntax_error.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: unexpected end of file
syntax_error.jsonnet:1:4
- syntax_error.jsonnet:1:4-0:0: parse imported
\ No newline at end of file
+ syntax_error.jsonnet:1:4: parse imported
\ No newline at end of file
tests/go_testdata_golden_override/unfinished_args.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/unfinished_args.jsonnet.golden
+++ b/tests/go_testdata_golden_override/unfinished_args.jsonnet.golden
@@ -1,3 +1,3 @@
syntax error: expected ')', got end of file
unfinished_args.jsonnet:1:17
- unfinished_args.jsonnet:1:17-0:0: parse imported
\ No newline at end of file
+ unfinished_args.jsonnet:1:17: parse imported
\ No newline at end of file
tests/go_testdata_golden_override/variable_not_visible.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/variable_not_visible.jsonnet.golden
+++ b/tests/go_testdata_golden_override/variable_not_visible.jsonnet.golden
@@ -1 +1,3 @@
-static analysis errors: undefined local: nested; unused local: x1
\ No newline at end of file
+static analysis errors
+ variable_not_visible.jsonnet:1:44-49 error: undefined local: nested
+ variable_not_visible.jsonnet:1:7-8 warning: unused local: x1
\ No newline at end of file
tests/tests/snapshots/golden__golden@issue172.jsonnet.snapdiffbeforeafterboth--- a/tests/tests/snapshots/golden__golden@issue172.jsonnet.snap
+++ b/tests/tests/snapshots/golden__golden@issue172.jsonnet.snap
@@ -3,4 +3,5 @@
expression: result
input_file: tests/golden/issue172.jsonnet
---
-static analysis errors: undefined local: b
+static analysis errors
+ issue172.jsonnet:1:45 error: undefined local: b
tests/tests/snapshots/golden__golden@missing_binding.jsonnet.snapdiffbeforeafterboth--- a/tests/tests/snapshots/golden__golden@missing_binding.jsonnet.snap
+++ b/tests/tests/snapshots/golden__golden@missing_binding.jsonnet.snap
@@ -3,5 +3,6 @@
expression: result
input_file: tests/golden/missing_binding.jsonnet
---
-static analysis errors: undefined local: sta
-There is a local with similar name present: std
+static analysis errors
+ missing_binding.jsonnet:1:1-3 error: undefined local: sta
+ There is a local with similar name present: std
xtask/src/sourcegen/kinds.rsdiffbeforeafterboth--- a/xtask/src/sourcegen/kinds.rs
+++ b/xtask/src/sourcegen/kinds.rs
@@ -294,7 +294,7 @@
lit("WHITESPACE") => r"[ \t\n\r]+";
lit("SINGLE_LINE_SLASH_COMMENT") => r"//[^\r\n]*?(\r\n|\n)?";
lit("SINGLE_LINE_HASH_COMMENT") => r"#[^\r\n]*?(\r\n|\n)?";
- lit("MULTI_LINE_COMMENT") => r"/\*([^*]|\*[^/])*\*/";
+ lit("MULTI_LINE_COMMENT") => r"/\*([^*]|\*+[^*/])*\*+/";
error("COMMENT_TOO_SHORT", "comment too short") => r"/\*/";
error("COMMENT_UNTERMINATED", "unterminated multi-line comment") => r"/\*([^*/]|\*[^/])+";
error("NO_OPERATOR", "expected operator");