-
Notifications
You must be signed in to change notification settings - Fork 34
Check
./godelw check
runs a set of static analysis checks on the project Go files.
-
${GOPATH}/src/${PROJECT_PATH}
exists, is the working directory and is initialized as a Git repository and Go module - Project contains
godel
andgodelw
- Project contains
main.go
- Project contains
.gitignore
that ignores GoLand files - Project contains
echo/echo.go
When writing Go code, it can be useful to check code for errors and consistency issues using static code analysis.
The current echo program simply echoes the user's input exactly. We will extend the program to allow different types of
echoes to be generated. As a first step to doing this, we will define an Echoer
interface that defines an Echo
function and refactor the current echo functionality to be a simple echoer that implements this interface.
Run the following to update the program to perform this refactor:
➜ echo 'package echo
type Echoer interface {
Echo(in string) string
}' > echo/echoer.go
➜ echo 'package echo
func NewEchoer() Echoer {
return &simpleEchoer{}
}
type simpleEchoer struct{}
func (_ *simpleEchoer) Echo(in string) string {
return in
}' > echo/echo.go
➜ SRC='package main
import (
"fmt"
"os"
"strings"
"PROJECT_PATH/echo"
)
func main() {
echoer := echo.NewEchoer()
fmt.Println(echoer.Echo(strings.Join(os.Args[1:], " ")))
}' && SRC=${SRC//PROJECT_PATH/$PROJECT_PATH} && echo "$SRC" > main.go
These files are formatted correctly and form a fully functioning program. Run ./godelw check
to run static code checks
on the project:
➜ ./godelw check
[compiles] Running compiles...
[importalias] Running importalias...
[errcheck] Running errcheck...
[golint] Running golint...
[deadcode] Running deadcode...
[govet] Running govet...
[importalias] Finished importalias
[ineffassign] Running ineffassign...
[golint] echo/echo.go:9:1: receiver name should not be an underscore, omit the name if it is unused
[golint] Finished golint
[outparamcheck] Running outparamcheck...
[ineffassign] Finished ineffassign
[unconvert] Running unconvert...
[deadcode] Finished deadcode
[varcheck] Running varcheck...
[unconvert] Finished unconvert
[varcheck] Finished varcheck
[outparamcheck] Finished outparamcheck
[compiles] Finished compiles
[errcheck] Finished errcheck
[govet] Finished govet
Check(s) produced output: [golint]
The output indicates that there was an issue identified by the golint
check. Fix the issue by updating the receiver
name:
➜ echo 'package echo
func NewEchoer() Echoer {
return &simpleEchoer{}
}
type simpleEchoer struct{}
func (e *simpleEchoer) Echo(in string) string {
return in
}' > echo/echo.go
Run ./godelw check
again to verify that the issue has been resolved:
➜ ./godelw check
[govet] Running govet...
[compiles] Running compiles...
[deadcode] Running deadcode...
[importalias] Running importalias...
[golint] Running golint...
[errcheck] Running errcheck...
[importalias] Finished importalias
[ineffassign] Running ineffassign...
[ineffassign] Finished ineffassign
[outparamcheck] Running outparamcheck...
[golint] Finished golint
[unconvert] Running unconvert...
[govet] Finished govet
[deadcode] Finished deadcode
[varcheck] Running varcheck...
[unconvert] Finished unconvert
[varcheck] Finished varcheck
[compiles] Finished compiles
[errcheck] Finished errcheck
[outparamcheck] Finished outparamcheck
Commit the changes to the repository:
➜ git add main.go echo
➜ git commit -m "Add echoer interface"
[master b931784] Add echoer interface
3 files changed, 14 insertions(+), 2 deletions(-)
create mode 100644 echo/echoer.go
Refer to the "More" sections below for examples of configuring the checks in different ways.
-
${GOPATH}/src/${PROJECT_PATH}
exists, is the working directory and is initialized as a Git repository and Go module - Project contains
godel
andgodelw
- Project contains
main.go
- Project contains
.gitignore
that ignores GoLand files - Project contains
echo/echo.go
andecho/echoer.go
In some instances, it may be desirable to suppress certain issues flagged by checks. As an example, modify
echo/echoer.go
as follows:
➜ echo 'package echo
// Echoes the input.
type Echoer interface {
Echo(in string) string
}' > echo/echoer.go
Running ./godelw check
flags the following:
➜ ./godelw check
[compiles] Running compiles...
[importalias] Running importalias...
[deadcode] Running deadcode...
[golint] Running golint...
[errcheck] Running errcheck...
[govet] Running govet...
[importalias] Finished importalias
[ineffassign] Running ineffassign...
[ineffassign] Finished ineffassign
[golint] echo/echoer.go:3:1: comment on exported type Echoer should be of the form "Echoer ..." (with optional leading article)
[golint] Finished golint
[outparamcheck] Running outparamcheck...
[unconvert] Running unconvert...
[govet] Finished govet
[deadcode] Finished deadcode
[varcheck] Running varcheck...
[unconvert] Finished unconvert
[varcheck] Finished varcheck
[outparamcheck] Finished outparamcheck
[compiles] Finished compiles
[errcheck] Finished errcheck
Check(s) produced output: [golint]
Although this is a valid check performed by go lint
, not all projects conform exactly with the Go style for comments.
In some cases, it makes sense to disable specific checks like this. This can be done by updating the
godel/config/check.yml
file to configure the check
command to ignore all output from the golint
check that
contains comment on exported type \w should be of the form
in its message.
The default configuration for godel/config/check-plugin.yml
is as follows:
➜ cat godel/config/check-plugin.yml
checks:
golint:
filters:
- value: "should have comment or be unexported"
- value: "or a comment on this block"
Add the line - value: "comment on exported type [[:word:]]+ should be of the form"
to this configuration:
➜ echo 'checks:
golint:
filters:
- value: "should have comment or be unexported"
- value: "or a comment on this block"
- value: "comment on exported type [[:word:]]+ should be of the form"' > godel/config/check-plugin.yml
Re-run ./godelw check
with the updated configuration to verify that lines that match this output are no longer
reported:
➜ ./godelw check
[importalias] Running importalias...
[compiles] Running compiles...
[golint] Running golint...
[govet] Running govet...
[errcheck] Running errcheck...
[deadcode] Running deadcode...
[importalias] Finished importalias
[ineffassign] Running ineffassign...
[ineffassign] Finished ineffassign
[golint] Finished golint
[outparamcheck] Running outparamcheck...
[unconvert] Running unconvert...
[deadcode] Finished deadcode
[govet] Finished govet
[varcheck] Running varcheck...
[unconvert] Finished unconvert
[varcheck] Finished varcheck
[compiles] Finished compiles
[outparamcheck] Finished outparamcheck
[errcheck] Finished errcheck
Revert the local changes by running the following:
➜ git checkout -- echo godel
Filters have a type
and a value
. When type
is not specified (as in the examples above), it defaults to message
,
which means that the value is matched against the message of the output. Currently, message
is the only filter.
Checks have an optional exclude
field that can be used to specify names or paths of files that should be excluded from
the check.
For example, the following configuration will ignore all issues reported by errcheck
for main.go
:
checks:
errcheck:
exclude:
paths:
- main.go
Because the exclude type is path
, this configuration would ignore errcheck
issues in ./main.go
. However, issues in
other files named main.go
in the project (for example, ./subproject/main.go
) would still be reported. Setting the
exclude type to names
would change the behavior so that issues in all files named main.go
would be ignored. Both
names
and paths
can be specified in the same exclude
configuration.
The name match values use Go regular expressions to perform matches. For example, the following configuration ignores
all golint
issues reported for any files that have the extension .pb.go
:
checks:
golint:
exclude:
names:
- ".*.pb.go"
The checks
configuration also supports specifying exclude
as a top-level value that applies to all checks (rather
than just an individual one).
Checks can be disabled completely for the entire project by setting the skip
field to true
.
For example, the following configuration will disable the golint
check for the project:
➜ echo 'checks:
golint:
skip: true' > godel/config/check-plugin.yml
Run ./godelw check
with the updated configuration to verify that the golint
check is no longer run:
➜ ./godelw check
[compiles] Running compiles...
[importalias] Running importalias...
[ineffassign] Running ineffassign...
[errcheck] Running errcheck...
[govet] Running govet...
[deadcode] Running deadcode...
[ineffassign] Finished ineffassign
[importalias] Finished importalias
[outparamcheck] Running outparamcheck...
[unconvert] Running unconvert...
[deadcode] Finished deadcode
[varcheck] Running varcheck...
[govet] Finished govet
[unconvert] Finished unconvert
[varcheck] Finished varcheck
[outparamcheck] Finished outparamcheck
[errcheck] Finished errcheck
[compiles] Finished compiles
Revert the local changes by running the following:
➜ git checkout -- godel
Many checks offer customizable parameters for the checks. Such parameters are specified in the config
field of the
check's configuration. Refer to the documentation for the asset that provides the check for details on configuring a
check.
Individual checks can be run in isolation by specifying the name of the check as an argument to check
. This can be
useful when iterating on code in an attempt to fix an issue flagged by a specific check.
For example, the following runs only deadcode
and govet
:
➜ ./godelw check deadcode govet
[govet] Running govet...
[deadcode] Running deadcode...
[deadcode] Finished deadcode
[govet] Finished govet
Running an individual check using check
runs just that check, but it still runs it through the check
task, which
uses the logic of the plugin and the asset to determine the arguments passed to the underlying check. However, sometimes
we may want to run the underlying check directly -- possibly to run it on a specific file or package or to specify flags
that are not available through configuration.
The run-check
task can be used to do this. Running ./godelw run-check [check] [flags] [args]
calls the "underlying"
check directly. It is up to an asset to determine what this means, but most assets wrap a standalone check implemented
as its own CLI, and "running" the check means invoking the CLI. If the underlying check accepts flags, it is safest to
place a --
after the check so that all of the flags and arguments are passed directly to the underlying check.
For example, errcheck
can be invoked directly as follows:
➜ ./godelw run-check errcheck -- --help
Usage of /root/.godel/assets/com.palantir.godel-okgo-asset-errcheck-errcheck-asset-1.8.0:
-abspath
print absolute paths to files
-asserts
if true, check for ignored type assertion results
-blank
if true, check for errors assigned to blank identifier
-exclude string
Path to a file containing a list of functions to exclude from checking
-ignore value
[deprecated] comma-separated list of pairs of the form pkg:regex
the regex is used to ignore names within pkg.
-ignoregenerated
if true, checking of files with generated code is disabled
-ignorepkg string
comma-separated list of package paths to ignore
-ignoretests
if true, checking of _test.go files is disabled
-tags value
space-separated list of build tags to include
-verbose
produce more verbose logging
The help output printed here is that of errcheck
, and we could have supplied the errcheck flags and arguments directly
in place of --help
. Also note the use of --
to indicate that all of the flags/arguments should be passed to the
underlying command. In this instance, if --
was not used, the --help
flag would have shown the output for
run-check errcheck
instead:
➜ ./godelw run-check errcheck --help
Usage:
okgo run-check errcheck [flags]
Flags:
-h, --help help for errcheck
Global Flags:
--assets strings path(s) to the plugin asset(s)
--config string path to the plugin configuration file
--debug run in debug mode
--godel-config string path to the godel.yml configuration file
--project-dir string path to project directory
Checks can be run sequentially by running with the --parallel=false
flag:
➜ ./godelw check --parallel=false
Running compiles...
Finished compiles
Running deadcode...
Finished deadcode
Running errcheck...
Finished errcheck
Running golint...
Finished golint
Running govet...
Finished govet
Running importalias...
Finished importalias
Running ineffassign...
Finished ineffassign
Running outparamcheck...
Finished outparamcheck
Running unconvert...
Finished unconvert
Running varcheck...
Finished varcheck
gödel includes the following checks by default:
-
compiles
verifies that all of the Go code in the project compiles, including code in test files (which is not checked bygo build
) -
deadcode
finds unused code -
errcheck
ensures that returned errors are checked -
extimport
verifies that all non-standard library packages that are imported by the project are present in a vendor directory within the project -
govet
runsgo vet
-
importalias
ensures that, if an import path in the package is imported using an alias, then all imports in the project that assign an alias for that path use the same alias -
ineffassign
flags ineffectual assignment statements -
novendor
flags projects that exist in thevendor
directory but are not used by the project -
outparamcheck
checks that functions that are meant to take an output parameter defined as aninterface{}
are passed pointers to an object rather than a concrete object -
unconvert
flags unnecessary conversions -
varcheck
checks for unused global variables and constants
The checks that are available to the check
task are determined by the assets provided to the okgo
plugin. Checks can
be added or removed by modifying the asset configuration for the okgo
plugin.
For example, we can add the nobadfuncs check by adding the
godel-okgo-asset-nobadfuncs asset. Modify the
godel/config/godel.yml
file as follows:
➜ echo 'default-tasks:
resolvers:
- https://github.com/{{index GroupParts 1}}/{{index GroupParts 2}}/releases/download/v{{Version}}/{{Product}}-{{Version}}-{{OS}}-{{Arch}}.tgz
tasks:
com.palantir.okgo:check-plugin:
assets:
- locator:
id: "com.palantir.godel-okgo-asset-nobadfuncs:nobadfuncs-asset:1.5.0"
exclude:
names:
- "\\\\..+"
- "vendor"
paths:
- "godel"' > godel/config/godel.yml
This adds the asset, which makes it available as a check:
➜ ./godelw check nobadfuncs
Getting package from https://github.com/palantir/godel-okgo-asset-nobadfuncs/releases/download/v1.5.0/nobadfuncs-asset-1.5.0-linux-amd64.tgz...
221.18 KiB / 4.76 MiB [-->_____________________________________________________________] 4.54% ? p/s
1.65 MiB / 4.76 MiB [---------------------->__________________________________________] 34.69% ? p/s
3.70 MiB / 4.76 MiB [-------------------------------------------------->______________] 77.70% ? p/s
4.76 MiB / 4.76 MiB [---------------------------------------------------------] 100.00% 9.23 MiB p/s
Running nobadfuncs...
Finished nobadfuncs
Although the skip
configuration makes it easy to disable a check, it is also possible to remove the check entirely.
This can be done by updating the exclude-default-assets
configuration. For example, the following configuration
removes the novendor
check entirely:
➜ echo 'default-tasks:
resolvers:
- https://github.com/{{index GroupParts 1}}/{{index GroupParts 2}}/releases/download/v{{Version}}/{{Product}}-{{Version}}-{{OS}}-{{Arch}}.tgz
tasks:
com.palantir.okgo:check-plugin:
exclude-default-assets:
- "com.palantir.godel-okgo-asset-novendor:novendor-asset"
exclude:
names:
- "\\\\..+"
- "vendor"
paths:
- "godel"' > godel/config/godel.yml
Verify that novendor
is no longer present as a check:
➜ ./godelw check novendor
Error: provided checker type(s) [novendor] not valid: valid values are [compiles deadcode errcheck golint govet importalias ineffassign outparamcheck unconvert varcheck]
Revert the local changes by running the following:
➜ git checkout -- godel
- Home
-
Tutorial
- Add gödel to a project
- Add Git hooks to enforce formatting
- Generate IDE project for GoLand
- Format Go files
- Run static checks on code
- Run tests
- Build
- Run
- Dist
- Publish
- Build and push Docker images
- Generate license headers
- Go generate tasks
- Define excludes
- Write integration tests
- Sync a documentation directory with GitHub wiki
- Verify project
- Set up CI to run tasks
- Update gödel
- Update legacy gödel
- Other commands
- Conclusion
- Name
- Philosophy
- Architecture
- Plugins
- Configuration