Skip to content

fix: Z029 composite-type false negatives + surface silent IO/config errors#7

Merged
EugOT merged 1 commit into
mainfrom
fix/z029-composite-types-and-silent-io
Jun 20, 2026
Merged

fix: Z029 composite-type false negatives + surface silent IO/config errors#7
EugOT merged 1 commit into
mainfrom
fix/z029-composite-types-and-silent-io

Conversation

@EugOT

@EugOT EugOT commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

A full multi-agent adversarial audit of the entire fork delta vs upstream (bfcb30d..b212557, 882+/465-, 20 files) ran 5 review dimensions → adversarial verification of every finding. 24 raw findings → 3 confirmed, 21 refuted. This PR fixes the 3 confirmed defects (all real and present at HEAD; pre-existing upstream but in the fork's shipped code).

🟠 #1 — Z029 false negatives on composite field types (high)

findContainerFieldTypeInTree (Linter.zig) only stringified .identifier field types, so redundant @as on pointer / optional / array / field-access struct fields was silently skipped:

const Foo = struct { p: *u32 };
const f: Foo = .{ .p = @as(*u32, &n) };  // was NOT flagged; now flagged

Fix: compare the raw source text of the field type and the @as cast type (Ast.getNodeSource), whitespace-normalized (typeTextEql). Handles all composite types uniformly. getAsTypeName is retained for the other Z029 paths (call args, array init, return, value).

🟡 #2 — Silent directory-walk failure (medium)

while (walker.next(io) catch null) in lintDirectory ended traversal silently on a permission error / symlink loop, leaving the rest of the tree un-linted with no diagnostic. Fix: print a warning before stopping (Z026-clean try).

🟡 #3 — Silent config-load error (medium)

FileConfig.load(...) catch .{} swallowed config-discovery errors (e.g. an allocation failure joining the search path) without logging. Fix: log the error before falling back to defaults.

Tests (TDD)

4 new Z029 regression tests in Linter.zig. The 3 detection tests (pointer / optional / array) failed with expected 1, found 0 before the fix and pass after; a 4th asserts a different pointer type still does not trigger (no false positive).

Validation

zig build test --summary all
Build Summary: 7/7 steps succeeded; 281/281 tests passed
+- run test 281 pass (281 total)
+- zig fmt success
+- run exe ziglint success   # self-lint clean

(277 prior + 4 new.) Notably, ziglint's own self-lint gate caught an empty catch {} (Z026) in the first draft of fix #2, which was corrected to try — the tool linting its own fix.

Audit notes

The adversarial verifier correctly refuted 21 findings — arena-allocator non-leaks, bounds-checked slices (guarded by startsWith/find contracts), the writer() "reversed params" false alarm, environ_map lifetime, etc. — and excluded real-but-pre-existing-and-untouched issues (e.g. a resolveNodeType ↔ resolveFieldAccess unbounded recursion that exists identically in upstream and is a separate path from the PR #5 alias-depth fix). That recursion is a good candidate for a follow-up (and an upstream PR), but is out of scope here since the fork didn't introduce or touch it.

This audit + report ran through a live Phoenix LiveView dashboard on the meshnet (himalayas:4000); the plan/observations are recorded in Tana.

…rrors

WHY: A full multi-agent adversarial audit of the fork delta vs upstream
(bfcb30d..b212557, 24 raw findings → 3 confirmed after verification) surfaced
three real, verified defects present at HEAD:
  1. Z029 (redundant @as in struct init) only compared bare-identifier field
     types, so `@as(*u32, ...)`, `@as(?T, ...)`, `@as([N]T, ...)` and
     `@as(a.b.C, ...)` on struct fields were silently skipped — genuine linter
     false negatives (the tool's core job).
  2. lintDirectory's `walker.next(io) catch null` ended traversal silently on a
     permission error / symlink loop, leaving the rest of the tree un-linted
     with no diagnostic.
  3. main's `FileConfig.load(...) catch .{}` swallowed config-discovery errors
     (e.g. an allocation failure joining the search path) without logging.
All three are pre-existing upstream but ship in the fork; this cycle's scope was
the whole fork-vs-upstream delta.

WHAT:
  - Linter.zig: findContainerFieldTypeInTree now returns the field type's raw
    source (Ast.getNodeSource) instead of only `.identifier`. Added getAsTypeText
    (source of an `@as` cast's type node) and typeTextEql (whitespace-insensitive
    compare). checkRedundantAsInStructInit compares source text, so all composite
    field types are handled. getAsTypeName is retained (still used by call-arg /
    array / return / value paths).
  - main.zig: directory-walk errors now print a warning before stopping; config
    load logs the error before falling back to defaults. Both Z026-clean.
  - Added 4 Z029 regression tests (pointer/optional/array detection + a
    different-pointer-type negative case).

IMPACT: src/Linter.zig, src/main.zig. No behavior change for the previously
handled identifier case; new detections are strict (whitespace-normalized exact
type-text match, so different types still don't trigger). Linter now catches a
class of redundant casts it previously missed and no longer fails silently on
unreadable directories/config.

VALIDATION: TDD — the 3 composite-type tests failed (expected 1, found 0) before
the fix and pass after. `zig build test --summary all` => 7/7 steps, 281/281
tests pass, zig fmt clean, self-lint (run exe ziglint) clean. The self-lint gate
also caught an empty `catch {}` in the first draft of fix #2 (Z026), which was
corrected to `try`.
Copilot AI review requested due to automatic review settings June 20, 2026 12:07
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@EugOT, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 50 minutes and 13 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

Review info
Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 26b1d8ad-2da1-4076-9001-44eb0292526b

Commits

Reviewing files that changed from the base of the PR and between b212557 and e738708.

Files selected for processing (2)
  • src/Linter.zig
  • src/main.zig
Finishing Touches
Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/z029-composite-types-and-silent-io

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@EugOT

EugOT commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e73870843c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Linter.zig
Comment on lines +2101 to +2102
while (ia < a.len and std.ascii.isWhitespace(a[ia])) ia += 1;
while (ib < b.len and std.ascii.isWhitespace(b[ib])) ib += 1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid concatenating type tokens when normalizing

When the field type contains whitespace that separates tokens, this byte-level normalization can turn a different type into the same text. For example, a field declared as *const u8 and a cast written as @as(*constu8, ...) become identical after stripping spaces if the user has a constu8 type alias, so Z029 reports the cast as redundant even though the cast type is not the field's context type. Normalize trivia without merging tokens, or compare the parsed type structure instead of deleting all whitespace.

Useful? React with 👍 / 👎.

@EugOT EugOT merged commit 1704982 into main Jun 20, 2026
6 of 7 checks passed
@EugOT EugOT deleted the fix/z029-composite-types-and-silent-io branch June 20, 2026 12:14
@kilo-code-bot

kilo-code-bot Bot commented Jun 20, 2026

Copy link
Copy Markdown

Code Review Roast 🔥

Verdict: 1 Issue Found | Recommendation: Consider before merge

Overview

Severity Count
🚨 critical 0
⚠️ warning 0
💡 suggestion 1
🤏 nitpick 0
Issue Details (click to expand)
File Line Roast
src/Linter.zig 2102 The P2 badge already called this out: whitespace normalization could merge tokens. See existing comment.

🏆 Best part: Finally catching redundant @as on pointer/optional/array types! This was a real blind spot that would let obvious redundancy slide.

💀 Worst part: The typeTextEql function is a clever hack that strips whitespace like it's trying to hide evidence. *const u8 and * constu8 both become the same string, which means that type alias named constu8 you swear doesn't exist? Yeah, it just produced a false positive.

📊 Overall: Like a double-shot espresso — hits the target but might keep you up at night worrying about false positives.

Files Reviewed (2 files)
  • src/Linter.zig - 1 issue (existing comment)
  • src/main.zig - clean

Reviewed by laguna-m.1-20260312:free · Input: 491K · Output: 5.7K · Cached: 284K

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