WARNING: This functionality is experimental, so its API might change. Please do not rely on it for production use, but feel free to use it and file issues.
nogo
is a tool that analyzes the source code of Go programs. It runs
alongside the Go compiler in the Bazel Go rules and rejects programs that
contain disallowed coding patterns. In addition, nogo
may report
compiler-like errors.
nogo
is a powerful tool for preventing bugs and code anti-patterns early
in the development process. It may be used to run the same analyses as vet,
and you can write new analyses for your own code base.
Create a nogo target in a BUILD
file in your workspace. The deps
attribute of this target must contain labels all the analyzers targets that you
want to run.
load("@io_bazel_rules_go//go:def.bzl", "nogo")
nogo(
name = "my_nogo",
deps = [
# analyzer from the local repository
":importunsafe",
# analyzer from a remote repository
"@org_golang_x_tools//go/analysis/passes/printf:go_tool_library",
],
visibility = ["//visibility:public"], # must have public visibility
)
go_tool_library(
name = "importunsafe",
srcs = ["importunsafe.go"],
importpath = "importunsafe",
deps = ["@org_golang_x_tools//go/analysis:go_tool_library"],
visibility = ["//visibility:public"],
)
Pass a label for your nogo target to go_register_toolchains
in your
WORKSPACE
file.
load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
go_rules_dependencies()
go_register_toolchains(nogo="@//:my_nogo") # my_nogo is in the top-level BUILD file of this workspace
NOTE: You must include "@//"
prefix when referring to targets in the local
workspace.
The nogo rule will generate a program that executes all the supplied
analyzers at build-time. The generated nogo
program will run alongside the
compiler when building any Go target (e.g. go_library) within your workspace,
even if the target is imported from an external repository. However, nogo
will not run when targets from the current repository are imported into other
workspaces and built there.
nogo
analyzers are Go packages that declare a variable named Analyzer
of type Analyzer from package analysis. Each analyzer is invoked once per
Go package, and is provided the abstract syntax trees (ASTs) and type
information for that package, as well as relevant results of analyzers that have
already been run. For example:
// package importunsafe checks whether a Go package imports package unsafe.
package importunsafe
import (
"strconv"
"golang.org/x/tools/go/analysis"
)
var Analyzer = &analysis.Analyzer{
Name: "importunsafe",
Doc: "reports imports of package unsafe",
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
for _, f := range pass.Files {
for _, imp := range f.Imports {
path, err := strconv.Unquote(imp.Path.Value)
if err == nil && path == "unsafe" {
pass.Reportf(imp.Pos(), "package unsafe must not be imported")
}
}
}
return nil, nil
}
Any diagnostics reported by the analyzer will stop the build. Do not emit diagnostics unless they are severe enough to warrant stopping the build.
Each analyzer must be written as a go_tool_library rule and must import @org_golang_x_tools//go/analysis:go_tool_library, the go_tool_library version of the package analysis target.
For example:
load("@io_bazel_rules_go//go:def.bzl", "go_tool_library")
go_tool_library(
name = "importunsafe",
srcs = ["importunsafe.go"],
importpath = "importunsafe",
deps = ["@org_golang_x_tools//go/analysis:go_tool_library"],
visibility = ["//visibility:public"],
)
go_tool_library(
name = "unsafedom",
srcs = [
"check_dom.go",
"dom_utils.go",
],
importpath = "unsafedom",
deps = ["@org_golang_x_tools//go/analysis:go_tool_library"],
visibility = ["//visibility:public"],
)
NOTE: go_tool_library is a limited variant of go_library
which avoids
a circular dependency: go_library implicitly depends on nogo, which
depends on analyzer libraries, which must not depend on nogo.
go_tool_library does not have the same implicit dependency.
Pass labels for these targets to the deps
attribute of your nogo target,
as described in the Setup section.
By default, nogo
analyzers will emit diagnostics for all Go source files
built by Bazel. This behavior can be changed with a JSON configuration file.
The top-level JSON object in the file must be keyed by the name of the analyzer
being configured. These names must match the Analyzer.Name
of the registered
analysis package. The JSON object's values are themselves objects which may
contain the following key-value pairs:
Key | Type |
"description" |
string |
Description of this analyzer configuration. | |
"only_files" |
dictionary, string to string |
Specifies files that this analyzer will emit diagnostics for.
Its keys are regular expression strings matching Go file names, and its values are strings
containing a description of the entry.
If both only_files and exclude_files are empty, this analyzer will emit diagnostics for
all Go files built by Bazel. |
|
"exclude_files" |
dictionary, string to string |
Specifies files that this analyzer will not emit diagnostics for.
Its keys and values are strings that have the same semantics as those in only_files .
Keys in exclude_files override keys in only_files . If a .go file matches a key present
in both only_files and exclude_files , the analyzer will not emit diagnostics for that
file. |
The following configuration file configures the analyzers named importunsafe
and unsafedom
. Since the loopclosure
analyzer is not explicitly
configured, it will emit diagnostics for all Go files built by Bazel.
{
"importunsafe": {
"exclude_files": {
"src/foo\\.go": "manually verified that behavior is working-as-intended",
"src/bar\\.go": "see issue #1337"
}
},
"unsafedom": {
"only_files": {
"src/js/.*": ""
},
"exclude_files": {
"src/(third_party|vendor)/.*": "enforce DOM safety requirements only on first-party code"
}
}
}
This label referencing this configuration file must be provided as the
config
attribute value of the nogo
rule.
nogo(
name = "my_nogo",
deps = [
":importunsafe",
":unsafedom",
"@analyzers//:loopclosure",
],
config = "config.json",
visibility = ["//visibility:public"],
)
vet is a tool that examines Go source code and reports correctness issues not caught by Go compilers. It is included in the official Go distribution. Vet runs analyses built with the Go analysis framework. nogo uses the same framework, which means vet checks can be run with nogo.
You can choose to run a safe subset of vet checks alongside the Go compiler by
setting vet = True
in your nogo target. This will only run vet checks
that are believed to be 100% accurate (the same set run by go test
by
default).
nogo(
name = "my_nogo",
vet = True,
visibility = ["//visibility:public"],
)
Setting vet = True
is equivalent to adding the atomic
, bools
,
buildtag
, nilfunc
, and printf
analyzers from
@org_golang_x_tools//go/analysis/passes
to the deps
list of your
nogo
rule.
See the full list of available nogo checks:
bazel query 'kind(go_tool_library, @org_golang_x_tools//go/analysis/passes/...)'
This generates a program that that analyzes the source code of Go programs. It runs alongisde the Go compiler in the Bazel Go rules and rejects programs that contain disallowed coding patterns.
Name | Type | Default value |
name | string | mandatory value |
A unique name for this rule. | ||
deps | label_list | None |
List of Go libraries that will be linked to the generated nogo binary. These libraries must declare an To avoid bootstrapping problems, these libraries must be go_tool_library targets, and must import @org_golang_x_tools//go/analysis:go_tool_library, the go_tool_library version of the package analysis target. |
||
config | label | None |
JSON configuration file that configures one or more of the analyzers in deps . |
||
vet | bool | False |
If true, a safe subset of vet checks will be run by nogo (the same subset run by ``go test ``). |
nogo(
name = "my_nogo",
deps = [
":importunsafe",
":otheranalyzer",
"@analyzers//:unsafedom",
],
config = ":config.json",
vet = True,
visibility = ["//visibility:public"],
)
The
nogo
logo was derived from the Go gopher, which was designed by Renee French. (http://reneefrench.blogspot.com/) The design is licensed under the Creative Commons 3.0 Attributions license. Read this article for more details: http://blog.golang.org/gopher