Skip to content

Commit

Permalink
staticcheck: configuration for staticcheck, gosimple, stylecheck (
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez committed May 27, 2021
1 parent fb7c90d commit b916c93
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 15 deletions.
13 changes: 13 additions & 0 deletions .golangci.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ linters-settings:
gosimple:
# Select the Go version to target. The default is '1.13'.
go: "1.15"
# https://staticcheck.io/docs/options#checks
checks: [ "all" ]

govet:
# report about shadowed variables
Expand Down Expand Up @@ -514,10 +516,21 @@ linters-settings:
staticcheck:
# Select the Go version to target. The default is '1.13'.
go: "1.15"
# https://staticcheck.io/docs/options#checks
checks: [ "all" ]

stylecheck:
# Select the Go version to target. The default is '1.13'.
go: "1.15"
# https://staticcheck.io/docs/options#checks
checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ]
# https://staticcheck.io/docs/options#dot_import_whitelist
dot-import-whitelist:
- fmt
# https://staticcheck.io/docs/options#initialisms
initialisms: [ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS" ]
# https://staticcheck.io/docs/options#http_status_code_whitelist
http-status-code-whitelist: [ "200", "400", "404", "500" ]

tagliatelle:
# check the struck tag name case
Expand Down
9 changes: 9 additions & 0 deletions pkg/config/linters_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,15 @@ type RowsErrCheckSettings struct {

type StaticCheckSettings struct {
GoVersion string `mapstructure:"go"`

Checks []string `mapstructure:"checks"`
Initialisms []string `mapstructure:"initialisms"` // only for stylecheck
DotImportWhitelist []string `mapstructure:"dot-import-whitelist"` // only for stylecheck
HTTPStatusCodeWhitelist []string `mapstructure:"http-status-code-whitelist"` // only for stylecheck
}

func (s *StaticCheckSettings) HasConfiguration() bool {
return len(s.Initialisms) > 0 || len(s.HTTPStatusCodeWhitelist) > 0 || len(s.DotImportWhitelist) > 0 || len(s.Checks) > 0
}

type StructCheckSettings struct {
Expand Down
4 changes: 3 additions & 1 deletion pkg/golinters/gosimple.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
)

func NewGosimple(settings *config.StaticCheckSettings) *goanalysis.Linter {
analyzers := setupStaticCheckAnalyzers(simple.Analyzers, settings)
cfg := staticCheckConfig(settings)

analyzers := setupStaticCheckAnalyzers(simple.Analyzers, getGoVersion(settings), cfg.Checks)

return goanalysis.NewLinter(
"gosimple",
Expand Down
4 changes: 3 additions & 1 deletion pkg/golinters/staticcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
)

func NewStaticcheck(settings *config.StaticCheckSettings) *goanalysis.Linter {
analyzers := setupStaticCheckAnalyzers(staticcheck.Analyzers, settings)
cfg := staticCheckConfig(settings)

analyzers := setupStaticCheckAnalyzers(staticcheck.Analyzers, getGoVersion(settings), cfg.Checks)

return goanalysis.NewLinter(
"staticcheck",
Expand Down
156 changes: 145 additions & 11 deletions pkg/golinters/staticcheck_common.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,167 @@
package golinters

import (
"strings"
"unicode"

"golang.org/x/tools/go/analysis"
scconfig "honnef.co/go/tools/config"

"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/logutils"
)

var debugf = logutils.Debug("megacheck")

func setupStaticCheckAnalyzers(m map[string]*analysis.Analyzer, settings *config.StaticCheckSettings) []*analysis.Analyzer {
var ret []*analysis.Analyzer
for _, v := range m {
setAnalyzerGoVersion(v, settings)
ret = append(ret, v)
func getGoVersion(settings *config.StaticCheckSettings) string {
var goVersion string
if settings != nil {
goVersion = settings.GoVersion
}

if goVersion != "" {
return goVersion
}
return ret
}

func setAnalyzerGoVersion(a *analysis.Analyzer, settings *config.StaticCheckSettings) {
// TODO: uses "1.13" for backward compatibility, but in the future (v2) must be set by using build.Default.ReleaseTags like staticcheck.
goVersion := "1.13"
if settings != nil && settings.GoVersion != "" {
goVersion = settings.GoVersion
return "1.13"
}

func setupStaticCheckAnalyzers(src map[string]*analysis.Analyzer, goVersion string, checks []string) []*analysis.Analyzer {
var names []string
for name := range src {
names = append(names, name)
}

filter := filterAnalyzerNames(names, checks)

var ret []*analysis.Analyzer
for name, a := range src {
if filter[name] {
setAnalyzerGoVersion(a, goVersion)
ret = append(ret, a)
}
}

return ret
}

func setAnalyzerGoVersion(a *analysis.Analyzer, goVersion string) {
if v := a.Flags.Lookup("go"); v != nil {
if err := v.Value.Set(goVersion); err != nil {
debugf("Failed to set go version: %s", err)
}
}
}

func staticCheckConfig(settings *config.StaticCheckSettings) *scconfig.Config {
var cfg *scconfig.Config

if settings == nil || !settings.HasConfiguration() {
return &scconfig.Config{
Checks: []string{"*"}, // override for compatibility reason. Must drop in the next major version.
Initialisms: scconfig.DefaultConfig.Initialisms,
DotImportWhitelist: scconfig.DefaultConfig.DotImportWhitelist,
HTTPStatusCodeWhitelist: scconfig.DefaultConfig.HTTPStatusCodeWhitelist,
}
}

cfg = &scconfig.Config{
Checks: settings.Checks,
Initialisms: settings.Initialisms,
DotImportWhitelist: settings.DotImportWhitelist,
HTTPStatusCodeWhitelist: settings.HTTPStatusCodeWhitelist,
}

if len(cfg.Checks) == 0 {
cfg.Checks = append(cfg.Checks, "*") // override for compatibility reason. Must drop in the next major version.
}

if len(cfg.Initialisms) == 0 {
cfg.Initialisms = append(cfg.Initialisms, scconfig.DefaultConfig.Initialisms...)
}

if len(cfg.DotImportWhitelist) == 0 {
cfg.DotImportWhitelist = append(cfg.DotImportWhitelist, scconfig.DefaultConfig.DotImportWhitelist...)
}

if len(cfg.HTTPStatusCodeWhitelist) == 0 {
cfg.HTTPStatusCodeWhitelist = append(cfg.HTTPStatusCodeWhitelist, scconfig.DefaultConfig.HTTPStatusCodeWhitelist...)
}

cfg.Checks = normalizeList(cfg.Checks)
cfg.Initialisms = normalizeList(cfg.Initialisms)
cfg.DotImportWhitelist = normalizeList(cfg.DotImportWhitelist)
cfg.HTTPStatusCodeWhitelist = normalizeList(cfg.HTTPStatusCodeWhitelist)

return cfg
}

// https://github.com/dominikh/go-tools/blob/9bf17c0388a65710524ba04c2d821469e639fdc2/lintcmd/lint.go#L437-L477
// nolint // Keep the original source code.
func filterAnalyzerNames(analyzers []string, checks []string) map[string]bool {
allowedChecks := map[string]bool{}

for _, check := range checks {
b := true
if len(check) > 1 && check[0] == '-' {
b = false
check = check[1:]
}

if check == "*" || check == "all" {
// Match all
for _, c := range analyzers {
allowedChecks[c] = b
}
} else if strings.HasSuffix(check, "*") {
// Glob
prefix := check[:len(check)-1]
isCat := strings.IndexFunc(prefix, func(r rune) bool { return unicode.IsNumber(r) }) == -1

for _, a := range analyzers {
idx := strings.IndexFunc(a, func(r rune) bool { return unicode.IsNumber(r) })
if isCat {
// Glob is S*, which should match S1000 but not SA1000
cat := a[:idx]
if prefix == cat {
allowedChecks[a] = b
}
} else {
// Glob is S1*
if strings.HasPrefix(a, prefix) {
allowedChecks[a] = b
}
}
}
} else {
// Literal check name
allowedChecks[check] = b
}
}
return allowedChecks
}

// https://github.com/dominikh/go-tools/blob/9bf17c0388a65710524ba04c2d821469e639fdc2/config/config.go#L95-L116
func normalizeList(list []string) []string {
if len(list) > 1 {
nlist := make([]string, 0, len(list))
nlist = append(nlist, list[0])
for i, el := range list[1:] {
if el != list[i] {
nlist = append(nlist, el)
}
}
list = nlist
}

for _, el := range list {
if el == "inherit" {
// This should never happen, because the default config
// should not use "inherit"
panic(`unresolved "inherit"`)
}
}

return list
}
13 changes: 12 additions & 1 deletion pkg/golinters/stylecheck.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package golinters

import (
"golang.org/x/tools/go/analysis"
scconfig "honnef.co/go/tools/config"
"honnef.co/go/tools/stylecheck"

"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
)

func NewStylecheck(settings *config.StaticCheckSettings) *goanalysis.Linter {
analyzers := setupStaticCheckAnalyzers(stylecheck.Analyzers, settings)
cfg := staticCheckConfig(settings)

// `scconfig.Analyzer` is a singleton, then it's not possible to have more than one instance for all staticcheck "sub-linters".
// When we will merge the 4 "sub-linters", the problem will disappear: https://github.com/golangci/golangci-lint/issues/357
// Currently only stylecheck analyzer has a configuration in staticcheck.
scconfig.Analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
return cfg, nil
}

analyzers := setupStaticCheckAnalyzers(stylecheck.Analyzers, getGoVersion(settings), cfg.Checks)

return goanalysis.NewLinter(
"stylecheck",
Expand Down
2 changes: 1 addition & 1 deletion pkg/golinters/unused.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter {
},
}

setAnalyzerGoVersion(analyzer, settings)
setAnalyzerGoVersion(analyzer, getGoVersion(settings))

lnt := goanalysis.NewLinter(
name,
Expand Down

0 comments on commit b916c93

Please sign in to comment.