Skip to content

feat(linter): add forbid_empty_enum rule#2287

Open
Vaibhav701161 wants to merge 8 commits intosourcemeta:mainfrom
Vaibhav701161:feat/linter-forbid-empty-enum
Open

feat(linter): add forbid_empty_enum rule#2287
Vaibhav701161 wants to merge 8 commits intosourcemeta:mainfrom
Vaibhav701161:feat/linter-forbid-empty-enum

Conversation

@Vaibhav701161
Copy link

Summary

This PR introduces a new lint rule: forbid_empty_enum.

The rule detects schemas declaring an empty enum array.

{
  "enum": []
}

An empty enum makes the schema unsatisfiable because no instance can ever match it.
This is usually an authoring mistake and should be reported to the user.

The rule emits a diagnostic when:

  • enum exists
  • the value is an array
  • the array is empty

The diagnostic points to the enum keyword location.

This rule is non auto-fixable because the correct resolution depends on the schema author's intent.


Implementation

The rule follows the existing alterschema linter architecture:

  • implemented as a header-only rule in
    src/extension/alterschema/linter/forbid_empty_enum.h
  • registered in alterschema.cc
  • added to the SOURCES list in the alterschema CMake configuration

The rule checks:

  • schema node is an object
  • enum keyword is defined
  • enum is an array
  • array length is zero

If these conditions are met the rule returns:

APPLIES_TO_KEYWORDS("enum")

Tests

Tests were added across supported dialects using the existing lint testing utilities.

The following scenarios are covered:

  1. Rule fires for an empty enum
  2. Rule does not fire when enum contains values
  3. Rule does not fire when enum is absent
  4. Rule fires inside nested subschemas (e.g. inside properties)

All tests follow the patterns used by existing lint rule tests.


Related Work

This rule addresses one of the pending lint rules listed in:

Refs: #1975

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 8 files

@Vaibhav701161
Copy link
Author

@jviotti Could you please review this pull request when you have a moment?

If the approach looks good, I’d be happy to continue implementing additional linting rules listed in #1975.

@Vaibhav701161 Vaibhav701161 force-pushed the feat/linter-forbid-empty-enum branch from 8006bfe to 43d131d Compare March 7, 2026 15:36
@jviotti
Copy link
Member

jviotti commented Mar 9, 2026

Ah, cool. Thanks for raising this up! I'm now thinking whether this can be auto-fixable or not. Here is the thing:

Can you send a PR to the tests checking what happens with an empty enum and cc me? I guess it means validate nothing, but sounds like something to discuss with the group. Depending on that outcome, maybe we can make it auto-fixable? For example:

  • If it does nothing, we can just auto-fix by removing the enum
  • If it validates nothing, you can turn the entire current subschema to false

@Vaibhav701161
Copy link
Author

Good point !
I'll open a PR in the JSON Schema Test Suite adding test cases for an empty enum so we can clarify the intended behaviour, and cc you there.

If the tests confirm that an empty enum makes the schema unsatisfiable, we could potentially make this rule auto-fixable by replacing the subschema with false. If the intended behaviour is that it has no effect, then the auto-fix could simply remove the enum keyword.

I'll report back once the test suite PR is up.

@jviotti
Copy link
Member

jviotti commented Mar 12, 2026

Great. I guess given the other PR is approved, we can attempt to auto-fix to false

@Vaibhav701161
Copy link
Author

Thanks! @jviotti , that makes sense.
I’ll update the forbid_empty_enum rule to make it auto-fixable and transform the subschema to false, and add the corresponding tests for the transformation.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 15 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/extension/alterschema/linter/forbid_empty_enum.h">

<violation number="1" location="src/extension/alterschema/linter/forbid_empty_enum.h:34">
P2: For Draft 1–4 schemas this transform rewrites a schema object to the boolean literal `false`, but those drafts require schemas to be JSON objects (boolean schemas only became valid in draft‑06). This can turn a valid Draft‑1–4 schema into an invalid schema document for those dialects.</violation>
</file>

<file name="test/alterschema/alterschema_lint_2020_12_test.cc">

<violation number="1" location="test/alterschema/alterschema_lint_2020_12_test.cc:9866">
P2: Autofix replaces any schema object with an empty `enum` by `false`, even when the object has sibling keywords (e.g., annotations like `x-note`). This drops annotations/identifiers such as `$id`/`$anchor` and is not semantics-preserving for schema documents. The new tests assert this unsafe rewrite (e.g., `items` with `x-note` becomes `false`).</violation>
</file>

<file name="src/core/yaml/stringify.h">

<violation number="1" location="src/core/yaml/stringify.h:216">
P2: Floating‑point YAML serialization uses the stream’s default precision/locale, which can round doubles and emit non‑portable numeric strings. This risks lossy or locale‑dependent YAML output.</violation>
</file>

<file name="test/alterschema/alterschema_lint_draft6_test.cc">

<violation number="1" location="test/alterschema/alterschema_lint_draft6_test.cc:3006">
P3: `forbid_empty_enum_4` expects `result.first` to be true even though a lint trace is asserted, which is inconsistent with the rest of the `LINT_AND_FIX` tests and the harness convention (false when lint findings occur).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Vaibhav701161 Vaibhav701161 force-pushed the feat/linter-forbid-empty-enum branch from 23840b3 to ec6066e Compare March 13, 2026 11:39
@Vaibhav701161
Copy link
Author

@jviotti I’ve pushed the updates to make the rule auto-fixable and added the corresponding tests.
When you have a moment, could you please review the changes?

Also, Cubic left a few automated comments , please let me know if any of them are relevant require changes on my side.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="test/alterschema/alterschema_lint_draft6_test.cc">

<violation number="1" location="test/alterschema/alterschema_lint_draft6_test.cc:2944">
P2: LINT_AND_FIX result semantics in this test suite expect result.first to be false when a rule applies and mutates the document. The new forbid_empty_enum_* tests assert true even when the document is changed, which is inconsistent and likely incorrect.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Vaibhav701161
Copy link
Author

@jviotti , I've updated the tests to always use LINT_AND_FIX and edge cases for $ref (direct and nested via $defs), subschemas with $id, and annotation preservation.

@Vaibhav701161 Vaibhav701161 force-pushed the feat/linter-forbid-empty-enum branch 2 times, most recently from c128d8d to 2520380 Compare March 21, 2026 09:47
@Vaibhav701161
Copy link
Author

@jviotti , any comments ?

@Vaibhav701161
Copy link
Author

@jviotti I think I missed a few conventions while focusing on fixing the CI issues . I’ll correct them and push an update shortly.

@Vaibhav701161 Vaibhav701161 force-pushed the feat/linter-forbid-empty-enum branch from 2520380 to 121ff34 Compare March 24, 2026 02:39
@Vaibhav701161 Vaibhav701161 force-pushed the feat/linter-forbid-empty-enum branch from 121ff34 to e8b8a1c Compare March 24, 2026 02:44
@Vaibhav701161 Vaibhav701161 force-pushed the feat/linter-forbid-empty-enum branch from e8b8a1c to ede594e Compare March 24, 2026 02:48
@Vaibhav701161 Vaibhav701161 requested a review from jviotti March 24, 2026 03:11
@Vaibhav701161
Copy link
Author

@jviotti Thanks for the detailed feedback , I went through all the points and made the updates.

For the transform, I’ve switched to converting the enum value into an empty object and then renaming it to not, so we preserve key ordering instead of appending at the end.

I also added a guard to skip the transformation when there are references through the current subschema, to avoid breaking cases where something points into paths like /not/....

On the test side, I fixed the structure to match the existing conventions LINT_AND_FIX now comes before defining expected, and I aligned the layout with other linter tests.

Let me know if anything still looks off or if I should handle any additional edge cases.

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.

2 participants