{"id":29192501,"url":"https://github.com/kurtwagner/zlinter","last_synced_at":"2026-02-02T08:31:52.349Z","repository":{"id":301483740,"uuid":"1009387544","full_name":"KurtWagner/zlinter","owner":"KurtWagner","description":"An extendable and customisable Zig linter that is integrated from source into your build.zig.","archived":false,"fork":false,"pushed_at":"2026-01-25T02:00:43.000Z","size":1114,"stargazers_count":45,"open_issues_count":17,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-25T15:37:39.375Z","etag":null,"topics":["linter","linters","zig","zig-package","ziglang"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KurtWagner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-27T03:49:53.000Z","updated_at":"2026-01-25T02:00:47.000Z","dependencies_parsed_at":"2025-06-27T05:31:41.246Z","dependency_job_id":"dc319589-ca75-4a34-9b95-27e88905f2a4","html_url":"https://github.com/KurtWagner/zlinter","commit_stats":null,"previous_names":["kurtwagner/zlinter"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/KurtWagner/zlinter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KurtWagner%2Fzlinter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KurtWagner%2Fzlinter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KurtWagner%2Fzlinter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KurtWagner%2Fzlinter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KurtWagner","download_url":"https://codeload.github.com/KurtWagner/zlinter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KurtWagner%2Fzlinter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29007963,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-02T08:20:25.892Z","status":"ssl_error","status_checked_at":"2026-02-02T08:20:04.345Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["linter","linters","zig","zig-package","ziglang"],"created_at":"2025-07-02T01:37:06.051Z","updated_at":"2026-02-02T08:31:52.344Z","avatar_url":"https://github.com/KurtWagner.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=center\u003e\n\n\u003cimg width=\"128\" height=\"128\" src=\"icon_512.png\" alt=\"Zlinter icon\"\u003e\n\n# Zlinter - Linter for Zig\n\n[![Zig support](https://img.shields.io/badge/Zig-0.14.x%20%7C%200.15.x%20%7C%20master-%23f3ab20?logo=zig\u0026style=flat)](http://github.com/kurtwagner/what-the-zig)\n[![linux](https://img.shields.io/github/actions/workflow/status/KurtWagner/zlinter/linux.yml?branch=master\u0026label=linux\u0026style=flat)](https://github.com/KurtWagner/zlinter/actions/workflows/linux.yml)\n[![windows](https://img.shields.io/github/actions/workflow/status/KurtWagner/zlinter/windows.yml?branch=master\u0026label=windows\u0026style=flat)](https://github.com/KurtWagner/zlinter/actions/workflows/windows.yml)\n[![Coverage Status](https://img.shields.io/coveralls/github/KurtWagner/zlinter/master?style=flat)](https://coveralls.io/github/KurtWagner/zlinter?branch=master)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat)](https://opensource.org/licenses/MIT)\n\nAn extendable and customizable **Zig linter** (with [AST explorer](https://kurtwagner.github.io/zlinter/explorer/)) that is integrated from source into your`build.zig`.\n\nA **linter** is a tool that automatically checks source code for style issues, bugs, or patterns that may lead to errors,\u003cbr/\u003e helping developers write cleaner and more reliable code.\n\n\u003cbr/\u003e\n\n![Screenshot](./screenshot.png)\n\u003c/div\u003e\n\n## Table of contents\n\n- [Getting Started](#getting-started)\n- [Autofix](#autofix)\n- [Custom Rules](#custom-rules)\n- [Built-in Rules](RULES.md)\n  - [declaration_naming](RULES.md#declaration_naming)\n  - [field_ordering](RULES.md#field_ordering)\n  - [field_naming](RULES.md#field_naming)\n  - [file_naming](RULES.md#file_naming)\n  - [function_naming](RULES.md#function_naming)\n  - [import_ordering](RULES.md#import_ordering)\n  - [max_positional_args](RULES.md#max_positional_args)\n  - [no_comment_out_code](RULES.md#no_comment_out_code)\n  - [no_deprecated](RULES.md#no_deprecated)\n  - [no_empty_block](RULES.md#no_empty_block)\n  - [no_hidden_allocations](RULES.md#no_hidden_allocations)\n  - [no_inferred_error_unions](RULES.md#no_inferred_error_unions)\n  - [no_literal_args](RULES.md#no_literal_args)\n  - [no_literal_only_bool_expression](RULES.md#no_literal_only_bool_expression)\n  - [no_orelse_unreachable](RULES.md#no_orelse_unreachable)\n  - [no_panic](RULES.md#no_panic)\n  - [no_swallow_error](RULES.md#no_swallow_error)\n  - [no_todo](RULES.md#no_todo)\n  - [no_undefined](RULES.md#no_undefined)\n  - [no_unused](RULES.md#no_unused)\n  - [require_braces](RULES.md#require_braces)\n  - [require_doc_comment](RULES.md#require_doc_comment)\n  - [require_errdefer_dealloc](RULES.md#require_errdefer_dealloc)\n  - [switch_case_ordering](RULES.md#switch_case_ordering)\n- [Configuration](#configuration)\n  - [Paths](#configure-paths)\n  - [Rules](#configure-rules)\n  - [Disable with Comments](#disable-with-comments)\n  - [Command-Line Arguments](#command-line-arguments)\n  - [Optimization](#configure-optimization)\n- [Supported zig versions](#supported-zig-versions)\n- [Milestones](#milestones)\n- [Versioning](#versioning)\n- [Contributing](#contributing)\n  - [How to Contribute](#contributions)\n  - [Run tests](#run-tests)\n  - [Run on self](#run-lint-on-self)\n\n## Getting started\n\n`zlinter` is not a standalone binary - it's built into your projects `build.zig`.\nThis makes it flexible to each projects needs. Simply add the dependency and\nhook it up to a build step, like `zig build lint`:\n\n**1. Save dependency to your zig project:**\n\n   For 0.14.x:\n\n   ```shell\n   zig fetch --save git+https://github.com/kurtwagner/zlinter#0.14.x\n   ```\n\n   For 0.15.x:\n\n   ```shell\n   zig fetch --save git+https://github.com/kurtwagner/zlinter#0.15.x\n   ```\n\n   For master (0.16.x-dev):\n\n   ```shell\n   zig fetch --save git+https://github.com/kurtwagner/zlinter#master\n   ```\n\n**2. Configure `lint` step in your `build.zig`:**\n\n  ```zig\n   const zlinter = @import(\"zlinter\");\n   // ...\n   const lint_cmd = b.step(\"lint\", \"Lint source code.\");\n   lint_cmd.dependOn(step: {\n       // Swap in and out whatever rules you see fit from RULES.md\n       var builder = zlinter.builder(b, .{});\n       builder.addRule(.{ .builtin = .field_naming }, .{});\n       builder.addRule(.{ .builtin = .declaration_naming }, .{});\n       builder.addRule(.{ .builtin = .function_naming }, .{});\n       builder.addRule(.{ .builtin = .file_naming }, .{});\n       builder.addRule(.{ .builtin = .switch_case_ordering }, .{});\n       builder.addRule(.{ .builtin = .no_unused }, .{});\n       builder.addRule(.{ .builtin = .no_deprecated }, .{});\n       builder.addRule(.{ .builtin = .no_orelse_unreachable }, .{});\n       break :step builder.build();\n   });\n   ```\n\n**3. Run linter:**\n\n  Keep in mind the first run will be slower as the cache isn't warmed:\n  \n  ```shell\n  zig build lint\n  ```\n\n  You can also be specific with paths (see [command-line arguments](#command-line-arguments) for more options):\n  \n  ```shell\n  zig build lint -- --include src/ file.zig\n  ```\n\n### Alternative: Enable all built in rules\n\nIf you just want to test out zlinter, you can also enable all rules and then\nselectively run rules from the command line. A lot of rules are quite pedantic\nso this is not recommended outside of testing zlinters rules for your project:\n\n1. Enable all built in rules in `build.zig`\n\n  ```zig\n  const zlinter = @import(\"zlinter\");\n  const lint_cmd = b.step(\"lint\", \"Lint source code.\");\n  lint_cmd.dependOn(step: {\n      var builder = zlinter.builder(b, .{});\n      inline for (@typeInfo(zlinter.BuiltinLintRule).@\"enum\".fields) |f| {\n          builder.addRule(.{ .builtin = @enumFromInt(f.value) }, .{});\n      }\n      break :step builder.build();\n  });\n  ```\n\n2. Selectively run rules:\n\n  ```shell\n  zig build lint -- --rule no_unused no_deprecated\n  ```\n\n## Autofix\n\nSome linter rules support auto fixing some problems.\n\n\u003e [!IMPORTANT]\n\u003e **Auto fixing** is an **experimental feature** so only use it if you use source control - **always back up your code first!**\n\nFor example, to auto fix unused declarations and field ordering issues, assuming your project has these rules configured:\n\n```shell\n# First ensure you're working branch is clean (or back up your code!)\n$ git status\n\n# Then run the fix command (you may need to run this multiple times)\n$ zig build lint -- --rule field_ordering --rule no_unused --fix\n```\n\nIt can sometimes require a multiple runs to completely resolve all fixable issues. i.e., run with `--fix` until it reports 0 fixes applied.\n\n## Custom rules\n\nBespoke rules can be added to your project. For example, maybe you really don't like cats, and refuse to let any `cats` exist in any identifier. See example rule [`no_cats`](./integration_tests/src/no_cats.zig), which is then integrated like builtin rules in your `build.zig`:\n\n```zig\nbuilder.addRule(b, .{ \n  .custom = .{\n    .name = \"no_cats\",\n    .path = \"src/no_cats.zig\",\n  },\n}, .{});\n```\n\nAlternatively, take a look at \u003chttps://github.com/KurtWagner/zlinter-custom-rule-example\u003e, which is a minimal custom rule example with accompanying zig project.\n\n## Configuration\n\n### Configure paths\n\nThe builder used in `build.zig` has a method `addPaths`, which can be used to\nadd included and excluded paths. For example,\n\n```zig\nbuilder.addPaths(.{\n    .include = \u0026.{ b.path(\"engine-src/\"), b.path(\"src/\") },\n    .exclude = \u0026.{ b.path(\"src/android/\"), b.path(\"engine-src/generated.zig\") },\n});\n```\n\nwould lint zig files under `engine-src/` and `src/` except for `engine-src/generated.zig` and any zig files under `src/android/`.\n\n### Configure Rules\n\n`addRule` accepts an anonymous struct representing the `Config` of rule being added. For example,\n\n```zig\nbuilder.addRule(.{ .builtin = .field_naming }, .{\n  .enum_field = .{ .style = .snake_case, .severity = .warning },\n  .union_field = .off,\n  .struct_field_that_is_type = .{ .style = .title_case, .severity = .@\"error\" },\n  .struct_field_that_is_fn = .{ .style = .camel_case, .severity = .@\"error\" },\n});\nbuilder.addRule(.{ .builtin = .no_deprecated }, .{\n  .severity = .warning,\n});\n```\n\nwhere `Config` struct are found in the rule source files [`no_deprecated.Config`](./src/rules/no_deprecated.zig) and [`field_naming.Config`](./src/rules/field_naming.zig).\n\n### Disable with comments\n\n#### Disable next line\n\nDisable all rules or an explicit set of rules for the next source code line.\n\nSyntax:\n\n```shell\nzlinter-disable-next-line [rule_1] [rule_n] [- comment]`\n```\n\nFor example,\n\n```zig\n// zlinter-disable-next-line no_deprecated - not updating so safe\nconst a = this.is.deprecated();\n```\n\n#### Disable current line\n\nDisable all rules or an explicit set of rules for the current source code line.\n\nSyntax:\n\n```shell\nzlinter-disable-current-line [rule_1] [rule_n] [- comment]\n```\n\nFor example,\n\n```zig\nconst a = this.is.deprecated(); // zlinter-disable-current-line\n```\n\n#### Disable multiple lines\n\nDisable all rules or an explicit set of rules for multiple source code lines.\n\nSyntax:\n\n```shell\nzlinter-disable [rule_1] [rule_n] [- comment]\nzlinter-enable [rule_1] [rule_n] [- comment]\n```\n\nFor example, to disable multiple lines for a given set of rules:\n\n```zig\n// zlinter-disable rule_a rule_b - rationale\nvar something = doSomethin();\nvar something_else = doSomethingElse();\n// zlinter-disable rule_a rule_b\n```\n\nFor example, to disable multiple lines for all rules:\n\n```zig\n// zlinter-disable - rationale\nvar something = doSomethin();\nvar something_else = doSomethingElse();\n// zlinter-disable\n```\n\nIf you omit `zlinter-enable`, all lines until EOF will be disabled.\n\n### Command-Line Arguments\n\n```shell\nzig build lint -- [--include \u003cpath\u003e ...] [--exclude \u003cpath\u003e ...] [--filter \u003cpath\u003e ...] [--rule \u003cname\u003e ...] [--fix] [--quiet] [--max-warnings \u003cu32\u003e]\n```\n\n- `--include` run the linter on these path ignoring the includes and excludes defined in the `build.zig` forcing these paths to be resolved and linted (if they exist).\n- `--exclude` exclude these paths from linting. This argument will be used in conjunction with the excludes defined in the `build.zig` unless used with `--include`.\n- `--filter` used to filter the run to a specific set of already resolved paths. Unlike `--include` this leaves the includes and excludes defined in the `build.zig` as is.\n- `--quiet` only report errors (not warnings).\n- `--max-warnings` fail if there are more than this number of warnings.\n- `--fix` used to automatically fix some issues (e.g., removal of unused container declarations) - **Only use this feature if you use source control as it can result loss of code!**\n\nFor example\n\n```shell\nzig build lint -- --include src/ android/ --exclude src/generated.zig --rule no_deprecated no_unused\n```\n\n- Will resolve all zig files under `src/` and `android/` but will exclude linting `src/generated.zig`; and\n- Only rules `no_deprecated` and `no_unused` will be ran.\n\n### Configure Optimization\n\n`zlinter.builder` accepts `.optimize` (defaults to `.ReleaseSafe`). For example,\n\n```zig\nvar builder = zlinter.builder(b, .{.optimize = .ReleaseFast });\n```\n\nIf your project is large it may be worth setting optimize to `.ReleaseFast`. Just keep in mind the first run may be slower as it builds the modules for the first time with the new optimisation.\n\nSince 0.16.x `.Debug` is significantly slower to run as it uses the debug allocator. Unless working on zlinter or a custom rule it should be avoided\n\n## Supported zig versions\n\nThe plan is to support `master` (mostly because its an important exercise in keeping up to date with whats changing in zig) and the latest previous version.\n\nCurrently, [`0.14.x`](https://github.com/KurtWagner/zlinter/tree/0.14.x), [`0.15.x`](https://github.com/KurtWagner/zlinter/tree/0.15.x) and [`master`](https://github.com/KurtWagner/zlinter/tree/master).\n\nFixes and improvements to rules may be cherry-picked to older versions if there's no API compatibility issues.\n\nThis may change once zig hits `1.x`.\n\n## Milestones\n\n### Background\n\n`zlinter` was written to be used across my personal projects. The main motivation was to have it integrated from source through a build step so that it can be\n\n1. customized at build time (e.g., byo rules); and\n2. versioned with your projects source control (no separate binary to juggle)\n\nI'm opening it up incase it's more generally useful, and happy to let it\norganically evolve around needs, if there's value in doing so.\n\nIt uses [`zls`](https://github.com/zigtools/zls) (an awesome project, go check it out if you haven't already) and `std.zig` to build and analyze zig source files.\n\n### Current limitations\n\n`zlinter` currently analyzes the Zig AST, which has limited context without trying to re-implement the Zig compiler (not doing).\n\nSee [limitations](./LIMITATIONS.md) for more information.\n\n---\n\n1. [done] **Rough implementaton of 20 diverse linter rules** - this is important to understanding limitations (e.g., the AST and design patterns to a stable API.)\n  \n1. [in-progress] **Run and review the results on at least 5 large open source Zig projects** - this is to discover unknown unknowns to populate caveats and limitations of current approach.\n\n1. [pending] **To be informed by (1) and (2)** - could be that AST is good enough for enough cases to provide value providing adequate documentation, AND/OR, could be that it's worth contributing time into Zigs efforts around \"multibuild\" and zig compiler server.\n\n## Versioning\n\n`zlinter` will:\n\n- follow the same semantic versioning as `zig`;\n- use branch `master` for `zig` `master` releases; and\n- use branch `0.14.x` for `zig` `0.14.x` releases.\n\nThis may change, especially when `zig` is \"stable\" at `1.x`. If you have opinions on this, feel free to comment on [#20](https://github.com/KurtWagner/zlinter/issues/20).\n\n## Contributing\n\n### Contributions\n\nContributions and new rules or formatters are very welcome.\n\nRules are per project configurable so I don't see any problems if new opinionated ones are added (assuming they're not completely bespoke).\n\nIf you notice breaking changes in `zig` that will not be picked up by a `Deprecated:` comment then consider contributing to the `no_deprecated.zig` rule, with a specific check for the change. For example, `zig` removed `usingnamespace` in `0.15` so `no_deprecated.zig` will explicitly check and report the usage of `usingnamespace` keyword in `0.14` runs.\n\n### Dependencies\n\nZlinter avoids dependencies. It's just too much of a burden right now to depend\non something written for Zig when Zig isn't 1.x.\n\nThe one exception is ZLS, as it's well maintained and doesn't appear to be\ngoing anywhere. More often than not I've wasted hours implementing a method to\nfind a very similar method already exists in ZLS, which makes sense, as ZLS\nanalyses Zig code using the AST like this linter currently does.\n\nThe AST Explorer provided with Zlinter will be similar and aims to be minimal.\nIdeally no build system, no dependencies, just plain JS and CSS targetting\nmodern browers as the target audience should all have access to such things.\n\n### Run tests\n\nUnit tests:\n\n```shell\nzig build unit-test\n```\n\nIntegration tests:\n\n```shell\nzig build integration-test\n```\n\nAll tests:\n\n```shell\nzig build test\n```\n\nTo focus on a single rule when running integration tests:\n\n```shell\nzig build integration-test -Dtest_focus_on_rule=require_braces\n```\n\n### Run lint on self\n\n```shell\nzig build lint\n```\n\n### Regenerate documentation\n\n```shell\nzig build docs\n```\n\n### Build and serve website (with AST explorer)\n\n```shell\nzig build website \u0026\u0026 npx http-server -c-1 zig-out/website\n```\n\nYou don't need to use `npx`, its just static content in `zig-out/website`. You may decide to use `python -m http.server` instead.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkurtwagner%2Fzlinter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkurtwagner%2Fzlinter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkurtwagner%2Fzlinter/lists"}