Skip to content

Reject duplicate function parameters#876

Open
rohan-patnaik wants to merge 1 commit into
google:masterfrom
rohan-patnaik:reject-duplicate-params
Open

Reject duplicate function parameters#876
rohan-patnaik wants to merge 1 commit into
google:masterfrom
rohan-patnaik:reject-duplicate-params

Conversation

@rohan-patnaik

Copy link
Copy Markdown

Rejects duplicate parameter names while parsing function and method parameter lists.

Previously, go-jsonnet accepted inputs like:

(function(x, x, x) x)(null, 3, 2)

That makes argument/default handling ambiguous and differs from other Jsonnet implementations, which reject duplicate parameters.

This change tracks parameter names while parsing and returns a static error when the same name appears twice. It covers normal functions, local function sugar, and object methods.

Fixes #873.

Testing:

  • go test ./internal/parser
  • go test ./...

@He-Pin

He-Pin commented May 22, 2026

Copy link
Copy Markdown
Contributor

Great, does the same problem exist in cpp?

@CertainLach

Copy link
Copy Markdown

CPP is fine

stephenamar-db pushed a commit to databricks/sjsonnet that referenced this pull request Jun 22, 2026
…ontexts (#1011)

## Motivation

go-jsonnet issue #873 reports that `(function(x, x, x) x)(null, 3, 2)`
silently returns `2` instead of rejecting duplicate parameter names.
sjsonnet already rejected duplicates in function expressions, but the
error message was misleading in local bindings and object methods
(e.g. "Expected \")\"" or "Expected \":\"").

## Modification

The `params` parser correctly detects duplicates via `flatMapX` +
`Fail`. However, in `bind` (`local f(x,x)=...`) and `field`
(`{f(x,x):...}`), the optional group `.?` around `params` swallowed
the "no duplicate parameter" error and backtracked, producing
misleading errors.

Two changes fix this:

1. In `field()`: added `~/` (cut) after `"("` to commit to the params
   path once `"("` is matched: `"(" ~/ params(...) ~ ")"`.
2. In `params()`: set `ctx.cut = true` programmatically when a
   duplicate is detected, preventing outer `.?` operators from
   swallowing the error. Also captures `Pos` for each parameter to
   position the error at the duplicate parameter name (not at the
   closing paren).

## Result

All duplicate parameter contexts now produce the correct error
`"Expected no duplicate parameter: x"` with accurate position info:

- `(function(x, x, x) x)` -> ParseError (already worked)
- `local f(x, x) = x` -> ParseError (was `"Expected )"`)
- `{ f(x, x): x }` -> ParseError (was `"Expected :"`)
- `{ local f(x, x) = x }` -> ParseError (was `"Expected )"`)
- Nested bindings also fixed as a bonus.

Tests pass on Scala 3.3.7, 2.13.18, 2.12.21.

## References

- google/go-jsonnet#873


## Cross-implementation comparison: Duplicate function parameters

| Implementation | Detection Phase | All Contexts Covered | Error
Message Example |
|---|---|---|---|
| **sjsonnet (this fix)** | Parse time | ✅ All 4 contexts | `Expected no
duplicate parameter: x` |
| **cpp-jsonnet** | Static analysis (post-parse) | ✅ All 4 (via
desugaring) | `Duplicate function parameter: x` |
| **go-jsonnet** (master) | ❌ None | ❌ Bug — silently accepts | N/A —
last argument wins silently |
| **go-jsonnet** ([PR
#876](google/go-jsonnet#876), unmerged) | Parse
time | ✅ All 4 | `Duplicate function parameter: a` / `Duplicate method
parameter: x` |
| **jrsonnet** | Static analysis (post-parse) | ✅ All 4 (via desugaring)
| `local is already defined in the current frame: x` |

### Behavior by test case

| Test Case | sjsonnet (fix) | cpp-jsonnet | go-jsonnet | jrsonnet |
|---|---|---|---|---|
| `(function(x, x, x) x)(null, 3, 2)` | ✅ ParseError | ✅ StaticError | ❌
Returns `2` | ✅ StaticError |
| `local f(x, x) = x; f(1, 2)` | ✅ ParseError | ✅ StaticError | ❌
Accepted | ✅ StaticError |
| `{ f(x, x): x }.f(1, 2)` | ✅ ParseError | ✅ StaticError | ❌ Accepted |
✅ StaticError |
| `{ local f(x, x) = x, a: f(1, 2) }` | ✅ ParseError | ✅ StaticError | ❌
Accepted | ✅ StaticError |

### Key differences

- **Detection timing**: sjsonnet and go-jsonnet (PR #876) detect at
parse time; cpp-jsonnet and jrsonnet detect during a separate static
analysis pass after parsing.
- **Error message specificity**: go-jsonnet PR #876 distinguishes
"function parameter" vs "method parameter"; cpp-jsonnet uses "function
parameter" uniformly (methods desugar to functions before the check).
- **Position accuracy**: sjsonnet points to the duplicate parameter
name; cpp-jsonnet points to the entire function AST node.
- **go-jsonnet master** has a known bug ([issue
#873](google/go-jsonnet#873)) where duplicate
parameters are silently accepted with "last argument wins" semantics.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

go-jsonnet does not reject duplicate parameter definition

3 participants