fix(pkg/go/utils): match declarations by full name, not by prefix - #652
fix(pkg/go/utils): match declarations by full name, not by prefix#652SoulPancake wants to merge 4 commits into
Conversation
The line-number helpers in pkg/go/utils matched a declaration with strings.HasPrefix on the name alone, so a name that is a prefix of another resolved to the wrong line: asking for `document` matched `type documentation`, `owner` matched `define owner_group:`, and `less` matched `condition less_than(`. Each helper now validates what follows the name, mirroring the JS reference in pkg/js/validator/validate-dsl.ts: - types and extended types accept only end-of-line or a trailing comment, which the module fixtures rely on (`type other # module: core, file: core.fga`). The `#` must be preceded by whitespace, so `type doc#x` is not a match for `doc`. - relations require a `:` after the name, and runs of spaces are collapsed first so `define owner:` matches as `define owner:`. - conditions require the parameter list's `(` after the name. Signatures, the `int` return and the `-1` miss sentinel are all unchanged, so the only visible difference is that the transformer's duplicate-type, duplicate-condition, extended-type and duplicate- relation errors now point at the correct line in these collision cases. No error identity or message text changes. This also brings the helpers into agreement with the equivalents in pkg/go/validation, which already matched exactly; a differential check over 540 comparisons finds no remaining divergence. That agreement is what lets the two copies be unified without a further behaviour change.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR fixes how the Go pkg/go/utils line-number helpers locate DSL declarations by ensuring they match full identifiers (and required trailing syntax) rather than using prefix-only matching, which could previously return the wrong line when names were prefixes of other names.
Changes:
- Introduces a shared
declarationIndexhelper and precise “remainder” validation to avoid prefix collisions fortype,extend type, andconditiondeclarations. - Tightens matching rules to mirror the JS validator behavior (e.g., types allow only EOL or trailing
#comment; conditions require(; relations require:and normalize repeated spaces). - Adds comprehensive unit tests covering collision cases, module comments, and whitespace variations.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| pkg/go/utils/line-numbers.go | Reworks declaration matching to validate what follows the name, preventing prefix-based false matches and aligning with JS behavior. |
| pkg/go/utils/line-numbers_test.go | Adds focused tests to ensure correct line selection across name-collision and whitespace/comment edge cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
9797316 to
a165bec
Compare
|
@SoulPancake why force the caller to validate the expected tail of the string? You could simply get the index immediately following the provided prefix -- if one exists -- and check for a word termination character. It seems that an invariant of this function is that |
The declaration helpers took a rest predicate so each caller could validate
what followed the name. prefix always spans whole words, so the invariant is
simply that the name ends there: check the next byte against the grammar's
identifier rules instead.
Drops rest, endOrComment, startsWith and intraLineWhitespace. \f needs no
special handling now, since it is a name terminator like any other
non-identifier byte. normalizeSpaces stays on the relation helper only,
matching the reference's ' {2,}' handling.
The condition lookup in pkg/go/validation shares the same helper rather than
keeping a second copy of the rule.
7c969a3 to
5569c1c
Compare
The prefix-matching fix had Go-only coverage, while the JS helpers carry the same rule. tests/data/transformer-module is read by both, so a case there pins the behaviour for each. dup.fga declares less_than before less, so a lookup that matches on prefix alone resolves the duplicate to the wrong line. Verified by reintroducing the bug in each implementation: Go and JS both report line 6 instead of 10.
|
Shared fixtures causing the npm audit failure to show up in this PR, checking that. Edit: Letting that remain as a separate fix PR to not digress from the topic here. |
Siddhant-K-code
left a comment
There was a problem hiding this comment.
Tests/lint seems to be failing - can i send a commit to fix it or you wanna address it, @SoulPancake ?
There was a problem hiding this comment.
Thanks for addressing the review comments! Changes look good to me; the prefix matching fix is clear & well tested. % address the lint failing please before merge!
I'd also wait for review from @senojj
The line-number helpers in
pkg/go/utilsmatched a declaration withstrings.HasPrefixon the name alone, so a name that is a prefix of another resolved to the wrong line: asking fordocumentmatchedtype documentation,ownermatcheddefine owner_group:, andlessmatchedcondition less_than(.Description
What problem is being solved?
GetTypeLineNumber,GetRelationLineNumber,GetConditionLineNumberandGetExtendedTypeLineNumbereach tested only that a trimmed line started with"<keyword> <name>", with nothing checking what followed the name:Reproduced against
develop— every one of these returns the line of the longer declaration that happens to come first:documenttype documentationownerdefine owner_group:lesscondition less_than(The helpers have four non-test callers, all in
pkg/go/transformer/module-to-model.go: the duplicate-type, duplicate-condition, extended-type and duplicate-relation errors. In a module set where one name is a prefix of another, those errors point at the wrong declaration.This is user-visible in a released package.
pkg/go/utils/line-numbers.goandpkg/go/transformer/module-to-model.goboth ship inpkg/go/v0.3.1, which is the tagopenfga/clipins.This is the Go half of #255, which listed all three implementations' files. #260 closed it after fixing only the JS and Java copies —
pkg/go/utils/line-numbers.gowas named in the issue but never in the diff, so the Go bug has been live since.How is it being solved?
By requiring the name to end where the prefix ends. The four helpers now share one matcher, and the terminator rule is derived from the lexer grammar rather than hand-written per helper:
declarationIndexfinds the first line starting with the prefix whose next byte is not a name byte (or which ends there). Because the prefix always spans whole words —type <name>,define <name>,condition <name>— that single check is what stopsdocumentmatchingdocumentation.Taking the terminator set from the grammar rather than requiring a specific punctuation character is what makes
type core.doca declaration ofcore.docand not ofcore, since.is a name byte inEXTENDED_IDENTIFIER. A rule that instead demanded:or(after the name would get that case wrong.Only the relation helper collapses runs of spaces, mirroring the reference implementation, where
{2,}normalization is applied ingetRelationLineNumberalone — sodefine owner:still matchesowner.What changes are made to solve it?
pkg/go/utils/line-numbers.go: addsIsNameByteand the shareddeclarationIndex; the four exported helpers become one-line calls into it. Signatures, theintreturn and the-1miss sentinel are all unchanged.pkg/go/validation/name_validation.go:GetConditionLineNumberhad its own inline terminator check requiring(after the name. It now callsutils.IsNameByte, so the rule lives in one place. This is a deliberate loosening — see the note below.tests/data/transformer-module/07-prefix-collisions/: a module fixture wheredocumentation/documentandless/less_thancollide across two files, asserting the duplicate-condition error lands ondup.fgaline 10 rather than an earlierless_thandeclaration.pkg/go/utils/line-numbers_test.go: 47 cases covering each helper, including the trailing-comment form the module fixtures depend on (type other # module: core, file: core.fga), collapsed spaces, dotted names, and the three collisions above.The only visible behaviour change is that the four transformer errors point at the correct line in collision cases. No error identity or message text changes.
A note on what this does not tighten
The matcher accepts anything that is not a name byte as a terminator, which is looser than the punctuation each declaration form actually requires. All of these match today:
None of these are valid DSL, and the helpers are only ever called with a name the parser has already accepted from a model that parsed — so a malformed line cannot reach them in practice. Deriving the rule from the grammar was preferred over four hand-written punctuation checks because it is the same rule in every helper and it handles extended identifiers correctly. Worth flagging explicitly rather than leaving it to be discovered.
For the same reason, the two Go copies are now closer but not identical.
pkg/go/validation's type and relation helpers parse the line (strings.Fields, then an exact compare; or split on:), so they rejecttype doc#x,type doc:xanddefine owner [user]whereutilsaccepts them. Both agree on every well-formed input, including all three collision cases and the trailing-comment form. Unifying the two copies is a follow-up; this PR makes them agree on the cases that occur, not on malformed input.Testing
From
pkg/go:go build ./...clean, andgo test ./... -count=1green —utils,transformerandvalidationall pass, with the new module fixture exercised through the transformer path.The three collisions were verified as failing on
developand fixed on this branch by building the helpers standalone against both versions, rather than only through the test suite.References
closes #255
Review Checklist
main