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.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir/src/visit.rs
+++ b/crates/jrsonnet-ir/src/visit.rs
@@ -178,7 +178,7 @@
}
pub fn visit_expr<V: Visitor>(v: &mut V, e: &Expr) {
match e {
- Expr::Literal(_literal_type) => {}
+ Expr::Literal(_span, _literal_type) => {}
Expr::Str(_istr) => {}
Expr::Num(_num) => {}
Expr::Var(_spanned) => {}
@@ -254,7 +254,7 @@
v.visit_expr(value);
}
}
- Expr::Function(expr_params, expr) => {
+ Expr::Function(_span, expr_params, expr) => {
visit_params(v, expr_params);
v.visit_expr(expr);
}
crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth1use jrsonnet_gcmodule::Acyclic;2use jrsonnet_ir::{3 ArgsDesc, AssertExpr, AssertStmt, BinaryOp, BindSpec, CompSpec, Destruct, DestructRest, Expr,4 ExprParam, ExprParams, FieldMember, FieldName, ForSpecData, IStr, IfElse, IfSpecData,5 ImportKind, IndexPart, LiteralType, Member, NumValue, ObjBody, ObjComp, ObjMembers, Slice,6 SliceDesc, Source, Span, Spanned, Visibility, unescape,7};8use peg::parser;910pub struct ParserSettings {11 pub source: Source,12}1314macro_rules! expr_bin {15 ($a:ident $op:ident $b:ident) => {16 Expr::BinaryOp(Box::new(BinaryOp {17 lhs: $a,18 op: $op,19 rhs: $b,20 }))21 };22}23macro_rules! expr_un {24 ($op:ident $a:ident) => {25 Expr::UnaryOp($op, Box::new($a))26 };27}2829parser! {30 pub grammar jsonnet_parser() for str {31 use peg::ParseLiteral;3233 rule eof() = quiet!{![_]} / expected!("<eof>")34 rule eol() = "\n" / eof()3536 /// Standard C-like comments37 rule comment()38 = "//" (!eol()[_])* eol()39 / "/*" (!("*/")[_])* "*/"40 / "#" (!eol()[_])* eol()4142 rule single_whitespace() = quiet!{([' ' | '\r' | '\n' | '\t'] / comment())} / expected!("<whitespace>")43 rule _() = quiet!{([' ' | '\r' | '\n' | '\t']+) / comment()}* / expected!("<whitespace>")4445 /// For comma-delimited elements46 rule comma() = quiet!{_ "," _} / expected!("<comma>")47 rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().next().unwrap()}48 rule digit() -> char = d:$(['0'..='9']) {d.chars().next().unwrap()}49 rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']50 /// Sequence of digits51 rule uint_str() -> &'input str = a:$(digit()+ ("_" digit()+)*) { a }52 /// Number in scientific notation format53 rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.replace('_',"").parse().map_err(|_| "<number>") }} / expected!("<number>")5455 /// Reserved word followed by any non-alphanumberic56 rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "importbin" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()57 rule id() -> IStr = v:$(quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")) { v.into() }5859 rule keyword(id: &'static str) -> ()60 = #{|input, pos| input.parse_string_literal(pos, id)} end_of_ident()6162 pub rule param(s: &ParserSettings) -> ExprParam = destruct:destruct(s) default:(_ "=" _ default:expr(s){default})? { ExprParam { destruct, default } }63 pub rule params(s: &ParserSettings) -> ExprParams64 = params:param(s) ** comma() comma()? { ExprParams::new(params) }65 / { ExprParams::new(Vec::new()) }6667 pub rule arg(s: &ParserSettings) -> (Option<IStr>, Expr)68 = name:(quiet! { (s:id() _ "=" !['='] _ {s})? } / expected!("<argument name>")) expr:expr(s) {(name, expr)}6970 pub rule args(s: &ParserSettings) -> ArgsDesc71 = args:arg(s)**comma() comma()? {?72 let unnamed_count = args.iter().take_while(|(n, _)| n.is_none()).count();73 let mut unnamed = Vec::with_capacity(unnamed_count);74 let mut names = Vec::with_capacity(args.len() - unnamed_count);75 let mut values = Vec::with_capacity(args.len() - unnamed_count);76 let mut named_started = false;77 for (name, value) in args {78 if let Some(name) = name {79 named_started = true;80 names.push(name);81 values.push(value);82 } else {83 if named_started {84 return Err("<named argument>")85 }86 unnamed.push(value);87 }88 }89 Ok(ArgsDesc{unnamed, names, values})90 }9192 pub rule destruct_rest() -> DestructRest93 = "..." into:(_ into:id() {into})? {into.map_or_else(|| DestructRest::Drop, DestructRest::Keep)}94 pub rule destruct_array(s: &ParserSettings) -> Destruct95 = "[" _ start:destruct(s)**comma() rest:(96 comma() _ rest:destruct_rest()? end:(97 comma() end:destruct(s)**comma() (_ comma())? {end}98 / comma()? {Vec::new()}99 ) {(rest, end)}100 / comma()? {(None, Vec::new())}101 ) _ "]" {?102 #[cfg(feature = "exp-destruct")] return Ok(Destruct::Array {103 start,104 rest: rest.0,105 end: rest.1,106 });107 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")108 }109 pub rule destruct_object(s: &ParserSettings) -> Destruct110 = "{" _111 fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:spanned(<expr(s)>, s) {v})? {(name, into, default)})**comma()112 rest:(113 comma() rest:destruct_rest()? {rest}114 / comma()? {None}115 )116 _ "}" {?117 #[cfg(feature = "exp-destruct")] return Ok(Destruct::Object {118 fields,119 rest,120 });121 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")122 }123 pub rule destruct(s: &ParserSettings) -> Destruct124 = v:spanned(<id()>, s) {Destruct::Full(v)}125 / "?" {?126 #[cfg(feature = "exp-destruct")] return Ok(Destruct::Skip);127 #[cfg(not(feature = "exp-destruct"))] Err("!!!experimental destructuring was not enabled")128 }129 / arr:destruct_array(s) {arr}130 / obj:destruct_object(s) {obj}131132 pub rule bind(s: &ParserSettings) -> BindSpec133 = into:destruct(s) _ "=" _ value:expr(s) {BindSpec::Field{into, value}}134 / name:spanned(<id()>, s) _ "(" _ params:params(s) _ ")" _ "=" _ value:expr(s) {BindSpec::Function{name, params, value}}135136 pub rule assertion(s: &ParserSettings) -> AssertStmt137 = keyword("assert") _ assertion:spanned(<expr(s)>, s) message:(_ ":" _ e:expr(s) {e})? { AssertStmt{assertion, message} }138139 pub rule whole_line() -> &'input str140 = str:$((!['\n'][_])* "\n") {str}141 pub rule string_block() -> String142 = "|||" chomped:"-"? (!['\n']single_whitespace())* "\n"143 empty_lines:$(['\n']*)144 prefix:[' ' | '\t']+ first_line:whole_line()145 lines:("\n" {"\n"} / [' ' | '\t']*<{prefix.len()}> s:whole_line() {s})*146 [' ' | '\t']*<, {prefix.len() - 1}> "|||"147 {148 let mut l = empty_lines.to_owned();149 l.push_str(first_line);150 l.extend(lines);151 if chomped.is_some() {152 debug_assert!(l.ends_with('\n'));153 l.truncate(l.len() - 1);154 }155 l156 }157158 rule hex_char()159 = quiet! { ['0'..='9' | 'a'..='f' | 'A'..='F'] } / expected!("<hex char>")160161 rule string_char(c: rule<()>)162 = (!['\\']!c()[_])+163 / "\\\\"164 / "\\u" hex_char() hex_char() hex_char() hex_char()165 / "\\x" hex_char() hex_char()166 / ['\\'] (quiet! { ['b' | 'f' | 'n' | 'r' | 't' | '"' | '\''] } / expected!("<escape character>"))167 pub rule string() -> String168 = ['"'] str:$(string_char(<"\"">)*) ['"'] {? unescape::unescape(str).ok_or("<escaped string>")}169 / ['\''] str:$(string_char(<"\'">)*) ['\''] {? unescape::unescape(str).ok_or("<escaped string>")}170 / quiet!{ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}171 / "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}172 / string_block() } / expected!("<string>")173174 pub rule field_name(s: &ParserSettings) -> FieldName175 = name:id() {FieldName::Fixed(name)}176 / name:string() {FieldName::Fixed(name.into())}177 / "[" _ expr:expr(s) _ "]" {FieldName::Dyn(expr)}178 pub rule visibility() -> Visibility179 = ":::" {Visibility::Unhide}180 / "::" {Visibility::Hidden}181 / ":" {Visibility::Normal}182 pub rule field(s: &ParserSettings) -> FieldMember183 = name:spanned(<field_name(s)>, s) _ plus:"+"? _ visibility:visibility() _ value:expr(s) {FieldMember{184 name,185 plus: plus.is_some(),186 params: None,187 visibility,188 value,189 }}190 / name:spanned(<field_name(s)>, s) _ "(" _ params:params(s) _ ")" _ visibility:visibility() _ value:expr(s) {FieldMember{191 name,192 plus: false,193 params: Some(params),194 visibility,195 value,196 }}197 pub rule obj_local(s: &ParserSettings) -> BindSpec198 = keyword("local") _ bind:bind(s) {bind}199 pub rule member(s: &ParserSettings) -> Member200 = bind:obj_local(s) {Member::BindStmt(bind)}201 / assertion:assertion(s) {Member::AssertStmt(assertion)}202 / field:field(s) {Member::Field(field)}203 pub rule objinside(s: &ParserSettings) -> ObjBody204 = members:(member(s) ** comma()) comma()? _ compspecs:compspecs(s)? {?205 Ok(if let Some(compspecs) = compspecs {206 let mut locals = Vec::new();207 let mut field = None;208 for member in members {209 match member {210 Member::Field(field_member) => if field.replace(field_member).is_some() {211 return Err("<object comprehension can only contain one field>")212 },213 Member::BindStmt(bind_spec) => locals.push(bind_spec),214 Member::AssertStmt(assert_stmt) => return Err("<asserts are unsupported in object comprehension>"),215 }216 }217 ObjBody::ObjComp(ObjComp {218 locals,219 field: Box::new(field.ok_or("<missing object comprehension field>")?),220 compspecs221 })222 } else {223 let mut locals = Vec::new();224 let mut asserts = Vec::new();225 let mut fields = Vec::new();226 for member in members {227 match member {228 Member::Field(field_member) => fields.push(field_member),229 Member::BindStmt(bind_spec) => locals.push(bind_spec),230 Member::AssertStmt(assert_stmt) => asserts.push(assert_stmt),231 }232 }233 ObjBody::MemberList(ObjMembers {234 locals,235 asserts,236 fields237 })238 })239 }240 pub rule ifspec(s: &ParserSettings) -> IfSpecData241 = i:spanned(<keyword("if")>, s) _ cond:expr(s) {IfSpecData { span: i.span, cond }}242 pub rule forspec(s: &ParserSettings) -> ForSpecData243 = keyword("for") _ destruct:destruct(s) _ keyword("in") _ over:expr(s) { ForSpecData { destruct, over } }244 rule ensure_object_iteration()245 = "" {?246 #[cfg(not(feature = "exp-object-iteration"))] return Err("!!!experimental object iteration was not enabled");247 #[cfg(feature = "exp-object-iteration")] Ok(())248 }249 pub rule forobjspec(s: &ParserSettings) -> CompSpec250 = ensure_object_iteration() keyword("for") _ "[" _ key:id() _ "]" _ vis:visibility() _ value:destruct(s) _ keyword("in") _ over:expr(s) {251 #[cfg(feature = "exp-object-iteration")] return CompSpec::ForObjSpec(jrsonnet_ir::ForObjSpecData { key, visibility: vis, value, over });252 #[cfg(not(feature = "exp-object-iteration"))] unreachable!("ensure_object_iteration will fail if feature is not enabled")253 }254 rule compspec(s: &ParserSettings) -> CompSpec255 = i:ifspec(s) { CompSpec::IfSpec(i) } / f:forobjspec(s) { f } / f:forspec(s) {CompSpec::ForSpec(f)}256 pub rule compspecs(s: &ParserSettings) -> Vec<CompSpec>257 = specs:compspec(s) ++ _ {?258 if matches!(specs[0], CompSpec::IfSpec(_)) {259 return Err("<first compspec should be for>")260 }261 Ok(specs)262 }263 pub rule local_expr(s: &ParserSettings) -> Expr264 = keyword("local") _ binds:bind(s) ** comma() (_ ",")? _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, Box::new(expr)) }265 pub rule string_expr(s: &ParserSettings) -> Expr266 = s:string() {Expr::Str(s.into())}267 pub rule obj_expr(s: &ParserSettings) -> Expr268 = "{" _ body:objinside(s) _ "}" {Expr::Obj(body)}269 pub rule array_expr(s: &ParserSettings) -> Expr270 = "[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}271 pub rule array_comp_expr(s: &ParserSettings) -> Expr272 = "[" _ expr:expr(s) _ comma()? _ specs:(r: compspecs(s) _ {r}) "]" {273 Expr::ArrComp(Box::new(expr), specs)274 }275 pub rule number_expr(s: &ParserSettings) -> Expr276 = n:number() {? NumValue::new(n).map_or_else(|| Err("!!!numbers are finite"), |n| Ok(Expr::Num(n)))}277278 rule spanned<T: Acyclic>(x: rule<T>, s: &ParserSettings) -> Spanned<T>279 = a:position!() n:x() b:position!() { Spanned::new(n, Span(s.source.clone(), codeidx(a), codeidx(b))) }280281 pub rule var_expr(s: &ParserSettings) -> Expr282 = n:spanned(<id()>, s) { Expr::Var(n) }283 pub rule id_loc(s: &ParserSettings) -> Spanned<Expr>284 = a:position!() n:id() b:position!() { Spanned::new(Expr::Str(n), Span(s.source.clone(), codeidx(a), codeidx(b))) }285 pub rule if_then_else_expr(s: &ParserSettings) -> Expr286 = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse(Box::new(IfElse{287 cond,288 cond_then,289 cond_else,290 }))}291292 pub rule literal(s: &ParserSettings) -> Expr293 = a:position!() v:(294 keyword("null") {LiteralType::Null}295 / keyword("true") {LiteralType::True}296 / keyword("false") {LiteralType::False}297 / keyword("self") {LiteralType::This}298 / keyword("$") {LiteralType::Dollar}299 / keyword("super") {LiteralType::Super}300 ) b:position!() {Expr::Literal(Span(s.source.clone(), codeidx(a), codeidx(b)), v)}301302 rule import_kind() -> ImportKind303 = keyword("importstr") { ImportKind::Str }304 / keyword("importbin") { ImportKind::Bin }305 / keyword("import") { ImportKind::Normal }306307 pub rule expr_basic(s: &ParserSettings) -> Expr308 = literal(s)309310 / string_expr(s) / number_expr(s)311 / array_expr(s)312 / obj_expr(s)313 / array_expr(s)314 / array_comp_expr(s)315316 / kind:spanned(<import_kind()>, s) _ path:expr(s) {Expr::Import(kind, Box::new(path))}317318 / var_expr(s)319 / local_expr(s)320 / if_then_else_expr(s)321322 / kw:spanned(<keyword("function")>, s) _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(kw.span, params, Box::new(expr))}323 / assert:assertion(s) _ ";" _ rest:expr(s) { Expr::AssertExpr(Box::new(AssertExpr{324 assert, rest325 })) }326327 / err_kw:spanned(<keyword("error")>, s) _ expr:expr(s) { Expr::ErrorStmt(err_kw.span, Box::new(expr)) }328329 rule slice_part(s: &ParserSettings) -> Option<Spanned<Expr>>330 = _ e:(e:spanned(<expr(s)>, s) _{e})? {e}331 pub rule slice_desc(s: &ParserSettings) -> SliceDesc332 = start:slice_part(s) ":" pair:(end:slice_part(s) step:(":" e:slice_part(s){e})? {(end, step.flatten())})? {333 let (end, step) = if let Some((end, step)) = pair {334 (end, step)335 }else{336 (None, None)337 };338339 SliceDesc { start, end, step }340 }341342 rule binop(x: rule<()>) -> ()343 = quiet!{ x() } / expected!("<binary op>")344 rule unaryop(x: rule<()>) -> ()345 = quiet!{ x() } / expected!("<unary op>")346347 rule ensure_null_coaelse()348 = "" {?349 #[cfg(not(feature = "exp-null-coaelse"))] return Err("!!!experimental null coaelscing was not enabled");350 #[cfg(feature = "exp-null-coaelse")] Ok(())351 }352 use jrsonnet_ir::BinaryOpType::*;353 use jrsonnet_ir::UnaryOpType::*;354 rule expr(s: &ParserSettings) -> Expr355 = precedence! {356 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}357 a:(@) _ binop(<"??">) _ ensure_null_coaelse() b:@ {358 #[cfg(feature = "exp-null-coaelse")] return expr_bin!(a NullCoaelse b);359 unreachable!("ensure_null_coaelse will fail if feature is not enabled")360 }361 --362 a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}363 --364 a:(@) _ binop(<"|">) _ b:@ {expr_bin!(a BitOr b)}365 --366 a:@ _ binop(<"^">) _ b:(@) {expr_bin!(a BitXor b)}367 --368 a:(@) _ binop(<"&">) _ b:@ {expr_bin!(a BitAnd b)}369 --370 a:(@) _ binop(<"==">) _ b:@ {expr_bin!(a Eq b)}371 a:(@) _ binop(<"!=">) _ b:@ {expr_bin!(a Neq b)}372 --373 a:(@) _ binop(<"<">) _ b:@ {expr_bin!(a Lt b)}374 a:(@) _ binop(<">">) _ b:@ {expr_bin!(a Gt b)}375 a:(@) _ binop(<"<=">) _ b:@ {expr_bin!(a Lte b)}376 a:(@) _ binop(<">=">) _ b:@ {expr_bin!(a Gte b)}377 a:(@) _ binop(<keyword("in")>) _ b:@ {expr_bin!(a In b)}378 --379 a:(@) _ binop(<"<<">) _ b:@ {expr_bin!(a Lhs b)}380 a:(@) _ binop(<">>">) _ b:@ {expr_bin!(a Rhs b)}381 --382 a:(@) _ binop(<"+">) _ b:@ {expr_bin!(a Add b)}383 a:(@) _ binop(<"-">) _ b:@ {expr_bin!(a Sub b)}384 --385 a:(@) _ binop(<"*">) _ b:@ {expr_bin!(a Mul b)}386 a:(@) _ binop(<"/">) _ b:@ {expr_bin!(a Div b)}387 a:(@) _ binop(<"%">) _ b:@ {expr_bin!(a Mod b)}388 --389 unaryop(<"+">) _ b:@ {expr_un!(Plus b)}390 unaryop(<"-">) _ b:@ {expr_un!(Minus b)}391 unaryop(<"!">) _ b:@ {expr_un!(Not b)}392 unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}393 --394 value:(@) _ "[" _ slice:slice_desc(s) _ "]" {Expr::Slice(Box::new(Slice{value, slice}))}395 indexable:(@) _ parts:index_part(s)+ {Expr::Index{indexable: Box::new(indexable), parts}}396 a:(@) _ args:spanned(<"(" _ a:args(s) _ ")" {a}>, s) ts:(_ keyword("tailstrict"))? {Expr::Apply(Box::new(a), args, ts.is_some())}397 a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(Box::new(a), body)}398 --399 e:expr_basic(s) {e}400 "(" _ e:expr(s) _ ")" {e}401 }402 pub rule index_part(s: &ParserSettings) -> IndexPart403 = n:("?" _ ensure_null_coaelse())? "." _ value:id_loc(s) {IndexPart {404 span: value.span,405 value: value.value,406 #[cfg(feature = "exp-null-coaelse")]407 null_coaelse: n.is_some(),408 }}409 / n:("?" _ "." _ ensure_null_coaelse())? value:spanned(<"[" _ v:expr(s) _ "]" {v}>, s) {IndexPart {410 span: value.span,411 value: value.value,412 #[cfg(feature = "exp-null-coaelse")]413 null_coaelse: n.is_some(),414 }}415416 pub rule jsonnet(s: &ParserSettings) -> Expr = _ e:expr(s) _ {e}417 }418}419420fn codeidx(i: usize) -> u32 {421 u32::try_from(i).expect("code has 4g hard limit")422}423424pub type ParseError = peg::error::ParseError<peg::str::LineCol>;425pub fn parse(str: &str, settings: &ParserSettings) -> Result<Expr, ParseError> {426 jsonnet_parser::jsonnet(str, settings)427}428/// Used for importstr values429pub fn string_to_expr(str: IStr, settings: &ParserSettings) -> Spanned<Expr> {430 let len = str.len();431 Spanned::new(432 Expr::Str(str),433 Span(settings.source.clone(), 0, codeidx(len)),434 )435}436437#[cfg(test)]438mod tests {439 #[test]440 #[cfg(not(feature = "exp-null-coaelse"))]441 fn snapshots() {442 use std::fs;443444 use insta::{assert_snapshot, glob};445 use jrsonnet_ir::{IStr, Source};446447 use crate::{ParserSettings, parse};448449 glob!("tests/*.jsonnet", |path| {450 let input = fs::read_to_string(path).expect("read test file");451 let v = parse(452 &input,453 &ParserSettings {454 source: Source::new_virtual("<test>".into(), IStr::empty()),455 },456 )457 .unwrap();458 let v = format!("{v:#?}");459 assert_snapshot!(v);460 });461 }462}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");