git.delta.rocks / jrsonnet / refs/commits / a6892a95138f

difftreelog

fix(rowan-parser) forbid assert in objcomp

Yaroslav Bolyukin2022-12-14parent: #cf173f6.patch.diff
in: master

3 files changed

modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/parser.rs
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -408,6 +408,7 @@
 
 	let mut elems = 0;
 	let mut compspecs = Vec::new();
+	let mut asserts = Vec::new();
 	loop {
 		if p.at(T!['}']) {
 			p.bump();
@@ -430,10 +431,10 @@
 		let m = p.start();
 		if p.at(T![local]) {
 			obj_local(p);
-			m.complete(p, MEMBER_BIND_STMT)
+			m.complete(p, MEMBER_BIND_STMT);
 		} else if p.at(T![assert]) {
 			assertion(p);
-			m.complete(p, MEMBER_ASSERT_STMT)
+			asserts.push(m.complete(p, MEMBER_ASSERT_STMT));
 		} else {
 			field_name(p);
 			if p.at(T![+]) {
@@ -455,14 +456,14 @@
 				expr(p);
 				false
 			};
+			elems += 1;
 
 			if params {
 				m.complete(p, MEMBER_FIELD_METHOD)
 			} else {
 				m.complete(p, MEMBER_FIELD_NORMAL)
-			}
+			};
 		};
-		elems += 1;
 		while p.at_ts(COMPSPEC) {
 			compspecs.push(compspec(p));
 		}
@@ -482,6 +483,9 @@
 		}
 		m.complete(p, OBJ_BODY_MEMBER_LIST);
 	} else if !compspecs.is_empty() {
+		for errored in asserts {
+			errored.wrap_error(p, "asserts can't be used in object comprehensions");
+		}
 		m.complete(p, OBJ_BODY_COMP);
 	} else {
 		m.complete(p, OBJ_BODY_MEMBER_LIST);
addedcrates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_asserts.snapdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-rowan-parser/src/snapshots/jrsonnet_rowan_parser__tests__obj_compspec_incompatible_with_asserts.snap
@@ -0,0 +1,57 @@
+---
+source: crates/jrsonnet-rowan-parser/src/tests.rs
+expression: "{assert 1, a: 1 for a in [1,2,3]}\n"
+---
+SOURCE_FILE@0..34
+  EXPR_OBJECT@0..33
+    OBJ_BODY_COMP@0..33
+      L_BRACE@0..1 "{"
+      ERROR_CUSTOM@1..9
+        MEMBER_ASSERT_STMT@1..9
+          ASSERTION@1..9
+            ASSERT_KW@1..7 "assert"
+            WHITESPACE@7..8 " "
+            LHS_EXPR@8..9
+              EXPR_NUMBER@8..9
+                FLOAT@8..9 "1"
+      COMMA@9..10 ","
+      WHITESPACE@10..11 " "
+      MEMBER_FIELD_NORMAL@11..15
+        FIELD_NAME_FIXED@11..12
+          NAME@11..12
+            IDENT@11..12 "a"
+        COLON@12..13 ":"
+        WHITESPACE@13..14 " "
+        EXPR_NUMBER@14..15
+          FLOAT@14..15 "1"
+      WHITESPACE@15..16 " "
+      FOR_SPEC@16..32
+        FOR_KW@16..19 "for"
+        WHITESPACE@19..20 " "
+        NAME@20..21
+          IDENT@20..21 "a"
+        WHITESPACE@21..22 " "
+        IN_KW@22..24 "in"
+        WHITESPACE@24..25 " "
+        EXPR_ARRAY@25..32
+          L_BRACK@25..26 "["
+          EXPR_NUMBER@26..27
+            FLOAT@26..27 "1"
+          COMMA@27..28 ","
+          EXPR_NUMBER@28..29
+            FLOAT@28..29 "2"
+          COMMA@29..30 ","
+          EXPR_NUMBER@30..31
+            FLOAT@30..31 "3"
+          R_BRACK@31..32 "]"
+      R_BRACE@32..33 "}"
+  WHITESPACE@33..34 "\n"
+===
+LocatedSyntaxError { error: Custom { error: "asserts can't be used in object comprehensions" }, range: 1..9 }
+===
+  x syntax error
+   ,----
+ 1 | {assert 1, a: 1 for a in [1,2,3]}
+   :  ^^^^|^^^
+   :      `-- asserts can't be used in object comprehensions
+   `----
modifiedcrates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth
before · crates/jrsonnet-rowan-parser/src/tests.rs
1#![cfg(test)]23use miette::{4	Diagnostic, GraphicalReportHandler, GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles,5};6use thiserror::Error;78use crate::{parse, AstNode};910#[derive(Debug, Error)]11#[error("syntax error")]12struct MyDiagnostic {13	code: String,14	spans: Vec<LabeledSpan>,15}16impl Diagnostic for MyDiagnostic {17	fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {18		None19	}2021	fn severity(&self) -> Option<miette::Severity> {22		None23	}2425	fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {26		None27	}2829	fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {30		None31	}3233	fn source_code(&self) -> Option<&dyn miette::SourceCode> {34		Some(&self.code)35	}3637	fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {38		Some(Box::new(self.spans.clone().into_iter()))39	}4041	fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {42		None43	}44}4546fn process(text: &str) -> String {47	use std::fmt::Write;48	let mut out = String::new();49	let (node, errors) = parse(text);50	write!(out, "{:#?}", node.syntax()).unwrap();51	if !errors.is_empty() && !text.is_empty() {52		writeln!(out, "===").unwrap();53		for err in &errors {54			writeln!(out, "{:?}", err).unwrap();55		}56		let mut code = text.to_string();5758		// Prettier errors at EOF position59		if code.ends_with('\n') {60			code.truncate(code.len() - 1);61			code += " ";62		}63		code += " ";6465		let diag = MyDiagnostic {66			code,67			spans: errors.into_iter().map(|e| e.into()).collect(),68		};6970		let handler = GraphicalReportHandler::new_themed(GraphicalTheme {71			characters: ThemeCharacters::ascii(),72			styles: ThemeStyles::none(),73		});7475		write!(out, "===").unwrap();76		handler77			.render_report(&mut out, &diag)78			.expect("fmt error?..");79	}80	out.split('\n')81		.map(|s| s.trim_end().to_string())82		.collect::<Vec<String>>()83		.join("\n")84		.trim_end()85		.to_string()86}87macro_rules! mk_test {88		($($name:ident => $test:expr)+) => {$(89			#[test]90			fn $name() {91				let src = indoc::indoc!($test);92				let result = process(&src);93				insta::assert_snapshot!(stringify!($name), result, src);9495			}96		)+};97	}98mk_test!(99	empty => r#" "#100	function => r#"101		function(a, b = 1) a + b102	"#103	function_error_no_value => r#"104		function(a, b = ) a + b105	"#106	function_error_rparen => r#"107		function(a, b108	"#109	function_error_body => r#"110		function(a, b)111	"#112	local_novalue => r#"113		local a =114	"#115	local_no_value_recovery => r#"116		local a =117		local b = 3;118		1119	"#120121122	no_rhs => r#"123		a +124	"#125	no_lhs => r#"126		+ 2127	"#128	no_operator => "129		2 2130	"131132	named_before_positional => "133		a(1, 2, b=4, 3, 5, k = 12, 6)134	"135136	wrong_field_end => "137		{138			a: 1;139			b: 2;140		}141	"142143144	plain_call => "145		std.substr(a, 0, std.length(b)) == b146	"147148	destruct => "149		local [a, b, c] = arr;150		local [a, ...] = arr_rest;151		local [..., a] = rest_arr;152		local [...] = rest_in_arr;153		local [a, ...n] = arr_rest_n;154		local [...n, a] = rest_arr_n;155		local [...n] = rest_in_arr_n;156157		local {a, b, c} = obj;158		local {a, b, c, ...} = obj_rest;159		local {a, b, c, ...n} = obj_rest_n;160161		null162	"163164	str_block_missing_indent => "165		|||166	"167	str_block_missing_termination => "168		|||169			hello170	"171	str_block_missing_newline => "172		|||hello173	"174	str_block_missing_indent_text => "175		|||176		hello177	"178179	unexpected_destruct => "180		local * = 1;181		a182	"183	arr_compspec => r#"184		[a for a in [1, 2, 3]]185	"#186	arr_compspec_comma => "187		[a, for a in [1, 2, 3]]188	"189	arr_compspec_no_elems => "190		[for a in [1, 2, 3]]191	"192	arr_compspec_incompatible_with_multiple_elems => r#"193		[a for a in [1, 2, 3], b]194	"#195	arr_compspec_incompatible_with_multiple_elems_w => r#"196		[a, b, for a in [1, 2, 3], c]197	"#198199	obj_compspec => r#"200		{a:1 for a in [1, 2, 3]}201	"#202	obj_compspec_comma => "203		{a:1, for a in [1, 2, 3]}204	"205	obj_compspec_no_elems => "206		{for a in [1, 2, 3]}207	"208	obj_compspec_incompatible_with_multiple_elems => r#"209		{a:1 for a in [1, 2, 3], b:1}210	"#211	obj_compspec_incompatible_with_multiple_elems_w => r#"212		{a:1, b:1, for a in [1, 2, 3], c:1}213	"#214215	local_method => r#"216		local217			a(x) = x,218			a = function(x) x,219		; c220	"#221	obj_method => r#"222		{223			a(x): x,224			a: function(x) x,225		}226	"#227);228229#[test]230fn stdlib() {231	let src = include_str!("../../jrsonnet-stdlib/src/std.jsonnet");232	let result = process(src);233	insta::assert_snapshot!("stdlib", result, src);234}
after · crates/jrsonnet-rowan-parser/src/tests.rs
1#![cfg(test)]23use miette::{4	Diagnostic, GraphicalReportHandler, GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles,5};6use thiserror::Error;78use crate::{parse, AstNode};910#[derive(Debug, Error)]11#[error("syntax error")]12struct MyDiagnostic {13	code: String,14	spans: Vec<LabeledSpan>,15}16impl Diagnostic for MyDiagnostic {17	fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {18		None19	}2021	fn severity(&self) -> Option<miette::Severity> {22		None23	}2425	fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {26		None27	}2829	fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {30		None31	}3233	fn source_code(&self) -> Option<&dyn miette::SourceCode> {34		Some(&self.code)35	}3637	fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {38		Some(Box::new(self.spans.clone().into_iter()))39	}4041	fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {42		None43	}44}4546fn process(text: &str) -> String {47	use std::fmt::Write;48	let mut out = String::new();49	let (node, errors) = parse(text);50	write!(out, "{:#?}", node.syntax()).unwrap();51	if !errors.is_empty() && !text.is_empty() {52		writeln!(out, "===").unwrap();53		for err in &errors {54			writeln!(out, "{:?}", err).unwrap();55		}56		let mut code = text.to_string();5758		// Prettier errors at EOF position59		if code.ends_with('\n') {60			code.truncate(code.len() - 1);61			code += " ";62		}63		code += " ";6465		let diag = MyDiagnostic {66			code,67			spans: errors.into_iter().map(|e| e.into()).collect(),68		};6970		let handler = GraphicalReportHandler::new_themed(GraphicalTheme {71			characters: ThemeCharacters::ascii(),72			styles: ThemeStyles::none(),73		});7475		write!(out, "===").unwrap();76		handler77			.render_report(&mut out, &diag)78			.expect("fmt error?..");79	}80	out.split('\n')81		.map(|s| s.trim_end().to_string())82		.collect::<Vec<String>>()83		.join("\n")84		.trim_end()85		.to_string()86}87macro_rules! mk_test {88		($($name:ident => $test:expr)+) => {$(89			#[test]90			fn $name() {91				let src = indoc::indoc!($test);92				let result = process(&src);93				insta::assert_snapshot!(stringify!($name), result, src);9495			}96		)+};97	}98mk_test!(99	empty => r#" "#100	function => r#"101		function(a, b = 1) a + b102	"#103	function_error_no_value => r#"104		function(a, b = ) a + b105	"#106	function_error_rparen => r#"107		function(a, b108	"#109	function_error_body => r#"110		function(a, b)111	"#112	local_novalue => r#"113		local a =114	"#115	local_no_value_recovery => r#"116		local a =117		local b = 3;118		1119	"#120121122	no_rhs => r#"123		a +124	"#125	no_lhs => r#"126		+ 2127	"#128	no_operator => "129		2 2130	"131132	named_before_positional => "133		a(1, 2, b=4, 3, 5, k = 12, 6)134	"135136	wrong_field_end => "137		{138			a: 1;139			b: 2;140		}141	"142143144	plain_call => "145		std.substr(a, 0, std.length(b)) == b146	"147148	destruct => "149		local [a, b, c] = arr;150		local [a, ...] = arr_rest;151		local [..., a] = rest_arr;152		local [...] = rest_in_arr;153		local [a, ...n] = arr_rest_n;154		local [...n, a] = rest_arr_n;155		local [...n] = rest_in_arr_n;156157		local {a, b, c} = obj;158		local {a, b, c, ...} = obj_rest;159		local {a, b, c, ...n} = obj_rest_n;160161		null162	"163164	str_block_missing_indent => "165		|||166	"167	str_block_missing_termination => "168		|||169			hello170	"171	str_block_missing_newline => "172		|||hello173	"174	str_block_missing_indent_text => "175		|||176		hello177	"178179	unexpected_destruct => "180		local * = 1;181		a182	"183	arr_compspec => r#"184		[a for a in [1, 2, 3]]185	"#186	arr_compspec_comma => "187		[a, for a in [1, 2, 3]]188	"189	arr_compspec_no_elems => "190		[for a in [1, 2, 3]]191	"192	arr_compspec_incompatible_with_multiple_elems => r#"193		[a for a in [1, 2, 3], b]194	"#195	arr_compspec_incompatible_with_multiple_elems_w => r#"196		[a, b, for a in [1, 2, 3], c]197	"#198199	obj_compspec => r#"200		{a:1 for a in [1, 2, 3]}201	"#202	obj_compspec_comma => "203		{a:1, for a in [1, 2, 3]}204	"205	obj_compspec_no_elems => "206		{for a in [1, 2, 3]}207	"208	obj_compspec_incompatible_with_multiple_elems => r#"209		{a:1 for a in [1, 2, 3], b:1}210	"#211	obj_compspec_incompatible_with_multiple_elems_w => r#"212		{a:1, b:1, for a in [1, 2, 3], c:1}213	"#214215	obj_compspec_incompatible_with_asserts => r#"216		{assert 1, a: 1 for a in [1,2,3]}217	"#218219	local_method => r#"220		local221			a(x) = x,222			a = function(x) x,223		; c224	"#225	obj_method => r#"226		{227			a(x): x,228			a: function(x) x,229		}230	"#231);232233#[test]234fn stdlib() {235	let src = include_str!("../../jrsonnet-stdlib/src/std.jsonnet");236	let result = process(src);237	insta::assert_snapshot!("stdlib", result, src);238}