
# Zlinter - Linter for Zig
[](http://github.com/kurtwagner/what-the-zig)
[](https://github.com/KurtWagner/zlinter/actions/workflows/linux.yml)
[](https://github.com/KurtWagner/zlinter/actions/workflows/windows.yml)
[](https://coveralls.io/github/KurtWagner/zlinter?branch=master)
[](https://opensource.org/licenses/MIT)
An extendable and customizable **Zig linter** (with [AST explorer](https://kurtwagner.github.io/zlinter/explorer/)) that is integrated from source into your `build.zig`.
A **linter** is a tool that automatically checks source code for style issues, bugs, or patterns that may lead to errors,
helping developers write cleaner and more reliable code.

## Table of contents
- [Getting Started](#getting-started)
- [Autofix](#autofix)
- [Custom Rules](#custom-rules)
- [Custom Formatters](#custom-formatters)
- [Built-in Rules](RULES.md)
- [declaration_naming](RULES.md#declaration_naming)
- [field_ordering](RULES.md#field_ordering)
- [field_naming](RULES.md#field_naming)
- [file_naming](RULES.md#file_naming)
- [function_naming](RULES.md#function_naming)
- [import_ordering](RULES.md#import_ordering)
- [max_positional_args](RULES.md#max_positional_args)
- [no_comment_out_code](RULES.md#no_comment_out_code)
- [no_deprecated](RULES.md#no_deprecated)
- [no_empty_block](RULES.md#no_empty_block)
- [no_global_vars](RULES.md#no_global_vars)
- [no_hidden_allocations](RULES.md#no_hidden_allocations)
- [no_inferred_error_unions](RULES.md#no_inferred_error_unions)
- [no_literal_args](RULES.md#no_literal_args)
- [no_literal_only_bool_expression](RULES.md#no_literal_only_bool_expression)
- [no_orelse_unreachable](RULES.md#no_orelse_unreachable)
- [no_panic](RULES.md#no_panic)
- [no_redundant_comptime](RULES.md#no_redundant_comptime)
- [no_swallow_error](RULES.md#no_swallow_error)
- [no_todo](RULES.md#no_todo)
- [no_unsafe_undefined](RULES.md#no_unsafe_undefined)
- [no_unused](RULES.md#no_unused)
- [require_braces](RULES.md#require_braces)
- [require_exhaustive_enum_switch](RULES.md#require_exhaustive_enum_switch)
- [require_fmt](RULES.md#require_fmt)
- [require_labeled_continue](RULES.md#require_labeled_continue)
- [require_doc_comment](RULES.md#require_doc_comment)
- [require_errdefer_dealloc](RULES.md#require_errdefer_dealloc)
- [switch_case_ordering](RULES.md#switch_case_ordering)
- [Configuration](#configuration)
- [Paths](#configure-paths)
- [Rules in build.zig](#configure-rules-in-buildzig)
- [Rules by Directory](#configure-rules-by-directory)
- [Disable with Comments](#disable-with-comments)
- [Command-Line Arguments](#command-line-arguments)
- [Optimization](#configure-optimization)
- [Supported zig versions](#supported-zig-versions)
- [Background](#background)
- [Versioning](#versioning)
- [Contributing](CONTRIBUTING.md)
- [Release Notes](RELEASES.md)
## Getting started
`zlinter` is not a standalone binary - it's built into your project's `build.zig`.
This makes it flexible to each project's needs. Simply add the dependency and
hook it up to a build step, like `zig build lint`:
**1. Save dependency to your zig project:**
For 0.14.x:
```shell
zig fetch --save git+https://github.com/kurtwagner/zlinter#0.14.x
```
For 0.15.x:
```shell
zig fetch --save git+https://github.com/kurtwagner/zlinter#0.15.x
```
For 0.16.x:
```shell
zig fetch --save git+https://github.com/kurtwagner/zlinter#0.16.x
```
For master (0.17.x-dev):
```shell
zig fetch --save git+https://github.com/kurtwagner/zlinter#master
```
**2. Configure `lint` step in your `build.zig`:**
```zig
const zlinter = @import("zlinter");
// ...
const lint_cmd = b.step("lint", "Lint source code.");
lint_cmd.dependOn(step: {
// Swap in and out whatever rules you see fit from RULES.md
var builder = zlinter.builder(b, .{});
builder.addRule(.{ .builtin = .field_naming }, .{});
builder.addRule(.{ .builtin = .declaration_naming }, .{});
builder.addRule(.{ .builtin = .function_naming }, .{});
builder.addRule(.{ .builtin = .file_naming }, .{});
builder.addRule(.{ .builtin = .switch_case_ordering }, .{});
builder.addRule(.{ .builtin = .no_unused }, .{});
builder.addRule(.{ .builtin = .no_deprecated }, .{});
builder.addRule(.{ .builtin = .no_orelse_unreachable }, .{});
break :step builder.build();
});
```
**3. Run linter:**
Keep in mind the first run will be slower as the cache isn't warmed:
```shell
zig build lint
```
You can also be specific with paths (see [command-line arguments](#command-line-arguments) for more options):
```shell
zig build lint -- --include src/ file.zig
```
### Alternative: Enable all built in rules
If you just want to test out zlinter, you can also enable all rules and then
selectively run rules from the command line. A lot of rules are quite pedantic
so this is not recommended outside of testing zlinters rules for your project:
1. Enable all built in rules in `build.zig`
```zig
const zlinter = @import("zlinter");
const lint_cmd = b.step("lint", "Lint source code.");
lint_cmd.dependOn(step: {
var builder = zlinter.builder(b, .{});
inline for (@typeInfo(zlinter.BuiltinLintRule).@"enum".field_values) |field_value| {
builder.addRule(.{ .builtin = @enumFromInt(field_value) }, .{});
}
break :step builder.build();
});
```
1. Selectively run rules:
```shell
zig build lint -- --rule no_unused no_deprecated
```
## Autofix
Some linter rules support auto fixing some problems.
> [!IMPORTANT]
> **Auto fixing** is an **experimental feature** so only use it if you use source control - **always back up your code first!**
For example, to auto fix unused declarations and field ordering issues, assuming your project has these rules configured:
```shell
# First ensure you're working branch is clean (or back up your code!)
$ git status
# Then run the fix command (you may need to run this multiple times)
$ zig build lint -- --rule field_ordering --rule no_unused --fix
```
It can sometimes require a multiple runs to completely resolve all fixable issues. i.e., run with `--fix` until it reports 0 fixes applied.
## Custom rules
Bespoke 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`:
```zig
builder.addRule(.{
.custom = .{
.name = "no_cats",
.path = "src/no_cats.zig",
},
}, .{});
```
Alternatively, take a look at