Skip to content

Conversation

@DonIsaac
Copy link
Owner

@DonIsaac DonIsaac commented Nov 29, 2025

Summary by CodeRabbit

  • Chores
    • Upgraded Zig compiler version from 0.14.1 to 0.15.2
    • Updated package version to 0.8.0
    • Updated minimum Zig version requirement to 0.15.0
    • Updated project dependencies and refactored internal code to maintain compatibility with Zig 0.15.2

✏️ Tip: You can customize this high-level summary in your review settings.

@DonIsaac DonIsaac self-assigned this Nov 29, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 29, 2025

Walkthrough

This PR upgrades the Zig compiler version from 0.14.1 to 0.15.2, migrates collection types from std.ArrayList to std.array_list.Managed throughout the codebase, refactors writer abstractions to use *io.Writer pointers, renames token variants (removing string-quoted forms), restructures the build system with module-centric patterns, and updates dependencies in build.zig.zon.

Changes

Cohort / File(s) Summary
Workflow & CI Configuration
.github/workflows/autofix.yaml, .github/workflows/ci.yaml, .github/workflows/codecov.yaml, .github/workflows/release.yaml
Zig version environment variable updated from 0.14.1 to 0.15.2; CI lint job condition changed to effectively disabled.
Build System & Manifest
build.zig, build.zig.zon, tasks/codegen_task.zig, tasks/confgen.zig, tasks/docgen.zig, tasks/gen_utils.zig
build.zig refactored to module-centric artifact creation using createModule and addLibrary; build.zig.zon version bumped to 0.8.0 with updated dependencies; tasks adjusted for buffered writer usage and removed optimize propagation.
ArrayList → Managed Collection Migration
src/cli/print_command.zig, src/cli/test/print_ast_test.zig, src/linter/fix.zig, src/linter/lint_context.zig, src/linter/linter.zig, src/linter/rules/homeless_try.zig, src/linter/rules/useless_error_return.zig, src/linter/LintService.zig, src/printer/SemanticPrinter.zig, src/visit/walk.zig, src/zig/0.14.1/Ast.zig, src/zig/0.14.1/render.zig, src/zig/0.14.1/string_literal.zig
Systematic replacement of std.ArrayList with std.array_list.Managed across collection-based storage and buffers.
*Writer API Migration to io.Writer
src/cli/Options.zig, src/cli/lint_command.zig, src/main.zig, src/printer/Printer.zig, src/reporter/Reporter.zig, src/reporter/StringWriter.zig, src/reporter/formatter.zig, src/reporter/formatters/GithubFormatter.zig, src/reporter/formatters/GraphicalFormatter.zig, src/reporter/formatters/JSONFormatter.zig, src/util/bitflags.zig, src/util/cow.zig, src/zig.zig, src/zig/0.14.1/render.zig, src/zig/0.14.1/string_literal.zig
Writer parameter types changed from generic/concrete implementations to *io.Writer pointers; formatting APIs updated to use new Writer interface; buffered writer patterns introduced.
Token/Tag Renaming
src/Semantic/SemanticBuilder.zig, src/visit/walk.zig, src/zig/0.14.1/Ast.zig, src/zig/0.14.1/Parse.zig, src/zig/0.14.1/render.zig
Token variants renamed: @"usingnamespace" → usingnamespace, @"await" → await; switch cases and node tag assignments updated.
Documentation & Examples
apps/site/docs/rules/must-return-ref.mdx, docs/rules/must-return-ref.md, zlint.schema.json, src/linter/rules/must_return_ref.zig
Type references in documentation and rule examples changed from std.ArrayList(u32) to std.array_list.Managed(u32).
Utility & Error Handling Updates
src/Error.zig, src/Semantic/test/util.zig, src/linter/tester.zig, src/source.zig, src/util.zig, src/linter/rules/suppressed_errors.zig
Error.jsonStringify simplified by removing WriteStream wrapper; test utilities and tester updated with buffered writers and expanded error types; util.zig inline convention updated; file.metadata() → file.stat().
Test Infrastructure & Harness
test/harness/TestSuite.zig, test/semantic/ecosystem_coverage.zig, test/semantic/snapshot_coverage.zig, src/root.zig
Test suite updated with buffered writer patterns; TTY detection refactored; json module added to recursive test reflection.
Typo Configuration
.typos.toml
Added "build.zig.zon" to extend-exclude list for typo checks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

Areas requiring extra attention:

  • build.zig refactoring: Module-centric build graph restructuring with new createModule/addLibrary patterns; verify all artifact exports and linking are correct and consistent.
  • Writer API migration: Extensive changes across Reporter, Printer, and formatters shifting to *io.Writer; cross-check error handling, flush semantics, and interface compliance across all formatter implementations.
  • ArrayList → Managed migration: Verify all deinit() calls are properly updated to handle the managed variant's allocator parameter semantics; check capacity handling (ensureTotalCapacityPrecise usage).
  • Token variant renames: Ensure all switch cases, node tag assignments, and dependent rendering logic properly reference the renamed @"await" and @"usingnamespace" variants.
  • build.zig.zon dependency updates: Validate that new dependency URLs and hashes are correct; verify compatibility with the 0.15.0 minimum Zig version.
  • Public API signature changes (LintTesterError, Reporter.reportErrors, parseAndPrint, renderToArrayList, etc.): Verify callers are updated and error handling paths align with new signatures.

Possibly related PRs

  • refactor: copy 0.14.1 parser from zig std #330: Related to zig.zig module and 0.14.1 token/tag definitions; shares adopted token renaming (@"await" → await, @"usingnamespace" → usingnamespace) and may coordinate on syntax updates.

Poem

🐰 Hops through collections, ArrayList's gone,
Managed arrays hop along all day long,
Writers now pointer, cleaner and bright,
Token names freed from their quotes in the night,
Zig 0.15 brings a burrow of change—
From 0.14's fields to a new, tidy range!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request - upgrading the project from Zig 0.14.1 to 0.15.2. This is reflected in the systematic version updates across workflow files, build configuration, and extensive code refactoring to align with Zig 0.15 APIs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch don/chore/zig-15

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 and usage tips.

@github-actions github-actions bot added A-semantic Area - semantic analysis A-linter Area - linter and lint rules C-test Category - unit or e2e tests A-reporter Area - Error reporting and formatting A-cli Area - Command Line Interface labels Nov 29, 2025
@github-actions github-actions bot added the C-ci Category - CI/CD label Nov 29, 2025
@DonIsaac DonIsaac linked an issue Nov 29, 2025 that may be closed by this pull request
4 tasks
@DonIsaac DonIsaac marked this pull request as ready for review November 30, 2025 22:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (4)
test/semantic/ecosystem_coverage.zig (1)

41-43: Remove unused function with outdated API.

The isTTY_() function is unused and still references the old std.io.getStdErr().isTTY() API that was migrated away from on line 23. Since this code is unreferenced, it should be removed as part of the Zig 0.15.2 upgrade.

-fn isTTY_() bool {
-    return std.io.getStdErr().isTTY();
-}
-
src/linter/rules/must_return_ref.zig (1)

11-24: Remove try from line 19 documentation example.

Line 19 shows .list = try std.array_list.Managed(u32).init(std.heap.page_allocator), but Managed.init() does not return an error. The try keyword should be removed. Use initCapacity() only when you need error handling for pre-allocation. Correct syntax: .list = std.array_list.Managed(u32).init(std.heap.page_allocator)

src/reporter/formatters/GithubFormatter.zig (1)

57-71: Pass &w.writer to format, not &w.

The format function expects a *io.Writer parameter, but the test passes &w where w is a Writer.Allocating. Looking at Reporter.zig (line ~115), the correct pattern is to pass &w.writer. Additionally, the buf created at line 57 is unused—remove it or connect the writer properly to it.

tasks/gen_utils.zig (1)

92-125: Add errdefer buf.deinit() to handle memory leaks on append failures

The function correctly uses std.array_list.Managed(u8) and toOwnedSlice() to transfer ownership to the caller on success. However, if appendSlice() or append() fail after initCapacity() succeeds, the allocated buffer is not freed because deinit() is never called.

Add an errdefer immediately after initialization:

    var buf = try std.array_list.Managed(u8).initCapacity(allocator, docs.len);
+   errdefer buf.deinit();
    var lines = mem.splitScalar(u8, docs, '\n');

This ensures the buffer is properly cleaned up on error paths while preserving the success path where toOwnedSlice() transfers ownership without requiring an explicit deinit().

🧹 Nitpick comments (8)
src/zig.zig (1)

1-4: Module naming may cause confusion.

The module is still named @"0.14.1" but now contains Zig 0.15.x-specific API patterns (e.g., std.Io.Writer, new std.fmt.Formatter signature). Consider renaming this module or adding a comment explaining that it's a compatibility shim that uses 0.15.x APIs while maintaining the 0.14.1 AST/parser code.

build.zig (2)

84-86: Commented-out code contains syntax error and should be cleaned up.

Line 85 has invalid Zig syntax (exe.root_module else &exe.root_module). If this code is no longer needed, consider removing the commented block entirely to avoid confusion.

-    // exe.want_lto
-    // l.link(exe.root_module else &exe.root_module, false, .{});
-

178-217: Consider removing the large commented-out check block.

The 35-line commented block (lines 178-212) has been replaced with a simpler implementation. If this code is no longer needed, removing it would improve readability. If it's kept for reference, a brief comment explaining why would be helpful.

tasks/confgen.zig (1)

18-18: Buffer reuse between file writes may cause confusion.

The global buf is reused for both the Rules.zig file (line 28) and the zlint.schema.json file (line 86). While this works in a single-threaded context, it makes the code less maintainable and could lead to issues if the code is refactored for concurrency.

Consider using separate local buffers for each file write:

-var buf: [1024]u8 = undefined;
-
 pub fn main() !void {
+    var rules_buf: [1024]u8 = undefined;
     // ...
-    var writer = out.writer(&buf);
+    var writer = out.writer(&rules_buf);

And similarly for the JSON schema write:

+    var schema_buf: [1024]u8 = undefined;
     var out = try fs.cwd().createFile(c.@"zlint.schema.json", .{});
-    var writer = out.writer(&buf);
+    var writer = out.writer(&schema_buf);

Also applies to: 86-86

src/reporter/formatters/GithubFormatter.zig (1)

60-60: Remove commented-out code.

The commented line should be removed to keep the codebase clean.

-    // var w = buf.writer();
src/cli/print_command.zig (1)

36-42: Potential redundant flush operations.

When writer_ is null, the code sets up stdout at line 36-40 and assigns writer = &stdout.?.interface. Then both stdout (line 37) and writer (line 42) have deferred flush operations. Since writer points to stdout.?.interface in this case, this results in flushing the same writer twice.

While not harmful, this is redundant.

Consider simplifying to avoid the double flush:

     const sema = &sema_result.value;
-    var stdout: ?std.fs.File.Writer = null;
-    defer if (stdout) |*out| out.interface.flush() catch @panic("failed to flush writer");
     var writer = writer_ orelse blk: {
-        stdout = std.fs.File.stdout().writer(&buf);
-        break :blk &stdout.?.interface;
+        var stdout = std.fs.File.stdout().writer(&buf);
+        break :blk &stdout.interface;
     };
     defer writer.flush() catch @panic("failed to flush writer");
src/linter/tester.zig (1)

111-116: Heap allocation for writer appears unnecessary.

The std.fs.File.Writer is a small struct that can live on the stack. Heap-allocating it introduces overhead and a potential leak path if an early return occurs before destroy. Consider using a stack allocation instead:

-var buf: [512]u8 = undefined;
-var writer = try self.alloc.create(std.fs.File.Writer);
-writer.* = std.fs.File.stderr().writer(&buf);
-defer self.alloc.destroy(writer);
-var stderr = &writer.interface;
-defer stderr.flush() catch @panic("failed to flush writer");
+var buf: [512]u8 = undefined;
+var writer = std.fs.File.stderr().writer(&buf);
+var stderr = &writer.interface;
+defer stderr.flush() catch @panic("failed to flush writer");
tasks/gen_utils.zig (1)

61-65: readFileAllocOptions migration looks correct; consider using Alignment.of for clarity

The readSourceFile call now matches the Zig 0.15 std.fs.Dir.readFileAllocOptions signature: allocator, path, max_bytes, size_hint, std.mem.Alignment, and sentinel. Using .@"1" and sentinel 0 is type-correct for returning [:0]u8.

If you want to avoid the magic 1 and track the element type more clearly, you could optionally switch to:

-    return root.readFileAllocOptions(alloc, path, MAX, null, .@"1", 0);
+    return root.readFileAllocOptions(
+        alloc,
+        path,
+        MAX,
+        null,
+        std.mem.Alignment.of(u8),
+        0,
+    );

This is stylistic only; behavior remains the same.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48d0266 and ea9f74f.

⛔ Files ignored due to path filters (2)
  • src/linter/rules/snapshots/homeless-try.snap is excluded by !**/*.snap
  • src/linter/rules/snapshots/must-return-ref.snap is excluded by !**/*.snap
📒 Files selected for processing (54)
  • .github/workflows/autofix.yaml (1 hunks)
  • .github/workflows/ci.yaml (2 hunks)
  • .github/workflows/codecov.yaml (1 hunks)
  • .github/workflows/release.yaml (1 hunks)
  • .typos.toml (1 hunks)
  • apps/site/docs/rules/must-return-ref.mdx (3 hunks)
  • build.zig (2 hunks)
  • build.zig.zon (2 hunks)
  • docs/rules/must-return-ref.md (3 hunks)
  • src/Error.zig (0 hunks)
  • src/Semantic/SemanticBuilder.zig (1 hunks)
  • src/Semantic/test/util.zig (2 hunks)
  • src/cli/Options.zig (2 hunks)
  • src/cli/lint_command.zig (4 hunks)
  • src/cli/print_command.zig (3 hunks)
  • src/cli/test/print_ast_test.zig (1 hunks)
  • src/linter/LintService.zig (4 hunks)
  • src/linter/fix.zig (1 hunks)
  • src/linter/lint_context.zig (1 hunks)
  • src/linter/linter.zig (1 hunks)
  • src/linter/rules/homeless_try.zig (4 hunks)
  • src/linter/rules/must_return_ref.zig (4 hunks)
  • src/linter/rules/suppressed_errors.zig (1 hunks)
  • src/linter/rules/useless_error_return.zig (2 hunks)
  • src/linter/tester.zig (4 hunks)
  • src/main.zig (2 hunks)
  • src/printer/Printer.zig (5 hunks)
  • src/printer/SemanticPrinter.zig (1 hunks)
  • src/reporter/Reporter.zig (9 hunks)
  • src/reporter/StringWriter.zig (3 hunks)
  • src/reporter/formatter.zig (1 hunks)
  • src/reporter/formatters/GithubFormatter.zig (4 hunks)
  • src/reporter/formatters/GraphicalFormatter.zig (10 hunks)
  • src/reporter/formatters/JSONFormatter.zig (3 hunks)
  • src/root.zig (1 hunks)
  • src/source.zig (1 hunks)
  • src/util.zig (1 hunks)
  • src/util/bitflags.zig (2 hunks)
  • src/util/bitflags_test.zig (1 hunks)
  • src/util/cow.zig (2 hunks)
  • src/visit/walk.zig (3 hunks)
  • src/zig.zig (1 hunks)
  • src/zig/0.14.1/Ast.zig (8 hunks)
  • src/zig/0.14.1/Parse.zig (2 hunks)
  • src/zig/0.14.1/render.zig (12 hunks)
  • src/zig/0.14.1/string_literal.zig (3 hunks)
  • tasks/codegen_task.zig (0 hunks)
  • tasks/confgen.zig (4 hunks)
  • tasks/docgen.zig (5 hunks)
  • tasks/gen_utils.zig (2 hunks)
  • test/harness/TestSuite.zig (1 hunks)
  • test/semantic/ecosystem_coverage.zig (1 hunks)
  • test/semantic/snapshot_coverage.zig (3 hunks)
  • zlint.schema.json (1 hunks)
💤 Files with no reviewable changes (2)
  • tasks/codegen_task.zig
  • src/Error.zig
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/ci.yaml

79-79: condition "false" is always evaluated to false. remove the if: section

(if-cond)

🔇 Additional comments (63)
src/root.zig (2)

26-26: Test reflection addition for json module looks good.

Adding json to the recursive reflection test block aligns with the pattern established by printer and zig, and ensures the exported json module is validated during testing. This change is appropriate.


3-3: Verify version constant alignment with Zig 0.15.2 upgrade.

Line 3 still references "0.14.1", but the PR objective indicates an upgrade to Zig 0.15.2. Clarify whether this version constant represents API/ABI compatibility (intentionally unchanged) or if it should be updated to reflect the new compiler version.

Confirm:

  • Is the version string on line 3 intentional (e.g., for API/compatibility purposes)?
  • Should it be updated to "0.15.2" as part of this upgrade?
  • Where is the actual Zig compiler version specified (e.g., build.zig.zon)?
src/cli/test/print_ast_test.zig (1)

21-23: Migration to std.array_list.Managed looks correct

Using std.array_list.Managed(u8).initCapacity(allocator, source.text().len) is correct here. The arena allocator (initialized at line 17 and deferred for cleanup) simplifies lifetime management—no explicit buf.deinit() is needed. The writer.any() call on line 24 works as expected with this setup.

src/util/cow.zig (1)

220-227: LGTM - Test updates align with Zig 0.15 formatter API.

The test changes correctly use the {f} format specifier to invoke the custom format function, which is the expected pattern for Zig 0.15.

src/util.zig (1)

10-10: CallingConvention migration to .@"inline" / .auto is correct and complete

The migration from .Inline / .Unspecified to .@"inline" / .auto is correct for Zig 0.14+/0.15 and is consistently applied across the codebase. All production function declarations in src/Semantic/SemanticBuilder.zig correctly reference callconv(util.@"inline"), and no legacy .Inline or .Unspecified tags remain in the source tree.

src/visit/walk.zig (2)

439-440: No action needed—std.array_list.Managed usage is correct for Zig 0.15.2.

The initialization pattern std.array_list.Managed(Item).init(temp.get()) and deinitialization with defer deinit() match the Zig 0.15.2 standard library documentation exactly. The implementation is already aligned with the current API.


704-704: No action needed—syntax change is correct for Zig 0.15.2.

The removal of string quotes from .usingnamespace and .await aligns with Zig 0.15.x language changes: both usingnamespace and the async/await keywords were removed from the language spec. They are no longer reserved keywords, so the escape syntax is unnecessary. Other keywords like if, defer, and comptime remain reserved and correctly retain the @"..." form.

test/semantic/ecosystem_coverage.zig (2)

64-64: LGTM!

The simplified test suite initialization with .{ .test_fn = &testSemantic } is clean and aligns with the modular test infrastructure refactoring.


23-23: LGTM! The migration from std.io.getStdErr().isTty() to std.fs.File.stderr().isTty() is correct for Zig 0.15.2. This API is documented in the standard library's std/fs/file.zig.

.typos.toml (1)

9-9: LGTM!

Adding build.zig.zon to the exclude list is appropriate, as build manifests often contain package names and identifiers that may trigger false positives in typo detection.

.github/workflows/codecov.yaml (1)

11-11: LGTM!

The Zig version update to 0.15.2 aligns with the PR objectives and is consistent across all workflow files.

.github/workflows/ci.yaml (2)

33-33: LGTM!

The Zig version update to 0.15.2 is consistent with the PR objectives.


75-79: Temporary workaround properly documented.

The lint job is intentionally disabled due to parser incompatibility with the usingnamespace keyword change (keyword in 0.14 → identifier in 0.15). The TODO comment clearly indicates this should be re-enabled after full migration.

.github/workflows/autofix.yaml (1)

15-15: LGTM!

The Zig version update to 0.15.2 aligns with the PR objectives.

.github/workflows/release.yaml (1)

9-9: LGTM!

The Zig version update to 0.15.2 is consistent with the PR objectives.

src/printer/SemanticPrinter.zig (1)

120-120: LGTM!

The migration from std.ArrayList to std.array_list.Managed aligns with the broader PR objective to use managed collection types throughout the codebase.

src/Semantic/test/util.zig (2)

40-40: LGTM!

The migration to toManaged(t.allocator) aligns with the PR's broader migration to managed collection types, and the OOM panic provides appropriate error handling for test utilities.


12-18: The writer initialization syntax is correct and valid for this codebase.

The pattern std.fs.File.stderr().writer(&buf) followed by accessing .interface is the standard approach used throughout the zlint codebase. This same pattern appears in src/main.zig, src/cli/Options.zig, src/cli/lint_command.zig, and src/linter/tester.zig, indicating it is a well-established convention compatible with Zig 0.15.0+. The File.writer() method accepts a buffer parameter and returns a writer struct with an interface field, making this syntax correct and idiomatic for this project.

Likely an incorrect or invalid review comment.

src/linter/lint_context.zig (1)

272-273: LGTM! Type migration to std.array_list.Managed is correct.

The migration from std.ArrayList to std.array_list.Managed aligns with Zig 0.15.x naming conventions. The TODO comment on line 272 could be clarified - it mentions Managed(Error) but the actual type is Managed(Diagnostic).

src/linter/rules/useless_error_return.zig (2)

211-211: LGTM! Type migration consistent with Zig 0.15.x.

The err_stack field correctly updated to std.array_list.Managed(ErrState).


255-260: LGTM! Initialization updated correctly.

The init function properly uses std.array_list.Managed(ErrState).init(alloc) matching the field type change.

src/zig.zig (1)

14-42: New escape formatting utilities look correct.

The fmtEscapes and stringEscape implementations properly handle:

  • Common escape sequences (\n, \r, \t, \\, \")
  • Single quotes passed through (correct for double-quoted string context)
  • Printable ASCII range written directly
  • Non-printable bytes escaped as \xNN
src/zig/0.14.1/string_literal.zig (3)

47-47: LGTM! Signature updated for Zig 0.15.x writer interface.

The formatMessage function now takes a concrete *std.io.Writer pointer instead of a generic anytype writer, returning std.io.Writer.Error!void. This aligns with the broader PR pattern of migrating to explicit writer types.


89-89: LGTM! Formatter signature updated for Zig 0.15.x.

The std.fmt.Formatter(FormatMessage, formatMessage) now takes two type parameters (data type and format function) matching the new Zig 0.15.x API.


359-359: LGTM! Buffer type migrated to Managed.

Consistent with the project-wide migration from std.ArrayList to std.array_list.Managed.

build.zig.zon (1)

29-40: Dependency chameleon version has a compatibility concern with Zig 0.15.x.

smart-pointers v0.0.4 requires 0.15.0 and recover v1.3.0 requires 0.14.0, both compatible with Zig 0.15.x. However, chameleon pinned to commit 414169de specifies minimum_zig_version = "0.15.0-dev.1228+6dbcc3bd5", which targets a specific development build rather than the stable Zig 0.15.x release. Verify this specific dev version is available in your target toolchain or consider using a commit that targets stable Zig 0.15.0.

src/cli/Options.zig (2)

108-117: Writer API usage is correct for Zig 0.15.x.

The pattern std.fs.File.stdout().writer(&buf) with .interface access is the proper Zig 0.15.x file writer API. The 512-byte buffer is sufficient for the ~500 character help text, and the deferred flush() call ensures the output is properly written before exit.


46-52: Remove unused error types from ParseError or verify actual writer error behavior.

The try stdout.writeAll(...) calls will propagate errors matching the buffered writer's actual error set. However, WriterError and WriteFailed are defined in ParseError but never returned by the stdout.writeAll() calls. For a buffered writer to stdout, the error set is typically empty (stdout is always writable), making these error types unnecessary. Either remove them from ParseError or explicitly document and handle the specific error cases they represent.

src/cli/lint_command.zig (1)

25-31: Heap-allocated writer pattern for cross-thread use looks correct.

The comment explains the rationale well. The defer ordering ensures flush happens before destroy.

src/printer/Printer.zig (1)

66-77: JSON stringify pattern updated correctly for Zig 0.15.

The migration to std.json.Stringify{ .writer = self.writer } with .write(value) follows the new API pattern.

src/zig/0.14.1/render.zig (4)

10-17: Import and type migrations for Zig 0.15 look correct.

The changes to use zig.fmtEscapes and std.array_list.Managed align with the Zig 0.15 API.


285-294: Token variant renames align with Zig 0.15 changes.

The changes from quoted token variants (.@"usingnamespace", .@"await") to bare identifiers (.usingnamespace, .await) are correct for Zig 0.15.

Also applies to: 626-632, 919-919


1621-1621: Format specifier change for fmtEscapes is correct.

Using "{any}" for fmtEscapes output aligns with Zig 0.15 formatting requirements.


3327-3376: AutoIndentingStream updates for Zig 0.15 are consistent.

The migration to GenericWriter and std.array_list.Managed for internal stacks follows the Zig 0.15 API changes correctly.

src/util/bitflags.zig (1)

140-156: Format function signature is incompatible with std.fmt.

The format function is missing required parameters for std.fmt compatibility. The standard format interface requires comptime fmt: []const u8 and std.fmt.FormatOptions parameters, along with a return type of Error!void. The current signature lacks these, which means the "{f}" format specifier in tests will not work correctly with this implementation.

To fix, update the signature to: fn format(self: Flags, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void

build.zig (1)

47-64: Module-centric build pattern looks correct for Zig 0.15.x.

The migration from addStaticLibrary with root_source_file to createModule + addLibrary with root_module follows the new Zig 0.15.x patterns correctly.

src/Semantic/SemanticBuilder.zig (1)

456-461: Token tag renames align with Zig 0.15.x changes.

The switch from .@"await" to .await and .@"usingnamespace" to .usingnamespace reflects the updated token tag enum in Zig 0.15.x standard library.

src/linter/fix.zig (1)

124-126: Migration to std.array_list.Managed is correct.

The change from std.ArrayList(Diagnostic) to std.array_list.Managed(Diagnostic).init(alloc) aligns with Zig 0.15.x naming conventions where ArrayList was renamed to array_list.Managed.

src/linter/rules/must_return_ref.zig (1)

138-156: Type registry correctly updated to include Managed.

Adding "Managed" to types_to_check ensures the rule catches std.array_list.Managed return types, maintaining the rule's effectiveness after the Zig 0.15.x migration.

src/source.zig (1)

22-26: File metadata API migration looks correct for Zig 0.15.x.

The changes from file.metadata() to file.stat() and from meta.size() method call to meta.size field access align with the Zig 0.15.x standard library API changes.

test/harness/TestSuite.zig (1)

134-159: Buffered snapshot writer integration looks solid

Using a fixed stack buffer with snapshot.writer(&buf), deferring w.interface.flush(), and routing all prints through w.interface.print is consistent with the new writer API and should give reliable snapshot output in tests.

src/reporter/formatter.zig (1)

34-38: FormatError alias matches the new io.Writer-based reporter API

Switching FormatError to io.Writer.Error || Allocator.Error and introducing the io alias aligns this module with the updated reporter/writer interfaces without changing observable behavior.

src/main.zig (1)

5-6: Main import and writer refactors look consistent with the Zig 0.15 toolchain

The non-relative imports, the adjusted error printing (err.message.borrow() plus {any} for Options.usage), and the use of std.fs.File.stdout().writer(&.{}) with stdout.interface.print all match the project-wide shift to the new writer API and should behave equivalently to the previous implementation.

Also applies to: 10-11, 33-36, 41-45

src/zig/0.14.1/Parse.zig (1)

680-692: Parser tag updates to .usingnamespace and .await are in line with AST enum changes

Updating the node tags in expectUsingNamespace and parsePrefixExpr to .usingnamespace and .await is the right adjustment to stay consistent with the renamed Ast.Node.Tag variants; no behavioral issues expected as long as Ast.zig matches.

Also applies to: 1745-1753

src/linter/rules/homeless_try.zig (1)

217-219: Test fixtures updated to std.array_list.Managed are consistent with the new list API

Switching the homeless_try rule’s example code from std.ArrayList to std.array_list.Managed (both for *u8 and u32 cases) keeps the tests aligned with the new standard container type without affecting the rule’s core logic.

Also applies to: 268-275, 277-283, 338-345

src/linter/LintService.zig (2)

80-90: Error collection migration to std.array_list.Managed(Error) is coherent

Using ?std.array_list.Managed(Error) as the out-parameter type in lintSource and wiring it through the semantic, analysis, and fix-disabled paths (including the initCapacity/appendAssumeCapacity conversion from diagnostics) results in a single, consistent managed error list that the caller (and reporter) can own and deinit; the control flow in the catch block of tryLintFile still preserves the original linting error semantics.

Also applies to: 99-149


151-170: applyFixes correctly returns a managed error list without double-freeing

Converting result.unfixed_errors to a std.array_list.Managed(Error) via toManaged(self.allocator) and then resetting result.unfixed_errors = .{} before result.deinit(self.allocator) ensures the unfixed errors are moved out safely and avoids deinitializing the same storage twice.

src/linter/linter.zig (1)

75-81: runOnSource now returning Managed diagnostics matches the linter’s new ownership model

Updating the errors out-parameter to *?std.array_list.Managed(Context.Diagnostic) and continuing to assign ctx.takeDiagnostics() keeps the public API in sync with the Managed diagnostic list type while preserving the existing “null on success, non-null plus error.LintingFailed on failure” contract.

Also applies to: 143-145

test/semantic/snapshot_coverage.zig (1)

73-76: Snapshot coverage tests correctly adopt buffered writers and the new Reporter API

Both runPass and runFail now use stack-backed buffered writers with explicit flush and pass &writer.interface into Printer.init/Reporter.init, and the added try around reporter.reportErrorSlice cleanly propagates I/O/formatting failures to the harness without changing the intended pass/fail semantics of these tests.

Also applies to: 112-119, 126-133

tasks/docgen.zig (1)

45-45: Good use of buffered writer pattern.

The global buffer is properly initialized for each document file generation, ensuring no cross-contamination between writes. The deferred flush handling at line 100 ensures data is written before the file closes.

Also applies to: 99-101

zlint.schema.json (1)

432-434: Documentation correctly updated to reflect API changes.

The schema documentation has been updated to use std.array_list.Managed(u32) instead of std.ArrayList(u32), which aligns with the broader migration in this PR.

src/reporter/formatters/JSONFormatter.zig (1)

15-18: Clean migration to io.Writer interface.

The formatter now uses *io.Writer and std.json.Stringify directly, which aligns with the broader refactoring across the codebase.

docs/rules/must-return-ref.md (1)

18-26: Documentation examples correctly updated.

The examples have been updated to use std.array_list.Managed(u32) instead of std.ArrayList(u32), maintaining consistency with the codebase migration.

src/reporter/StringWriter.zig (2)

4-4: Well-designed writer interface separation.

The separation between GenericWriter (for generic writing) and ioWriter() (returning io.Writer) provides clear, flexible APIs for different use cases. The rename from Writer to GenericWriter improves clarity.

Also applies to: 30-36


7-7: The undefined initialization is valid — no fix needed for that.

Zig's io.Writer.interface() accepts undefined buffers as backing storage; it doesn't initialize them. The writer maintains an end index starting from 0, so uninitialized data is never accessed. This pattern is standard in Zig (see stdlib examples).

However, writer_buffer appears to be unused: the write() method writes to self.buf (the ArrayList), not to writer_buffer. Consider whether ioWriter() is actually needed or if this is dead code.

Likely an incorrect or invalid review comment.

apps/site/docs/rules/must-return-ref.mdx (1)

20-21: Documentation correctly updated for Zig 0.15 API migration.

The examples properly reflect the migration from std.ArrayList to std.array_list.Managed, which aligns with the Zig 0.15.2 upgrade.

src/linter/tester.zig (1)

327-334: Snapshot writing implementation looks correct.

The buffered writer pattern with a fixed 8KB buffer and explicit flush is appropriate for file I/O operations.

src/zig/0.14.1/Ast.zig (2)

513-513: Token variant renames align with Zig 0.15 changes.

The removal of @"" quoting for reserved word enum variants (.await, .usingnamespace) is consistent with Zig 0.15's language changes.


468-476: Error formatting correctly updated for Zig 0.15.

The change from '{'} to '{f}' with zig.fmtEscapes is the correct pattern for formatting escaped bytes in Zig 0.15+.

src/reporter/formatters/GraphicalFormatter.zig (2)

31-37: Writer interface migration is consistent.

The migration from the Writer alias to *io.Writer is consistently applied across all public methods in this formatter.


473-483: Replace custom writeByteNTimes with io.Writer's built-in method.

The writeByteNTimes function duplicates functionality already provided by std.io.Writer. The built-in method is more efficient (buffering writes in 256-byte chunks instead of single bytes). Replace the custom implementation:

fn writeByteNTimes(w: *io.Writer, byte: u8, n: usize) FormatError!void {
-    for (0..n) |_| {
-        try w.writeByte(byte);
-    }
+    try w.writeByteNTimes(byte, n);
}

Keep writeBytesNTimes as-is—there is no equivalent built-in method.

Likely an incorrect or invalid review comment.

src/reporter/Reporter.zig (2)

118-137: Buffering strategy change enables atomic writes.

The new pattern of collecting all formatted output into a Writer.Allocating buffer before writing atomically to self.writer under a lock ensures that diagnostic output from concurrent threads won't be interleaved. This is a good improvement for multi-threaded linting.


190-192: Atomic initialization updated for Zig 0.15.

The .init(0) pattern for std.atomic.Value is the correct idiom for Zig 0.15+.

Comment on lines 18 to +19
// NOTE: we use 0.14.1, but 0.14.0 should still work.
.minimum_zig_version = "0.14.0",
.minimum_zig_version = "0.15.0",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Stale comment on line 18.

The comment "NOTE: we use 0.14.1, but 0.14.0 should still work" is now outdated since minimum_zig_version is set to 0.15.0. Update or remove this comment to reflect the actual Zig version requirements.

-    // NOTE: we use 0.14.1, but 0.14.0 should still work.
+    // NOTE: we use 0.15.2, but 0.15.0 should still work.
     .minimum_zig_version = "0.15.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// NOTE: we use 0.14.1, but 0.14.0 should still work.
.minimum_zig_version = "0.14.0",
.minimum_zig_version = "0.15.0",
// NOTE: we use 0.15.2, but 0.15.0 should still work.
.minimum_zig_version = "0.15.0",
🤖 Prompt for AI Agents
In build.zig.zon around lines 18 to 19, the inline comment stating "NOTE: we use
0.14.1, but 0.14.0 should still work" is stale because minimum_zig_version is
now "0.15.0"; update the comment to reflect the current version requirement
(e.g., change it to mention 0.15.0 and any compatible older versions) or remove
the comment entirely so it no longer contradicts .minimum_zig_version.

Comment on lines +22 to +23
var buf: [4096]u8 = undefined;

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Global mutable buffer may cause thread-safety issues.

The module-level var buf is used by the stdout writer which is shared across threads via the Reporter. If multiple threads write concurrently, this buffer could be corrupted.

Consider making this buffer local to the lint function or using thread-local storage.

-var buf: [4096]u8 = undefined;
-
 pub fn lint(alloc: Allocator, options: Options) !u8 {
+    var buf: [4096]u8 = undefined;
     // writer cannot live on the stack.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var buf: [4096]u8 = undefined;
pub fn lint(alloc: Allocator, options: Options) !u8 {
var buf: [4096]u8 = undefined;
// writer cannot live on the stack.
🤖 Prompt for AI Agents
In src/cli/lint_command.zig around lines 22-23 the module-level mutable buffer
`var buf: [4096]u8` is shared across threads and can be corrupted by concurrent
Reporter stdout writes; move the buffer out of module scope and make it
per-invocation or per-thread: remove the global `buf`, declare a local `buf`
inside the `lint` function (or the function that constructs the Reporter/writer)
or mark it thread-local (e.g., with @threadLocal) so each thread gets its own
buffer, and update all references to use the new local/thread-local buffer (or
allocate a buffer from an allocator) to eliminate the race.

Comment on lines +193 to +205
pub fn readUntilDelimiterOrEof(self: *std.io.Reader, buffer: []u8, delimiter: u8) anyerror!?[]u8 {
var fbw = std.io.Writer.fixed(buffer);
const bytes_read = self.streamDelimiter(&fbw, delimiter) catch |err| switch (err) {
error.EndOfStream => if (fbw.end == 0) {
return null;
} else return err,

else => |e| return e,
};
if (bytes_read == 0) return null;
self.toss(1); // throw out the delimiter
return buffer[0..bytes_read];
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potential bug in EndOfStream handling with partial data.

When EndOfStream is encountered but fbw.end != 0 (meaning some data was read), the function returns the error instead of the partial data. This seems incorrect - the partial data should be returned.

Also, the comment says "discards the delimiter" but on EndOfStream there is no delimiter to discard, yet toss(1) is still called on line 203 after reading non-zero bytes.

 pub fn readUntilDelimiterOrEof(self: *std.io.Reader, buffer: []u8, delimiter: u8) anyerror!?[]u8 {
     var fbw = std.io.Writer.fixed(buffer);
     const bytes_read = self.streamDelimiter(&fbw, delimiter) catch |err| switch (err) {
         error.EndOfStream => if (fbw.end == 0) {
             return null;
-        } else return err,
+        } else {
+            return buffer[0..fbw.end];
+        },

         else => |e| return e,
     };
     if (bytes_read == 0) return null;
     self.toss(1); // throw out the delimiter
     return buffer[0..bytes_read];
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn readUntilDelimiterOrEof(self: *std.io.Reader, buffer: []u8, delimiter: u8) anyerror!?[]u8 {
var fbw = std.io.Writer.fixed(buffer);
const bytes_read = self.streamDelimiter(&fbw, delimiter) catch |err| switch (err) {
error.EndOfStream => if (fbw.end == 0) {
return null;
} else return err,
else => |e| return e,
};
if (bytes_read == 0) return null;
self.toss(1); // throw out the delimiter
return buffer[0..bytes_read];
}
pub fn readUntilDelimiterOrEof(self: *std.io.Reader, buffer: []u8, delimiter: u8) anyerror!?[]u8 {
var fbw = std.io.Writer.fixed(buffer);
const bytes_read = self.streamDelimiter(&fbw, delimiter) catch |err| switch (err) {
error.EndOfStream => if (fbw.end == 0) {
return null;
} else {
return buffer[0..fbw.end];
},
else => |e| return e,
};
if (bytes_read == 0) return null;
self.toss(1); // throw out the delimiter
return buffer[0..bytes_read];
}
🤖 Prompt for AI Agents
In src/cli/lint_command.zig around lines 193-205, the EndOfStream branch
incorrectly returns an error when partial data exists and always calls toss(1)
even though no delimiter was read; change the error handling so that when
streamDelimiter returns EndOfStream but fbw.end > 0 you treat that as a
successful partial read (set bytes_read = fbw.end and continue to return the
slice) instead of returning the error, and only call self.toss(1) when the
delimiter was actually consumed (i.e., when streamDelimiter succeeded without
EndOfStream).

Comment on lines +186 to +187
.{"writeByteNTimes"}, // note: removed in 0.15
.{"writeBytesNTimes"}, // note: removed in 0.15
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Zig 0.15.2 std.io.Writer writeByteNTimes writeBytesNTimes removed

💡 Result:

They were removed as part of the big std.io/std.Io overhaul in the 0.15+ series ("Writergate"). The old std.io Writer helpers (including writeByteNTimes / writeBytesNTimes) were deleted or deprecated in favor of the new non‑generic std.Io.Writer and the new File/Writer APIs (e.g. std.fs.File.stdout().writer(&buf).interface, writeAll, the new Writer.print/drain style). See the 0.15 release notes and write‑up on the new Writer for details and migration guidance. [1][2]

Sources:

  • Zig 0.15.1 release notes (deletions / Writergate). [1]
  • Article / blog describing the new std.Io.Writer and its API changes. [2]

Remove the stale method references from the printMethods map.

The methods writeByteNTimes and writeBytesNTimes were removed in Zig 0.15+ as part of the "Writergate" std.io/std.Io overhaul. Since these methods no longer exist in the standard library, the entries at lines 186-187 should be deleted to avoid maintaining incorrect references.

🤖 Prompt for AI Agents
In src/linter/rules/suppressed_errors.zig around lines 186-187, remove the two
stale entries .{"writeByteNTimes"} and .{"writeBytesNTimes"} from the
printMethods map so the map no longer references methods removed in Zig 0.15;
update surrounding comma placement if needed to keep valid Zig list syntax and
run tests/formatting to ensure the file compiles.

Comment on lines 17 to 18
const ContainerKind = enum { object, array };
const ContainerStack = std.ArrayList(ContainerKind);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Inconsistency between ArrayList and Managed usage.

Line 18 declares ContainerStack = std.ArrayList(ContainerKind), but line 35 calls container_stack.deinit(self.alloc) which is the Managed API. std.ArrayList.deinit() takes no arguments.

Similarly, lines 125 and 133 call append(self.alloc, ...) which is the Managed API, not ArrayList.

 const ContainerKind = enum { object, array };
-const ContainerStack = std.ArrayList(ContainerKind);
+const ContainerStack = std.array_list.Managed(ContainerKind);
 const NEWLINE = if (builtin.target.os.tag == .windows) "\r\n" else "\n";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ContainerKind = enum { object, array };
const ContainerStack = std.ArrayList(ContainerKind);
const ContainerKind = enum { object, array };
const ContainerStack = std.array_list.Managed(ContainerKind);
🤖 Prompt for AI Agents
In src/printer/Printer.zig around lines 17-18 (and usages at ~35, 125, 133): you
declared ContainerStack as std.ArrayList(ContainerKind) but are calling the
Managed API (deinit(self.alloc), append(self.alloc, ...)). Fix by making the
APIs consistent: either change the type to the Managed wrapper (ContainerStack =
std.ArrayList(ContainerKind).Managed) and keep the current calls
(Managed.init(self.alloc), deinit(self.alloc), append(self.alloc, ...)), or keep
std.ArrayList(ContainerKind) and update call sites to the non-Managed API
(initialize with ContainerStack.init(self.alloc) or
ContainerStack.init(allocator) as appropriate, call container_stack.deinit()
with no allocator, and call container_stack.append(value) without passing
alloc). Ensure init/deinit and append usages match the chosen ArrayList variant.

Comment on lines 163 to 174
pub fn deinit(self: *Reporter) void {
self.writer.flush() catch {};
self.writer_lock.lock();
self.writer.flush() catch |e| std.debug.panic("Reporter failed to flush writer: {s}", .{@errorName(e)});
self.vtable.deinit(self.ptr, self.alloc);
self.vtable.destroy(self.ptr, self.alloc);
self.writer = undefined;

if (comptime util.IS_DEBUG) {
self.vtable.format = &PanicFormatter.format;
self.vtable.deinit = &PanicFormatter.deinit;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing unlock in deinit - potential deadlock on re-entry.

The lock is acquired at line 164 but never released. While deinit typically isn't called multiple times, this could cause issues if the reporter is reused or if there's any debug logging that tries to use the reporter during cleanup. Additionally, the lock should be released before setting writer = undefined:

 pub fn deinit(self: *Reporter) void {
     self.writer_lock.lock();
+    defer self.writer_lock.unlock();
     self.writer.flush() catch |e| std.debug.panic("Reporter failed to flush writer: {s}", .{@errorName(e)});
     self.vtable.deinit(self.ptr, self.alloc);
     self.vtable.destroy(self.ptr, self.alloc);
     self.writer = undefined;

     if (comptime util.IS_DEBUG) {
         self.vtable.format = &PanicFormatter.format;
         self.vtable.deinit = &PanicFormatter.deinit;
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn deinit(self: *Reporter) void {
self.writer.flush() catch {};
self.writer_lock.lock();
self.writer.flush() catch |e| std.debug.panic("Reporter failed to flush writer: {s}", .{@errorName(e)});
self.vtable.deinit(self.ptr, self.alloc);
self.vtable.destroy(self.ptr, self.alloc);
self.writer = undefined;
if (comptime util.IS_DEBUG) {
self.vtable.format = &PanicFormatter.format;
self.vtable.deinit = &PanicFormatter.deinit;
}
}
pub fn deinit(self: *Reporter) void {
self.writer_lock.lock();
defer self.writer_lock.unlock();
self.writer.flush() catch |e| std.debug.panic("Reporter failed to flush writer: {s}", .{@errorName(e)});
self.vtable.deinit(self.ptr, self.alloc);
self.vtable.destroy(self.ptr, self.alloc);
self.writer = undefined;
if (comptime util.IS_DEBUG) {
self.vtable.format = &PanicFormatter.format;
self.vtable.deinit = &PanicFormatter.deinit;
}
}
🤖 Prompt for AI Agents
In src/reporter/Reporter.zig around lines 163-174, the writer_lock is acquired
but never released which can deadlock on re-entry; change deinit so the lock is
released after flushing (and in the error path as well) and before mutating
writer (setting writer = undefined) — use a defer or explicit unlock to ensure
unlock happens even if flush errors, and ensure the unlock happens prior to
calling vtable.deinit/destroy or changing writer.

Comment on lines +127 to +131
try expectFmt("0", "{d}", .{empty.repr()});
const name = "util.bitflags_test.TestFlags";
try expectFmt(name ++ "()", "{}", .{empty});
try expectFmt(name ++ "(a | c)", "{}", .{some});
try expectFmt(name ++ "(a | b | c | d)", "{}", .{all});
try expectFmt(name ++ "()", "{f}", .{empty});
try expectFmt(name ++ "(a | c)", "{f}", .{some});
try expectFmt(name ++ "(a | b | c | d)", "{f}", .{all});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the bitflags files
find . -name "bitflags*" -type f

Repository: DonIsaac/zlint

Length of output: 110


🏁 Script executed:

# Examine the bitflags.zig file to see the format function signature
cat -n src/util/bitflags.zig | head -100

Repository: DonIsaac/zlint

Length of output: 4503


🏁 Script executed:

# Look at the full bitflags_test.zig file to understand context
wc -l src/util/bitflags_test.zig

Repository: DonIsaac/zlint

Length of output: 88


🏁 Script executed:

# Continue viewing bitflags.zig to find the format function
sed -n '100,200p' src/util/bitflags.zig

Repository: DonIsaac/zlint

Length of output: 3517


🏁 Script executed:

# View the test file around the lines in question (127-131)
sed -n '120,132p' src/util/bitflags_test.zig

Repository: DonIsaac/zlint

Length of output: 511


🏁 Script executed:

# Let me check Zig's std.fmt to understand the expected format function signature
# Search for how format functions are typically implemented in Zig
find . -type f -name "*.zig" | xargs grep -l "@TypeOf.*format" | head -5

Repository: DonIsaac/zlint

Length of output: 95


🏁 Script executed:

# Check if there are any other examples of custom format implementations in this codebase
rg "pub fn format\(" --type zig -A 2 | head -30

Repository: DonIsaac/zlint

Length of output: 2086


Format function signature is incompatible with Zig's std.fmt protocol; tests using "{f}" format specifier will fail.

The format function in bitflags.zig has signature format(self: Flags, writer: *std.Io.Writer), but Zig's std.fmt expects the signature format(self, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype). Without the format string and options parameters, std.fmt will not invoke the custom format function.

The tests on lines 129–131 using "{f}" will not work as intended. Line 127 using "{d}" with empty.repr() will work correctly since it formats the integer directly.

Update the format function signature to match the std.fmt protocol, as shown in src/util/id.zig for reference.

🤖 Prompt for AI Agents
In src/util/bitflags_test.zig around lines 127 to 131, the tests using the "{f}"
specifier fail because the Flags.format function has the wrong signature; update
the format function in src/util/bitflags.zig to match Zig's std.fmt protocol
(format(self, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer:
anytype)) like src/util/id.zig does, then adapt its implementation to use the
provided fmt/options/writer (write the integer repr for "{d}" and the
human-friendly flag names for "{f}", handling empty/all cases and composing
strings via the writer) so std.fmt will invoke it correctly.

};
}

/// Use a `{s}` specifier to print the contained string. Use `{}` or
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update the outdated comment.

The comment references format specifiers like {s}, {}, and {any}, but the new implementation no longer accepts format specifiers. Update this comment to reflect that the format function now writes the string directly using the Zig 0.15 Writer API.

Apply this diff to update the comment:

-        /// Use a `{s}` specifier to print the contained string. Use `{}` or
+        /// Format function for the new Zig 0.15 Writer API. Use `{f}` to invoke.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Use a `{s}` specifier to print the contained string. Use `{}` or
/// Format function for the new Zig 0.15 Writer API. Use `{f}` to invoke.
🤖 Prompt for AI Agents
In src/util/cow.zig around line 154, the comment is outdated because it mentions
format specifiers like `{s}`, `{}`, and `{any}` which are no longer supported;
update the comment to state that the format function now writes the contained
string directly using the Zig 0.15 Writer API and does not accept format
specifiers, remove any references to old specifiers, and briefly note that
callers should pass the writer and string directly (or rely on the Writer API)
instead of using format tokens.

Comment on lines +156 to +157
pub fn format(self: Self, writer: *std.Io.Writer) std.Io.Writer.Error!void {
return writer.writeAll(self.str);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Zig 0.15.2 standard library Writer type std.io.Writer

💡 Result:

Short summary — Zig 0.15.x std.io.Writer

  • In 0.15.x the io API was redesigned: Writer is no longer the old generic std.io.Writer; the new writer interface places the buffer "above the vtable" (buffer is part of the interface) to enable zero-copy/hot-path optimizations and explicit buffering. This is a breaking change from pre-0.15 usage. [1][2]

  • Practical effect: you create a writer from a File (or other sink) by supplying a buffer, then call methods on that writer (write/writeAll/print/flush, etc.). Formatted printing now requires a buffer-backed writer (or you must allocate the formatted string first). Example pattern:
    var out_buf: [4096]u8 = undefined;
    var w = try std.fs.File.stdout().writer(&out_buf);
    try w.print("Hello {s}\n", .{"world"});
    try w.flush();
    (See docs/examples). [1][3]

  • Migration notes / release commentary: the change was documented in the 0.15 release notes as a major overhaul of Reader/Writer (nicknamed “Writergate” in community writeups); code using the old generic Writer must be updated to the new buffer-driven API. [2][4]

Sources

  • Zig docs (0.15.*) — standard library I/O examples and API notes. [1]
  • 0.15 release notes describing the Reader/Writer redesign. [2]
  • Tutorial / examples showing std.io.Writer usage and custom Writer implementation. [3]

If you want, I can paste the exact API signatures and a minimal copy-paste migration example for your codebase.


🏁 Script executed:

# Search for Writer usage patterns in the codebase
rg "std\.I[io]\.Writer" --type zig -A 2 -B 2

Repository: DonIsaac/zlint

Length of output: 1000


🏁 Script executed:

# Check if there are other imports or usages of io/Io module
rg "std\.(io|Io)" --type zig | head -20

Repository: DonIsaac/zlint

Length of output: 1715


Change std.Io.Writer to std.io.Writer (with lowercase io).

In Zig 0.15+, the I/O module is std.io, not std.Io. This should be std.io.Writer and std.io.Writer.Error.

🤖 Prompt for AI Agents
In src/util/cow.zig around lines 156-157 the function signature uses the wrong
I/O module casing; replace occurrences of std.Io.Writer and std.Io.Writer.Error
with std.io.Writer and std.io.Writer.Error respectively so the signature reads
using std.io types and the return/error types match Zig 0.15+.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-cli Area - Command Line Interface A-linter Area - linter and lint rules A-reporter Area - Error reporting and formatting A-semantic Area - semantic analysis C-ci Category - CI/CD C-test Category - unit or e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[RFC] Update to 0.15

2 participants