Skip to content

fix(joiner): stop the nil-schema panic in deep comparison - #419

Merged
erraggy merged 1 commit into
mainfrom
fix/nil-schema-panic-in-deep-comparison
Jul 31, 2026
Merged

fix(joiner): stop the nil-schema panic in deep comparison#419
erraggy merged 1 commit into
mainfrom
fix/nil-schema-panic-in-deep-comparison

Conversation

@erraggy

@erraggy erraggy commented Jul 31, 2026

Copy link
Copy Markdown
Owner

oastools join --semantic-dedup panicked on a valid document. 💥

components:
  schemas:
    Thing:
      type: object
      properties:
        name:

properties: {name:} is legal YAML and parses to a present key with a nil value. compareDeep dereferenced both operands immediately, so comparing two such schemas segfaulted.

Fixes #417

The fix

CompareSchemasWithOptions guards its own two arguments, but nothing re-guarded the recursion — so all fourteen nested walks that can produce a nil were exposed. #416 patched two of them where they were found; that approach makes correctness something each future nested walk has to re-derive.

One guard at the top of compareDeep instead, and the two spot fixes revert to plain calls. A nil against a present schema is recorded as a difference at the caller's path, which already names the field or entry, so diagnostics land exactly where they did before. Two nils are equivalent.

The *Schema pointer fields — not, if, then, else, contains, propertyNames, contentSchema — each carry their own presence check with a field-specific message and are untouched. They aren't the bug, and rewriting them would change existing diagnostic strings for no correctness gain. The central guard sits under them as a backstop. That seven-fold duplication is what #418 is filed for.

Verification

Every test here was run twice — once with the fix, once with it stripped out:

Suite Without the fix
internal/driftguard nested-nil cases 13 of 20 fail
joiner public-API table panics
join --semantic-dedup CLI repro segfault

A test that has never been observed failing asserts that the code compiles, not that the bug is fixed — and assert.NotPanics is especially prone to passing vacuously, since a function that returns early for the wrong reason also doesn't panic.

The seven that pass without the fix are precisely the pointer fields listed above, which confirms the boundary rather than undermining it.

Coverage

The integration test parses real YAML rather than building structs, because the claim under test is that an ordinary document produces this shape. It asserts the nil parse actually happened before asserting anything else, so it can't silently stop covering the bug if parsing changes. Dedup still consolidates the two schemas to one — surviving a nil is not the same as treating it as a difference.

Checks

make check green — 10243 tests, no lint or gopls findings. CodeRabbit CLI run pre-push: no critical or warning findings; one minor applied, one skipped with the reasoning recorded in the test file.

`oastools join --semantic-dedup` panicked on a valid document. `properties:
{name:}` is legal YAML and parses to a present key with a nil value, and
`compareDeep` dereferenced both operands immediately.

`CompareSchemasWithOptions` guards its own two arguments, but nothing
re-guarded the recursion, so every one of the fourteen nested walks that can
produce a nil was exposed. #416 patched two of them individually; that
approach needed each new nested walk to remember.

Guard once at the top of `compareDeep` instead, and revert the two spot fixes
to plain calls. A nil against a present schema is recorded as a difference at
the caller's path, which already names the field or entry, so the diagnostics
land where they did before. Two nils are equivalent.

The `*Schema` pointer fields (`not`, `if`, `then`, `else`, `contains`,
`propertyNames`, `contentSchema`) each carry their own presence check with a
field-specific message and are left alone; the central guard is a backstop
under them, not a replacement.

A driftguard case populates each of the twenty nested-schema fields with a nil
in turn, so the class cannot come back by way of a field added later. Thirteen
of those twenty fail without this fix.

Fixes #417
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b6fc6f85-df16-4fa5-ba22-95b4e323ce88

📥 Commits

Reviewing files that changed from the base of the PR and between 9c28590 and 04c84eb.

📒 Files selected for processing (4)
  • internal/driftguard/equivalence_test.go
  • joiner/equivalence.go
  • joiner/equivalence_test.go
  • joiner/nil_schema_regression_test.go

📝 Walkthrough

Walkthrough

The schema equivalence comparator now handles nil nested schemas at the recursion boundary. Tests cover nested schema shapes, comparison directions, equivalent nil values, and semantic deduplication of parsed OpenAPI documents.

Changes

Nil-schema equivalence

Layer / File(s) Summary
Centralized nil-schema comparison
joiner/equivalence.go
compareDeep detects nil operands and records schema-presence mismatches before dereferencing. Map and schema-or-boolean comparisons delegate to this logic.
Nested-schema and deduplication regression coverage
internal/driftguard/equivalence_test.go, joiner/equivalence_test.go, joiner/nil_schema_regression_test.go
Tests cover nil schemas in maps, slices, pointers, typed interfaces, composition arrays, prefix items, properties, dependent schemas, and semantic deduplication.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • erraggy/oastools#416: Extends earlier nil-map and nested-schema comparison fixes in the same equivalence code.
  • erraggy/oastools#412: Modifies the same equivalence implementation and tests for additional schema comparison behavior.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the nil-schema panic fix in deep comparison.
Description check ✅ Passed The description explains the panic, central fix, regression coverage, and verification results.
Linked Issues check ✅ Passed The changes satisfy issue #417 by centralizing nil handling, preserving diagnostics, covering nested fields, and testing end-to-end deduplication.
Out of Scope Changes check ✅ Passed All code and test changes support the linked issue and stated objectives; no unrelated changes are identified.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/nil-schema-panic-in-deep-comparison

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.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.63%. Comparing base (9c28590) to head (04c84eb).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #419      +/-   ##
==========================================
+ Coverage   86.62%   86.63%   +0.01%     
==========================================
  Files         200      200              
  Lines       28778    28778              
==========================================
+ Hits        24930    24933       +3     
+ Misses       2549     2546       -3     
  Partials     1299     1299              
Files with missing lines Coverage Δ
joiner/equivalence.go 93.55% <100.00%> (+0.37%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@erraggy
erraggy merged commit 797453c into main Jul 31, 2026
8 checks passed
@erraggy
erraggy deleted the fix/nil-schema-panic-in-deep-comparison branch July 31, 2026 15:13
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.

joiner: semantic deduplication panics on a schema with an empty property value

1 participant