Skip to content

fix: correct error locations when type/schema lines have trailing comments#628

Open
SoulPancake wants to merge 11 commits into
mainfrom
fix/error-locations-with-comments
Open

fix: correct error locations when type/schema lines have trailing comments#628
SoulPancake wants to merge 11 commits into
mainfrom
fix/error-locations-with-comments

Conversation

@SoulPancake

@SoulPancake SoulPancake commented Jul 6, 2026

Copy link
Copy Markdown
Member

The DSL validator resolved error line numbers with anchored regexes (^type NAME$, ^schema V$) that failed to match lines carrying a trailing comment (e.g. type page # module: ...). A miss returned -1, which was then propagated as a negative skipIndex into the relation lookup, producing nonsensical negative lines (-1, -2) and, for the invalid-type path, a crash (rawLine.split on undefined).

Make the type/schema lookups comment-tolerant and guard against negative skipIndex propagation in both the JS validator and its module transformer copy, and mirror the fix in the Java validator. Add 7 shared regression cases (simple + the full reported model) covering missing-definition, reserved type/relation names, invalid type, no entrypoint, and invalid schema with trailing comments.

Description

What problem is being solved?

How is it being solved?

What changes are made to solve it?

References

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation error line and column reporting when source lines include trailing comments.
    • Prevented invalid or negative line indexes from causing incorrect error locations.
    • Fixed several cases where missing schema, type, or relation errors could point to the wrong line.
  • Tests
    • Added coverage for semantic validation scenarios involving trailing comments and module-style DSL input.

…ments

The DSL validator resolved error line numbers with anchored regexes
(`^type NAME$`, `^schema V$`) that failed to match lines carrying a
trailing comment (e.g. `type page # module: ...`). A miss returned -1,
which was then propagated as a negative `skipIndex` into the relation
lookup, producing nonsensical negative lines (-1, -2) and, for the
invalid-type path, a crash (`rawLine.split` on `undefined`).

Make the type/schema lookups comment-tolerant and guard against negative
`skipIndex` propagation in both the JS validator and its module
transformer copy, and mirror the fix in the Java validator. Add 7 shared
regression cases (simple + the full reported model) covering
missing-definition, reserved type/relation names, invalid type, no
entrypoint, and invalid schema with trailing comments.
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fce2dedd-3923-4ee2-88f8-4043d5baed50

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Line-number lookup helpers in Java and JavaScript are updated to clamp/normalize negative skip indexes and return safe failure values, and to match type/schema lines followed by trailing comments. Error builders add bounds/negative-index guards. New YAML test cases and a module test filter validate these behaviors.

Changes

Line number and validation error fixes

Layer / File(s) Summary
Java line-number and error-property guards
pkg/java/.../validation/Dsl.java, pkg/java/.../validation/ValidationErrorsBuilder.java
findLine clamps skipIndex to non-negative, getTypeLineNumber allows trailing comments after type names, and buildErrorProperties bounds-checks lineIndex before indexing into lines.
JS line-numbers.ts lookup refactor
pkg/js/util/line-numbers.ts
getConditionLineNumber, getTypeLineNumber, and getRelationLineNumber normalize negative skipIndex, return -1 on failed matches instead of skipIndex - 1, and getTypeLineNumber allows trailing comments.
validate-dsl.ts line lookup helpers
pkg/js/validator/validate-dsl.ts
geConditionLineNumber, getTypeLineNumber, getRelationLineNumber, and getSchemaLineNumber normalize negative skipIndex, return undefined when lines are missing, and allow trailing comments after type/schema tokens.
Exceptions guard against negative lineIndex
pkg/js/util/exceptions.ts
Guards in createAssignableRelationMustHaveTypesError, createDuplicateRelationshipDefinitionError, constructValidationError, and constructTransformationError now require lineIndex >= 0 before computing line/column info.
Semantic validation and module test coverage
tests/data/dsl-semantic-validation-cases.yaml, pkg/js/tests/modules/module-to-model.test.ts
New YAML cases verify correct error line/column reporting with trailing comments and module/file comments; module test filter now runs only schema 1.1 cases instead of skipping 0.9 cases.

Estimated code review effort: 2 (Simple) | ~15 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main fix: error locations for type/schema lines with trailing comments.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/error-locations-with-comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@SoulPancake SoulPancake left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: After Go semantic validation is generally done, we can run these fixtures and see if it needs the same fix too

@SoulPancake SoulPancake changed the title fix: correct error locations when type/schema lines have trailing com… fix: correct error locations when type/schema lines have trailing comments Jul 6, 2026
Line-number lookups return -1 when they cannot locate a line. The JS and
Java error builders dereferenced lines[lineIndex] without checking for a
miss, which could crash (e.g. custom word resolvers calling methods on
undefined) or attach negative line locations to errors. Treat a negative
lineIndex like a missing one and emit the error without line/column
information instead.
@SoulPancake

Copy link
Copy Markdown
Member Author

Tested it in the VSCode extension

image image

@SoulPancake SoulPancake marked this pull request as ready for review July 9, 2026 06:27
@SoulPancake SoulPancake requested review from a team as code owners July 9, 2026 06:27
Copilot AI review requested due to automatic review settings July 9, 2026 06:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes inaccurate (and sometimes negative) error line/column reporting in the DSL semantic validator when type/schema lines include trailing # ... comments, and mirrors the fix across the JS validator, the module-transformer copy, and the Java validator. It also adds shared regression cases to ensure error locations remain correct for these commented lines.

Changes:

  • Make JS type/schema line lookup comment-tolerant and prevent negative skipIndex propagation from producing bogus locations or crashes.
  • Mirror the negative-index / bounds guards in the Java validator’s line lookup and error property building.
  • Add 7 shared semantic-validation regression cases covering trailing-comment scenarios across multiple error types.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/data/dsl-semantic-validation-cases.yaml Adds regression cases ensuring correct error locations when trailing comments exist on type/schema lines.
pkg/js/validator/validate-dsl.ts Makes type/relation/schema line lookups comment-tolerant and guards against negative skipIndex.
pkg/js/util/line-numbers.ts Mirrors JS line-lookup fixes in the shared utility used by the module transformer.
pkg/js/util/exceptions.ts Guards against negative line indices when computing error properties/columns.
pkg/js/tests/modules/module-to-model.test.ts Skips semantic DSL validation cases in module tests when schema is not 1.1 (since rewrite only handles that).
pkg/java/src/main/java/dev/openfga/language/validation/ValidationErrorsBuilder.java Prevents crashes by ensuring lineIndex is within bounds before reading lines[lineIndex].
pkg/java/src/main/java/dev/openfga/language/validation/Dsl.java Prevents negative skipIndex and makes type line lookup tolerate trailing comments.
Comments suppressed due to low confidence (1)

pkg/js/util/exceptions.ts:444

  • constructValidationError uses a RegExp built from metadata.symbol (and \b word boundaries) to locate the symbol column. Symbols in this DSL can contain non-word characters (e.g. . or -), which makes \b unreliable and also risks regex metacharacters changing the match. Using a literal indexOf avoids both issues and keeps error columns stable for extended identifiers.
  if (lines?.length && lineIndex != undefined && lineIndex >= 0) {
    const rawLine = lines[lineIndex];

    const re = new RegExp("\\b" + metadata.symbol + "\\b");
    let wordIdx = rawLine?.search(re);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/js/validator/validate-dsl.ts
Comment thread pkg/js/validator/validate-dsl.ts Outdated
Comment thread pkg/js/validator/validate-dsl.ts Outdated
Comment thread pkg/js/util/line-numbers.ts
Comment thread pkg/js/util/line-numbers.ts Outdated
Comment thread pkg/java/src/main/java/dev/openfga/language/validation/Dsl.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/java/src/main/java/dev/openfga/language/validation/ValidationErrorsBuilder.java (1)

99-100: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

raiseAssignableRelationMustHaveTypes bypasses the bounds check and can crash on negative lineIndex.

Line 100 directly accesses lines[lineIndex] without the bounds guard added to buildErrorProperties on line 28. If a upstream lookup returns -1 (the exact scenario this PR hardens against), this will throw ArrayIndexOutOfBoundsException.

🔒 Proposed fix
 public void raiseAssignableRelationMustHaveTypes(int lineIndex, String symbol) {
-    var rawLine = lines[lineIndex];
+    var rawLine = (lines != null && lineIndex >= 0 && lineIndex < lines.length) ? lines[lineIndex] : "";
     var actualValue =
             rawLine.contains("[") ? rawLine.substring(rawLine.indexOf('['), rawLine.lastIndexOf(']') + 1) : "self";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@pkg/java/src/main/java/dev/openfga/language/validation/ValidationErrorsBuilder.java`
around lines 99 - 100, The `raiseAssignableRelationMustHaveTypes` method still
reads `lines[lineIndex]` directly, so it can throw on a negative or out-of-range
index. Update this method in `ValidationErrorsBuilder` to use the same
bounds-safe handling as `buildErrorProperties`, checking `lineIndex` before
accessing `lines` and falling back to an empty/placeholder line when the index
is invalid.
🧹 Nitpick comments (2)
pkg/js/util/line-numbers.ts (1)

24-24: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Consider escaping typeName in the regex pattern.

typeName is interpolated directly into the regex string. If it contains regex metacharacters (e.g., ., *, +), the pattern will match incorrectly. This is the same concern flagged in the Java Dsl.java counterpart. Since the pattern was modified in this PR, it's a good opportunity to escape it.

🛡️ Proposed fix
-    .findIndex((line: string) => line.trim().match(`^${extension ? "extend " : ""}type ${typeName}\\s*(#.*)?$`));
+    .findIndex((line: string) => line.trim().match(`^${extension ? "extend " : ""}type ${typeName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*(#.*)?$`));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/util/line-numbers.ts` at line 24, The regex in the line lookup logic
currently interpolates typeName directly, so special characters can change the
match behavior. Update the findIndex pattern in the line-number utility to
escape typeName before building the regex, keeping the existing extension prefix
and optional comment handling intact. Use the line-number helper function where
this pattern is built to apply the escaping consistently.
pkg/js/validator/validate-dsl.ts (1)

307-317: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Consider escaping typeName in the regex to prevent false matches.

typeName is interpolated directly into the regex string. Type names can contain . (per naming rule ^[^:#@\*\s]{1,254}$), which acts as a regex wildcard. For example, type a.b would also match type aXb. This is pre-existing, but since the regex is being modified here, it's a good opportunity to fix it.

♻️ Proposed fix using a literal string match instead of regex
-  const index = lines.slice(skipIndex).findIndex((line: string) => line.trim().match(`^type ${typeName}\\s*(#.*)?$`));
+  const index = lines.slice(skipIndex).findIndex((line: string) => {
+    const trimmed = line.trim();
+    return trimmed.startsWith(`type ${typeName}`) && (
+      trimmed.length === `type ${typeName}`.length ||
+      /^\s*(#.*)?$/.test(trimmed.slice(`type ${typeName}`.length))
+    );
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/js/validator/validate-dsl.ts` around lines 307 - 317, The regex in
getTypeLineNumber is interpolating typeName directly, so names containing regex
metacharacters like . can match the wrong type line. Update the matching logic
in getTypeLineNumber to treat typeName as a literal when comparing against the
line contents, using a safely escaped pattern or a non-regex literal string
check. Keep the existing skipIndex and trailing-comment behavior intact while
fixing the matching for all valid type names.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/java/src/main/java/dev/openfga/language/validation/Dsl.java`:
- Around line 54-55: Escape the interpolated type name in Dsl.getTypeLineNumber
before building the regex, since the current line-level match in findLine treats
typeName as raw regex input. Update the pattern to use a literal-safe form such
as Pattern.quote(typeName) so names passed from ModelValidator
reserved/invalid-name paths cannot cause incorrect matches or regex backtracking
issues. Keep the optional trailing comment handling in the same match logic.

---

Outside diff comments:
In
`@pkg/java/src/main/java/dev/openfga/language/validation/ValidationErrorsBuilder.java`:
- Around line 99-100: The `raiseAssignableRelationMustHaveTypes` method still
reads `lines[lineIndex]` directly, so it can throw on a negative or out-of-range
index. Update this method in `ValidationErrorsBuilder` to use the same
bounds-safe handling as `buildErrorProperties`, checking `lineIndex` before
accessing `lines` and falling back to an empty/placeholder line when the index
is invalid.

---

Nitpick comments:
In `@pkg/js/util/line-numbers.ts`:
- Line 24: The regex in the line lookup logic currently interpolates typeName
directly, so special characters can change the match behavior. Update the
findIndex pattern in the line-number utility to escape typeName before building
the regex, keeping the existing extension prefix and optional comment handling
intact. Use the line-number helper function where this pattern is built to apply
the escaping consistently.

In `@pkg/js/validator/validate-dsl.ts`:
- Around line 307-317: The regex in getTypeLineNumber is interpolating typeName
directly, so names containing regex metacharacters like . can match the wrong
type line. Update the matching logic in getTypeLineNumber to treat typeName as a
literal when comparing against the line contents, using a safely escaped pattern
or a non-regex literal string check. Keep the existing skipIndex and
trailing-comment behavior intact while fixing the matching for all valid type
names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb2ac57d-cd79-41af-9320-dc10fff02abe

📥 Commits

Reviewing files that changed from the base of the PR and between 906bdc5 and 30b5703.

📒 Files selected for processing (7)
  • pkg/java/src/main/java/dev/openfga/language/validation/Dsl.java
  • pkg/java/src/main/java/dev/openfga/language/validation/ValidationErrorsBuilder.java
  • pkg/js/tests/modules/module-to-model.test.ts
  • pkg/js/util/exceptions.ts
  • pkg/js/util/line-numbers.ts
  • pkg/js/validator/validate-dsl.ts
  • tests/data/dsl-semantic-validation-cases.yaml

Comment thread pkg/java/src/main/java/dev/openfga/language/validation/Dsl.java Outdated
Type, relation and schema names were interpolated into regexes
unescaped. Extended identifiers may contain regex metacharacters
(e.g. '.'), which could match the wrong line and report incorrect
error locations. Use literal prefix matching in JS and Pattern.quote
in Java.
…schema lines

Line lookups used bare prefix matching, so a name that is a prefix of
another (e.g. relation 'writer' vs 'writers', condition 'less' vs
'less_than') could attach errors to the wrong line when the longer name
was defined first. Require the structural suffix that must follow each
name: ':' for relations, '(' for conditions (per grammar,
conditionName is always followed by LPAREN), and end-of-line or a
comment for schema versions.
…on names

Add shared fixtures where a relation ('writer' vs 'writers') and a
condition ('less' vs 'less_than') are prefixes of earlier definitions,
asserting errors point at the correct lines. Both cases fail against
the previous prefix-matching lookups.
…nMustHaveTypes

The method read lines[lineIndex] without the bounds check used by
buildErrorProperties, so a failed line lookup (-1) would throw
ArrayIndexOutOfBoundsException. Mirror the JS implementation, which
falls back to an empty value when the line cannot be resolved.
Comment thread pkg/js/validator/validate-dsl.ts Outdated
const typePrefix = `type ${typeName}`;
const index = lines.slice(skipIndex).findIndex((line: string) => {
const trimmed = line.trim();
return trimmed.startsWith(typePrefix) && /^\s*(#.*)?$/.test(trimmed.slice(typePrefix.length));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return trimmed.startsWith(typePrefix) && /^\s*(#.*)?$/.test(trimmed.slice(typePrefix.length));
return trimmed.startsWith(typePrefix) && /^\s+(#.*)?$/.test(trimmed.slice(typePrefix.length));

require a space before the comment, otherwise you'll trigger on e.g. team#member

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
I pushed a fix. \s+ would actually regress the common no-comment case (type user → empty remainder → no match), and the team#member example can't reach this matcher since it only tests type declration lines. But \s*(#.*)? did treat a glued # as a comment. Changed both the type and schema matchers to /^(\s+#.*)?$/, which requires a space before an inline comment while still allowing the empty case.

…schema lines

A glued `#` (e.g. `type team#member`) was matched as a trailing comment
by `\s*(#.*)?`. Require a preceding space via `(\s+#.*)?` so only real
inline comments are treated as such, while still allowing the no-comment case.
The type/schema matchers exist in two places; mirror the `(\s+#.*)?`
fix in util/line-numbers.ts (used by the modules transformer) so both
paths treat a glued `#` as part of the token rather than a comment.
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.

Language is not properly exporting error locations when there are comments in the model

3 participants