check is a tool for static analysis of code.
It is inspired by Go vet and similar tools for other programming languages, like Clang-Tidy.
Built as a plugin architecture, it is easy to extend check to handle additional rules.
It is designed to handle many different programming languages, allowing for reuse of plugins over multi-language codebases as long as the individual programming languages aren't too different.
This allows you to slim down on the additional tools you're running.
There are two major limitations that a check plugin has to contend with.
They are:
- A
checkplugin is passed a representation of the abstract syntax tree of code. It's not possible to build a plugin that needs more context, like an already run preprocessor or code generator, information about struct layouts, or similar. This means that several subgroups of static analysis tasks can't be implemented withcheck. - A
checkplugin gets every code file individually in an unspecified order. While a plugin can store information gathered from one file, it won't be able to reliably evaluate a holistic view of the entire codebase until the end of the run.
check follows a three-part plugin architecture.
- The library
commondoes the heavy lifting and provides types and functions for the other parts to use - The plugins export a
common.Pluginand report violations - The main executable
wrappercollects all plugins with a single method call into an executable, powered by thecommonlibrary
Additionally, the test package facilitates tests of the entire system by running the plugins against real code and ensuring
- all violations in the code files part of the testsuite are justified and therefore known
- all justifications are found
See Testing below for more.
check depends on go-tree-sitter which is a Go binding for Tree Sitter.
It uses C code, so you'll have to enable CGo and have a C compiler available on your system.
Have an executable with a single name available that invokes zig cc, for example (on Windows) zigcc.bat next to zig.exe, with content zig cc %*.
set CGO_ENABLED=1
set CC="zigcc"
go build .
Pass in the directory you want to analyze as a parameter.
There are multiple output format available:
- Use
-o jsonto output JSON format - Use
-o csvto output CSV format - By default the tool pretty-prints its results on the terminal
The check tool communicates status with exit codes:
- 2 means that an error happened during the run
- 1 means that there were violations found and at least one violation wasn't justified
- 0 means that no violations were found or all found violations were justified
You can justify violations with a comment directly in code. Put the justification comment directly above the offending line.
// JUSTIFY(unwanted-imports): it's okay this time, I swearHere unwanted-imports is the tag to look for.
It is name of the plugin, optionally followed by the error code the plugin produced, e.g. unwanted-imports/E001.
A single justification can handle multiple tags separated by commas.
The text after the colon is your comment on why this violation is okay.
The justification comment may only be one line long.
There are unit tests in the common library; they are handled like normal in Go.
All test files for the system tests go into test/data.
The test files are passed to all plugins and resulting violations are collected.
Every violation has to be justified, allowing for self-documenting test cases.
Justification messages have to be unique over all test cases.
Unjustified violations are reported as errors as well as superflous justifications.
All tests (unit tests in the common library and system tests in test) are run like this together:
go test ./common ./testTo include coverage, use this call:
go test -v -coverpkg=./... -coverprofile=cov.out ./common ./test && go tool cover -html=cov.out -o=cov.html