Skip to content

Commit f3f5dfc

Browse files
ernadojirfag
authored andcommitted
govet: implement analyzers config (#697)
1 parent 9161de5 commit f3f5dfc

File tree

8 files changed

+339
-9
lines changed

8 files changed

+339
-9
lines changed

.golangci.example.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ linters-settings:
9595
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
9696
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
9797
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
98+
99+
# enable or disable analyzers by name
100+
enable:
101+
- atomicalign
102+
enable-all: false
103+
disable:
104+
- shadow
105+
disable-all: false
98106
golint:
99107
# minimal confidence for issues, default is 0.8
100108
min-confidence: 0.8

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,14 @@ linters-settings:
677677
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
678678
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
679679
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
680+
681+
# enable or disable analyzers by name
682+
enable:
683+
- atomicalign
684+
enable-all: false
685+
disable:
686+
- shadow
687+
disable-all: false
680688
golint:
681689
# minimal confidence for issues, default is 0.8
682690
min-confidence: 0.8

pkg/config/config.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,24 @@ type LintersSettings struct {
183183
type GovetSettings struct {
184184
CheckShadowing bool `mapstructure:"check-shadowing"`
185185
Settings map[string]map[string]interface{}
186+
187+
Enable []string
188+
Disable []string
189+
EnableAll bool `mapstructure:"enable-all"`
190+
DisableAll bool `mapstructure:"disable-all"`
191+
}
192+
193+
func (cfg GovetSettings) Validate() error {
194+
if cfg.EnableAll && cfg.DisableAll {
195+
return errors.New("enable-all and disable-all can't be combined")
196+
}
197+
if cfg.EnableAll && len(cfg.Enable) != 0 {
198+
return errors.New("enable-all and enable can't be combined")
199+
}
200+
if cfg.DisableAll && len(cfg.Disable) != 0 {
201+
return errors.New("disable-all and disable can't be combined")
202+
}
203+
return nil
186204
}
187205

188206
type ErrcheckSettings struct {

pkg/config/reader.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ func (r *FileReader) validateConfig() error {
110110
return fmt.Errorf("error in exclude rule #%d: %v", i, err)
111111
}
112112
}
113-
113+
if err := c.LintersSettings.Govet.Validate(); err != nil {
114+
return fmt.Errorf("error in govet config: %v", err)
115+
}
114116
return nil
115117
}
116118

pkg/golinters/govet.go

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"golang.org/x/tools/go/analysis/passes/asmdecl"
1212
"golang.org/x/tools/go/analysis/passes/assign"
1313
"golang.org/x/tools/go/analysis/passes/atomic"
14+
"golang.org/x/tools/go/analysis/passes/atomicalign"
1415
"golang.org/x/tools/go/analysis/passes/bools"
1516
"golang.org/x/tools/go/analysis/passes/buildtag"
1617
"golang.org/x/tools/go/analysis/passes/cgocall"
@@ -33,9 +34,37 @@ import (
3334
"golang.org/x/tools/go/analysis/passes/unusedresult"
3435
)
3536

36-
func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter {
37-
analyzers := []*analysis.Analyzer{
38-
// the traditional vet suite:
37+
func getAllAnalyzers() []*analysis.Analyzer {
38+
return []*analysis.Analyzer{
39+
asmdecl.Analyzer,
40+
assign.Analyzer,
41+
atomic.Analyzer,
42+
atomicalign.Analyzer,
43+
bools.Analyzer,
44+
buildtag.Analyzer,
45+
cgocall.Analyzer,
46+
composite.Analyzer,
47+
copylock.Analyzer,
48+
errorsas.Analyzer,
49+
httpresponse.Analyzer,
50+
loopclosure.Analyzer,
51+
lostcancel.Analyzer,
52+
nilfunc.Analyzer,
53+
printf.Analyzer,
54+
shadow.Analyzer,
55+
shift.Analyzer,
56+
stdmethods.Analyzer,
57+
structtag.Analyzer,
58+
tests.Analyzer,
59+
unmarshal.Analyzer,
60+
unreachable.Analyzer,
61+
unsafeptr.Analyzer,
62+
unusedresult.Analyzer,
63+
}
64+
}
65+
66+
func getDefaultAnalyzers() []*analysis.Analyzer {
67+
return []*analysis.Analyzer{
3968
asmdecl.Analyzer,
4069
assign.Analyzer,
4170
atomic.Analyzer,
@@ -59,20 +88,64 @@ func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter {
5988
unsafeptr.Analyzer,
6089
unusedresult.Analyzer,
6190
}
91+
}
92+
93+
func isAnalyzerEnabled(name string, cfg *config.GovetSettings, defaultAnalyzers []*analysis.Analyzer) bool {
94+
if cfg.EnableAll {
95+
return true
96+
}
97+
// Raw for loops should be OK on small slice lengths.
98+
for _, n := range cfg.Enable {
99+
if n == name {
100+
return true
101+
}
102+
}
103+
for _, n := range cfg.Disable {
104+
if n == name {
105+
return false
106+
}
107+
}
108+
if cfg.DisableAll {
109+
return false
110+
}
111+
for _, a := range defaultAnalyzers {
112+
if a.Name == name {
113+
return true
114+
}
115+
}
116+
return false
117+
}
118+
119+
func analyzersFromConfig(cfg *config.GovetSettings) []*analysis.Analyzer {
120+
if cfg == nil {
121+
return getDefaultAnalyzers()
122+
}
123+
if cfg.CheckShadowing {
124+
// Keeping for backward compatibility.
125+
cfg.Enable = append(cfg.Enable, shadow.Analyzer.Name)
126+
}
127+
128+
var enabledAnalyzers []*analysis.Analyzer
129+
defaultAnalyzers := getDefaultAnalyzers()
130+
for _, a := range getAllAnalyzers() {
131+
if isAnalyzerEnabled(a.Name, cfg, defaultAnalyzers) {
132+
enabledAnalyzers = append(enabledAnalyzers, a)
133+
}
134+
}
135+
136+
return enabledAnalyzers
137+
}
62138

139+
func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter {
63140
var settings map[string]map[string]interface{}
64141
if cfg != nil {
65-
if cfg.CheckShadowing {
66-
analyzers = append(analyzers, shadow.Analyzer)
67-
}
68142
settings = cfg.Settings
69143
}
70-
71144
return goanalysis.NewLinter(
72145
"govet",
73146
"Vet examines Go source code and reports suspicious constructs, "+
74147
"such as Printf calls whose arguments do not align with the format string",
75-
analyzers,
148+
analyzersFromConfig(cfg),
76149
settings,
77150
)
78151
}

pkg/golinters/govet_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package golinters
2+
3+
import (
4+
"sort"
5+
"testing"
6+
7+
"github.com/golangci/golangci-lint/pkg/config"
8+
9+
"golang.org/x/tools/go/analysis"
10+
"golang.org/x/tools/go/analysis/passes/asmdecl"
11+
"golang.org/x/tools/go/analysis/passes/assign"
12+
"golang.org/x/tools/go/analysis/passes/atomic"
13+
"golang.org/x/tools/go/analysis/passes/bools"
14+
"golang.org/x/tools/go/analysis/passes/buildtag"
15+
"golang.org/x/tools/go/analysis/passes/cgocall"
16+
"golang.org/x/tools/go/analysis/passes/shadow"
17+
)
18+
19+
func TestGovet(t *testing.T) {
20+
// Checking that every default analyzer is in "all analyzers" list.
21+
allAnalyzers := getAllAnalyzers()
22+
checkList := append(getDefaultAnalyzers(),
23+
shadow.Analyzer, // special case, used in analyzersFromConfig
24+
)
25+
for _, defaultAnalyzer := range checkList {
26+
found := false
27+
for _, a := range allAnalyzers {
28+
if a.Name == defaultAnalyzer.Name {
29+
found = true
30+
break
31+
}
32+
}
33+
if !found {
34+
t.Errorf("%s is not in allAnalyzers", defaultAnalyzer.Name)
35+
}
36+
}
37+
}
38+
39+
type sortedAnalyzers []*analysis.Analyzer
40+
41+
func (p sortedAnalyzers) Len() int { return len(p) }
42+
func (p sortedAnalyzers) Less(i, j int) bool { return p[i].Name < p[j].Name }
43+
func (p sortedAnalyzers) Swap(i, j int) { p[i].Name, p[j].Name = p[j].Name, p[i].Name }
44+
45+
func TestGovetSorted(t *testing.T) {
46+
// Keeping analyzers sorted so their order match the import order.
47+
t.Run("All", func(t *testing.T) {
48+
if !sort.IsSorted(sortedAnalyzers(getAllAnalyzers())) {
49+
t.Error("please keep all analyzers list sorted by name")
50+
}
51+
})
52+
t.Run("Default", func(t *testing.T) {
53+
if !sort.IsSorted(sortedAnalyzers(getDefaultAnalyzers())) {
54+
t.Error("please keep default analyzers list sorted by name")
55+
}
56+
})
57+
}
58+
59+
func TestGovetAnalyzerIsEnabled(t *testing.T) {
60+
defaultAnalyzers := []*analysis.Analyzer{
61+
asmdecl.Analyzer,
62+
assign.Analyzer,
63+
atomic.Analyzer,
64+
bools.Analyzer,
65+
buildtag.Analyzer,
66+
cgocall.Analyzer,
67+
}
68+
for _, tc := range []struct {
69+
Enable []string
70+
Disable []string
71+
EnableAll bool
72+
DisableAll bool
73+
74+
Name string
75+
Enabled bool
76+
}{
77+
{Name: "assign", Enabled: true},
78+
{Name: "cgocall", Enabled: false, DisableAll: true},
79+
{Name: "errorsas", Enabled: false},
80+
{Name: "bools", Enabled: false, Disable: []string{"bools"}},
81+
{Name: "unsafeptr", Enabled: true, Enable: []string{"unsafeptr"}},
82+
{Name: "shift", Enabled: true, EnableAll: true},
83+
} {
84+
cfg := &config.GovetSettings{
85+
Enable: tc.Enable,
86+
Disable: tc.Disable,
87+
EnableAll: tc.EnableAll,
88+
DisableAll: tc.DisableAll,
89+
}
90+
if enabled := isAnalyzerEnabled(tc.Name, cfg, defaultAnalyzers); enabled != tc.Enabled {
91+
t.Errorf("%+v", tc)
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)