Skip to content

Commit c5da414

Browse files
authored
Align string member empty needle semantics (#1084)
## Motivation `std.member` accepts either an array or a string as its first argument. For strings, the official Jsonnet stdlib defines membership through `std.findSubstr(x, arr)`, and `std.findSubstr("", "abc")` returns an empty array. That means an empty string should not be considered a string member. sjsonnet's native implementation delegated directly to JVM `String.contains`, which treats the empty string as contained in every string. This made `std.member("abc", "")` and `std.member("", "")` return `true`, even though the official stdlib-derived semantics return `false`. ## Modification - Require the string needle in `std.member` to be non-empty before calling `String.contains`. - Add regression coverage for empty string needles in string membership. - Add array membership coverage to show that `std.member([""], "")` remains `true`. ## Result | Implementation | `std.member("abc", "")` | `std.member("", "")` | `std.member([""], "")` | `std.findSubstr("", "abc")` | | --- | --- | --- | --- | --- | | cpp-jsonnet | `false` | `false` | `true` | `[]` | | go-jsonnet | `true` | `true` | `true` | `[]` | | jrsonnet | `false` | `false` | `true` | `[]` | | sjsonnet before this PR | `true` | `true` | `true` | `[]` | | sjsonnet after this PR | `false` | `false` | `true` | `[]` | sjsonnet now matches cpp-jsonnet and jrsonnet for string membership with an empty needle, while preserving normal substring membership and array element membership. ## References - Jsonnet stdlib docs for `std.member`: https://jsonnet.org/ref/stdlib.html#std-member - Jsonnet stdlib docs for `std.findSubstr`: https://jsonnet.org/ref/stdlib.html#std-findSubstr - Official Jsonnet stdlib `member` definition: https://github.com/google/jsonnet/blob/master/stdlib/std.jsonnet - go-jsonnet native `member` implementation: https://github.com/google/go-jsonnet/blob/master/builtins.go
1 parent fe92c46 commit c5da414

3 files changed

Lines changed: 8 additions & 1 deletion

File tree

sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ object ArrayModule extends AbstractFunctionModule {
574574
case n =>
575575
Error.fail("second argument must be a string, got " + n.prettyName)
576576
}
577-
str.str.contains(secondArg)
577+
secondArg.nonEmpty && str.str.contains(secondArg)
578578
case a: Val.Arr =>
579579
var i = 0
580580
var found = false
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
std.assertEqual(std.member('abc', ''), false) &&
2+
std.assertEqual(std.member('', ''), false) &&
3+
std.assertEqual(std.member('', 'a'), false) &&
4+
std.assertEqual(std.member('abc', 'bc'), true) &&
5+
std.assertEqual(std.member([''], ''), true) &&
6+
std.assertEqual(std.member([''], 'a'), false)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
true

0 commit comments

Comments
 (0)